自著——30天自制计算机语言解释器 第2天 A+B-C+D运算的准备 :得到词语的数组
根据第一天的开发,发现,程序解析时处理的最基础的单元
是词语(TOKEN),所以需要在做其它事情之前,先把整个表达式
拆分成多个词语,以方便之后的处理。
例如 1+2+3+4 处理成如下的数组
1 5 1 1 0 ----------
2 8 + 1 1 ----------
3 5 2 1 2 ----------
4 8 + 1 3 ----------
5 5 3 1 4 ----------
6 8 + 1 5 ----------
7 5 4 1 6 ----------
数组中的每个元素都是一个对象。有序号 类型 值 行号 行内起始位置 这五个属性组成。
例如如下:
id type value linenumber startIndex
1 keyword int 1 10
第2.1版本的程序
需求 针对 1+2+3+4 这样的表达式,检查语法错误。 例如操作数与操作数相连
或者是操作符与操作符 相连即为出错。
实现
循环遍历 词语的数组,判断相邻的词语 的类型是否一样,一样即为出错,并报出出错的位置。
词语 分成了九种类型
1 字符串型, 5数字型, 6分隔符型 7 块分隔符 8.操作符,9 空格符
BNF的定义如下:
<operand> :== + | - | * | / | = | < | > | % | ^ | & | | | !
<block> :== { | } | [ | ] | ( | )
<delimeter> :== , | ; | : | ' | "
<digit> :== 0 | 1|2|3|4|5|6|7|8|9
<number> :== <digit> | <digit> <number>|<digit> . <number>
a~Z letter keyword ,identifier ,variable
get_token函数实现的思路
采用类似于超级玛利的策略。
以 上跳,下跳 ,平跑 来分隔 词语。
off on 上跳 得到token 首字符
off off 平跑 过滤空格与分隔符
on on 平跑 得到token 其它字符
on off 下跳 token 结束
html页面代码如下
<HTML>
<HEAD>
<TITLE> 30天自制解释器 2.1 </TITLE>
<META NAME="Generator" CONTENT="EditPlus">
<META NAME="Author" CONTENT="">
<META NAME="Keywords" CONTENT="">
<META NAME="Description" CONTENT="">
<script src='get_token_general.js' ></script>
</HEAD>
<BODY>
<p>本版本只能计算 a+b ,a-b,a*b ,a/b ,且本版有多个操作符,解析 操作数与操作数
操作符与操作符相连的错误 </p>
<p>a和b 都是正整数,而且 a 与 +,b 与+ 之间都可以有空格 </p>
<textarea id='txt1' rows="20" cols="80"></textarea>
<input type='button' value='执行' οnclick='eval()'>第二天第一版</input>
<textarea id='txt2' rows="20" cols="80"></textarea>
<script>
function eval()
{
var str=document.getElementById("txt1").innerText;
var result=0;
var token_array=[];
token_array=get_token_general({"lineno":1,"linelength":str.length,"str":str},token_array);
result=parse_exp(token_array);
if (temp.length>1)
{
if(temp[0]=='+')
{
result=addcalc(temp[1],temp[2]);}
else if (temp[0]=='-')
{result=subcalc(temp[2],temp[1]);}
else if(temp[0]=='*')
{
result=mulcalc(temp[1],temp[2]);}
else if (temp[0]=='/')
{result=divcalc(temp[2],temp[1]);}
}
else
{result=temp[0]+" can't eval"}
if (result==0)
result="parse success";
document.getElementById("txt2").innerText=result;
}
function parse_exp(arr)
{
var result=0;
var pretype=null;
for(var i=0;i<arr.length;i++)
{
if(pretype==null)
{pretype=arr[i].type;}
else if(pretype==arr[i].type)
{ result="error parse:at "+arr[i].startindex+" value: "+arr[i].value;}
else {pretype=arr[i].type}
}
return result;
}
function addcalc(operator1,operator2)
{
return parseInt(operator1)+parseInt(operator2);
}
function subcalc(operator1,operator2)
{
return parseInt(operator1)-parseInt(operator2);
}
function mulcalc(operator1,operator2)
{
return parseInt(operator1)*parseInt(operator2);
}
function divcalc(operator1,operator2)
{
var result =0;
if (operator2!=0)
{ result=parseInt(operator1)/parseInt(operator2);
}
else
{ result='runtime error : div is 0.'}
return result;
}
</script>
</BODY>
</HTML>
get_token_general.js 的代码如下:
//var b=[];
// a="int a,b;char c;c=a&&b;";
function get_token_general(a,b)
{
var str='';
var preState=0;
var curState=0;
var pretype=0;
var curtype=0;
var temp='';
var tempString='';
var id=b.length;
var startindex=0;
for(var i=0;i<a.linelength;i++)
{
str=a.str.substr(i,1);
curtype=getType(str);
curState=getState(preState,pretype,curtype);
temp=str;
if (curState==0) //(preState==0&&curState==0) // stop
{
id=id+1;
b.push({"id":id,"type":curtype,"value":temp,"lineno":a.lineno,"startindex":i});
}
else if (curState==1) //(preState==0&&curState==1) //jump up
{
startindex=i;
tempString=temp;
}
else if (curState==3) //(preState==1&&curState==0) //jump down
{
id=id+1;
b.push({"id":id,"type":pretype,"value":tempString,"lineno":a.lineno,"startindex":startindex});
id=id+1;
b.push({"id":id,"type":curtype,"value":temp,"lineno":a.lineno,"startindex":i});
tempString='';
}
else if (curState==8)
{
id=id+1;
b.push({"id":id,"type":pretype,"value":tempString,"lineno":a.lineno,"startindex":startindex});
tempString='';
}
else if (curState==2) //(preState==1&&curState==1) //run
{
tempString=tempString+temp;
}
else if (curState==4)
{
id=id+1;
b.push({"id":id,"type":pretype,"value":tempString,"lineno":a.lineno,"startindex":startindex});
startindex=i;
tempString=temp;
}
pretype=curtype;
preState=curState;
}
if(tempString!='')
{
id=id+1;
b.push({"id":id,"type":pretype,"value":tempString,"lineno":a.lineno,"startindex":startindex});
}
return b;
}
function getType(str)
{
var type=0;
if(isAlphabet(str)==1)
{
type=1;
}
else if(isDigit(str)==1)
{
type=5;
}
else if (isDelimiter(str)==1)
{
type=6;
}
else if (isBlock(str)==1)
{
type=7;
}
else if (isOperand(str)==1)
{
type=8;
}
else if ( isSpace(str)==1)
{
type=9;
}
return type;
}
function getState(preState,pretype,curtype)
{
var curState=0;
if(preState==0||preState==9||preState==3||preState==8)
{
if(curtype==5||curtype==1)
{curState=1;}
else if(curtype==6||curtype==7)
{ curState=0;}
else if(curtype==9)
{curState=9;}
}
else if(preState==1||preState==2)
{
if(curtype==5)
{
if(pretype!=8)
{curState=2;}
else
{curState=4;}
}
else if (curtype==1)
{
if(pretype==1)
{curState=2;}
else if (pretype==8)
{curState=4;}
else
{curState=2;}
}
else if(curtype==6||curtype==7)
{ curState=3;}
else if(curtype==8)
{curState=4;}
else if(curtype==9)
{curState=8;}
}
else if (preState==4)
{ if(curtype<=5&&pretype<=5)
{
curState=2;
}
else if(curtype==6||curtype==7)
{ curState=3;}
else if(curtype==8&&pretype==8)
{curState=2;}
else if(curtype==9)
{curState=8;}
else
{curState=4;}
}
else
{
curState=0;
}
return curState;
}
function isAlphabet(str)
{
var result=0;
if(str>='a' &&str<='z'||(str>='A'&&str<='Z'))
{
result=1;
}
return result;
}
function isDigit(str)
{
var result=0;
if(str>='0' &&str<='9'||(str=='.'))
{
result=1;
}
return result;
}
function isOperand(str)
{
var result=0;
if(str=='+')
{ result=1;}
else if (str=='-')
{ result=1;}
else if (str=='*')
{ result=1;}
else if (str=='/')
{ result=1;}
else if (str=='=')
{ result=1;}
else if (str=='>')
{ result=1;}
else if (str=='<')
{ result=1;}
else if (str=='%')
{ result=1;}
else if (str=='^')
{ result=1;}
else if (str=='&')
{ result=1;}
else if (str=='|')
{ result=1;}
return result;
}
function isDelimiter(str)
{
var result=0;
if(str==',')
{ result=1;}
else if (str==';')
{ result=1;}
else if (str==':')
{ result=1;}
return result;
}
function isBlock(str)
{
var result=0;
if(str=='(')
{ result=1;}
else if (str==')')
{ result=1;}
else if (str=='{')
{ result=1;}
else if (str=='}')
{ result=1;}
else if (str=='[')
{ result=1;}
else if (str==']')
{ result=1;}
return result;
}
function isSpace(str)
{
var result=0;
if(str==' ')
{ result=1;}
else if (str==' ')
{ result=1;}
return result;
}