一个简单词法分析器的实现代码(java实现)
http://www.cnblogs.com/xuqiang/archive/2010/09/21/1953501.html
Main.java
/* * 主程序 */import java.io.*;import lexer.*;public class Main { public static void main(String[] args) throws IOException { Lexer lexer = new Lexer(); while (lexer.getReaderState() == false) { lexer.scan(); } /* 保存相关信息 */ lexer.saveTokens(); lexer.saveSymbolsTable(); }}
Lexer.java
package lexer;import java.io.*;import java.util.*;import symbols.*;public class Lexer { public static int line = 1; /* 记录行号 */ char peek = ' '; /* 下一个读入字符 */ Hashtable<String, Word> words = new Hashtable<String, Word>(); /* 符号表 */ private Hashtable<Token, String> table = new Hashtable<Token, String>(); /* token序列 */ private List<String> tokens = new LinkedList<String> (); /* 读取文件变量 */ BufferedReader reader = null; /* 保存当前是否读取到了文件的结尾 */ private Boolean isEnd = false; /* 是否读取到文件的结尾 */ public Boolean getReaderState() { return this.isEnd; } /* 保存存储在table中的 */ public void saveSymbolsTable() throws IOException { FileWriter writer = new FileWriter("符号表.txt"); writer.write("[符号] [符号类型信息]\n"); writer.write("\r\n"); Enumeration<Token> e = table.keys(); while( e.hasMoreElements() ){ Token token = (Token)e.nextElement(); String desc = table.get(token); /* 写入文件 */ writer.write(token + "\t\t\t" + desc + "\r\n"); } writer.flush(); } /* 保存Tokens */ public void saveTokens() throws IOException { FileWriter writer = new FileWriter("Tokens表.txt"); writer.write("[符号] \n"); writer.write("\r\n"); for(int i = 0; i < tokens.size(); ++i) { String tok = (String)tokens.get(i); /* 写入文件 */ writer.write(tok + "\r\n"); } writer.flush(); } void reserve(Word w) { words.put(w.lexme, w); } /* * 构造函数中将关键字和类型添加到hashtable words中 */ public Lexer() { /* 初始化读取文件变量 */ try { reader = new BufferedReader(new FileReader("输入.txt")); } catch(IOException e) { System.out.print(e); } /* 关键字 */ this.reserve(new Word("if", Tag.IF)); this.reserve(new Word("then", Tag.THEN)); this.reserve(new Word("else", Tag.ELSE)); this.reserve(new Word("while", Tag.WHILE)); this.reserve(new Word("do", Tag.DO)); /* 类型 */ this.reserve(Word.True); this.reserve(Word.False); this.reserve(Type.Int); this.reserve(Type.Char); this.reserve(Type.Bool); this.reserve(Type.Float); } public void readch() throws IOException { /* 这里应该是使用的是 */ peek = (char)reader.read(); if((int)peek == 0xffff){ this.isEnd = true; } // peek = (char)System.in.read(); } public Boolean readch(char ch) throws IOException { readch(); if (this.peek != ch) { return false; } this.peek = ' '; return true; } public Token scan() throws IOException { /* 消除空白 */ for( ; ; readch() ) { if(peek == ' ' || peek == '\t') continue; else if (peek == '\n') line = line + 1; else break; } /* 下面开始分割关键字,标识符等信息 */ switch (peek) { /* 对于 ==, >=, <=, !=的区分使用状态机实现 */ case '=' : if (readch('=')) { tokens.add("=="); return Word.eq; } else { tokens.add("="); return new Token('='); } case '>' : if (readch('=')) { tokens.add(">="); return Word.ge; } else { tokens.add(">"); return new Token('>'); } case '<' : if (readch('=')) { tokens.add("<="); return Word.le; } else { tokens.add("<"); return new Token('<'); } case '!' : if (readch('=')) { tokens.add("!="); return Word.ne; } else { tokens.add("!"); return new Token('!'); } } /* 下面是对数字的识别,根据文法的规定的话,这里的 * 数字只要是能够识别整数就行. */ if(Character.isDigit(peek)) { int value = 0; do { value = 10 * value + Character.digit(peek, 10); readch(); } while (Character.isDigit(peek)); Num n = new Num(value); tokens.add(n.toString()); //table.put(n, "Num"); return n; } /* * 关键字或者是标识符的识别 */ if(Character.isLetter(peek)) { StringBuffer sb = new StringBuffer(); /* 首先得到整个的一个分割 */ do { sb.append(peek); readch(); } while (Character.isLetterOrDigit(peek)); /* 判断是关键字还是标识符 */ String s = sb.toString(); Word w = (Word)words.get(s); /* 如果是关键字或者是类型的话,w不应该是空的 */ if(w != null) { // table.put(w, "KeyWord or Type"); tokens.add(w.toString()); return w; /* 说明是关键字 或者是类型名 */ } /* 否则就是一个标识符id */ w = new Word(s, Tag.ID); tokens.add(w.toString()); table.put(w, "id"); words.put(s, w); return w; } /* peek中的任意字符都被认为是词法单元返回 */ Token tok = new Token(peek); // table.put(tok, "Token or Seprator"); if ((int)peek != 0xffff ) tokens.add(tok.toString()); peek = ' '; return tok; }}
Num.java
package lexer;public class Num extends Token{ public final int value; public Num(int v) { super(Tag.NUM); this.value = v; } public String toString() { return "" + value; }}
Tag.java
package lexer;public class Tag { public final static int AND = 256, BASIC = 257, BREAK = 258, DO = 259, ELSE = 260, EQ = 261, /* == */ FALSE = 262, GE = 263, ID = 264, IF = 265, INDEX = 266, LE = 267, MINUS = 268, NE = 269, NUM = 270, OR = 271, REAL = 272, TEMP = 273, TRUE = 274, WHILE = 275, /* 后面添加 */ THEN = 276;}
Token.java
package lexer;public class Token { public final int tag; public Token(int t) { this.tag = t; } public String toString() { return "" + (char)tag; } public static void main(String[] args) { Token tok = new Token('a'); System.out.println(tok); }}
Word.java
/* * 类word用于管理保留字,标识符以及像&&这样的复合单词元素 。 */package lexer;public class Word extends Token { public String lexme = ""; public Word (String s, int t) { super(t); this.lexme = s; } public String toString() { return this.lexme; } public static final Word and = new Word("&&", Tag.AND), or = new Word("||", Tag.OR), eq = new Word ("==", Tag.EQ), ne = new Word("!=", Tag.NE), le = new Word("<=", Tag.LE), ge = new Word(">=", Tag.GE), minus = new Word("minus", Tag.MINUS), True = new Word("true", Tag.TRUE), False = new Word("false", Tag.FALSE), temp = new Word("t", Tag.TEMP);}
Type.java
/* * 说明数据类型 */package symbols;import lexer.*;public class Type extends Word{ public Type(String s, int tag) { super(s, tag); } public static final Type Int = new Type("int", Tag.BASIC), Float = new Type("float", Tag.BASIC), Char = new Type ("char", Tag.BASIC), Bool = new Type("bool", Tag.BASIC); }
============
http://freewxy.iteye.com/blog/870016
什么是词法?
所谓词法,源代码由字符流组成,字符流中包括关键字,变量名,方法名,括号等等符号,其中变量名要满足不能包括标点符号,不能以数字开头的数字与字母的字符串这个条件,对于括号要成对出现等等,这就是词法;
什么是词法分析?
词法分析阶段是编译过程的第一个阶段。这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构词规则识别单词(也称单词符号或符号)。
待分析的简单语言的词法:
1) 关键字
begin if then while do end
2) 运算符和界符
:= + - * / < <= > >= <> = ; ( ) #
3) 其他单词是标识符(ID)和整形常数(NUM),通过以下正规式定义:
ID=letter(letter|digit)*
NUM=digitdigit*
4) 空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM、运算符、界符和关键字,词法分析阶段通常被忽略。
各种单词符号对应的种别编码
单词符号 |
种别码 |
单词符号 |
种别码 |
begin |
1 |
: |
17 |
if |
2 |
:= |
18 |
then |
3 |
< |
20 |
while |
4 |
<> |
21 |
do |
5 |
<= |
22 |
end |
6 |
> |
23 |
letter(letter|digit)* |
10 |
>= |
24 |
digitdigit* |
11 |
= |
25 |
+ |
13 |
; |
26 |
- |
14 |
( |
27 |
* |
15 |
) |
28 |
/ |
16 |
# |
0 |
词法分析程序的功能:
输入:所给文法的源程序字符串
输出:二元组(syn, token或sum)构成的序列。
syn为单词种别码;
token为存放的单词自身字符串;
sum为整形常数。
例如:对源程序begin x:=9;if x>0 then x:=2*x+1/3;end# 经词法分析后输出如下序列:(1,begin)(10,’x’) (18,:=) (11,9) (26,;) (2,if)……
流程图:
源码:
- public class 词法分析 {
- /* 初始化数据
- syn为单词种别码;
- token为存放的单词自身字符串;
- sum为整型常数。
- */
- static String prog;
- static char ch;
- static char[]token=new char[8];
- static int syn,p,m,n,sum;
- static //关键字表的初值
- String[] rwtable={"begin","if","then","while","do","end"};
- /**
- * @param args
- * @throws IOException
- */
- public static void main(String[] args) throws IOException {
- //1、输入字符串
- //prog="begin x:=9; if x>0 then x:=2*x+1/3;end #";
- //1、从文件中读取字符串
- prog=dofile.readFileByChars("src/data.txt");
- //2、扫描输出
- p=0;
- do{
- scaner();
- switch(syn){
- case 11:System.out.print("("+syn+" , ");//单词符号:Digit digit*
- System.out.print(sum);
- System.out.println(")");
- break;
- case -1:System.out.println("error!");
- break;
- default:
- System.out.print("(");
- System.out.print(syn);
- System.out.print(" , ");
- String str=new String(token);
- System.out.print(str);
- System.out.println(")");
- }
- }while(syn!=0);
- }
- //扫描程序
- private static void scaner() throws IOException {
- // 1、初始化
- for(int i=0;i<8;i++)
- token[i]=' ';
- // 2、读字母
- ch=prog.charAt(p++);
- while(ch==' '){//如果是空格,则取下一个字符
- ch=prog.charAt(p++);
- }
- // 3、开始执行扫描
- // 1、是字母
- // 读标识符,查保留字表
- // 查到,换成属性字表,写到输出流
- // 没查到, 查名表,换成属性字,写到输出流
- if(ch>='a'&&ch<='z'){
- m=0;
- //获取完整单词
- while((ch>='a'&&ch<='z')||(ch>='0'&&ch<='9')){
- token[m++]=ch;
- ch=prog.charAt(p++);
- }
- token[m++]='\0';
- --p;
- syn=10;//单词符号为letter(letter|digit)*
- //判断是哪个关键字
- String newStr=new String(token);
- newStr=newStr.trim();
- //System.out.println("newStr:"+newStr);
- for(n=0;n<6;n++){
- //System.out.println("rwtable:"+rwtable[n]);
- if(newStr.equals(rwtable[n])){
- syn=n+1;
- System.out.println("syn 的值是:"+syn);
- break;
- }
- }
- token[m++]='\0';
- }
- // 2、是数字
- // 取数字,查常量表,换成属性字表,写到输出流
- else if(ch>='0'&&ch<='9'){
- while(ch>='0'&&ch<='9'){
- sum=sum*10+ch-'0';
- ch=prog.charAt(p++);
- }
- --p;
- syn=11;//digitdigit*
- token[m++]='\0';
- }
- // 3、是特殊符号
- // 查特殊符号表,换成属性字。写到输出流
- // 4、错误error
- // 4、是否分析结束
- // 未结束,到2
- // 结束,到出口
- else
- switch(ch){
- case'<':
- m=0;
- token[m++]=ch;
- ch=prog.charAt(p++);
- if(ch=='>'){
- syn=21;//<>
- }
- else if(ch=='='){
- syn=22;//<=
- token[m++]=ch;
- }
- else{
- syn=20;//<
- --p;
- }
- break;
- case'>':
- token[m++]=ch;
- ch=prog.charAt(p++);
- if(ch=='='){
- syn=24;//>=
- }
- else{
- syn=23;//>
- --p;
- }
- break;
- case':':
- token[m++]=ch;
- ch=prog.charAt(p++);
- if(ch=='='){
- syn=18;//:=
- token[m++]=ch;
- }
- else{
- syn=17;//:
- --p;
- }
- break;
- case'+':
- syn=13;token[0]=ch;token[1]='\0';break;
- case'-':
- syn=14;token[0]=ch;token[1]='\0';break;
- case'*':
- syn=15;token[0]=ch;token[1]='\0';break;
- case'/':
- syn=16;token[0]=ch;token[1]='\0';break;
- case'=':
- syn=25;token[0]=ch;token[1]='\0';break;
- case';':
- syn=26;token[0]=ch;token[1]='\0';break;
- case'(':
- syn=27;token[0]=ch;token[1]='\0';break;
- case')':
- syn=28;token[0]=ch;token[1]='\0';break;
- case'#':
- syn=0;token[0]=ch;token[1]='\0';break;
- default:
- syn=-1;
- }
- File txt=new File("src/nihao.txt");
- if(!txt.exists()){
- txt.createNewFile();
- }
- byte[] bytes=new byte[token.length];//定义一个长度与需要转换的char数组相同的byte数组
- for(int i=0;i<bytes.length ;i++){//循环将char的每个元素转换并存放在上面定义的byte数组中
- byte b=(byte)token[i];//将每个char转换成byte
- bytes[i]=b;//保存到数组中
- }
- FileOutputStream fos;
- try {
- fos = new FileOutputStream(txt,true);
- fos.write(syn);
- fos.write(bytes);
- fos.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
public class 词法分析 { /* 初始化数据 syn为单词种别码; token为存放的单词自身字符串; sum为整型常数。 */ static String prog; static char ch; static char[]token=new char[8]; static int syn,p,m,n,sum; static //关键字表的初值 String[] rwtable={"begin","if","then","while","do","end"}; /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { //1、输入字符串 //prog="begin x:=9; if x>0 then x:=2*x+1/3;end #"; //1、从文件中读取字符串 prog=dofile.readFileByChars("src/data.txt"); //2、扫描输出 p=0; do{ scaner(); switch(syn){ case 11:System.out.print("("+syn+" , ");//单词符号:Digit digit* System.out.print(sum); System.out.println(")"); break; case -1:System.out.println("error!"); break; default: System.out.print("("); System.out.print(syn); System.out.print(" , "); String str=new String(token); System.out.print(str); System.out.println(")"); } }while(syn!=0); } //扫描程序 private static void scaner() throws IOException { // 1、初始化 for(int i=0;i<8;i++) token[i]=' ';// 2、读字母 ch=prog.charAt(p++); while(ch==' '){//如果是空格,则取下一个字符 ch=prog.charAt(p++); }// 3、开始执行扫描// 1、是字母// 读标识符,查保留字表// 查到,换成属性字表,写到输出流// 没查到, 查名表,换成属性字,写到输出流 if(ch>='a'&&ch<='z'){ m=0; //获取完整单词 while((ch>='a'&&ch<='z')||(ch>='0'&&ch<='9')){ token[m++]=ch; ch=prog.charAt(p++); } token[m++]='\0'; --p; syn=10;//单词符号为letter(letter|digit)* //判断是哪个关键字 String newStr=new String(token); newStr=newStr.trim(); //System.out.println("newStr:"+newStr); for(n=0;n<6;n++){ //System.out.println("rwtable:"+rwtable[n]); if(newStr.equals(rwtable[n])){ syn=n+1; System.out.println("syn 的值是:"+syn); break; } } token[m++]='\0'; }// 2、是数字// 取数字,查常量表,换成属性字表,写到输出流 else if(ch>='0'&&ch<='9'){ while(ch>='0'&&ch<='9'){ sum=sum*10+ch-'0'; ch=prog.charAt(p++); } --p; syn=11;//digitdigit* token[m++]='\0'; }// 3、是特殊符号// 查特殊符号表,换成属性字。写到输出流// 4、错误error// 4、是否分析结束// 未结束,到2// 结束,到出口 else switch(ch){ case'<': m=0; token[m++]=ch; ch=prog.charAt(p++); if(ch=='>'){ syn=21;//<> } else if(ch=='='){ syn=22;//<= token[m++]=ch; } else{ syn=20;//< --p; } break; case'>': token[m++]=ch; ch=prog.charAt(p++); if(ch=='='){ syn=24;//>= } else{ syn=23;//> --p; } break; case':': token[m++]=ch; ch=prog.charAt(p++); if(ch=='='){ syn=18;//:= token[m++]=ch; } else{ syn=17;//: --p; } break; case'+': syn=13;token[0]=ch;token[1]='\0';break; case'-': syn=14;token[0]=ch;token[1]='\0';break; case'*': syn=15;token[0]=ch;token[1]='\0';break; case'/': syn=16;token[0]=ch;token[1]='\0';break; case'=': syn=25;token[0]=ch;token[1]='\0';break; case';': syn=26;token[0]=ch;token[1]='\0';break; case'(': syn=27;token[0]=ch;token[1]='\0';break; case')': syn=28;token[0]=ch;token[1]='\0';break; case'#': syn=0;token[0]=ch;token[1]='\0';break; default: syn=-1; } File txt=new File("src/nihao.txt"); if(!txt.exists()){ txt.createNewFile(); } byte[] bytes=new byte[token.length];//定义一个长度与需要转换的char数组相同的byte数组 for(int i=0;i<bytes.length ;i++){//循环将char的每个元素转换并存放在上面定义的byte数组中 byte b=(byte)token[i];//将每个char转换成byte bytes[i]=b;//保存到数组中 } FileOutputStream fos; try { fos = new FileOutputStream(txt,true); fos.write(syn); fos.write(bytes); fos.close(); } catch (Exception e) { e.printStackTrace(); } }}
文件data.txt中的内容为:
begin x:=9; if x>0 then x:=2*x+1/3;end #
程序执行结果(控制台输出):
打开文件 src/data.txt 读取内容为:
beginx:=9;ifx>0thenx:=2*x+1/3;end#
syn 的值是:1
(1 , begin)
(10,x)
(18,:=)
(11,9)
(26,;)
syn的值是:2
(2,if)
(10,x)
(23,>)
(11,90)
syn的值是:3
(3,then)
(10,x)
(13,+)
(11,902)
(15,*)
(10,x)
(13,+)
(11,9021)
(16,)
(11,90213)
(26,;)
syn的值是:6
(6,end)
(0,#)
<!--EndFragment-->
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.****.net/jiangjunshow