一、实验目的
- 加深对词法分析器的工作过程的理解;加强对词法分析方法的掌握;能够采用一种编程 语言实现简单的词法分析程序;能够使用自己编写的分析程序对简单的程序段进行词法分析。
二、实验内容
- 自定义一种程序设计语言,或者选择已有的一种高级语言,编制它的词法分析程序。词法分析程序的实现可以采用任何一种编程语言和编程工具。
- 从输入的源程序中,识别出各个具有独立意义的单词,即关键字、标识符、常数、运算符、界符。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示)
三、实验方法
1、实验使用Java语言为程序设计语言,使用文本读入输入的数据;
2、对单词的构词规则进行定义;
3、编写对每种类的单词进行识别的语句并进行单独测试;
4、绘制词法分析流程图;
5、将所有识别功能代码汇总,编写成一个整体,再进行数据测试。
四、实验步骤
1、确定单词构词规则:
关键字:main、int、char、string、bool、float、double、true、false、return、if、else、while、for、default、do、public、static、switch。关键字的内部编码为1;
标识符:①标识符由字母、数字、下划线(_)组成;②不能把C语言关键字作为标识符;③标识符对大小写敏感;④首字符只能是字母或下划线,不能是数字。标识符的内部编码为2。
常数:无符号整数值。常量的内部编码为3。
运算符:+、-、*、/、^、=、<=、>=、!=、 |、&、!、||、&&。运算符的内部编码为4;
界符:,、;、{、}、(、)。界符的内部编码为5。
2、绘制词法分析流程图
3、代码编写
import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import static java.lang.Character.*;
public class Analyzer {
//关键字、界符、运算符
private static String[] key;
private static char[] boundSign;
private static char[] operator;
private static ArrayList<String> resultSet;
//存放读入的字符
private char ch;
//当前读入的行
private String strToken;
public Analyzer(){
}
//初始化分析器
private void init() throws Exception {
//C语言常用关键字
key = new String[]{"main","int","char","string","bool","float","double","for","default","do",
"if","else","while","public","static","switch","true","false","return"};
//C语言常见的界限符
boundSign = new char[]{',', ';', '(', ')', '[', ']', '{', '}'};
//C语言常见的操作符
operator = new char[]{'+', '-', '*', '/', '!', '&', '^', '|', '=', '>', '<'};
resultSet = new ArrayList<String>();
readText();//读入文件中的字符串
}
//读入文件
private void readText() throws Exception{
File file = new File("D:\\Java\\lexical_analyzer\\text.txt");//读入文件
List<String> ss = Files.readAllLines(file.toPath());
for(String s : ss){
strToken = s;
String str = prepared(strToken);
start(str);
}
}
//判断是否是关键字
private boolean is_key(String s){
for(String s1 : key){
if(s.equals(s1)) return true;
}
return false;
}
//判断是否是界符
private boolean is_boundSign(char ch){
for(char c1 : boundSign){
if(ch == c1) return true;
}
return false;
}
//判断是否是运算符
private boolean is_operator(char ch){
for(char c1 : operator){
if(ch == c1) return true;
}
return false;
}
//判断是否为有效字符
private boolean is_effective(char ch){
if(isLetter(ch) || ch == '_'){
}else if(isDigit(ch)){//判断是否是数字
}else if(is_boundSign(ch)){//判断是否为界符
}else if(is_operator(ch)){//判断是否是操作符
}else {
return false;
}
return true;
}
//对输入的代码先进行预处理,删除多余空格和注释
private String prepared(String strToken) {
strToken = strToken.trim() + " ";
String str = " ";
//经过第一次扫描,把代码中间的多个空格变成一个
//i为当前指的单词开头,j是指的单词结尾的后一个空格
for(int i = 0, j = 0; j < strToken.length(); ){
while(strToken.charAt(j) != ' ' && j < strToken.length() - 1) j++;
str = str + strToken.substring(i, j + 1);
i = j;
while(strToken.charAt(i) == ' ' && i < strToken.length() - 1) i++;
j = i + 1;
}
//第二次扫描去除注释
for(int i = 0; i < str.length() - 1; i++){
if(str.charAt(i) == '/' && str.charAt(i + 1) == '/'){
str = str.substring(0, i);
break;
}
}
str = str.trim();
return str;
}
//主方法,功能逻辑入口,完成词法分析
private void start(String strToken){
if(!strToken.equals("")){
//存放结果
String result;
//最后留一个空格,单词识别
strToken += " ";
//记录读到的字符的位置
int resultIndex = 0;
//去掉无效字符
while (!is_effective(strToken.charAt(resultIndex)) && strToken.charAt(resultIndex) != ' ') resultIndex ++;
//记录无效字符
if(resultIndex != 0){
resultSet.add("( error , \"" + strToken.substring(0,resultIndex) + "\" )" );
}
//读入第一个有效字符
ch = strToken.charAt(resultIndex ++);
//将字符存放到结果字符串中
result = String.valueOf(ch);
while (resultIndex <= strToken.length()){
//判断是否为字母或下划线
if(isLetter(ch) || ch == '_'){
if(is_key(result.trim())){
resultSet.add("( 1 , \"" + result.trim() + "\" )");
result = " ";
}
//继续读入字母、数字、下划线,可以组成标识符或关键字
while(isLetter(strToken.charAt(resultIndex)) || strToken.charAt(resultIndex) == '_' || isDigit(strToken.charAt(resultIndex))){
result += strToken.charAt(resultIndex ++);
}
//判断将这个单词全部读入后是否是关键字,如果不是关键字的话,那只可能是标识符
if(is_key(result.trim())){
resultSet.add("( 1 , \"" + result.trim() + "\" )");
result = " ";
}else{
resultSet.add("( 2 , \"" + result.trim() + "\" )");
result = " ";
}
//标识符或关键字后面可能不加空格跟着界符或者运算符
if(is_boundSign(strToken.charAt(resultIndex))){//跟着界符
resultSet.add("( 5 , \"" + strToken.charAt(resultIndex ++) + "\" )");
}else if(is_operator(strToken.charAt(resultIndex))){//运算符
result += strToken.charAt(resultIndex ++);
while (is_operator(strToken.charAt(resultIndex))){
result += strToken.charAt(resultIndex ++);
}
resultSet.add("( 4 , \"" + result.trim() + "\" )");
result = " ";
}
}else if(isDigit(ch)){//判断是否是数字
//如果还是数字就继续加入
while (isDigit(strToken.charAt(resultIndex))){
result += strToken.charAt(resultIndex ++);
}
//加入结果集
resultSet.add("( 3 , \"" + result.trim() + "\" )");
result = " ";
}else if(is_boundSign(ch)){//判断是否为界符
resultSet.add("( 5 , \"" + ch + "\" )");
}else if(is_operator(ch)){//判断是否是运算符
while (is_operator(strToken.charAt(resultIndex))){
result += strToken.charAt(resultIndex ++);
}
resultSet.add("( 4 , \"" + result.trim() + "\" )");
result = " ";
}
if(resultIndex >= strToken.length() - 1){//判断是否已经到达边界
break;
}else {
while (strToken.charAt(resultIndex) == ' ') resultIndex ++;//空格跳过
ch = strToken.charAt(resultIndex ++);//读入下一个字符
if(is_effective(ch)){//判断是否为有效字符
result = result + ch;
}else {
int resultIndexL = resultIndex - 1;
while (!is_effective(strToken.charAt(resultIndex))) resultIndex ++;
resultSet.add("( error ,\"" + strToken.substring(resultIndexL,resultIndex) + "\" )" );
ch = strToken.charAt(resultIndex ++);
}
}
}
}
}
public static void main(String[] args) throws Exception {
Analyzer analyzer = new Analyzer();
//初始化
analyzer.init();
//打印结果集
for (String tmp : resultSet){
System.out.println(tmp);
}
}
}
五、实验结果
测试数据如下图所示
输出结果
输出结果符合实验预期,代码测试成功。