📄 sbasic.java
字号:
/*
* 创建日期 2005-4-4
*
* TODO 要更改此生成的文件的模板,请转至
* 窗口 - 首选项 - Java - 代码样式 - 代码模板
*/
import java.io.*;
import java.util.*;
/**
* @author 姜周扬
*
* TODO Small Basic 语言解释器
* 解释器包含了:表达式解析和解释器,两者合为一个类有助于优化解释器性能
*/
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;
final int TOEXPECTED = 10;
final int NEXTWITHOUTFOR = 11;
final int RETURNWITHOUTGOSUB = 12;
final int MISSINGQUOTE = 13;
final int FILENOTFOUND = 14;
final int FILEIOERROR = 15;
final int INPUTIOERROR = 16;
//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[];
/*
*
* @author Administrator
*
* TODO 这个类链接关键字到它的关键项
*
*/
class Keyword{
//关键字来源
String keyword;
//关键字内部表示方法索引
int keywordTok;
Keyword(String str,int t){
keyword = str;
keywordTok = t;
}
}
/*
* 关键字内部映射表:所有关键字输入均要求为小写字母
*/
Keyword kwTable[] = {
new Keyword("print",PRINT),
new Keyword("input",INPUT),
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;
/*
*
* @author Administrator
*
* TODO 提供FOR循环支持
*
*/
class ForInfo{
//记录循环次数
int var;
//临时变量
double target;
//源程序代码中LOOP地址下标
int loc;
}
//FOR循环支持栈
private Stack fStack;
/*
*
* @author Administrator
*
* TODO 定义标签实体类
*
*/
class Label{
//标签名称
String name;
//源程序中标签地址
int loc;
public Label(String n,int i){
name = n;
loc = i;
}
}
//标签表 Map
private TreeMap labelTable;
//GOSUB子程序调用栈
private Stack gStack;
//逻辑操作符
char rops[] = {
GE,NE,LE,'<','>','=',0
};
/* 创建一个逻辑操作符容器
* 以便于检查
*/
String relops = new String(rops);
//**************** 类属性定义完毕 解释器部分开始 ********************************
/*
* SBasic 构造函数
*/
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];
//FOR循环支持栈
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++;
}
/* X=3
* 赋值语句 标记一个变量的值
*/
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 X
* PRINT "...",X;X*X
* 打印语句处理
*/
private void print() throws InterpreterException{
double result;
int len = 0;
int 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(","));
//输出一行后,换行(PRINT 不能以; , 结束)
if(kwToken==EOL || token.equals(EOP)){
if(!lastDelim.equals(";") && !lastDelim.equals(","))
System.out.println();
}
else handleErr(SYNTAX);
}
/* GOTO 行号
* GOTO 语句执行块
*/
private void execGoto() throws InterpreterException{
Integer loc;
//获得行号
getToken();
loc = (Integer)labelTable.get(token);
//未定义行号
if(loc == null)
handleErr(UNDEFLABEL);
else//程序跳转到指定的行号
progIdx = loc.intValue();
}
/* IF 表达式 op 表达式 THEN 语句
* IF 语句执行模块
*/
private void execIf() throws InterpreterException{
double result;
//获得表达式的结果
result = evaluate();
/*如果表达式为真(即非0) 执行 IF语句块,否则
* 跳过THEN 到下一行,继续解释
*/
if(result != 0.0){
getToken();
if(kwToken != THEN){//没有THEN语句块
handleErr(THENEXPECTED);
return;
}
}//跳过此行
else findEOL();
}
/* FOR 变量=初值 TO 结束值
* 执行语句
* NEXT
* 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;
//读取TO关键字
getToken();
if(kwToken != TO) handleErr(TOEXPECTED);
//计算循环结束值
stckvar.target = evaluate();
/* 如果循环可以至少执行一次的话
* 将变量压到FOR支持栈中
*/
if(value >= vars[stckvar.var]){
stckvar.loc = progIdx;
fStack.push(stckvar);
}
else//否则跳过FOR循环语句
while(kwToken != NEXT) getToken();
}
/* FOR语句的NEXT处理
* NEXT 从栈中弹出变量
*/
private void next() throws InterpreterException{
ForInfo stckvar;
try{
//完成本次FOR循环
stckvar = (ForInfo)fStack.pop();
//将变量值加1 控制变量只能递增
vars[stckvar.var]++;
//循环完毕
if(vars[stckvar.var] > stckvar.target) return;
//继续循环
fStack.push(stckvar);
progIdx = stckvar.loc;
}//FOR 不配对 NEXT
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -