From a978336e52f7ab15b3fb7c843a236d7a164c8620 Mon Sep 17 00:00:00 2001 From: Mikail Khan Date: Tue, 19 Oct 2021 16:39:10 -0400 Subject: [PATCH 1/6] restructured project to add tests --- bench.sh | 4 +--- bin/dune | 3 +++ bin/rustscript_cli.ml | 27 ++++++++++++++++++++++++++ dune-project | 1 + lib/dune | 8 ++++++++ {src => lib}/eval.ml | 17 +++++++++++++--- {src => lib}/parser.ml | 0 lib/run.ml | 20 +++++++++++++++++++ {src => lib}/scanner.ml | 0 {src => lib}/types.ml | 0 rustscript.opam | 11 +++++++++++ src/dune | 7 ------- src/rustscript.ml | 43 ----------------------------------------- test/dune | 3 +++ test/fib.ml | 11 +++++++++++ test/tuple.ml | 13 +++++++++++++ test/util.ml | 16 +++++++++++++++ 17 files changed, 128 insertions(+), 56 deletions(-) create mode 100644 bin/dune create mode 100644 bin/rustscript_cli.ml create mode 100644 lib/dune rename {src => lib}/eval.ml (91%) rename {src => lib}/parser.ml (100%) create mode 100644 lib/run.ml rename {src => lib}/scanner.ml (100%) rename {src => lib}/types.ml (100%) create mode 100644 rustscript.opam delete mode 100644 src/dune delete mode 100644 src/rustscript.ml create mode 100644 test/dune create mode 100644 test/fib.ml create mode 100644 test/tuple.ml create mode 100644 test/util.ml diff --git a/bench.sh b/bench.sh index 24d8526..ba86d13 100755 --- a/bench.sh +++ b/bench.sh @@ -1,4 +1,2 @@ #!/bin/sh -hyperfine "dune exec ./src/rustscript.exe \ - 'let fib = fn(n) => if n < 1 then 1 else fib(n - 1) + fib(n - 2)'\ - 'fib(30)'" +hyperfine --warmup 3 "dune exec ./bin/rustscript_cli.exe examples/fib.rsc" diff --git a/bin/dune b/bin/dune new file mode 100644 index 0000000..c57c250 --- /dev/null +++ b/bin/dune @@ -0,0 +1,3 @@ +(executable + (name rustscript_cli) + (libraries rustscript)) diff --git a/bin/rustscript_cli.ml b/bin/rustscript_cli.ml new file mode 100644 index 0000000..bfab5b3 --- /dev/null +++ b/bin/rustscript_cli.ml @@ -0,0 +1,27 @@ +open Rustscript +open Base +open Stdio + +let rec repl state = + printf "> "; + Out_channel.flush stdout; + match In_channel.input_line ~fix_win_eol:true stdin with + | Some "\n" -> () + | None -> () + | Some line -> + match Rustscript.Run.eval state line with + | (Unit, new_state) -> repl new_state + | (evaled, new_state) -> + printf "%s\n" (Rustscript.Types.string_of_val evaled); + Out_channel.flush stdout; + repl new_state + +let () = + let args = Sys.get_argv () in + let state = Map.empty (module String) in + match args |> Array.to_list with + | [_; filename] -> let _state = Run.run_file filename state in () + | [_] -> + repl state + | _ -> + printf "Usage: 'rustscript ' or just 'rustscript' for REPL\n" diff --git a/dune-project b/dune-project index c994249..9021c15 100644 --- a/dune-project +++ b/dune-project @@ -1 +1,2 @@ (lang dune 2.9) +(name rustscript) diff --git a/lib/dune b/lib/dune new file mode 100644 index 0000000..7d34131 --- /dev/null +++ b/lib/dune @@ -0,0 +1,8 @@ +(library + (public_name rustscript) + (libraries base stdio) + (modules run types parser scanner eval)) + +(env + (release + (ocamlopt_flags (:standard -O3)))) diff --git a/src/eval.ml b/lib/eval.ml similarity index 91% rename from src/eval.ml rename to lib/eval.ml index bb95549..59dc013 100644 --- a/src/eval.ml +++ b/lib/eval.ml @@ -22,11 +22,22 @@ let val_div lhs rhs = match lhs, rhs with | Number lhs, Number rhs -> Number (lhs /. rhs) | _ -> assert false -let val_eq lhs rhs = match lhs, rhs with +let val_is_true = function + | Boolean true -> true + | _ -> false + +let rec val_eq lhs rhs = match lhs, rhs with | Number lhs, Number rhs -> Boolean (Float.equal lhs rhs) | Boolean lhs, Boolean rhs -> Boolean (Bool.equal lhs rhs) - | Tuple [], Tuple [] -> Boolean (true) - | _ -> assert false (* TODO *) + | Tuple lhs, Tuple rhs -> + if phys_equal (List.length lhs) (List.length rhs) + then + let zipped = List.zip_exn lhs rhs in + let res = List.for_all zipped ~f:(fun (a, b) -> val_is_true (val_eq a b)) + in Boolean res + else + Boolean false + | _ -> assert false let val_lt lhs rhs = match lhs, rhs with | Number lhs, Number rhs -> Boolean (Float.compare lhs rhs < 0) diff --git a/src/parser.ml b/lib/parser.ml similarity index 100% rename from src/parser.ml rename to lib/parser.ml diff --git a/lib/run.ml b/lib/run.ml new file mode 100644 index 0000000..9cfd662 --- /dev/null +++ b/lib/run.ml @@ -0,0 +1,20 @@ +open Stdio +open Types + +let eval state s = + let (parsed, _remaining) = Parser.parse_str s in + let eval_closure = Eval.eval_expr parsed in + eval_closure state + +let run_line state line = + match eval state line with + | (Unit, new_state) -> new_state + | (evaled, new_state) -> + printf "%s\n" (string_of_val evaled); + Out_channel.flush stdout; + new_state + + +let run_file filename state = + let in_stream = In_channel.create filename in + In_channel.fold_lines in_stream ~fix_win_eol:true ~init:state ~f:run_line diff --git a/src/scanner.ml b/lib/scanner.ml similarity index 100% rename from src/scanner.ml rename to lib/scanner.ml diff --git a/src/types.ml b/lib/types.ml similarity index 100% rename from src/types.ml rename to lib/types.ml diff --git a/rustscript.opam b/rustscript.opam new file mode 100644 index 0000000..9122a9f --- /dev/null +++ b/rustscript.opam @@ -0,0 +1,11 @@ +opam-version: "2.0" +version: "0.1.0" +maintainer: "mikail@mikail-khan.com" +authors: ["Mikail Khan" "William Ragstad"] +homepage: "https://github.com/mkhan45/RustScript2" +bug-reports: "https://github.com/mkhan45/RustScript2/issues" +dev-repo: "git+https://github.com/mkhan45/RustScript2.git" +license: "MIT + Apache" +build: [ + ["dune" "build" "-p" name "-j" jobs] +] diff --git a/src/dune b/src/dune deleted file mode 100644 index d7a5031..0000000 --- a/src/dune +++ /dev/null @@ -1,7 +0,0 @@ -(executable - (name rustscript) - (libraries base stdio)) - -(env - (release - (ocamlopt_flags (:standard -O3)))) diff --git a/src/rustscript.ml b/src/rustscript.ml deleted file mode 100644 index 8215636..0000000 --- a/src/rustscript.ml +++ /dev/null @@ -1,43 +0,0 @@ -open Types -open Base -open Stdio - -let eval state s = - let (parsed, _remaining) = Parser.parse_str s in - let eval_closure = Eval.eval_expr parsed in - eval_closure state - -let rec repl state = - printf "> "; - Out_channel.flush stdout; - match In_channel.input_line ~fix_win_eol:true stdin with - | Some "\n" -> () - | None -> () - | Some line -> - match eval state line with - | (Unit, new_state) -> repl new_state - | (evaled, new_state) -> - printf "%s\n" (string_of_val evaled); - Out_channel.flush stdout; - repl new_state - -let run_line state line = - match eval state line with - | (Unit, new_state) -> new_state - | (evaled, new_state) -> - printf "%s\n" (string_of_val evaled); - Out_channel.flush stdout; - new_state - -let () = - let args = Sys.get_argv () in - let state: state = Map.empty (module String) in - match args |> Array.to_list with - | [_; filename] -> - let in_stream = In_channel.create filename in - let _ = In_channel.fold_lines in_stream ~fix_win_eol:true ~init:state ~f:run_line - in () - | [_] -> - repl state - | _ -> - printf "Usage: 'rustscript ' or just 'rustscript' for REPL\n" diff --git a/test/dune b/test/dune new file mode 100644 index 0000000..ee3f5db --- /dev/null +++ b/test/dune @@ -0,0 +1,3 @@ +(tests + (names fib tuple) + (libraries base stdio rustscript)) diff --git a/test/fib.ml b/test/fib.ml new file mode 100644 index 0000000..ad50dce --- /dev/null +++ b/test/fib.ml @@ -0,0 +1,11 @@ +open Base +open Stdio + +open Rustscript.Run +open Util + +let () = + let state = Map.empty (module String) in + let (_, state) = eval state "let fib = fn(n) => if n < 1 then 1 else fib(n - 1) + fib(n - 2)" in + assert_equal_expressions "fib(10)" "144" state;; + printf "Passed\n" diff --git a/test/tuple.ml b/test/tuple.ml new file mode 100644 index 0000000..d296944 --- /dev/null +++ b/test/tuple.ml @@ -0,0 +1,13 @@ +open Base +open Stdio + +open Rustscript.Run +open Util + +let () = + let state = Map.empty (module String) in + let (_, state) = eval state "let x = (1, 2, (3, 4, 5), (6, 7), 8)" in + let (_, state) = eval state "let (a, b, c, d, e) = x" in + let (_, state) = eval state "let (f, g, h) = c" in + assert_equal_expressions "(a, b, e, f, g, h)" "(1, 2, 8, 3, 4, 5)" state; + printf "Passed\n" diff --git a/test/util.ml b/test/util.ml new file mode 100644 index 0000000..9d18c80 --- /dev/null +++ b/test/util.ml @@ -0,0 +1,16 @@ +open Stdio + +open Rustscript.Run +open Rustscript.Eval +open Rustscript.Types + +let assert_equal_expressions lhs rhs state = + let (lhs_res, _) = eval state lhs in + let (rhs_res, _) = eval state rhs in + match (val_eq lhs_res rhs_res) with + | Boolean true -> assert true + | _ -> + printf "Expected LHS: %s\n" (string_of_val lhs_res); + printf "Got RHS: %s\n" (string_of_val rhs_res); + printf "\n"; + assert false From fc949dfef3e7fdee5816b572bbfe3d313e944b01 Mon Sep 17 00:00:00 2001 From: Mikail Khan Date: Tue, 19 Oct 2021 22:42:44 -0400 Subject: [PATCH 2/6] added block expressions --- examples/block.rsc | 12 ++++++++++++ examples/test.rsc | 2 ++ lib/eval.ml | 6 ++++++ lib/parser.ml | 19 +++++++++++++++++-- lib/run.ml | 20 +++++++++++++++++--- lib/scanner.ml | 17 ++++++++++++----- lib/types.ml | 13 ++++++++----- test/block.ml | 12 ++++++++++++ test/dune | 2 +- test/util.ml | 2 ++ 10 files changed, 89 insertions(+), 16 deletions(-) create mode 100644 examples/block.rsc create mode 100644 examples/test.rsc create mode 100644 test/block.ml diff --git a/examples/block.rsc b/examples/block.rsc new file mode 100644 index 0000000..d3347e3 --- /dev/null +++ b/examples/block.rsc @@ -0,0 +1,12 @@ +let (a, b) = { + let a = (4, 2) + let (a, b) = a + (a * b, 12) +} + +let f = fn(a, b, c) => { + let g = fn(a, b) => a * b + c + g(b, c) + a +} + +f(10, 5, 3) diff --git a/examples/test.rsc b/examples/test.rsc new file mode 100644 index 0000000..35b3682 --- /dev/null +++ b/examples/test.rsc @@ -0,0 +1,2 @@ +let x = 5 +x diff --git a/lib/eval.ml b/lib/eval.ml index 59dc013..567bdee 100644 --- a/lib/eval.ml +++ b/lib/eval.ml @@ -92,6 +92,11 @@ and eval_if_expr if_expr = fun state -> (eval_expr if_expr.else_expr) state | _ -> assert false +and eval_block_expr ls state = + let (res, _) = + List.fold_left ~init:(Unit, state) ~f:(fun (_, state) e -> (eval_expr e) state) ls + in (res, state) + and eval_expr: expr -> state -> value * state = fun expr -> (* printf "Evaluating: %s\n" (string_of_expr expr); *) match expr with @@ -142,3 +147,4 @@ and eval_expr: expr -> state -> value * state = fun expr -> Tuple (List.rev eval_ls), state | LambdaCall l -> fun s -> (eval_lambda_call l) s | IfExpr i -> fun s -> (eval_if_expr i) s + | BlockExpr ls -> fun s -> eval_block_expr ls s diff --git a/lib/parser.ml b/lib/parser.ml index 58270e0..4357daf 100644 --- a/lib/parser.ml +++ b/lib/parser.ml @@ -97,7 +97,9 @@ and parse_lambda = function let (lambda_expr, rest) = parse xs (-1) in let lambda = Lambda {lambda_expr = lambda_expr; lambda_args = args} in (Atomic lambda, rest) - | _ -> assert false + | _ -> + printf "Expected an arrow, got (%s)\n" (string_of_toks rest); + assert false end | _ -> assert false @@ -129,8 +131,18 @@ and parse_if_expr = function end | _ -> assert false +and parse_block_expr ls = + let rec aux ls acc = match ls with + | (RBrace::rest)|(Newline::RBrace::rest) -> (BlockExpr (List.rev acc), rest) + | Newline::rest -> + let (next_expr, rest) = parse rest 0 in + aux rest (next_expr::acc) + | _ -> assert false + in aux ls [] + and parse: token list -> int -> expr * (token list) = fun s min_bp -> match s with + | LBrace::xs -> parse_block_expr xs | (Ident _)::LParen::_ -> let (call, xs) = parse_lambda_call s in complete_expr call xs min_bp @@ -143,6 +155,9 @@ and parse: token list -> int -> expr * (token list) = fun s min_bp -> | If::_ -> let (if_parsed, xs) = parse_if_expr s in complete_expr if_parsed xs min_bp - | _ -> assert false;; + | Newline::xs -> parse xs 0 + | _ -> + printf "Expected expression, got (%s)\n" (string_of_toks s); + assert false let parse_str s = parse (Scanner.scan s) 0 diff --git a/lib/run.ml b/lib/run.ml index 9cfd662..90aaf17 100644 --- a/lib/run.ml +++ b/lib/run.ml @@ -1,5 +1,6 @@ open Stdio open Types +open Scanner let eval state s = let (parsed, _remaining) = Parser.parse_str s in @@ -11,10 +12,23 @@ let run_line state line = | (Unit, new_state) -> new_state | (evaled, new_state) -> printf "%s\n" (string_of_val evaled); - Out_channel.flush stdout; + Out_channel.flush Stdio.stdout; new_state -let run_file filename state = +let run_file ?print_exprs:(print_exprs=true) filename state = let in_stream = In_channel.create filename in - In_channel.fold_lines in_stream ~fix_win_eol:true ~init:state ~f:run_line + let in_string = In_channel.input_all in_stream in + let tokens = Scanner.scan in_string in + let rec aux (parsed, remaining) state = + match (Eval.eval_expr parsed state), remaining with + | (res, _), ([]|[Newline]) -> + if print_exprs then + printf "%s\n" (string_of_val res); + state + | (Unit, new_state), remaining -> aux (Parser.parse remaining 0) new_state + | (res, new_state), remaining -> + if print_exprs then + printf "%s\n" (string_of_val res); + aux (Parser.parse remaining 0) new_state + in aux (Parser.parse tokens 0) state diff --git a/lib/scanner.ml b/lib/scanner.ml index 5b586dc..a6954d8 100644 --- a/lib/scanner.ml +++ b/lib/scanner.ml @@ -11,11 +11,14 @@ type token = | Equal | LParen | RParen + | LBrace + | RBrace | Fn | If | Then | Else | Arrow + | Newline | Comma;; let is_numeric d = Base.Char.is_digit d || phys_equal d '.';; @@ -37,8 +40,8 @@ and scan_ident ls = and scan_ls = function | [] -> [] - | ' '::xs -> scan_ls xs - | '\t'::xs -> scan_ls xs + | (' '|'\t')::xs -> scan_ls xs + | '\n'::xs -> Newline :: scan_ls xs | '='::'>'::xs -> Arrow :: scan_ls xs | '+'::xs -> Operator Add :: (scan_ls xs) | '-'::xs -> Operator Sub :: scan_ls xs @@ -49,6 +52,8 @@ and scan_ls = function | '='::'='::xs -> Operator EQ :: scan_ls xs | '('::xs -> LParen :: scan_ls xs | ')'::xs -> RParen :: scan_ls xs + | '{'::xs -> LBrace :: scan_ls xs + | '}'::xs -> RBrace :: scan_ls xs | '='::xs -> Equal :: scan_ls xs | ','::xs -> Comma :: scan_ls xs | 'l'::'e'::'t'::xs -> Let :: scan_ls xs @@ -74,6 +79,8 @@ let string_of_tok = function | Equal -> "Equal" | LParen -> "LParen" | RParen -> "RParen" + | LBrace -> "LBrace" + | RBrace -> "RBrace" | Comma -> "Comma" | Fn -> "Fn" | Arrow -> "Arrow" @@ -82,7 +89,7 @@ let string_of_tok = function | If -> "If" | Then -> "Then" | Else -> "Else" + | Newline -> "Newline" -let print_toks ls = - List.iter ~f:(fun t -> printf "%s " (string_of_tok t)) ls; - printf "\n";; +let string_of_toks ls = String.concat ~sep:" " (List.map ~f:string_of_tok ls) +let print_toks ls = ls |> string_of_toks |> printf "%s\n" diff --git a/lib/types.ml b/lib/types.ml index 8499ec2..78439c7 100644 --- a/lib/types.ml +++ b/lib/types.ml @@ -1,4 +1,5 @@ open Base +open Printf type operator = | Add @@ -32,7 +33,8 @@ and expr = | Let of {assignee: pattern; assigned_expr: expr} | LambdaCall of lambda_call | IfExpr of if_expr - | TupleExpr of expr list;; + | TupleExpr of expr list + | BlockExpr of expr list;; let rec string_of_val = function | Number n -> Float.to_string n @@ -44,11 +46,12 @@ let rec string_of_val = function let rec string_of_expr = function | Atomic v -> string_of_val v | Ident s -> s - | Binary (_ as b) -> "{lhs: " ^ (string_of_expr b.lhs) ^ ", rhs: " ^ (string_of_expr b.rhs) ^ "}" - | Let {assignee = a; assigned_expr = e} -> "Let " ^ (string_of_pat a) ^ " = " ^ (string_of_expr e) - | LambdaCall call -> "{Call: " ^ call.callee ^ ", args: " ^ (string_of_expr call.call_args) ^ "}" - | TupleExpr ls -> "(" ^ (String.concat ~sep:", " (List.map ~f:string_of_expr ls)) ^ ")" + | Binary (_ as b) -> sprintf "{lhs: %s, rhs: %s}" (string_of_expr b.lhs) (string_of_expr b.rhs) + | Let (_ as l) -> sprintf "Let %s = %s" (string_of_pat l.assignee) (string_of_expr l.assigned_expr) + | LambdaCall call -> sprintf "{Call: %s, args: %s}" call.callee (string_of_expr call.call_args) + | TupleExpr ls -> sprintf "(%s)" (String.concat ~sep:", " (List.map ~f:string_of_expr ls)) | IfExpr _ -> "IfExpr" + | BlockExpr ls -> sprintf "{\n\t%s\n}" (String.concat ~sep:"\n\t" (List.map ~f:string_of_expr ls)) and string_of_pat = function | SinglePat s -> s diff --git a/test/block.ml b/test/block.ml new file mode 100644 index 0000000..d3250ec --- /dev/null +++ b/test/block.ml @@ -0,0 +1,12 @@ +open Base +open Stdio + +open Rustscript.Run +open Util + +let () = + let state = + Map.empty (module String) |> run_file ~print_exprs:false (test_file "block.rsc") in + assert_equal_expressions "a + b" "20" state; + assert_equal_expressions "f(10, 5, 3)" "28" state; + printf "Passed\n" diff --git a/test/dune b/test/dune index ee3f5db..1b15853 100644 --- a/test/dune +++ b/test/dune @@ -1,3 +1,3 @@ (tests - (names fib tuple) + (names fib tuple block) (libraries base stdio rustscript)) diff --git a/test/util.ml b/test/util.ml index 9d18c80..b78761b 100644 --- a/test/util.ml +++ b/test/util.ml @@ -4,6 +4,8 @@ open Rustscript.Run open Rustscript.Eval open Rustscript.Types +let test_file filename = Printf.sprintf "../../../examples/%s" filename + let assert_equal_expressions lhs rhs state = let (lhs_res, _) = eval state lhs in let (rhs_res, _) = eval state rhs in From 72b9f11bda8be7eed77d28416bfddfdd7634fc77 Mon Sep 17 00:00:00 2001 From: Mikail Khan Date: Tue, 19 Oct 2021 22:51:24 -0400 Subject: [PATCH 3/6] added block expr example to readme --- README.md | 16 ++++++++++++++++ lib/parser.ml | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c54a089..ced25d9 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,19 @@ written to learn OCaml. Still WIP > (a, b, c, d) (4., 5., 6., 10.) ``` + +``` +let (a, b) = { + let a = (4, 2) + let (a, b) = a + (a * b, 12) +} + +let f = fn(a, b, c) => { + let g = fn(a, b) => a * b + c + g(b, c) + a +} + +f(10, 5, 3) +``` +Prints 28 diff --git a/lib/parser.ml b/lib/parser.ml index 4357daf..b3d8732 100644 --- a/lib/parser.ml +++ b/lib/parser.ml @@ -116,10 +116,10 @@ and parse_if_expr = function | If::xs -> begin let (cond, xs) = parse xs 0 in match xs with - | Then::xs -> begin + | (Newline::Then::xs)|(Then::xs) -> begin let (then_expr, xs) = parse xs 0 in match xs with - | Else::xs -> + | (Newline::Else::xs)|Else::xs -> let (else_expr, rest) = parse xs 0 in (IfExpr {cond = cond; then_expr = then_expr; else_expr = else_expr}, rest) | _ -> From ad2c18ac9af8fd4b62879b045d4e5b12c7f6f003 Mon Sep 17 00:00:00 2001 From: Mikail Khan Date: Tue, 19 Oct 2021 23:26:22 -0400 Subject: [PATCH 4/6] fixed some bugs, added fmap test --- examples/fib.rsc | 6 +++++- examples/fmap_tuple.rsc | 12 ++++++++++++ examples/test.rsc | 2 -- lib/parser.ml | 7 +++---- lib/run.ml | 9 +++++---- lib/scanner.ml | 5 +++++ test/block.ml | 7 +++++++ 7 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 examples/fmap_tuple.rsc delete mode 100644 examples/test.rsc diff --git a/examples/fib.rsc b/examples/fib.rsc index dd0578a..d76b39d 100644 --- a/examples/fib.rsc +++ b/examples/fib.rsc @@ -1,2 +1,6 @@ -let fib = fn (n) => if n < 2 then 1 else fib(n - 1) + fib(n - 2) +let fib = fn (n) => { + if n < 2 + then 1 + else fib(n - 1) + fib(n - 2) +} fib(30) diff --git a/examples/fmap_tuple.rsc b/examples/fmap_tuple.rsc new file mode 100644 index 0000000..f516e6c --- /dev/null +++ b/examples/fmap_tuple.rsc @@ -0,0 +1,12 @@ +let fmap = fn (f, ls) => { + if ls == () then { + () + } else { + let (hd, tl) = ls + (f(hd), fmap(f, tl)) + } +} + +let f = fn(x) => x * 2 + +fmap(f, (5, (10, (20, (30, (1, ())))))) diff --git a/examples/test.rsc b/examples/test.rsc deleted file mode 100644 index 35b3682..0000000 --- a/examples/test.rsc +++ /dev/null @@ -1,2 +0,0 @@ -let x = 5 -x diff --git a/lib/parser.ml b/lib/parser.ml index b3d8732..38c0412 100644 --- a/lib/parser.ml +++ b/lib/parser.ml @@ -115,11 +115,11 @@ and parse_lambda_call = function and parse_if_expr = function | If::xs -> begin let (cond, xs) = parse xs 0 in - match xs with - | (Newline::Then::xs)|(Then::xs) -> begin + match skip_newlines xs with + | Then::xs -> begin let (then_expr, xs) = parse xs 0 in match xs with - | (Newline::Else::xs)|Else::xs -> + | Else::xs -> let (else_expr, rest) = parse xs 0 in (IfExpr {cond = cond; then_expr = then_expr; else_expr = else_expr}, rest) | _ -> @@ -155,7 +155,6 @@ and parse: token list -> int -> expr * (token list) = fun s min_bp -> | If::_ -> let (if_parsed, xs) = parse_if_expr s in complete_expr if_parsed xs min_bp - | Newline::xs -> parse xs 0 | _ -> printf "Expected expression, got (%s)\n" (string_of_toks s); assert false diff --git a/lib/run.ml b/lib/run.ml index 90aaf17..7b63eee 100644 --- a/lib/run.ml +++ b/lib/run.ml @@ -19,14 +19,15 @@ let run_line state line = let run_file ?print_exprs:(print_exprs=true) filename state = let in_stream = In_channel.create filename in let in_string = In_channel.input_all in_stream in - let tokens = Scanner.scan in_string in + let tokens = in_string |> Scanner.scan |> skip_newlines in let rec aux (parsed, remaining) state = - match (Eval.eval_expr parsed state), remaining with - | (res, _), ([]|[Newline]) -> + match (Eval.eval_expr parsed state), (skip_newlines remaining) with + | (res, _), [] -> if print_exprs then printf "%s\n" (string_of_val res); state - | (Unit, new_state), remaining -> aux (Parser.parse remaining 0) new_state + | (Unit, new_state), remaining -> + aux (Parser.parse remaining 0) new_state | (res, new_state), remaining -> if print_exprs then printf "%s\n" (string_of_val res); diff --git a/lib/scanner.ml b/lib/scanner.ml index a6954d8..6418796 100644 --- a/lib/scanner.ml +++ b/lib/scanner.ml @@ -93,3 +93,8 @@ let string_of_tok = function let string_of_toks ls = String.concat ~sep:" " (List.map ~f:string_of_tok ls) let print_toks ls = ls |> string_of_toks |> printf "%s\n" + +let toks_empty toks = List.for_all toks ~f:(fun tok -> phys_equal tok Newline) +let rec skip_newlines = function + | Newline :: xs -> skip_newlines xs + | ls -> ls diff --git a/test/block.ml b/test/block.ml index d3250ec..e68535c 100644 --- a/test/block.ml +++ b/test/block.ml @@ -1,5 +1,6 @@ open Base open Stdio +open Printf open Rustscript.Run open Util @@ -9,4 +10,10 @@ let () = Map.empty (module String) |> run_file ~print_exprs:false (test_file "block.rsc") in assert_equal_expressions "a + b" "20" state; assert_equal_expressions "f(10, 5, 3)" "28" state; + + let state = + Map.empty (module String) |> run_file ~print_exprs:false (test_file "fmap_tuple.rsc") in + let input = "(5, (10, (20, (30, (1, ())))))" in + let output = "(10, (20, (40, (60, (2, ())))))" in + assert_equal_expressions (sprintf "fmap(f, %s)" input) output state; printf "Passed\n" From d02208a0cfc682475f348cdc1d3b1c597594d250 Mon Sep 17 00:00:00 2001 From: Mikail Khan Date: Tue, 19 Oct 2021 23:27:31 -0400 Subject: [PATCH 5/6] added fmap example to readme --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ced25d9..3364c61 100644 --- a/README.md +++ b/README.md @@ -45,4 +45,20 @@ let f = fn(a, b, c) => { f(10, 5, 3) ``` -Prints 28 +Result: `28` + +``` +let fmap = fn (f, ls) => { + if ls == () then { + () + } else { + let (hd, tl) = ls + (f(hd), fmap(f, tl)) + } +} + +let f = fn(x) => x * 2 + +fmap(f, (5, (10, (20, (30, (1, ())))))) +``` +Result: `(10, (20, (40, (60, (2, ())))))` From a6198c05e6d079506cfe2d665bd27c61001b7e25 Mon Sep 17 00:00:00 2001 From: Mikail Khan Date: Wed, 20 Oct 2021 00:48:50 -0400 Subject: [PATCH 6/6] added comments --- examples/comment.rsc | 6 ++++++ lib/parser.ml | 2 +- lib/scanner.ml | 17 +++++++++++++++-- test/comments.ml | 22 ++++++++++++++++++++++ test/dune | 2 +- 5 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 examples/comment.rsc create mode 100644 test/comments.ml diff --git a/examples/comment.rsc b/examples/comment.rsc new file mode 100644 index 0000000..d7cafd8 --- /dev/null +++ b/examples/comment.rsc @@ -0,0 +1,6 @@ +let a = 5 # set a to 5 +# aaaaaa +let b = (5, 10, 15) +a +b +# diff --git a/lib/parser.ml b/lib/parser.ml index 38c0412..9e7b59d 100644 --- a/lib/parser.ml +++ b/lib/parser.ml @@ -140,7 +140,7 @@ and parse_block_expr ls = | _ -> assert false in aux ls [] -and parse: token list -> int -> expr * (token list) = fun s min_bp -> +and parse: token list -> int -> expr * (token list) = fun s min_bp -> match s with | LBrace::xs -> parse_block_expr xs | (Ident _)::LParen::_ -> diff --git a/lib/scanner.ml b/lib/scanner.ml index 6418796..32c1d93 100644 --- a/lib/scanner.ml +++ b/lib/scanner.ml @@ -19,6 +19,7 @@ type token = | Else | Arrow | Newline + | Hashtag | Comma;; let is_numeric d = Base.Char.is_digit d || phys_equal d '.';; @@ -56,6 +57,7 @@ and scan_ls = function | '}'::xs -> RBrace :: scan_ls xs | '='::xs -> Equal :: scan_ls xs | ','::xs -> Comma :: scan_ls xs + | '#'::xs -> Hashtag :: scan_ls xs | 'l'::'e'::'t'::xs -> Let :: scan_ls xs | 'f'::'n'::xs -> Fn :: scan_ls xs | 'i'::'f'::xs -> If :: scan_ls xs @@ -67,9 +69,19 @@ and scan_ls = function | i::_ as ls when not (Base.Char.is_digit i) -> scan_ident ls | _ as ls -> printf "Scan Error: %s\n" (String.of_char_list ls); - assert false;; + assert false -let scan s = s |> String.to_list |> scan_ls;; +and remove_comments ls = + let rec skip_until_endline = function + | [] -> [] + | Newline::xs -> remove_comments xs + | _::xs -> skip_until_endline xs + in match ls with + | [] -> [] + | Hashtag::xs -> skip_until_endline xs + | t::xs -> t :: (remove_comments xs) + +let scan s = s |> String.to_list |> scan_ls |> remove_comments;; let string_of_tok = function | Number f -> Float.to_string f @@ -90,6 +102,7 @@ let string_of_tok = function | Then -> "Then" | Else -> "Else" | Newline -> "Newline" + | Hashtag -> "Hashtag" let string_of_toks ls = String.concat ~sep:" " (List.map ~f:string_of_tok ls) let print_toks ls = ls |> string_of_toks |> printf "%s\n" diff --git a/test/comments.ml b/test/comments.ml new file mode 100644 index 0000000..a0b91cf --- /dev/null +++ b/test/comments.ml @@ -0,0 +1,22 @@ +open Base +open Stdio + +open Rustscript.Run +open Util + +let () = + let state = + Map.empty (module String) |> run_file ~print_exprs:false (test_file "block.rsc") in + assert_equal_expressions "a + b" "20" state; + assert_equal_expressions "f(10, 5, 3)" "28" state; + + let state = + Map.empty (module String) |> run_file ~print_exprs:false (test_file "comment.rsc") in + let input = "a" in + let output = "5" in + assert_equal_expressions input output state; + + let input = "b" in + let output = "(5, 10, 15)" in + assert_equal_expressions input output state; + printf "Passed\n" diff --git a/test/dune b/test/dune index 1b15853..6357c74 100644 --- a/test/dune +++ b/test/dune @@ -1,3 +1,3 @@ (tests - (names fib tuple block) + (names fib tuple block comments) (libraries base stdio rustscript))