发表时间:2010-04-18
最后修改:2010-04-18
词法设计理念
1. 注释处理: 在处理注释的问题上,我采用了正则表达式处理,在词法分析程序执行前,先讲注释匹配掉,将其换成一个空格。在处理空格上,为了避免对空格多重过滤,依然使用正则表达式将2个或者两个以上的空格匹配成一个空格。这样后在词法分析过程中,逻辑的处理变得更简单。
对注释的处理方法:
public static String omitComment(String str)
2. 词法分析的原理:依然使用的是每次获取一个字符,分析它是字母、数字、操作符、分隔符等以及之前设置状态。这样就可确定碰到这个字符后是什么状态。一共设置了3种状态。
3. 程序的结构说明:
l 程序一开始需要实例一个名为LexicalAnalysis类的对象。这个对象的 构造函数有两个
1. LexicalAnalysis(String sourcefilename)这个构造函数的参数为cmm源码的路径,这样在实例化这个类时就已经将源码加载在实例中。
2. LexicalAnalysis(String sourcefilename, String[] reservedwords)
第二个构造函数较第一个函数多了一个数组的参数,从字面上可以知道,这个参数为关键字的数组,这样以来,用户可以自定义自己关键字。然后构造属于自定义的词法分析程序。同时,如果用户没想自定义关键字就可以使用第一个构造函数并使用了默认的关键字。
在实例化词法分析对象时使用了工具类,对要分析的cmm源文件进行读取。读取后将其存在字符串中,便于后面的词法分析。
l 在实例化词法分析对象后,就可以调用分析方法parse() ,
l 在分析过程中,设置一个全局的变量
StringBuilder resultStringBuilder = new StringBuilder();
将其用来保存词法分析的结果。并将其输出。
4.错误的处理:
w 词法分析对数值的错误处理:数值不能以.结尾。
if (charArray[index - 1] == '.') {
resultStringBuilder.append(s + ":数值不能以\".\"结尾。\n");
w 词法分析对字符串的处理:标识符不能以下划线结尾。
if (charArray[index - 1] == '_') {
resultStringBuilder.append(s + ":标识符不能以\"_\"结尾。\n");
5.测试
测试数据
int i;
i=1;
/**
*Author:Hyvi
*
*lexical Analysis
*/
if(i==1){
write i;
}else{
i=0;
}
输出的结果如下
int:保留字。
t:标识符。
i:标识符。
;:分隔符。
i:标识符。
=:操作符。
1:整数。
;:分隔符。
if:保留字。
(:分隔符。
i:标识符。
==:操作符。
1:整数。
):分隔符。
{:分隔符。
write:保留字。
i:标识符。
;:分隔符。
}:分隔符。
else:保留字。
{:分隔符。
i:标识符。
=:操作符。
0:整数。
;:分隔符。
}:分隔符。
======================================
问题列表:
问题1
如上cmm源码在第一次运行时出项如下错误:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at exercise.LexicalAnalysis.parse(LexicalAnalysis.java:66)
at exercise.LexicalAnalysis.main(LexicalAnalysis.java:188)
解决方法:
根据提示问题错在:ch = charArray[index]; 字符串尾部的空格引起的。
修改前:
ch = charArray[index];
修改后
while (ch == ' ') {
sourcetemp1 = sourcetemp1.substring(1);
charArray = sourcetemp1.toCharArray();
if(sourcetemp1.length()!=0)ch = charArray[index];
else {
flag =true ;
state =4;
break;
}
}
if(flag){
break;
}
这样后在遇到文件结尾时,就跳switch以及while循环。
分析及评价:
词法分析采用有穷机的机制。根据不同的状态及读取的字符决定有穷机的走向。在本次试验中,对于注释的处理是采用了java中正则表达式方法处理,这是一个独到的处理。试验中使用java中对字符串的处理以及数组的操作来完成词法分析。当然一个程序不可能达到完美,总存在着不足的地方。这个词法分析程序也不例外。在对注释的处理里,依然存在这个不足,使用java中的正则表达式的处理的效率可能不及对字符的处理。
词法分析主类:
package exercise;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import exercise;
public class LexicalAnalysis {
private String[] reservedwords;
private String source;
private String result;
private static Character[] operators = new Character[] { '+', '-', '*',
'/', '=', '<', '<', '>' };
private static Character[] seperators = new Character[] { '(', ')', ';',
'{', '}', '[', ']' };
LexicalAnalysis(String sourcefilename) {
try {
this.source = BufferedInputFile.read(sourcefilename);
} catch (IOException e) {
this.source = "filenotexist";
// 在console里输出
System.err.println("读取代码文件失败,可能文件路径出错了");
}
this.reservedwords = new String[] { "if", "while", "else", "read",
"write", "int", "real" };
}
public LexicalAnalysis(String sourcefilename, String[] reservedwords) {
try {
this.source = BufferedInputFile.read(sourcefilename);
} catch (IOException e) {
this.source = "filenotexist";
// 在console里输出
System.err.println("读取代码文件失败,可能文件路径出错了");
}
this.reservedwords = reservedwords;
}
// once parse_begind
public String parse() {
if (this.source.equals("filenotexist")) {
return (this.result = this.source);
}
// drop comment and spaces
String sourcetemp1 = omitComment(this.source);
// string to charArray
char[] charArray = (sourcetemp1.trim()).toCharArray();
//System.out.println(sourcetemp1);
int state = 0;
int index = 0;
StringBuilder resultStringBuilder = new StringBuilder();
while (index <= charArray.length) {
switch (state) {
// 开始状态
case 0: {
index = 0;
boolean flag =false;
char ch = charArray[index];
//System.out.println(ch);
while (ch == ' ') {
sourcetemp1 = sourcetemp1.substring(1);
charArray = sourcetemp1.toCharArray();
if(sourcetemp1.length()!=0)ch = charArray[index];
else {
flag =true ;
state =4;
break;
}
}
if(flag){
break;
}
if (Character.isLetter(ch) || '_' == ch) {
state = 1;
} else if (Character.isDigit(ch)) {
state = 2;
} else if (contain(operators, (Character) ch)) {
state = 3;
} else if (contain(seperators, (Character) ch)) {
String s = charArrayToString(charArray, index+1);
resultStringBuilder.append(s + ":分隔符。\n");
sourcetemp1 = sourcetemp1.substring(index + 1);
charArray = sourcetemp1.toCharArray();
state = 0;
} else {
resultStringBuilder.append("error");
sourcetemp1 = sourcetemp1.substring(index + 1);
charArray = sourcetemp1.toCharArray();
state = 0;
}
break;
}
case 1: {
char ch = charArray[index];
if (Character.isLetter(ch) || '_' == ch
|| Character.isDigit(ch)) {
} else {
String s = charArrayToString(charArray, index);
if (contain(reservedwords, s)) {
resultStringBuilder.append(s + ":保留字。\n");
} else if (charArray[index - 1] == '_') {
resultStringBuilder.append(s + ":标识符不能以\"_\"结尾。\n");
} else {
resultStringBuilder.append(s + ":标识符。\n");
}
sourcetemp1 = sourcetemp1.substring(index);
charArray = sourcetemp1.toCharArray();
state = 0;
}
break;
}
case 2: {
char ch = charArray[index];
if (Character.isDigit(ch))
state = 2;
else if (ch == '.')
state = 2;
else {
String s = charArrayToString(charArray, index);
if (charArray[index - 1] == '.') {
resultStringBuilder.append(s + ":数值不能以\".\"结尾。\n");
}else if(s.contains(".")){
resultStringBuilder.append(s + ":实数。\n");
}
else {
resultStringBuilder.append(s + ":整数。\n");
}
sourcetemp1 = sourcetemp1.substring(index);
charArray = sourcetemp1.toCharArray();
state = 0;
}
break;
}
case 3: {
char ch = charArray[index];
if (ch == '>' || ch == '=') {
state = 3;
} else {
String s = charArrayToString(charArray, index);
resultStringBuilder.append(s + ":操作符。\n");
sourcetemp1 = sourcetemp1.substring(index);
charArray = sourcetemp1.toCharArray();
state = 0;
}
break;
}
default: {
resultStringBuilder.append("");
break;
}
}
index++;
}
if (resultStringBuilder.toString().contains("error"))
resultStringBuilder = new StringBuilder("error");
return this.result = resultStringBuilder.toString();
}
public static String charArrayToString(char[] array, int index) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < index; i++) {
sb.append(array[i]);
}
return sb.toString();
}
public static String omitComment(String str) {
String result = "";
Pattern pattern = Pattern
.compile("(/\\u002A(.*?)(\n(.*?))*\\u002A/|//.*\\n)");
Matcher matcher = pattern.matcher(str);
String temp = matcher.replaceAll(" ");
result = (pattern.compile("(\\n|\\t|\\r| |\f)+")).matcher(temp)
.replaceAll(" ");
return result;
}
public static <T> boolean contain(T[] array, T t) {
boolean flag = false;
for (T a : array) {
if (a.equals(t))
flag = true;
}
return flag;
}
public static void main(String[] args) {
LexicalAnalysis s = new LexicalAnalysis("C:/1.txt");
System.out.println(s.parse());
}
public String[] getReservedwords() {
return reservedwords;
}
public String getSource() {
return source;
}
public String getResult() {
return result;
}
public void setReservedwords(String[] reservedwords) {
this.reservedwords = reservedwords;
}
public void setSource(String source) {
this.source = source;
}
public void setResult(String result) {
this.result = result;
}
}
工具类:
package exercise;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedInputFile {
public static String read(String filename) throws IOException{
BufferedReader bufferedReader =new BufferedReader(new FileReader(filename));
StringBuilder builder =new StringBuilder();
String str;
while((str=bufferedReader.readLine())!=null){
builder.append(str+"\n");
}
bufferedReader.close();
return builder.toString();
}
}