Small Basic语言解释器源代码
宗政浩慨
2023-12-01
import java.io.*;
import java.util.*;
/**
*一个BASIC的解释器
*同样,它的变量只能有26个,只识别首字母
*牟勇:2006年8月27日
*/
//解释器的异常处理类
class InterpreterException extends Exception{
String errStr;
public InterpreterException(String str){
errStr=str;
}
public String toString(){
return errStr;
}
}
/**
*本类完成对程序的解析和对程序的解释和执行
*出于对效率的考虑所以将解析器和解释器均写在同一个类中
*关于解析器部分的代码可以参看ParserV2类
*/
public class SBasic{
final int PROG_SIZE=10000;//程序的最大字节数(最多写一万个字符,包含所有字符)
//这些是标记的类型
final int NONE=0;//非标记
final int DELIMITER=1;//分隔符
final int VARIABLE=2;//变量
final int NUMBER=3;//数字
final int COMMAND=4;//关键字
final int QUOTEDSTR=5;//字符串常量
//这些是错误的类型
final int SYNTAX=0;//语法错
final int UNBALPARENS=1;//
final int NOEXP=2;//非表达式
final int DIVBYZERO=3;//除零
final int EQUALEXPECTED=4;//期待比较运算符
final int NOTVAR=5;//非变量
final int LABELTABLEFULL=6;//标签制表符满
final int DUPLABEL=7;//重复的标签
final int UNDEFLABEL=8;//无标签定义
final int THENEXPECTED=9;//期待then
final int TOEXPECTED=10;//期待to
final int NEXTWITHOUTFOR=11;//next无for对应
final int RETURNWITHOUTGOSUB=12;//return无gosub对应
final int MISSINGQUOTE=13;//错过引用
final int FILENOTFOUND=14;//文件末发现
final int FILEIOERROR=15;//文件IO错
final int INPUTIOERROR=16;//输入IO错
//Small Basic关键字的内部表达
final int UNKNCOM=0;
final int PRINT=1;
final int INPUT=2;
final int IF=3;
final int THEN=4;
final int FOR=5;
final int NEXT=6;
final int TO=7;
final int GOTO=8;
final int GOSUB=9;
final int RETURN=10;
final int END=11;
final int EOL=12;
//这个标记说明程序结束
final String EOP="/0";
//双字符操作符,例如:<=
final char LE=1;
final char GE=2;
final char NE=3;
//存储变量值的数据
private double vars[];
//这个内部类将关键字的字符串与它们的内部标记关联起来
class Keyword{
String keyword;//关键字的字符串表达
int keywordTok;//关键字的内部表达
Keyword(String str,int t){
keyword=str;
keywordTok=t;
}
}
/*
*关键字外部表达与内部表达的列表,所有的关键字字符串必须小写
*/
Keyword[] kwTable={
new Keyword("print",PRINT),
new Keyword("if",IF),
new Keyword("then",THEN),
new Keyword("goto",GOTO),
new Keyword("for",FOR),
new Keyword("next",NEXT),
new Keyword("to",TO),
new Keyword("gosub",GOSUB),
new Keyword("return",RETURN),
new Keyword("end",END)
};
private char[] prog;//程序数组的引用,使用字符数组可以大大提高效率
private int progIdx;//程序中的当前索引,用于程序语句解析
private String token;//保存当前标记
private int tokType;//保存当前标记的类型
private int kwToken;//关键字的内部表达
//对for循环的支持
class ForInfo{
int var;//记数变量
double target;//目标值
int loc;//index in source code to loop to
}
//为For循环准备的栈
private Stack fStack;
//定义标签表条目
class Label{
String name;//标签
int loc;//标签在源文件中的位置索引
public Label(String n,int i){
name=n;
loc=i;
}
}
//标签的映射表
private TreeMap labelTable;
//用于gosub的栈
private Stack gStack;
//关系运算符
char[] rops={
GE,NE,LE,'<','>','=',0
};
//建立一个字符串包含关系运算符以便更好的进行检查
String relops=new String(rops);
//Small BASIC的构造函数
public SBasic(String progName) throws InterpreterException{
char[] tempbuf=new char[PROG_SIZE];
int size;
//加载需要执行的程序,计算其实际长度
size=loadProgram(tempbuf,progName);
if(size!=-1){
//建立适当大小的数组来保存程序
prog=new char[size];
//复制程序到数组中
System.arraycopy(tempbuf,0,prog,0,size);
}
}
private int loadProgram(char[] p,String fname) throws InterpreterException{
int size=0;
try{
FileReader fr=new FileReader(fname);
BufferedReader br=new BufferedReader(fr);
size=br.read(p,0,PROG_SIZE);
fr.close();
}catch(FileNotFoundException exc){
handleErr(FILENOTFOUND);
}catch(IOException exc){
handleErr(FILEIOERROR);
}
//如果文件尾有EOF标记,减去它
if(p[size-1]==(char)26){
size--;
}
return size;
}
//执行程序
public void run() throws InterpreterException{
//为新程序执行进行初始化
vars=new double[26];
fStack=new Stack();
labelTable=new TreeMap();
gStack=new Stack();
progIdx=0;
scanLabels();//搜索程序中的所有标签
sbInterp();//执行
}
//Small BASIC解释器的入口点
private void sbInterp()throws InterpreterException{
//这是解释器的主循环
do{
getToken();
//检查赋值表达式
if(tokType==VARIABLE){
putBack();//返回值给输入流
assignment();//处理赋值表达式
}else{
switch(kwToken){
case PRINT:
print();
break;
case GOTO:
execGoto();
break;
case IF:
execIf();
break;
case FOR:
execFor();
break;
case NEXT:
next();
break;
case INPUT:
input();
break;
case GOSUB:
gosub();
break;
case RETURN:
greturn();
break;
case END:
return;
}
}
}while(!token.equals(EOP));
}
//搜索所有的标签
private void scanLabels() throws InterpreterException{
int i;
Object result;
//假设第一个标记是一个标签
getToken();
if(tokType==NUMBER){
labelTable.put(token,new Integer(progIdx));
}
findEOL();
do{
getToken();
if(tokType==NUMBER){//一定是一个行号
result=labelTable.put(token,new Integer(progIdx));
if(result!=null){
handleErr(DUPLABEL);
}
//如果是个空行,搜索下一行
if(kwToken!=EOL){
findEOL();
}
}
}while(!token.equals(EOP));
progIdx=0;//重置程序索引起始
}
//搜索下一行的开始
private void findEOL(){
while(progIdx<prog.length && prog[progIdx]!='/n'){
progIdx++;
}
if(progIdx<prog.length){
progIdx++;
}
}
//完成赋值的方法
private void assignment() throws InterpreterException{
int var;
double value;
char vname;
//获得变量名
getToken();
vname=token.charAt(0);
if(!Character.isLetter(vname)){
handleErr(NOTVAR);
return;
}
//从变量表转换索引
var=(int)Character.toUpperCase(vname)-'A';
//获得比较符
getToken();
if(!token.equals("=")){
handleErr(EQUALEXPECTED);
return;
}
//获得要赋的值
value=evaluate();
//赋值
vars[var]=value;
}
//执行一个简单版本的PRINT语句
private void print() throws InterpreterException{
double result;
int len=0,spaces;
String lastDelim="";
do{
getToken();//获得下一个元素
if(kwToken==EOL || token.equals(EOP)){
break;
}
if(tokType==QUOTEDSTR){//是字符串
System.out.print(token);
len+=token.length();
getToken();
}else{//是表达式
putBack();
result=evaluate();
getToken();
System.out.print(result);
//将输出长度添加至运行总长度中
Double t=new Double(result);
len+=t.toString().length();//保存长度
}
lastDelim=token;
//如果是逗号,移动至下一个结束点
if(lastDelim.equals(",")){
//计算移动到下一个标签所需要的空格数
spaces=8-(len%8);
len+=spaces;//添加到标签位置
while(spaces!=0){
System.out.print(" ");
spaces--;
}
}else if(token.equals(";")){
System.out.print(" ");
len++;
}else if(kwToken!=EOL && !token.equals(EOP)){
handleErr(SYNTAX);
}
}while(lastDelim.equals(";") || lastDelim.equals(","));
if(kwToken==EOL || token.equals(EOP)){
if(!lastDelim.equals(";") && !lastDelim.equals(",")){
System.out.println();
}
}else{
handleErr(SYNTAX);
}
}
//执行GOTO语句
private void execGoto() throws InterpreterException{
Integer loc;
getToken();//获得goto的标签
//寻找标签的位置
loc=(Integer)labelTable.get(token);
if(loc==null){
handleErr(UNDEFLABEL);//标签未定义
}else{//将程序运行位置转到标签位置
progIdx=loc.intValue();
}
}
//执行IF语句
private void execIf() throws InterpreterException{
double result;
result=evaluate();//得到表达式的结果
//如果结果是true(非零)则处理IF语句的目标代码,否则移至程序下一行
if(result!=0.0){
getToken();
if(kwToken!=THEN){
handleErr(THENEXPECTED);
return;
}//else,目标语句将被执行
}else{
findEOL();//寻找下一行语句的开头
}
}
//执行FOR循环
private void execFor() throws InterpreterException{
ForInfo stckvar=new ForInfo();
double value;
char vname;
getToken();//读控制变量
vname=token.charAt(0);
if(!Character.isLetter(vname)){
handleErr(NOTVAR);
return;
}
//获得并保存控制变量的索引
stckvar.var=Character.toUpperCase(vname)-'A';
getToken();//读取等号
if(token.charAt(0)!='='){
handleErr(EQUALEXPECTED);
return;
}
value=evaluate();//获得初始化值
vars[stckvar.var]=value;
getToken();//读并抛弃TO
if(kwToken!=TO){
handleErr(TOEXPECTED);
}
stckvar.target=evaluate();//获得循环终值
//如果循环至少可以执行一次,则压相关信息入栈
if(value>=vars[stckvar.var]){
stckvar.loc=progIdx;
fStack.push(stckvar);
}else{//否则,完全跳过循环代码
while(kwToken!=NEXT){
getToken();
}
}
}
//执行NEXT语句
private void next() throws InterpreterException{
ForInfo stckvar;
try{
//找回for循环信息
stckvar=(ForInfo)fStack.pop();
vars[stckvar.var]++;//自增控制变量
//如果循环结束,则返回
if(vars[stckvar.var]>stckvar.target){
return ;
}
//否则重置循环信息
fStack.push(stckvar);
progIdx=stckvar.loc;//循环
}catch(EmptyStackException exc){
handleErr(NEXTWITHOUTFOR);
}
}
//执行一个简单的Input表单
private void input() throws InterpreterException{
int var;
double val=0.0;
String str;
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
getToken();//查看是否提示字符串
if(tokType==QUOTEDSTR){
//如果是,打印并检查逗号
System.out.print(token);
getToken();
if(!token.equals(",")){
handleErr(SYNTAX);
}
getToken();
}else{//否则输出?号提标
System.out.print("? ");
}
//获得输入变量的索引
var=Character.toUpperCase(token.charAt(0))-'A';
try{
str=br.readLine();
val=Double.parseDouble(str);//读值
}catch(IOException exc){
handleErr(INPUTIOERROR);
}catch(NumberFormatException exc){
//你也许希望用不同于其他解释器错误的处理方法来处理这个错误
System.out.println("Inalid input");
}
vars[var]=val;//保存值
}
//执行GOSUB
private void gosub() throws InterpreterException{
Integer loc;
getToken();
//查找调用的标签
loc=(Integer)labelTable.get(token);
if(loc==null){
handleErr(UNDEFLABEL);//未定义的标签
}else{
//保存当前位置,以备返回使用
gStack.push(new Integer(progIdx));
//程序转至loc处执行
progIdx=loc.intValue();
}
}
//从GOSUB返回
private void greturn() throws InterpreterException{
Integer t;
try{
//回复程序位置
t=(Integer)gStack.pop();
progIdx=t.intValue();
}catch(EmptyStackException exc){
handleErr(RETURNWITHOUTGOSUB);
}
}
//************************表达式解析器***********************
//解析器入口
private double evaluate() throws InterpreterException{
double result=0.0;
getToken();
if(token.equals(EOP)){
handleErr(NOEXP);//没有表达式存在
}
//解析并计算表达式
result=evalExp1();
return result;
}
//处理相关操作符
private double evalExp1() throws InterpreterException{
double l_temp,r_temp,result=0.0;
char op;
result=evalExp2();
//如果程序已经结束,返回
if(token.equals(EOP)){
return result;
}
op=token.charAt(0);
if(isRelop(op)){
l_temp=result;
getToken();
r_temp=evalExp1();
switch(op){//完成关系运算
case '<':
if(l_temp<r_temp){
result=1.0;
}else{
result=0.0;
}
break;
case LE:
if(l_temp<=r_temp){
result=1.0;
}else{
result=0.0;
}
break;
case '>':
if(l_temp>r_temp){
result=1.0;
}else{
result=0.0;
}
break;
case GE:
if(l_temp>=r_temp){
result=1.0;
}else{
result=0.0;
}
break;
case '=':
if(l_temp==r_temp){
result=1.0;
}else{
result=0.0;
}
break;
case NE:
if(l_temp!=r_temp){
result=1.0;
}else{
result=0.0;
}
break;
}
}
return result;
}
//加或减两个项
private double evalExp2() throws InterpreterException{
char op;
double result=0.0;
double partialResult;
result=evalExp3();
while((op=token.charAt(0))=='+'||op=='-'){
getToken();
partialResult=evalExp3();
switch(op){
case '-':
result=result-partialResult;
break;
case '+':
result=result+partialResult;
break;
}
}
return result;
}
//乘或除两因素
private double evalExp3() throws InterpreterException{
char op;
double result=0.0;
double partialResult;
result=evalExp4();
while((op=token.charAt(0))=='*'||op=='/'||op=='%'){
getToken();
partialResult=evalExp4();
switch(op){
case '*':
result=result*partialResult;
break;
case '/':
if(partialResult==0.0){
handleErr(DIVBYZERO);
}
result=result/partialResult;
break;
case '%':
if(partialResult==0.0){
handleErr(DIVBYZERO);
}
result=result%partialResult;
break;
}
}
return result;
}
//处理指数
private double evalExp4() throws InterpreterException{
double result=0.0;
double partialResult;
double ex;
int t;
result=evalExp5();
if(token.equals("^")){
getToken();
partialResult=evalExp4();
ex=result;
if(partialResult==0.0){
result=0.0;
}else{
for(t=(int)partialResult-1;t>0;t--){
result=result*ex;
}
}
}
return result;
}
//计算一元+或-
private double evalExp5() throws InterpreterException{
double result=0.0;
String op;
op="";
if((tokType==DELIMITER)&&token.equals("+")||token.equals("-")){
op=token;
getToken();
}
result=evalExp6();
if(op.equals("-")){
result=-result;
}
return result;
}
//处理括号
private double evalExp6() throws InterpreterException{
double result=0.0;
if(token.equals("(")){
getToken();
result=evalExp2();
if(!token.equals(")")){
handleErr(UNBALPARENS);
}
getToken();
}else{
result=atom();
}
return result;
}
//获得一个数字或变量的值
private double atom() throws InterpreterException{
double result=0.0;
switch(tokType){
case NUMBER:
try{
result=Double.parseDouble(token);
}catch(NumberFormatException exc){
handleErr(SYNTAX);
}
getToken();
break;
case VARIABLE:
result=findVar(token);
getToken();
break;
default:
handleErr(SYNTAX);
break;
}
return result;
}
//返回一个变量的值
private double findVar(String vname) throws InterpreterException{
if(!Character.isLetter(vname.charAt(0))){
handleErr(SYNTAX);
return 0.0;
}
return vars[Character.toUpperCase(vname.charAt(0))-'A'];
}
//返回一个标记到输入流
private void putBack(){
if(token==EOP){
return;
}
for(int i=0;i<token.length();i++){
progIdx--;
}
}
//处理一个错误
private void handleErr(int error) throws InterpreterException{
String[] err={
"语法错",
"不匹配的括号",
"无表达式存在",
"除零错",
"需要等号",
"不是一个变量",
"标签满",
"相同的标签",
"未定义标签",
"需要THEN",
"需要TO",
"NEXT没有匹配的FOR",
"RETURN没有匹配的GOSUB",
"没有右/"",
"文件未发现",
"当载入文件时I/O错",
"INPUT声明I/O错"
};
throw new InterpreterException(err[error]);
}
//获得下一个标记
private void getToken() throws InterpreterException{
char ch;
tokType=NONE;
token="";
kwToken=UNKNCOM;
//检查程序的结束
if(progIdx==prog.length){
token=EOP;
return;
}
//跳过空格
while(progIdx<prog.length && isSpaceOrTab(prog[progIdx])){
progIdx++;
}
//如果跳过空格后已经到了程序的未尾
if(progIdx==prog.length){
token=EOP;
tokType=DELIMITER;
return;
}
//处理回车
if(prog[progIdx]=='/r'){
progIdx+=2;
kwToken=EOL;
token="/r/n";
return;
}
//检查关系运算符
ch=prog[progIdx];
if(ch=='<'|| ch=='>'){
if(progIdx+1==prog.length){
handleErr(SYNTAX);
}
switch(ch){
case '<':
if(prog[progIdx+1]=='>'){
progIdx+=2;
token=String.valueOf(NE);
}else if(prog[progIdx+1]=='='){
progIdx+=2;
token=String.valueOf(LE);
}else{
progIdx++;
token="<";
}
break;
case '>':
if(prog[progIdx+1]=='='){
progIdx+=2;
token=String.valueOf(GE);
}else{
progIdx++;
token=">";
}
break;
}
tokType=DELIMITER;
return;
}
if(isDelim(prog[progIdx])){
//是一个操作符
token+=prog[progIdx];
progIdx++;
tokType=DELIMITER;
}else if(Character.isLetter(prog[progIdx])){
//是一个变量或者关键字
while(!isDelim(prog[progIdx])){
token+=prog[progIdx];
progIdx++;
if(progIdx>=prog.length){
break;
}
}
kwToken=lookUp(token);
if(kwToken==UNKNCOM){
tokType=VARIABLE;
}else{
tokType=COMMAND;
}
}else if(prog[progIdx]=='"'){
//是一个字符串
progIdx++;
ch=prog[progIdx];
while(ch!='"' && ch!='/r'){
token+=ch;
progIdx++;
ch=prog[progIdx];
}
if(ch=='/r'){
handleErr(MISSINGQUOTE);
}
progIdx++;
tokType=QUOTEDSTR;
}else{
//未知字符结束程序
token=EOP;
return;
}
}
//返回真,如果c是一个定界符
private boolean isDelim(char c){
if((" /r,;<>+-*/%^=()".indexOf(c)!=-1)){
return true;
}
return false;
}
//返回真,如果c是一个空格或tab
private boolean isSpaceOrTab(char c){
if(c==' ' || c=='/t'){
return true;
}
return false;
}
//返回真,如果c是一个关系运算符
private boolean isRelop(char c){
if(relops.indexOf(c)!=-1){
return true;
}
return false;
}
//查看符号是不是SBASIC的保留字
private int lookUp(String s){
int i;
//转换为小写
s=s.toLowerCase();
//查看token是否在标签中
for(i=0;i<kwTable.length;i++){
if(kwTable[i].keyword.equals(s)){
return kwTable[i].keywordTok;
}
}
return UNKNCOM;//未知关键字
}
}