📄 pl0.java
字号:
import java.io.*;
import java.lang.*;
import java.util.*;
/*错误号
0 ,"缺少左括号"
1 ,"非法字符:赋值符号:="
2 ,"等号后的字符为非法字符"
3 ,"缺少等号"
4 ,"声明过程中遇到的字符不是标识符"
5 ,"缺少分号"
6 ,"非法语句"
7 ,"整数大小越界"
8 ,"整数位数越界"
9 ,"缺少右括号"
10 ,"语句和语句之间没有分号"
11 ,"标识符不存在"
12 ,"标识符不是变量名"
13 ,"缺少赋值符号"
14 ,"call后不是标识符"
15 ,"call后不是过程名"
16 ,"if后不是then"
17 ,"没有遇到end"
18 ,"while循环缺少do"
19 ,"标识符长度越界"
20 ,"缺少逻辑运算符"
21 ,"标识符为过程名"
22 ,"缺少右括号"
*/
public class pl0//主程序
{
public static void main(String [] args)
{
String filename=" ";
new pl0();//初始化一个PL0对象
System.out.print("请输入PL源程序的文件名:");
try
{
InputStreamReader fin=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(fin);
filename=br.readLine();
}
catch(IOException e)
{
System.out.println("错误!请输入源程序文件名");
}
//声明一个词法分析类,获得源程序的第一个词(token)
CiFaAnalyse Cifa=new CiFaAnalyse(filename);
YuFaAnalyse YuFa=new YuFaAnalyse(Cifa);
int errorNumber=Cifa.getErroNumber()+YuFa.getErroNumber();
if(errorNumber==0)
{
YuFa.printCode();
System.out.println("错误总数为"+errorNumber);
YuFa.interpret();
}
else {System.out.println("错误总数为"+errorNumber);}
//YuFa.printCode();
//YuFa.printName();
}
}
class erro //错误处理类
{
String[] err={" 缺少左括号"
,"非法字符:赋值符号:="
,"等号后的字符为非法字符"
,"缺少等号"
,"声明过程中遇到的字符不是标识符"
,"缺少分号"
,"非法语句"
,"整数大小越界"
,"整数位数越界"
,"缺少右括号"
,"语句和语句之间没有分号"
,"标识符不存在"
,"标识符不是变量名"
,"缺少赋值符号"
,"call后不是标识符"
,"call后不是过程名"
,"if后不是then"
,"没有遇到end"
,"while循环缺少do"
,"标识符长度越界"
,"缺少逻辑运算符"
,"标识符为过程名"
,"缺少右括号"
};
erro(int linenumber,int t)
{
System.out.println("行:"+linenumber+" 错误号"+t+" "+err[t]);
}
}
class nameClass //用于表示符号类,存放于符号表中
{
String name=null;
String kind=null;
int val;
int level;
int adr;
public nameClass(String a,String b,int c,int d,int e)
{
name=a;
kind=b;
val=c;
level=d;
adr=e;
}
public void setAdr(int cx0)
{
adr=cx0;
}
public int getAdr()
{
return adr;
}
public String getKind()
{
return kind;
}
public int getVal()
{
return val;
}
public String getNam()
{
return name;
}
public int getLev()
{
return level;
}
}
class pcode//pcode类 表示pcode指令
{
String f=" ";//第一个参数:操作码
int l; //第二个参数:层次差
int a; //
public pcode(String x,int y,int z)
{
f=x;
l=y;
a=z;
}
public void setA(int t)
{
a=t;
}
public String getF()
{return f;}
public int getL()
{return l;}
public int getA()
{return a;}
}
class token//声明一个类"token类” 用于存放读取的token单词
{
private String sym="原来的"; //保存token的类型
private String name="原来的"; //保存token的内容
public int lineNum;
token(String name1,String sym1)
{
name=name1;
sym=sym1;
}
token(String name1,String sym1,int l)
{
name=name1;
sym=sym1;
lineNum=l;
}
public void setLineNum(int l)
{
lineNum=l;
}
public int getLineNum()
{return lineNum;}
public String getNam()
{
return name;
}
public String getSym()
{
return sym;
}
}
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());
}
}
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号错误
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -