以下のプログラムに ・引き算と割り算の処理を加える ・単項演算の+とーを付け加える ・詳しいエラーメッセージを出力する ・数式の計算をdoubleで行うようにして1.23等の小数点での数値も入力できるようにする ・巾乗の数式も扱えるようにする、2の3乗を2^3とするかFortanのように2**3と入力する ・定義pi(円周率)、e(ネピア数)が使えるように拡張 ・SIN COS LOGなどの1引数の組み込み関数が使えるように拡張 ・変数を定義できるように拡張 例 let x=2; let y=x+5; x+2*y; ・ユーザーが自分の関数を定義できるように拡張※この拡張では関数の定義本体を木構造の形で内部に保持する必要がある。 例 fun f(x,y) = x + y * 2; f(1,2)*f(2,3);
// 次の文字を読み込む void nextSym() throws Exception { int c = input.read(); if (c == -1) currSym = CHAR_EOF; else currSym = (char)c; } // 行末まで読み飛ばす void skipLine() throws Exception { while (currSym != '\n' && currSym != CHAR_EOF) { nextSym(); } } /*=============================================================== 字句解析 ===============================================================*/ int currTok; // 現在のトークン int tokvalNum; // 数値のトークンの値 String tokvalName; // 変数名のトークンの値
// トークンの種類 final static int TOK_COMMA = 1; // , final static int TOK_SEM = 2; // ; final static int TOK_LP = 3; // ( final static int TOK_RP = 4; // ) final static int TOK_PLUS = 11; // + final static int TOK_MINUS = 12; // - final static int TOK_MUL = 13; // * final static int TOK_DIV = 14; // / final static int TOK_NAME = -1; // 名前、識別子 final static int TOK_NUM = -2; // 数値 final static int TOK_EOF = -4; // End of File final static int TOK_ERROR = -99; // エラー
// 変数名の最大長 final static int MAX_LEN_NAME = 50;
// 数値のトークンを生成 void getNum() throws Exception { int v = 0; while (Character.isDigit(currSym)) { v = v * 10 + Character.digit(currSym, 10); nextSym(); } tokvalNum = v; } // 名前(変数)のトークンを生成 char[] buf = new char[MAX_LEN_NAME]; void getName() throws Exception { int i; for (i = 0; Character.isLetterOrDigit(currSym); i++) { buf[i] = currSym; nextSym(); } tokvalName = new String(buf, 0, i); // 最大長を越える名前の後ろは読み飛ばす */ while (Character.isLetterOrDigit(currSym)) nextSym(); } // トークンを生成して大域変数 currTok に代入 void nextToken() throws Exception { // 空白文字の読み飛ばし while (Character.isWhitespace(currSym)) nextSym(); // ファイルの終わり if (currSym == CHAR_EOF) { currTok = TOK_EOF; } // 数値 else if (Character.isDigit(currSym)) { getNum(); currTok = TOK_NUM; } // 名前(識別子) else if (Character.isLetter(currSym)) { getName(); currTok = TOK_NAME; } // 特殊記号 else { switch (currSym) { case ',' : currTok = TOK_COMMA; nextSym(); break; case ';' : currTok = TOK_SEM; nextSym(); break; case '(' : currTok = TOK_LP; nextSym(); break; case ')' : currTok = TOK_RP; nextSym(); break; case '+' : currTok = TOK_PLUS; nextSym(); break; case '-' : currTok = TOK_MINUS; nextSym(); break; case '*' : currTok = TOK_MUL; nextSym(); break; case '/' : currTok = TOK_DIV; nextSym(); break; default: currTok = TOK_ERROR; nextSym(); } } } /*========================================================== 構文解析 ==========================================================*/ void calcExp() throws Exception { try { int val = E(); if (currTok != TOK_SEM) { // 式の終わりは ; throw new ParseError(); } System.out.println(val); nextToken(); // ; の次トークンを読む } catch (ParseError e) { System.out.println(e); skipLine(); nextToken(); } } int E() throws Exception { int val = T(); while (currTok == TOK_PLUS) { nextToken(); // + の次のトークンを読む val += T(); } return val; } int T() throws Exception { int val = F(); while (currTok == TOK_MUL) { nextToken(); // * の次のトークンを読む val *= T(); } return val; } int F() throws Exception { if (currTok == TOK_LP) { nextToken(); // ( の次のトークンを読む int val = E(); if (currTok != TOK_RP) { throw new ParseError(); } nextToken(); // ) の次のトークンを読む return val; } else if (currTok == TOK_NAME) { nextToken(); // 識別子の次のトークンを読む return 0; } else if (currTok == TOK_NUM) { nextToken(); // 数値の次のトークンを読む return tokvalNum; } else { throw new ParseError(); } } /*=============================================================== テスト ===============================================================*/ void test(String[] args) { try { initialize(null); nextSym(); nextToken(); while (currTok != TOK_EOF) { calcExp(); } } catch (Exception e) { System.out.println(e); } } public static void main(String[] args) { Parser p = new Parser(); p.test(args); } }