diff --git a/ocamllex+menhir/.gitignore b/ocamllex+menhir/.gitignore new file mode 100644 index 0000000..caeb268 --- /dev/null +++ b/ocamllex+menhir/.gitignore @@ -0,0 +1,2 @@ +_build/ +tiny diff --git a/ocamllex+menhir/Makefile b/ocamllex+menhir/Makefile new file mode 100644 index 0000000..c8ce553 --- /dev/null +++ b/ocamllex+menhir/Makefile @@ -0,0 +1,12 @@ +OCAMLBUILD := ocamlbuild -no-links -use-menhir -menhir "menhir -v" + +main: + @$(OCAMLBUILD) main.byte + @ln -sf _build/main.byte tiny + +check: + ./check + +clean: + $(OCAMLBUILD) -clean + rm -f tiny result diff --git a/ocamllex+menhir/check b/ocamllex+menhir/check new file mode 100755 index 0000000..73dc052 --- /dev/null +++ b/ocamllex+menhir/check @@ -0,0 +1,8 @@ +#!/bin/bash +if [ -f result ] ; then + rm result +fi + +for exp in `cat ../testcases`; do + echo $exp | ./tiny >> result +done diff --git a/ocamllex+menhir/eval.ml b/ocamllex+menhir/eval.ml new file mode 100644 index 0000000..0eb4c9c --- /dev/null +++ b/ocamllex+menhir/eval.ml @@ -0,0 +1,19 @@ +open Tree + +let rec eval_exp (e: exp) = + match e with + | Identifier (_, line) -> failwith ("(line " ^ string_of_int line ^ ") Cannot evaluate the value of an identifier\n") + | IntLiteral (v, _) -> v + | Binary (lhs, rhs, op) -> eval_binary_exp lhs rhs op +and eval_binary_exp (lhs: exp) (rhs: exp) (op: operator) = + match op with + | Plus -> eval_exp lhs + eval_exp rhs + | Minus -> eval_exp lhs - eval_exp rhs + | Mult -> eval_exp lhs * eval_exp rhs + | Div -> eval_exp lhs / eval_exp rhs +;; + +let eval_program (prog: program) = + match prog with + | Program e -> eval_exp e +;; \ No newline at end of file diff --git a/ocamllex+menhir/main.ml b/ocamllex+menhir/main.ml new file mode 100644 index 0000000..0822f93 --- /dev/null +++ b/ocamllex+menhir/main.ml @@ -0,0 +1,11 @@ +print_endline "Enter a \"TinyLang\" expression followed by Ctrl-d:"; +let lexbuf = Lexing.from_channel stdin in +try + let prog = Tiny_parser.program Tiny_lexer.token lexbuf in + print_string "Pretty print: "; + Pretty.pretty_program prog; + let evaluated_val = Eval.eval_program prog in + print_endline ("Evaluated value: " ^ string_of_int evaluated_val) +with + | Tiny_parser.Error -> print_endline "Parse error" + | Failure msg -> print_endline ("Error: " ^ msg) \ No newline at end of file diff --git a/ocamllex+menhir/pretty.ml b/ocamllex+menhir/pretty.ml new file mode 100644 index 0000000..af84149 --- /dev/null +++ b/ocamllex+menhir/pretty.ml @@ -0,0 +1,39 @@ +open Tree + +let rec pretty_exp (e: exp) = + match e with + | Identifier (id, _) -> print_string id + | IntLiteral (v, _) -> print_string (string_of_int v) + | Binary (lhs, rhs, op) -> pretty_binary_exp lhs rhs op +and pretty_binary_exp (lhs: exp) (rhs: exp) (op: operator) = + match op with + | Plus -> + print_string "("; + pretty_exp lhs; + print_string " + "; + pretty_exp rhs; + print_string ")" + | Minus -> + print_string "("; + pretty_exp lhs; + print_string " - "; + pretty_exp rhs; + print_string ")" + | Mult -> + print_string "("; + pretty_exp lhs; + print_string " * "; + pretty_exp rhs; + print_string ")" + | Div -> + print_string "("; + pretty_exp lhs; + print_string " / "; + pretty_exp rhs; + print_string ")" +;; + +let pretty_program (prog: program) = + match prog with + | Program e -> pretty_exp e; print_string "\n" +;; \ No newline at end of file diff --git a/ocamllex+menhir/tiny_lexer.mll b/ocamllex+menhir/tiny_lexer.mll new file mode 100644 index 0000000..0f574aa --- /dev/null +++ b/ocamllex+menhir/tiny_lexer.mll @@ -0,0 +1,31 @@ +{ + open Tiny_parser + open Lexing + + (* Get the text of the current buffer *) + let get_text = Lexing.lexeme + let get_line_num buf = let p = buf.lex_curr_p in p.pos_lnum + +} + (* Helper regex definitions *) + let letter = ['a'-'z' 'A'-'Z'] + let digit = ['0'-'9'] + + let identifier = ((letter | '_') (letter | digit | '_')*) + +rule token = parse + | eof { EOFt } + | (' ' | '\t' | '\n' | '\r') { token lexbuf } + + | '(' { LPARENt } + | ')' { RPARENt } + + | '+' { PLUSt } + | '-' { MINUSt } + | '*' { MULTt } + | '/' { DIVt } + + | identifier { IDENTIFIERt (get_text lexbuf, get_line_num lexbuf) } + | ('0' | ['1'-'9']digit*) { INTVALt (int_of_string (get_text lexbuf), get_line_num lexbuf) } + + | _ { failwith ("(line " ^ (string_of_int (get_line_num lexbuf)) ^ ") Unexpected char '" ^ (get_text lexbuf) ^ "'") } \ No newline at end of file diff --git a/ocamllex+menhir/tiny_parser.mly b/ocamllex+menhir/tiny_parser.mly new file mode 100644 index 0000000..7cada35 --- /dev/null +++ b/ocamllex+menhir/tiny_parser.mly @@ -0,0 +1,27 @@ +%{ + +%} + +%token EOFt +%token LPARENt RPARENt +%token PLUSt MINUSt MULTt DIVt +%token INTVALt +%token IDENTIFIERt + +%left PLUSt MINUSt +%left MULTt DIVt + +%start program + + +%% +program: exp EOFt { Tree.Program $1 }; + +exp: IDENTIFIERt { Tree.Identifier (fst $1, snd $1) } + | INTVALt { Tree.IntLiteral (fst $1, snd $1) } + | LPARENt exp RPARENt { $2 } + | exp MULTt exp { Tree.Binary ($1, $3, Tree.Mult) } + | exp DIVt exp { Tree.Binary ($1, $3, Tree.Div) } + | exp PLUSt exp { Tree.Binary ($1, $3, Tree.Plus) } + | exp MINUSt exp { Tree.Binary ($1, $3, Tree.Minus) } +; diff --git a/ocamllex+menhir/tree.ml b/ocamllex+menhir/tree.ml new file mode 100644 index 0000000..4e24a68 --- /dev/null +++ b/ocamllex+menhir/tree.ml @@ -0,0 +1,19 @@ +type operator = + | Plus + | Minus + | Mult + | Div +;; + +type exp = + (* id string, line number *) + | Identifier of string * int + (* literal value, line number *) + | IntLiteral of int * int + (* lhs, rhs, operator *) + | Binary of exp * exp * operator +;; + +type program = + Program of exp +;; \ No newline at end of file