当前位置: 首页 > 工具软件 > Small BASIC > 使用案例 >

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;//未知关键字
     }
}
 类似资料: