自著——30天自制红孩儿解释器 第6天 支持更多的操作符
在第5天时,已经实现了带括号的四则混合运算的式子的计算。
为了在以后能够实现 对条件语句的解释执行,现在需要加上关系操作符
与逻辑操作符。
优先级是 运算操作符 优先于 关系操作符,关系操作符优先于逻辑操作符。
关系操作符的解释执行的结果只有真与假两种值。分别用1和0 来表示。1表示真。
表达式的BNF定义在第5天的基础上,增加以下的内容。
<relation_exp> :== <simple_exp> > <simple_exp> |<simple_exp> < <simple_exp> |
<simple_exp> >= <simple_exp> |<simple_exp> <= <simple_exp> |
<simple_exp> == <simple_exp> |<simple_exp> != <simple_exp>
<boolean_exp> :==<relation_exp> |<relation_exp> && <relation_exp> |
<relation_exp> ||<relation_exp> | <relation_exp> ^^ <relation_exp>
第6天第一版本的程序
需求: 实现 例如以下的关系表达式的执行。 5 > 4 , 5+9 == 14
实现: 因为关系操作符与运算操作符不在同一个优先级上,所以需要新开发
parse_relation_exp 函数,进行解析。
在执行函数中,也需要新增加 相应的执行函数。
形如以下的关系表达式,只能解析到第二个关系操作符之前的部分。
9<(2*(1+(5*5)))-6>((1+2)*3)+1 > ((9))
本版程序,在执行程序中,修改了逻辑错误,扩展了程序的适用范围。由原来的只能计算
整数的对象,扩展到有理数。新增了小数作为运算对象。
同时把 程序上用于执行的程序部分,与之相关的十多个函数独立成一个函数库,放在一个
单独的文件中,便于管理与维护。函数的名称进行了相关的规范化命名。
例如以下的修改。
addcalc --->execute_calc_add
subcalc ---->execute_calc_subtraction
mulcalc ----> execute_calc_multiply
divcalc ---->execute_calc_divide
新的函数命名,更好的体现了函数的模块性。
第6天第二版本的程序
需求:在今天上一版本的基础上,实现 例如 以下的逻辑表达式的执行。 5 >4 && 5+9==14
第6天第三版本的程序
需求:功能与上一版本的相同。在执行过程中有优化的操作。 与逻辑操作符
的左侧 执行为假时,右侧的不需要再执行。直接返回值。
在或逻辑操作符的左侧 执行为真时,右侧的不需要再执行。 直接返回值。
新增加 异或逻辑操作.
<HTML>
<HEAD>
<TITLE> 30天自制解释器 6.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>
<script src='execute_lib.js' ></script>
</HEAD>
<BODY>
<p>本版本只能计算 a+b ,a-b,a*b ,a/b ,且本版有多个操作符,解析执行该表达式</p>
<p>允许有多个优先级存在 例如 : ((1+2)*3)+1 > ((9)) ,9< (2*(1+(5*5)))-6</p>
<p>a和b 都是正整数,而且 a 与 +,b 与+ 之间都可以有空格 </p>
<p>提供对括号改变运算的优先级的功能,本版本 支持多层嵌套的括号。和关系操作符</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_sim_exp(token_array,0,0);
result=parse_relation_exp(token_array,0,0);
// if (hasError(result.res)==0)
// result=execute_exp(token_array);
document.getElementById("txt2").innerText=result.res;
}
function parse_relation_exp(arr,i,level)
{
var operator1={};
var operator2={};
var result="";
operator1= parse_sim_exp(arr,i,level);
if(hasError(operator1.res)>=0)
{
return {"res":operator1.res,"i":i};
}
else {result =operator1.res;
i=operator1.i;
}
if ( i<arr.length-2 &&(arr[i+1].value=='>'||arr[i+1].value=='<'||arr[i+1].value=='>='||arr[i+1].value=='<='||arr[i+1].value=='=='||arr[i+1].value=='<>') )
{
operator2= parse_sim_exp(arr,i+2,level);
if( hasError(operator2.res)==-1)
{
result=execute_binary_relation_operator_action(arr[i+1].value,result,operator2.res);
i=operator2.i;
}
else if(hasError(operator2.res)>=0)
{result=operator2.res;
// break;
}
}
return {"res":result,"i":i};
}
function parse_sim_exp(arr,i,level)
{
var operator1={};
var operator2={};
var result="";
operator1= parse_term(arr,i,level);
if(hasError(operator1.res)>=0)
{
return {"res":operator1.res,"i":i};
}
else {result =operator1.res;
i=operator1.i;
}
while( i<arr.length-2 &&(arr[i+1].value=='+'||arr[i+1].value=='-') )
{
operator2= parse_term(arr,i+2,level);
if( hasError(operator2.res)==-1)
{
result=execute_binary_calc_operator_action(arr[i+1].value,result,operator2.res);
i=operator2.i;
}
else if(hasError(operator2.res)>=0)
{result=operator2.res;
break;
}
}
return {"res":result,"i":i};
}
function parse_term(arr,i,level)
{
var operator1=0;
var operator2=0;
var result="";
operator1= parse_factor(arr,i,level);
if(hasError(operator1.res)>=0)
{
return {"res":operator1.res,"i":i};
}
else {result =operator1.res;
i=operator1.i;}
while( i<arr.length-2 &&(arr[i+1].value=='*'||arr[i+1].value=='/') )
{
operator2= parse_factor(arr,i+2,level);
if( hasError(operator2.res)==-1)
{
result=execute_binary_calc_operator_action(arr[i+1].value,result,operator2.res);
i=operator2.i;
}
else if(hasError(operator2.res)>=0)
{result=operator2.res;
break;
}
}
return {"res":result,"i":i};
}
function parse_factor(arr,i,level)
{
var result=0;
var resultObj={};
if(arr[i].type==5)
{result=arr[i].value;}
else if (arr[i].type==7)
{
if (arr[i].value=='(')
{
resultObj=parse_sim_exp(arr,i+1,level+1);
result=resultObj.res;
i=resultObj.i;
}
if (i<arr.length-1&& arr[i+1].value==')')
{
i=i+1;
}
else if (i==arr.length-1 && arr[i]!=')')
{
result='[error]: should be ) : at '+arr[i].startindex+" but value: "+arr[i].value;
}
else
{
result='[error]: should be ) : at '+arr[i].startindex+" but value: "+arr[i].value;
}
}
else
{result='[error]: is not number: at '+arr[i].startindex+" value: "+arr[i].value;}
return {"res":result,"i":i};
}
function hasError(str)
{
if(isNum(str)==1)
return -1;
else
return str.indexOf("error");
}
function make_lisp_program_action(operand,operator1,operator2)
{
return "("+ operand + " "+operator1+" "+operator2+")";
}
</script>
</BODY>
</HTML>
execute_lib.js文件的代码如下:
function execute_exp(arr)
{
var start=0;
var result=-1;
var params=[];
if (arr[start].type!=5)
{start=1;}
for (var i=start;i<arr.length-1;i+=2)
{
if(result==-1)
{params.push(arr[i+1].value);
params.push(arr[i+2].value);
params.push(arr[i].value);
}
else
{
params=[];
params.push(arr[i+1].value);
params.push(arr[i+2].value);
params.push(result);
}
result=execute_exp_bytwoOperators(params);
}
return result;
}
function execute_exp_bytwoOperators(arr)
{
var result=0;
var temp=arr;
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"}
return result;
}
function execute_binary_logic_operator_action(operand,oper1,oper2)
{
var result=0;
if(operand=='>')
{
result=execute_relation_greater(oper1,oper2);}
else if (operand=='&&')
{result=execute_logic_and(oper1,oper2);}
else if(operand=='||')
{
result=execute_logic_or(oper1,oper2);}
else if(operand=='^^')
{
result=execute_logic_xor(oper1,oper2);}
else
{result=operand+" can't eval"}
return result;
}
function execute_binary_relation_operator_action(operand,oper1,oper2)
{
var result=0;
if(operand=='>')
{
result=execute_relation_greater(oper1,oper2);}
else if (operand=='<')
{result=execute_relation_less(oper1,oper2);}
else if(operand=='>=')
{
result=execute_relation_greaterEqual(oper1,oper2);}
else if (operand=='<=')
{result=execute_relation_lessEqual(oper1,oper2);}
else if(operand=='<>')
{
result=execute_relation_notEqual(oper1,oper2);}
else if (operand=='==')
{result=execute_relation_Equal(oper1,oper2);}
else
{result=operand+" can't eval"}
return result;
}
function execute_binary_calc_operator_action(operand,oper1,oper2)
{
var result=0;
if(operand=='+')
{
result=execute_calc_add(oper1,oper2);}
else if (operand=='-')
{result=execute_calc_subtraction(oper1,oper2);}
else if(operand=='*')
{
result=execute_calc_multiply(oper1,oper2);}
else if (operand=='/')
{result=execute_calc_divide(oper1,oper2);}
else
{result=operand+" can't eval"}
return result;
}
function execute_logic_and(oper1,oper2)
{
if(oper1==0)
return 0;
if(oper1==1&&oper2==1)
return 1;
else
{return 0;}
}
function execute_logic_or(oper1,oper2)
{
if(oper1==1)
return 1;
if(oper1==0||oper2==1)
return 1;
else
{return 0;}
}
function execute_logic_xor(oper1,oper2)
{
if(oper1==oper2)
return 0;
else
{return 1;}
}
function execute_logic_and(oper1,oper2)
{
if(oper1==0)
return 0;
if(oper1==1&&oper2==1)
return 1;
else
{return 0;}
}
// relation
function execute_relation_greater(oper1,oper2)
{
if (oper1>oper2)
return 1;
else
return 0;
}
function execute_relation_less(oper1,oper2)
{
if (oper1<oper2)
return 1;
else
return 0;
}
function execute_relation_greaterEqual(oper1,oper2)
{
if (oper1>=oper2)
return 1;
else
return 0;
}
function execute_relation_lessEqual(oper1,oper2)
{
if (oper1<=oper2)
return 1;
else
return 0;
}
function execute_relation_notEqual(oper1,oper2)
{
if (oper1!=oper2)
return 1;
else
return 0;
}
function execute_relation_Equal(oper1,oper2)
{
if (oper1==oper2)
return 1;
else
return 0;
}
//addcalc --->execute_calc_add
function execute_calc_add(operator1,operator2)
{
return parseFloat(operator1)+parseFloat(operator2);
}
// subcalc ----execute_calc_subtraction
function execute_calc_subtraction(operator1,operator2)
{
return parseFloat(operator1)-parseFloat(operator2);
}
//mulcalc ----> execute_calc_multiply
function execute_calc_multiply(operator1,operator2)
{
return parseFloat(operator1)*parseFloat(operator2);
}
//divcalc ---->execute_calc_divide
function execute_calc_divide(operator1,operator2)
{
var result =0;
if (operator2!=0)
{ result=parseFloat(operator1)/parseFloat(operator2);
}
else
{ result='runtime error : div is 0.'}
return result;
}
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;
}
/*4.3 版本时加上*/
function isNum(str)
{
var result=1;
for (var i=0; i<str.length ;i++ )
{
if (isDigit()!=1)
{
result=0;
break;
}
}
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;}
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;
}