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
 |