Tigerインタプリタで文字列リテラルを評価したい
https://gyazo.com/5ee5fd67da12869886a4e9e23a1de96e
evalの返り値が int 型固定だったので string 型を返せるようにした。あわせて、文字列リテラルを評価できるようにした。
文字列リテラルは日本語も扱えるようにしたいんだけど、Lexerの書き方が難しかったため ASCII のみを対象とした。
code:diff
diff --git a/bin/main.ml b/bin/main.ml
index 67f5dc8..6da3359 100644
--- a/bin/main.ml
+++ b/bin/main.ml
@@ -4,5 +4,5 @@ let () =
let expr = Tiger.Parser.program Tiger.Lexer.token buff in
let result = Tiger.Eval.f expr in
print_string "Result: ";
- print_int result;
+ Tiger.Eval.print_val result;
print_newline ()
diff --git a/lib/dune b/lib/dune
index a32b54b..c3efbf4 100644
--- a/lib/dune
+++ b/lib/dune
@@ -2,4 +2,5 @@
(name tiger))
(ocamllex lexer)
+
(ocamlyacc parser)
diff --git a/lib/eval.ml b/lib/eval.ml
index bdec676..52d98d6 100644
--- a/lib/eval.ml
+++ b/lib/eval.ml
@@ -1,11 +1,24 @@
+type val_t = IntVal of int | StringVal of string
+
let rec f expr =
match expr with
- Syntax.IntExp n -> n
+ Syntax.IntExp n -> IntVal(n)
+ | Syntax.StringExp s -> StringVal(s)
| Syntax.OpExp(e1, op, e2) ->
- let v1 = f e1 in
- let v2 = f e2 in
+ let v1 = match (f e1) with
+ IntVal(n) -> n
+ | _ -> failwith "integer value expected" in
+ let v2 = match (f e2) with
+ IntVal(n) -> n
+ | _ -> failwith "integer value expected" in
(match op with
- Syntax.Plus -> v1 + v2
- | Syntax.Minus -> v1 - v2
- | Syntax.Times -> v1 * v2
- | Syntax.Divide -> v1 / v2)
+ Syntax.Plus -> IntVal(v1 + v2)
+ | Syntax.Minus -> IntVal(v1 - v2)
+ | Syntax.Times -> IntVal(v1 * v2)
+ | Syntax.Divide -> IntVal(v1 / v2))
+
+let string_of_val v = match v with
+ IntVal(n) -> string_of_int n
+ | StringVal(s) -> s
+
+let print_val v = print_string (string_of_val v)
diff --git a/lib/lexer.mll b/lib/lexer.mll
index 2af6c5e..f8e9759 100644
--- a/lib/lexer.mll
+++ b/lib/lexer.mll
@@ -5,7 +5,13 @@
(* 正規表現の略記 *)
let space = ' '\t' '\r' '\n'
+let letter = lower | upper
+let symbol = '#' '$' '%' '&' '*' '+' '-' '.' '/' ':' '<' '=' '>' '?' '@' '^' '_' '`' '|' '~'
+let printable = symbol | letter | digit
+(* 字句解析器 *)
(* 字句解析の規則 *)
rule token = parse
space+ { token lexbuf } (* 空白は読み飛ばす *)
@@ -15,4 +21,10 @@ rule token = parse
| "*" { Parser.TIMES }
| "/" { Parser.DIVIDE }
| eof { Parser.EOF }
+ (* 文字列リテラル *)
+ | "\"" (printable | space)* "\"" {
+ (* 両端のダブルクォートを取り除く *)
+ let str = String.sub (Lexing.lexeme lexbuf) 1 ((String.length (Lexing.lexeme lexbuf)) - 2) in
+ Parser.STRING(str)
+ }
| _ { failwith ("invalid character " ^ (Lexing.lexeme lexbuf)) }
diff --git a/lib/parser.mly b/lib/parser.mly
index b1df3e8..e973287 100644
--- a/lib/parser.mly
+++ b/lib/parser.mly
@@ -4,6 +4,7 @@
// トークンの定義
%token EOF
%token <int> INT
+%token <string> STRING
%token PLUS MINUS TIMES DIVIDE
%token EOF
@@ -21,6 +22,7 @@ program: exp EOF { $1 }
exp:
INT { Syntax.IntExp($1) }
+| STRING { Syntax.StringExp($1) }
| exp PLUS exp { Syntax.OpExp($1, Syntax.Plus, $3) }
| exp MINUS exp { Syntax.OpExp($1, Syntax.Minus, $3) }
| exp TIMES exp { Syntax.OpExp($1, Syntax.Times, $3) }
diff --git a/lib/syntax.ml b/lib/syntax.ml
index a03eb72..a9e9c04 100644
--- a/lib/syntax.ml
+++ b/lib/syntax.ml
@@ -2,4 +2,5 @@ type op_t = Plus | Minus | Times | Divide
type t =
IntExp of int (* 整数 *)
+ | StringExp of string (* 文字列 *)
| OpExp of t * op_t * t (* 二項演算子 *)
diff --git a/test/tiger_test.expected b/test/tiger_test.expected
index 4ae9527..4cda5b1 100644
--- a/test/tiger_test.expected
+++ b/test/tiger_test.expected
@@ -3,3 +3,4 @@ result: 60
result: 20
result: 3
result: 100
+result: A
diff --git a/test/tiger_test.ml b/test/tiger_test.ml
index 181b65f..58401ff 100644
--- a/test/tiger_test.ml
+++ b/test/tiger_test.ml
@@ -7,33 +7,40 @@ let eval src =
let () =
let src = "4649" in
print_string "result: ";
- print_int (eval src);
+ Tiger.Eval.print_val (eval src);
print_newline ()
(* 足し算 *)
let () =
let src = "10 + 20 + 30" in
print_string "result: ";
- print_int (eval src);
+ Tiger.Eval.print_val (eval src);
print_newline ()
(* 引き算 *)
let () =
let src = "50 - 20 - 10" in
print_string "result: ";
- print_int (eval src);
+ Tiger.Eval.print_val (eval src);
print_newline ()
(* 掛け算 *)
let () =
let src = "1 + 2 * 3 - 4" in
print_string "result: ";
- print_int (eval src);
+ Tiger.Eval.print_val (eval src);
print_newline ()
(* 割り算 *)
let () =
let src = "98 + 20 / 2 / 5" in
print_string "result: ";
- print_int (eval src);
+ Tiger.Eval.print_val (eval src);
+ print_newline ()
+
+(* 文字列リテラル *)
+let () =
+ let src = "\"A\"" in
+ print_string "result: ";
+ Tiger.Eval.print_val (eval src);
print_newline ()