bd0bf6ea7d
Support an array that dynamically resizes itself, and replace usages of `List`, `Array`, and `Queue` with `Vec`. Change-Id: I910b140b7c1bdddae40e08f8191986dccbc6fddf Reviewed-on: https://cl.tvl.fyi/c/depot/+/7080 Tested-by: BuildkiteCI Reviewed-by: wpcarro <wpcarro@gmail.com>
187 lines
4.7 KiB
OCaml
187 lines
4.7 KiB
OCaml
(*******************************************************************************
|
|
* CLI REPL for an s-expression Lambda Calculus.
|
|
*
|
|
* Lambda Calculus Expression Language:
|
|
*
|
|
* Helpers:
|
|
* symbol -> [-a-z]+
|
|
* string -> '"' [^"]* '"'
|
|
* boolean -> 'true' | 'false'
|
|
* integer -> [1-9][0-9]*
|
|
*
|
|
* Core:
|
|
* expression -> funcdef
|
|
* binding -> '(' 'let' symbol expr expr ')'
|
|
* funcdef -> '(' 'fn' symbol expr ')'
|
|
* funccall -> '(' ( symbol | funcdef) expr ')'
|
|
* literal -> string | boolean | integer
|
|
* variable -> symbol
|
|
*
|
|
* Example Usage:
|
|
* $ ocamlopt types.ml str.cmxa inference.ml parser.ml expr_parser.ml && ./a.out
|
|
* repl> true
|
|
* tokens: [ "true" ]
|
|
* ast: ValueLiteral (LiteralBool true)
|
|
* Boolean
|
|
* repl>
|
|
*
|
|
******************************************************************************)
|
|
|
|
open Parser
|
|
open Inference
|
|
open Debug
|
|
open Prettify
|
|
open Vec
|
|
|
|
type literal = LiteralBool of bool | LiteralInt of int
|
|
|
|
let ( let* ) = Option.bind
|
|
let map = Option.map
|
|
|
|
let tokenize (x : string) : token vec =
|
|
let xs = Vec.create () in
|
|
let i = ref 0 in
|
|
while !i < String.length x do
|
|
match x.[!i] with
|
|
| ' ' -> i := !i + 1
|
|
(* strings *)
|
|
| '"' ->
|
|
let curr = ref "\"" in
|
|
i := !i + 1;
|
|
while x.[!i] != '"' do
|
|
curr := !curr ^ "?";
|
|
i := !i + 1
|
|
done;
|
|
curr := !curr ^ "\"";
|
|
Vec.append !curr xs;
|
|
i := !i + 1
|
|
| '(' ->
|
|
Vec.append "(" xs;
|
|
i := !i + 1
|
|
| ')' ->
|
|
Vec.append ")" xs;
|
|
i := !i + 1
|
|
| _ ->
|
|
let token = ref "" in
|
|
while !i < String.length x && not (String.contains "() " x.[!i]) do
|
|
token := !token ^ String.make 1 x.[!i];
|
|
i := !i + 1
|
|
done;
|
|
Vec.append !token xs
|
|
done;
|
|
xs
|
|
|
|
let parse_symbol (p : parser) : string option =
|
|
let* x = p#curr in
|
|
if Str.string_match (Str.regexp "[-a-z][0-9]*") x 0 then
|
|
begin
|
|
p#advance;
|
|
Some x
|
|
end
|
|
else
|
|
None
|
|
|
|
let parse_variable (p : parser) : Types.value option =
|
|
let* x = parse_symbol p in
|
|
Some (Types.ValueVariable x)
|
|
|
|
let parse_literal (p : parser) : Types.value option =
|
|
match p#curr with
|
|
| Some "true" ->
|
|
p#advance;
|
|
Some (ValueLiteral (LiteralBool true))
|
|
| Some "false" ->
|
|
p#advance;
|
|
Some (ValueLiteral (LiteralBool false))
|
|
| Some x ->
|
|
(match int_of_string_opt x with
|
|
| Some n ->
|
|
p#advance;
|
|
Some (ValueLiteral (LiteralInt n))
|
|
| _ ->
|
|
if String.starts_with ~prefix:"\"" x then
|
|
begin
|
|
p#advance;
|
|
Some (ValueLiteral (LiteralString x))
|
|
end
|
|
else
|
|
parse_variable p)
|
|
| _ -> None
|
|
|
|
let rec parse_expression (p : parser) : Types.value option =
|
|
parse_binding p
|
|
|
|
and parse_funccall (p : parser) : Types.value option =
|
|
match (p#curr, p#next) with
|
|
| (Some "(", Some "(") ->
|
|
p#advance;
|
|
let* f = parse_funcdef p in
|
|
let* x = parse_expression p in
|
|
p#expect ")";
|
|
Some (Types.ValueApplication (f, x))
|
|
| (Some "(", _) ->
|
|
p#advance;
|
|
let* f = parse_symbol p in
|
|
let* x = parse_expression p in
|
|
p#expect ")";
|
|
Some (Types.ValueVarApplication (f, x))
|
|
| _ -> parse_literal p
|
|
|
|
and parse_funcdef (p : parser) : Types.value option =
|
|
match (p#curr, p#next) with
|
|
| (Some "(", Some "fn") ->
|
|
p#advance;
|
|
p#advance;
|
|
let* name = parse_symbol p in
|
|
let* body = parse_expression p in
|
|
p#expect ")";
|
|
Some (Types.ValueFunction (name, body))
|
|
| _ -> parse_funccall p
|
|
|
|
and parse_binding (p : parser) : Types.value option =
|
|
match (p#curr, p#next) with
|
|
| (Some "(", Some "let") ->
|
|
p#advance;
|
|
p#advance;
|
|
let* name = parse_symbol p in
|
|
let* value = parse_expression p in
|
|
let* body = parse_expression p in
|
|
Some (Types.ValueBinder (name, value, body))
|
|
| _ -> parse_funcdef p
|
|
|
|
let print_tokens (xs : string vec) : unit =
|
|
xs
|
|
|> Vec.map (Printf.sprintf "\"%s\"")
|
|
|> Vec.join ", "
|
|
|> Printf.sprintf "tokens: [ %s ]"
|
|
|> print_string
|
|
|> print_newline
|
|
|
|
let parse_language (x : string) : Types.value option =
|
|
let tokens = tokenize x in
|
|
print_tokens tokens;
|
|
parse_expression (new parser tokens)
|
|
|
|
let main =
|
|
while true do
|
|
begin
|
|
print_string "repl> ";
|
|
let x = read_line () in
|
|
match parse_language x with
|
|
| Some ast ->
|
|
(match ast |> Debug.print Debug.ast "ast" |> do_infer with
|
|
| None ->
|
|
"Type-check failed"
|
|
|> print_string
|
|
|> print_newline
|
|
| Some x ->
|
|
x
|
|
|> Prettify.type'
|
|
|> print_string
|
|
|> print_newline)
|
|
| None ->
|
|
"Could not parse"
|
|
|> print_string
|
|
|> print_newline
|
|
end
|
|
done
|