📄 yufaanalyse.java
字号:
package ecust.edu.cn.pl0;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Vector;
public class YuFaAnalyse // 语法分析类
{
public CiFaAnalyse CiFa;
public token word;
public String id = null; // 用于登录名字表时的token名字
public int lineNum; // 用于错误处理时的行数记录
public int errorNumber = 0; // 用于保存语法分析中的错误数目
int cx;
int cx0;
int dx;
int lev = -1;
int errorNum = 0;
public int t = -1; // 堆栈顶指针
final public int stacksize = 500;
public int p = 0; // 下一条指令位置
public int b = 0; // 基地址指针
public pcode i; // 指令
public int[] s = new int[501];
Vector pcodeArray = new Vector();; // 生成一个vector矢量! // 此矢量用作存放pcode代码的数组
Vector nameArray = new Vector();// 生成一个存放矢量 作为名字表;
public YuFaAnalyse(CiFaAnalyse CF)// 语法分析构造函数.接收一个词法分析对象
{
CiFa = CF;// 从主程序传入一个词法分析对象,将其赋给CiFa
word = CiFa.GetToken(); // 获得一个token
analyse(); // 然后调用分析程序
}
public int getErroNumber() { //用于返回在语法分析时繁盛的错误的个数
return errorNumber;
}
public void printCode()// pcode的显示方法,如果没有错误,则打印pcode代码
{
for (int n = 0; n < pcodeArray.size(); n++) {
pcode code = (pcode) pcodeArray.get(n);
System.out.println(n + " " + code.getF() + " " + code.getL()
+ " " + code.getA());
}
}
// 分程序分析,相当于block()
public void analyse() {
int tx0;
lev++;
dx = 3;
tx0 = nameArray.size(); // 用tx0保存当前层的符号在符号表中的起始位置
nameArray.addElement(new nameClass(" ", " ", 0, 0, 0));// 在这里给符号表填加一个元素,否则下一条代码会运行出界
((nameClass) nameArray.get(tx0)).setAdr(pcodeArray.size());// 在上面加的那个元素里的私有字段adr里保存当前层代码的开始位置
pcodeArray.addElement(new pcode("jmp", 0, 0)); // 生成跳转指令 由于跳转位置未知 暂时添0
while (word.getSym().equals("constsym")
|| word.getSym().equals("varsym")
|| word.getSym().equals("procsym")) {
if (word.getSym().equals("constsym"))// 常量处理
{
word = CiFa.GetToken();
delcaration(); // 调用常量声明,注意常量声明完毕时还要再次读取一个token
while (word.getSym().equals("comma"))// 若在常量声明后读取的token为逗号,则要继续进行常量声明,这里采用while循环,直到token不是逗号
{
word = CiFa.GetToken();
delcaration(); // 调用常量声明,注意常量声明完毕时还要再次读取一个token
}
if (word.getSym().equals("semicolon"))// 若token不是逗号,则应该是分号了,标志常量声明结束
{
word = CiFa.GetToken();
} else {
errorNumber++;
new erro(word.getLineNum(), 5);// 如果常量声明完毕后没有遇到分号;则抛出15号错误
}
}
// 变量处理
else if (word.getSym().equals("varsym")) {
word = CiFa.GetToken();// 读取一个token,应该是一个ident标识符
vardeclaration(); // 调用变量声明,注意常量声明完毕时还要再次读取一个token
while (word.getSym().equals("comma")) {
word = CiFa.GetToken();
vardeclaration(); // 调用变量声明,注意常量声明完毕时还要再次读取一个token
}
if (word.getSym().equals("semicolon")) {
word = CiFa.GetToken();
} else {
errorNumber++;
new erro(word.getLineNum(), 5);// 如果变量声明完毕后没有遇到分号;则调用错误处理程序抛出5号错误
}
}
// 循环声明各个子过程
while (word.getSym().equals("procsym")) {
word = CiFa.GetToken();
if (word.getSym().equals("ident")) {
id = word.getNam();
enter("procedure");
word = CiFa.GetToken();
} else {
errorNumber++;
new erro(word.getLineNum(), 4);// 过程名不是标识符,抛出4号错误
}
if (word.getSym().equals("semicolon")) {
word = CiFa.GetToken();
} else {
errorNumber++;
new erro(word.getLineNum(), 5);
}// 过程名后没有分号
// System.out.println(dx);
int dxxxxx = dx;
int levv = lev;
analyse();// 在这里递归调用下一层的分程序段分析
dx = dxxxxx;
lev = levv;
// System.out.println(dx);
if (word.getSym().equals("semicolon")) {
word = CiFa.GetToken();
// 接下来是一段test程序
// 若end;后的符号不在ident,procsym,begin,call,if,while中,则调用6号错误
String sym = word.getSym();
if (!(sym.equals("ident") || sym.equals("procsym")
|| sym.equals("beginsym") || sym.equals("callsym")
|| sym.equals("ifsym") || sym.equals("while"))) {
errorNumber++;
new erro(word.getLineNum(), 6);
}
} else {
errorNumber++;
new erro(word.getLineNum(), 5);
}
// 这儿也是有一段test程序 当前符号不在ident,begin,call,if,while中,则调用7号错误
}
}
((pcode) pcodeArray.get(((nameClass) nameArray.get(tx0)).getAdr()))
.setA(pcodeArray.size());// 把前面生成的跳转指令的跳转位置改成当天前位置
((nameClass) nameArray.get(tx0)).setAdr(pcodeArray.size());// 在符号表中,对于过程名,adr应填入编译该过程所生成的pcode指令序列的入口地址
cx0 = pcodeArray.size();// 在cx0中保存当前代码的分配地址
pcodeArray.addElement(new pcode("int", 0, dx));// 生成空间分配指令,分配dx个空间(3个空间+变量的数目)
statement1();// 处理当前遇到的语句;
pcodeArray.addElement(new pcode("opr", 0, 0));// 生成子程序返回指令;
}
public void delcaration() // 常量声明的方法
{
if (word.getSym().equals("ident"))// 在analyse调用常量声明时,上一个token为const,这一个应该是ident标识符
{
id = word.getNam();// 调用token类的getNam方法,获得当前token的内容,这里将其保存在id中,为的是一会进行的符号表插入工作
word = CiFa.GetToken();// 然后再读取一个token,应该是等号"="了
if (word.getSym().equals("becomes")) {
errorNumber++;
new erro(word.getLineNum(), 1);// 如果不是等号,而是赋值符号:=,抛出1号错误
} else if (word.getSym().equals("eql")) {
word = CiFa.GetToken();// 继续读取一个token,现在应该是一个数字了
if (word.getSym().equals("number")) {
enter("constant");// 如果当前token是数字,则调用符号表插入方法,将常量插入符号表;
word = CiFa.GetToken();// 再次读取一个token,为后边做准备,应该是一个逗号或者是分号
} else// 如果等号后不是数字,调用2号错误
{
errorNumber++;
new erro(word.getLineNum(), 2);
}
} else// 如果标识符后不是等号,调用3号错误
{
errorNumber++;
new erro(word.getLineNum(), 3);
}
} else// 如果常量声明过程中遇到的第一个字符不是标识符,调用4号错误
{
errorNumber++;
new erro(word.getLineNum(), 4);
}
}
public void vardeclaration() // 变量声明的方法
{
if (word.getSym().equals("ident")) {
id = word.getNam();
enter("variable");// 调用符号表注册方法 进行变量的注册
word = CiFa.GetToken();
} else// 如果变量声明过程中遇到的第一个字符不是标识符,调用4号错误
{
errorNumber++;
new erro(word.getLineNum(), 4);
}
}
public void enter(String k) // 注册符号表
{ // k中保存的是传过来的 string
if (k.equals("constant")) {// 如果是常量,则要判断此常量的赋值是否大于整数的最大允许值2047
int num = Integer.parseInt(word.getNam());
if (num > 2047) {
nameArray.addElement(new nameClass(id, k, 0, 0, 0));
new erro(word.getLineNum(), 31);// 若整数越界,抛出31号错误,并将其赋为0,继续进行分析
} else
// 若整数没有越界,则给nameArray增加一个nameClass对象
nameArray.addElement(new nameClass(id, k, num, 0, 0));
} else if (k.equals("variable")) {
nameArray.addElement(new nameClass(id, k, 0, lev, dx));
dx = dx + 1;
} else if (k.equals("procedure")) {
nameArray.addElement(new nameClass(id, k, 0, lev, 0));
}
}// 符号表注册完成
public int position(String id) // 查找符号表
{
int i = 0;
while (i < nameArray.size()
&& (!((nameClass) nameArray.get(i)).getNam().equals(id))) {
i++;
}
if (i >= nameArray.size()) {
return -1;
}// 如果没有找到,则返回负值
else {
return i;
}
}
public void statement1()// 语句的分析处理
{
int i;
int cx1;
int cx2;
if (word.getSym().equals("ident")) // 所谓语句可能是赋值语句,以标识符开头;
{
i = position(word.getNam()); // 在符号表中找到该标识符所在位置;
if (i == -1) // 如果返回的i为负值,则表示没有找到;抛出11号错误
{
errorNumber++;
new erro(word.getLineNum(), 11);
} else if (!((nameClass) nameArray.get(i)).getKind().equals(
"variable"))// 如果在符号表中找到了,但是该标识符不是变量名,则抛出12号错误
{
errorNumber++;
new erro(word.getLineNum(), 12);
i = -1;// 并将i置为-1做为错误的标志;
}
word = CiFa.GetToken(); // 继续读取下一个token,正常应该是赋值符号
if (word.getSym().equals("becomes")) {
word = CiFa.GetToken();// 如果是赋值符号,则继续读取一个token,正常应该是一个表达式;
} else {
errorNumber++;
new erro(word.getLineNum(), 13);// 如果不是赋值符号,则抛出13号错误
}
expression(); // 进行表达式的处理;
if (i != -1)// 如果i不是负值,则表示未曾出错,i所表示的是当前语句左边标识符在符号表中的位置;
{// 生成一条把表达式的值写往指定内存的代码;
pcodeArray.addElement(new pcode("sto", lev
- ((nameClass) nameArray.get(i)).getLev(),
((nameClass) nameArray.get(i)).getAdr()));
}
} else if (word.getSym().equals("callsym"))// 如果遇到了call语句
{
word = CiFa.GetToken();// 读取一个token,正常应该是一个过程名型的标识符
if (!word.getSym().equals("ident"))// 如果不是标识符,抛出14号错误
{
errorNumber++;
new erro(word.getLineNum(), 14);
} else {
i = position(word.getNam());// 查找符号表
if (i == -1) {
errorNumber++;
new erro(word.getLineNum(), 11);
}// 如果没有找到,抛出11号错误
else if (((nameClass) nameArray.get(i)).getKind().equals(
"procedure")) {// 生成call指令,call这个过程
pcodeArray.addElement(new pcode("cal", lev
- ((nameClass) nameArray.get(i)).getLev(),
((nameClass) nameArray.get(i + 1)).getAdr()));
} else {
errorNumber++;
new erro(word.getLineNum(), 15);
}// 如果找到的不是过程名,抛出15号错误
word = CiFa.GetToken();
}
} else if (word.getSym().equals("ifsym"))// 如果是if语句
{
word = CiFa.GetToken();// if后正常是条件,所以执行condition函数
condition();
if (word.getSym().equals("thensym")) {
word = CiFa.GetToken();
} else {
errorNumber++;
new erro(word.getLineNum(), 16);
}// 如果if后不是then 则抛出16号错误!
cx = pcodeArray.size();
cx1 = cx;
pcodeArray.addElement(new pcode("jpc", 0, 0));
statement1();
((pcode) pcodeArray.get(cx1)).setA(pcodeArray.size());
} else if (word.getSym().equals("beginsym")) {
word = CiFa.GetToken();
statement1();
while (word.getSym().equals("semicolon")
|| word.getSym().equals("beginsym")
|| word.getSym().equals("callsym")
|| word.getSym().equals("ifsym")
|| word.getSym().equals("whilesym")) {
if (word.getSym().equals("semicolon")) {
word = CiFa.GetToken();
} else {
errorNumber++;
new erro(word.getLineNum(), 10);
}
statement1();
}
if (word.getSym().equals("endsym")) {
word = CiFa.GetToken();
} else {
errorNumber++;
new erro(word.getLineNum(), 17);
}
} else if (word.getSym().equals("whilesym")) {
cx = pcodeArray.size();
cx1 = cx;// 记录当前代码分配位置,这是while循环的开始位置
word = CiFa.GetToken();// 读取token,应该是一个条件表达式
condition();
cx = pcodeArray.size();
cx2 = cx;// 记录当天代码位置,这是while循环的do中的语句的开始位置
pcodeArray.addElement(new pcode("jpc", 0, 0));// 生成条件跳转指令,跳转位置暂时填0
if (word.getSym().equals("dosym"))// 判断是否是do,否则抛出18号错误
{
word = CiFa.GetToken();
} else {
errorNumber++;
new erro(word.getLineNum(), 18);
}
statement1();// 开始分析do后的语句块
pcodeArray.addElement(new pcode("jmp", 0, cx1));// 跳转到cx1处,即再次进行判断是否进行循环
((pcode) pcodeArray.get(cx2)).setA(pcodeArray.size());// 把刚才跳转暂时填0的位置改成当前位置,完成对循环的处理
} else if (word.getSym().equals("readsym")) {
word = CiFa.GetToken();// 如果遇到了“读”语句块,则继续读取token,应该是左括号
if (word.getSym().equals("lparen")) {
word = CiFa.GetToken();
if (word.getSym().equals("ident")) {
i = position(word.getNam());
if (i == -1) {
errorNumber++;
new erro(word.getLineNum(), 11);
} else {
pcodeArray.addElement(new pcode("opr", 0, 16));// 生成16号读指令,从键盘读取数字
pcodeArray.addElement(new pcode("sto", lev
- ((nameClass) nameArray.get(i)).getLev(),
((nameClass) nameArray.get(i)).getAdr()));// 生成sto指令,存入指定变量
}
}
word = CiFa.GetToken();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -