用Java写java的编译器和jvm
为什么用java,java的结构最便于理解,其丰富的设计模式能使编译器的结构十分鲜明
一个编译器的前端模型
源代码–词法分析器-(词法单元)-语法分析器-(语法分析树)-中间代码生成器–三地址代码
再加上一个符号表连接所有的结构
文法定义
一个上下文无关方法由四个元素组成
1.一个终结符号集合,也就是“词法单元”终结符号是该文法定义的语言的基本符号的集合
2.一个非终结符号集合,“语法变量”每个非终结符号表示一个终结符号串的集合
3.一个产生式集合,其中每个产生式包括 一个称为产生式左头或左部的非终结符号一个箭头,和一个称为产生式体或右部的由终结符号及非终结符号组成的序列。
4.指定一个非终结符号的开始符号
词法单元由两部分组成:名字和属性值。
语法分析树
1.根结点的标号为文法的开始符号
2.每个叶子结点的标号为一个终结符号或ε
3.每个内部结点的标号为一个非终结符号
4.如果非终结符号A是某个内部结点的符号,并且它的子结点的标号从左至右分别为X1,x2,x3..,那么必然存在产生式A→X1,X2..Xn,其中X1,X2,..Xn既可以是终结符号,也可以是非终结符号,作为一种特殊情况 ,如果A→ε是一个产生式,那么一个标号为A的结点可以只有 一个标号为ε的子结点。
那么基本概念就说完了,直接上代码,因为代码比较直观
词法分析
先说一个Code类,为什么有它,我是为了更好的进行测试。同时以后也可以直接把代码赋值给它,只要可以做到一个一个的读入字符就可以,同学们可以另想办法。
public class Code {
public static String content="int a=9;";
private static int index=0;
public static char read(){
return content.charAt(index++);
}
}
剔除空白和注释
for (;; peek = (char) Code.read()) {
if (peek == ‘ ‘ || peek == ‘\t‘)
continue;
else if (peek == ‘\n‘)
line++;
else
break;
}
预读
例如,我们需要在1后面预读一个字符来区分1和10,t预读一个来区分 t和true,当然像*这种是不需要预读的
常量
if (Character.isDigit(peek)) {
int v = 0;
do {
v = 10 * v + Character.digit(peek, 10);
peek = (char) Code.read();
} while (Character.isDigit(peek));
return new Num(v);
}
我们在输入 1+2+3时就会转成序列
<num,1><+><num,2><+><num,3>
+就是终结符号,没有属性,所以它的元组就是<+>
识别关键字和标识符
for do if
如果 输入
a=a+b
终结符号 就是id=id+id
<id,"a"><+><id,"a"><+><id,"b">
关键字是不能用于标识符的,所以我们要识别关键字和标识符
这就用到了一个符号表,我们用一个hashtable来存储关键字
private Hashtable words = new Hashtable();
@SuppressWarnings({ "unchecked" })
private void reserve(Word t) {
words.put(t.lexeme, t);
}
public Lexer() {
reserve(new Word(Tag.TRUE, "true"));
reserve(new Word(Tag.FALSE, "false"));
}
if (Character.isLetter(peek)) {
StringBuffer b = new StringBuffer();
do {
b.append(peek);
peek = (char) Code.read();
} while (Character.isLetterOrDigit(peek));
String s = b.toString();
Word w = (Word) words.get(s);
if (w != null)
return w;
w = new Word(Tag.ID, s);
words.put(s, w);
return w;
}
词法分析器
package com.bigbear.lexer;
public class Tag {
public final static int NUM=256,ID=257,TRUE=258,FALSE=259;
}
package com.bigbear.lexer;
public class Token {
public final int tag;
public Token(int tag) {
this.tag = tag;
}
}
package com.bigbear.lexer;
public class Num extends Token {
public final int value;
public Num(int v) {
// TODO Auto-generated constructor stub
super(Tag.NUM);
this.value=v;
}
}
package com.bigbear.lexer;
public class Word extends Token {
public final String lexeme;
public Word(int t,String s) {
super(t);
this.lexeme=new String(s);
}
}
package com.bigbear.lexer;
import java.io.IOException;
import java.util.Hashtable;
import com.bigbear.main.Code;
/**
* @author winney 词法分析器
*
*/
public class Lexer {
public int line = 1;
private char peek = ‘ ‘;
@SuppressWarnings("rawtypes")
private Hashtable words = new Hashtable();
@SuppressWarnings({ "unchecked" })
private void reserve(Word t) {
words.put(t.lexeme, t);
}
public Lexer() {
reserve(new Word(Tag.TRUE, "true"));
reserve(new Word(Tag.FALSE, "false"));
}
@SuppressWarnings("unchecked")
public Token scan() throws IOException {
for (;; peek = (char) Code.read()) {
if (peek == ‘ ‘ || peek == ‘\t‘)
continue;
else if (peek == ‘\n‘)
line++;
else
break;
}
if (Character.isDigit(peek)) {
int v = 0;
do {
v = 10 * v + Character.digit(peek, 10);
peek = (char) Code.read();
} while (Character.isDigit(peek));
return new Num(v);
}
if (Character.isLetter(peek)) {
StringBuffer b = new StringBuffer();
do {
b.append(peek);
peek = (char) Code.read();
} while (Character.isLetterOrDigit(peek));
String s = b.toString();
Word w = (Word) words.get(s);
if (w != null)
return w;
w = new Word(Tag.ID, s);
words.put(s, w);
return w;
}
Token t = new Token(peek);
peek = ‘ ‘;
return t;
}
public static void main(String[] args) {
try {
Lexer lx = new Lexer();
for (int i = 0; i <5; i++) {
System.out.println(lx.scan().tag);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}