yaccとかCompilerProviderクラスとかを使わずに、四則演算式を解析するプログラムを作成してみた。
字句解析器を分離してないので無駄にハマった・・。
参考サイト
https://blog.tiqwab.com/2017/01/04/recursive-descent-parser.html
今回の四則演算のBNF風メモ
E -> T E2
E2 -> "+" T E2 | "-" T E2 | empty
T -> F T2
T2 -> "*" F T2 | "/" F T2 | empty
F -> "(" E ")" | number
サンプルコード
上のBNFに沿って文字列を解析して、演算します。
エラーとかオーバーフローは放置です。
usingSystem;usingSystem.IO;usingSystem.Text;classTestLL{MemoryStreamms;TestLL(){}boolParse(stringexpr){ms=newMemoryStream(Encoding.UTF8.GetBytes(expr));intx=0;if(Expr_E(refx)){Console.WriteLine(x);returntrue;}returnfalse;}boolExpr_E(refinty){intx1=0;stringope="";intx2=0;if(Expr_T(refx1)){y=x1;if(Expr_E2(refope,refx2)){if(ope=="+"){y+=x2;}elseif(ope=="-"){y-=x2;}returntrue;}}thrownewException("Expr error");}boolExpr_E2(refstringyOpe,refinty){intx1=0;stringope="";intx2=0;if(TokenCheck("+")){yOpe="+";if(Expr_T(refx1)){y=x1;if(Expr_E2(refope,refx2)){if(ope=="+"){y+=x2;}elseif(ope=="-"){y-=x2;}returntrue;}}}elseif(TokenCheck("-")){yOpe="-";if(Expr_T(refx1)){y=x1;if(Expr_E2(refope,refx2)){if(ope=="+"){y+=x2;}elseif(ope=="-"){y-=x2;}// else{y=y;}returntrue;}}}else{returntrue;}thrownewException("Expr error");}boolExpr_T(refinty){intx1=0;stringope="";intx2=0;if(Expr_F(refx1)){y=x1;if(Expr_T2(refope,refx2)){if(ope=="*"){y*=x2;}elseif(ope=="/"){y/=x2;}returntrue;}}thrownewException("Expr error");}boolExpr_T2(refstringyOpe,refinty){intx1=0;stringope="";intx2=0;if(TokenCheck("*")){yOpe="*";if(Expr_F(refx1)){y=x1;if(Expr_T2(refope,refx2)){if(ope=="*"){y*=x2;}elseif(ope=="/"){y/=x2;}returntrue;}}}elseif(TokenCheck("/")){yOpe="/";if(Expr_F(refx1)){y=x1;if(Expr_T2(refope,refx2)){if(ope=="*"){y*=x2;}elseif(ope=="/"){y/=x2;}returntrue;}}}else{returntrue;}thrownewException("Expr error");}boolExpr_F(refinty){if(TokenCheck("(")){if(Expr_E(refy)){if(TokenCheck(")")){returntrue;}}}elseif(TokenIntCheck(refy)){returntrue;}ms.Position=pos;thrownewException("Expr error");}// ---------------------------------------------boolTerminalCheck(){intb=ms.ReadByte();if(b<0){returntrue;}else{ms.Position--;returnfalse;}}boolTokenIntCheck(refintx){longpos=ms.Position;inti=0;intb;x=0;//Console.WriteLine("CheckNum");while((b=ms.ReadByte())>=0){if('0'<=b&&b<='9'){x*=10;x+=b-'0';i++;}elseif('a'<=b&&b<='z'){ms.Position=pos;returnfalse;}elseif('A'<=b&&b<='Z'){ms.Position=pos;returnfalse;}else{break;}}if(i==0){ms.Position=pos;returnfalse;}if(b>=0){ms.Position--;}Console.WriteLine("Num");returntrue;}boolTokenCheck(strings){byte[]t=Encoding.UTF8.GetBytes(s);//Console.WriteLine("CheckToken('"+s+"')");longpos=ms.Position;inti=0;intb;while(i<t.Length){b=ms.ReadByte();if(b==t[i]){i++;}else{ms.Position=pos;returnfalse;}}Console.WriteLine("Token('"+s+"')");returntrue;}[STAThread]staticvoidMain(string[]args){stringexprStr="1+2+3";if(args.Length>=1){exprStr=String.Join("",args);}Console.WriteLine("Input: "+exprStr);vart=newTestLL();t.Parse(exprStr);}}
実行結果
Input: 1+2+3
6
Input: 1+2*3
7
Input: 1+2*(3-1)
5
Input: 7/2
3
※除算は整数除算なので、7/2
は3.5にはならず、3になります。