Tigerインタプリタで条件分岐したい
やったこと
IfExp を追加
関数から返ってきた際に関数内の env をそのまま利用してしまうバグを修正
おまけ ( exp ) にも対応した
フィボナッチ数が計算できるようになった
code:diff
diff --git a/lib/eval.ml b/lib/eval.ml
index 7cb0bed..d759c21 100644
--- a/lib/eval.ml
+++ b/lib/eval.ml
@@ -17,6 +17,15 @@ let rec f expr env: val_t * table =
env
decs
in f body new_env
+ | Syntax.IfExp { test; then'; else' } ->
+ let v, env = f test env in
+ (match v with
+ | IntVal 1 -> f then' env
+ | IntVal 0 ->
+ (match else' with
+ | Some else' -> f else' env
+ | None -> (IntVal 0, env))
+ | _ -> failwith "type error")
| Syntax.CallExp { id; args } ->
(let func = List.assoc id env in
match func with
@@ -30,7 +39,8 @@ let rec f expr env: val_t * table =
| _ -> failwith "type error"
in
let new_env = get_args args env field_names in
- f body new_env
+ let result, _ = f body new_env
+ in result, env
| BuiltInFunction builtin_func ->
(let rec eval_args args env =
match args with
diff --git a/lib/lexer.mll b/lib/lexer.mll
index 2c651ef..8031094 100644
--- a/lib/lexer.mll
+++ b/lib/lexer.mll
@@ -19,6 +19,9 @@ rule token = parse
| "end" { Parser.END }
| "var" { Parser.VAR }
| "function" { Parser.FUNCTION }
+ | "if" { Parser.IF }
+ | "then" { Parser.THEN }
+ | "else" { Parser.ELSE }
| "+" { Parser.PLUS }
| "-" { Parser.MINUS }
| "*" { Parser.TIMES }
diff --git a/lib/parser.mly b/lib/parser.mly
index df68faa..fa74fa2 100644
--- a/lib/parser.mly
+++ b/lib/parser.mly
@@ -8,7 +8,7 @@
%token <string> ID
%token PLUS MINUS TIMES DIVIDE
%token EQ NEQ LT LE GT GE
-%token LET IN END
+%token LET IN END IF THEN ELSE
%token VAR FUNCTION ASSIGN
%token LPAREN RPAREN COMMA COLON
%token EOF
@@ -17,7 +17,7 @@
// %token COMMA COLON SEMICOLON LPAREN RPAREN LBRACK RBRACK
// %token LBRACE RBRACE DOT
// %token AND OR
-// %token ARRAY IF THEN ELSE WHILE FOR TO DO OF
+// %token ARRAY WHILE FOR TO DO OF
// %token BREAK NIL
// %token FUNCTION TYPE
@@ -38,6 +38,7 @@ exp:
INT { Syntax.IntExp($1) }
| STRING { Syntax.StringExp($1) }
| ID { Syntax.VarExp($1) }
+| LPAREN exp RPAREN { $2 }
| exp PLUS exp { Syntax.OpExp { left = $1; op = Syntax.PlusOp; right = $3} }
| exp MINUS exp { Syntax.OpExp { left = $1; op = Syntax.MinusOp; right = $3} }
| exp TIMES exp { Syntax.OpExp { left = $1; op = Syntax.TimesOp; right = $3} }
@@ -50,6 +51,8 @@ exp:
| exp GE exp { Syntax.OpExp { left = $1; op = Syntax.GeOp; right = $3} }
| LET decs IN exp END { Syntax.LetExp { decs = $2; body = $4 } }
| ID LPAREN args RPAREN { Syntax.CallExp { id = $1; args = $3 } }
+| IF exp THEN exp { Syntax.IfExp { test = $2; then' = $4; else' = None } }
+| IF exp THEN exp ELSE exp { Syntax.IfExp { test = $2; then' = $4; else' = Some ($6) } }
args:
{ [] } // 空の場合
diff --git a/lib/syntax.ml b/lib/syntax.ml
index f01250b..fdbabc8 100644
--- a/lib/syntax.ml
+++ b/lib/syntax.ml
@@ -19,6 +19,7 @@ and op_t =
| VarExp of string
| CallExp of { id: string; args: t list } (* 関数呼び出し *)
| OpExp of { left: t; op: op_t; right: t } (* 二項演算子 *)
+ | IfExp of { test: t; then': t; else': t option } (* if文 *)
and dec_t =
| VarDec of symbol * t (* 変数宣言 *)
diff --git a/samples/fib.tiger b/samples/fib.tiger
new file mode 100644
index 0000000..8fba652
--- /dev/null
+++ b/samples/fib.tiger
@@ -0,0 +1,7 @@
+let
+ function fib(n:int) =
+ if n < 2 then n
+ else fib(n-1) + fib(n-2)
+in
+ fib(10)
+end
diff --git a/test/tiger_test.expected b/test/tiger_test.expected
index aac8ff3..f44e0a3 100644
--- a/test/tiger_test.expected
+++ b/test/tiger_test.expected
@@ -28,3 +28,5 @@ result: 100
result: 20
result: 4649
Hello World!!
+Goodbye World!!
+result: 55
diff --git a/test/tiger_test.ml b/test/tiger_test.ml
index 3ada260..ea6f020 100644
--- a/test/tiger_test.ml
+++ b/test/tiger_test.ml
@@ -267,13 +267,38 @@ let () =
Tiger.Eval.print_val (eval_with_env src env);
print_newline ()
-(* 組み込み関数 print(s: string) の呼び出し *)
+(* if-then *)
let () =
let src = {|
let in
- print("Hello World!!")
+ if 1 = 1 then print("Hello World!!\n")
end
|}
in
- ignore(eval src);
+ ignore(eval src)
+
+(* if-then-else *)
+let () =
+ let src = {|
+ let in
+ if 1 = 0 then print("Hello World!!\n")
+ else print("Goodbye World!!\n")
+ end
+ |}
+ in
+ ignore(eval src)
+
+(* フィボナッチ数 *)
+let () =
+ let src = {|
+ let
+ function fib(n:int) =
+ if n < 2 then n
+ else fib(n-1) + fib(n-2)
+ in
+ fib(10)
+ end
+ |}
+ in print_string "result: ";
+ Tiger.Eval.print_val (eval src);
print_newline ()
yaccのコンフリクトの解消法
if-then-else まわりで yacc のコンフリクトが発生していた。最低限の lex/yacc に解決方法が載っていたのでそのまま採用。
code:diff
diff --git a/lib/parser.mly b/lib/parser.mly
index fa74fa2..4040da7 100644
--- a/lib/parser.mly
+++ b/lib/parser.mly
@@ -26,9 +26,12 @@
%type <Syntax.t> program
-%left EQ NEQ LT LE GT GE
+%nonassoc THEN
+%nonassoc ELSE
%left PLUS MINUS
%left TIMES DIVIDE
+%nonassoc EQ NEQ LT LE GT GE
// 文法規則の定義
%%