Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions ml-proto/given/lib.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ struct
| x::[] -> [], x
| x::xs -> let ys, y = split_last xs in x::ys, y
| [] -> failwith "split_last"

let rec index_of x xs =
index_of' x xs 0

and index_of' x xs i =
match xs with
| [] -> None
| y::xs' when x = y -> Some i
| y::xs' -> index_of' x xs' (i+1)
end

module Option =
Expand Down
2 changes: 2 additions & 0 deletions ml-proto/given/lib.mli
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ sig

val last : 'a list -> 'a (* raise Failure *)
val split_last : 'a list -> 'a list * 'a (* raise Failure *)

val index_of : 'a -> 'a list -> int option
end

module Option :
Expand Down
13 changes: 8 additions & 5 deletions ml-proto/host/builtins.ml
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
open Source
open Types
open Ast

let print vs =
List.iter Print.print_value (List.map (fun v -> Some v) vs);
None

let match_import i =
let {Ast.module_name; func_name; func_params; func_result} = i.it in
let match_import m i =
let {module_name; func_name; itype} = i.it in
let {ins; out} = List.nth m.it.types itype.it in
if module_name <> "stdio" then
Error.error i.at ("no builtin module \"" ^ module_name ^ "\"");
match func_name with
| "print" ->
if func_result <> None then
if out <> None then
Error.error i.at "stdio.print has no result";
print
| _ ->
Error.error i.at ("no \"stdio." ^ func_name ^ "\"")

let match_imports (is : Ast.import list) =
List.map match_import is
let match_imports m =
List.map (match_import m) m.it.imports
2 changes: 1 addition & 1 deletion ml-proto/host/builtins.mli
Original file line number Diff line number Diff line change
@@ -1 +1 @@
val match_imports : Ast.import list -> Eval.import list
val match_imports : Ast.module_ -> Eval.import list
9 changes: 5 additions & 4 deletions ml-proto/host/lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,10 @@ rule token = parse
| '"'character*'\\'_
{ error_nest (Lexing.lexeme_end_p lexbuf) lexbuf "illegal escape" }

| "i32" { TYPE Types.Int32Type }
| "i64" { TYPE Types.Int64Type }
| "f32" { TYPE Types.Float32Type }
| "f64" { TYPE Types.Float64Type }
| "i32" { VALUE_TYPE Types.Int32Type }
| "i64" { VALUE_TYPE Types.Int64Type }
| "f32" { VALUE_TYPE Types.Float32Type }
| "f64" { VALUE_TYPE Types.Float64Type }

| "nop" { NOP }
| "block" { BLOCK }
Expand Down Expand Up @@ -241,6 +241,7 @@ rule token = parse
| "memory_size" { MEMORY_SIZE }
| "grow_memory" { GROW_MEMORY }

| "type" { TYPE }
| "func" { FUNC }
| "param" { PARAM }
| "result" { RESULT }
Expand Down
177 changes: 122 additions & 55 deletions ml-proto/host/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
open Source
open Ast
open Sugar
open Types
open Script


Expand Down Expand Up @@ -35,10 +36,10 @@ let parse_error s = Error.error Source.no_region s
let literal s t =
try
match t with
| Types.Int32Type -> Values.Int32 (I32.of_string s.it) @@ s.at
| Types.Int64Type -> Values.Int64 (I64.of_string s.it) @@ s.at
| Types.Float32Type -> Values.Float32 (F32.of_string s.it) @@ s.at
| Types.Float64Type -> Values.Float64 (F64.of_string s.it) @@ s.at
| Int32Type -> Values.Int32 (I32.of_string s.it) @@ s.at
| Int64Type -> Values.Int64 (I64.of_string s.it) @@ s.at
| Float32Type -> Values.Float32 (F32.of_string s.it) @@ s.at
| Float64Type -> Values.Float64 (F64.of_string s.it) @@ s.at
with
| Failure reason -> Error.error s.at ("constant out of range: " ^ reason)
| _ -> Error.error s.at "constant out of range"
Expand All @@ -47,20 +48,29 @@ let literal s t =
(* Symbolic variables *)

module VarMap = Map.Make(String)

type space = {mutable map : int VarMap.t; mutable count : int}
let empty () = {map = VarMap.empty; count = 0}

type types = {mutable tmap : int VarMap.t; mutable tlist : Types.func_type list}
let empty_types () = {tmap = VarMap.empty; tlist = []}

type context =
{funcs : space; imports : space; locals : space; labels : int VarMap.t}
{types : types; funcs : space; imports : space; locals : space;
labels : int VarMap.t}

let empty () = {map = VarMap.empty; count = 0}
let c0 () =
{funcs = empty (); imports = empty ();
{types = empty_types (); funcs = empty (); imports = empty ();
locals = empty (); labels = VarMap.empty}

let enter_func c =
assert (VarMap.is_empty c.labels);
{c with labels = VarMap.add "return" 0 c.labels; locals = empty ()}

let lookup_type c x =
try VarMap.find x.it c.types.tmap
with Not_found -> Error.error x.at ("unknown type " ^ x.it)

let lookup category space x =
try VarMap.find x.it space.map
with Not_found -> Error.error x.at ("unknown " ^ category ^ " " ^ x.it)
Expand All @@ -73,6 +83,12 @@ let label c x =
try VarMap.find x.it c.labels
with Not_found -> Error.error x.at ("unknown label " ^ x.it)

let bind_type c x ty =
if VarMap.mem x.it c.types.tmap then
Error.error x.at ("duplicate type " ^ x.it);
c.types.tmap <- VarMap.add x.it (List.length c.types.tlist) c.types.tmap;
c.types.tlist <- c.types.tlist @ [ty]

let bind category space x =
if VarMap.mem x.it space.map then
Error.error x.at ("duplicate " ^ category ^ " " ^ x.it);
Expand All @@ -85,20 +101,41 @@ let bind_local c x = bind "local" c.locals x
let bind_label c x =
{c with labels = VarMap.add x.it 0 (VarMap.map ((+) 1) c.labels)}

let anon_type c ty =
c.types.tlist <- c.types.tlist @ [ty]

let anon space n = space.count <- space.count + n

let anon_func c = anon c.funcs 1
let anon_import c = anon c.imports 1
let anon_locals c ts = anon c.locals (List.length ts)
let anon_label c = {c with labels = VarMap.map ((+) 1) c.labels}

let empty_type = {ins = []; out = None}

let explicit_decl c name t at =
let x = name c lookup_type in
if x.it < List.length c.types.tlist &&
t <> empty_type &&
t <> List.nth c.types.tlist x.it then
Error.error at "signature mismatch";
x

let implicit_decl c t at =
match Lib.List.index_of t c.types.tlist with
| None -> let i = List.length c.types.tlist in anon_type c t; i @@ at
| Some i -> i @@ at


%}

%token INT FLOAT TEXT VAR TYPE LPAR RPAR
%token INT FLOAT TEXT VAR VALUE_TYPE LPAR RPAR
%token NOP BLOCK IF LOOP LABEL BREAK SWITCH CASE FALLTHROUGH
%token CALL CALL_IMPORT CALL_INDIRECT RETURN
%token GET_LOCAL SET_LOCAL LOAD STORE
%token CONST UNARY BINARY COMPARE CONVERT
%token FUNC PARAM RESULT LOCAL MODULE MEMORY SEGMENT IMPORT EXPORT TABLE
%token FUNC TYPE PARAM RESULT LOCAL
%token MODULE MEMORY SEGMENT IMPORT EXPORT TABLE
%token PAGE_SIZE MEMORY_SIZE GROW_MEMORY
%token ASSERT_INVALID ASSERT_RETURN ASSERT_RETURN_NAN ASSERT_TRAP INVOKE
%token EOF
Expand All @@ -107,7 +144,7 @@ let anon_label c = {c with labels = VarMap.map ((+) 1) c.labels}
%token<string> FLOAT
%token<string> TEXT
%token<string> VAR
%token<Types.value_type> TYPE
%token<Types.value_type> VALUE_TYPE
%token<Types.value_type> CONST
%token<Types.value_type> SWITCH
%token<Ast.unop> UNARY
Expand All @@ -129,12 +166,19 @@ let anon_label c = {c with labels = VarMap.map ((+) 1) c.labels}

/* Types */

value_type :
| TYPE { $1 @@ at () }
;
value_type_list :
| /* empty */ { [] }
| value_type value_type_list { $1 :: $2 }
| VALUE_TYPE value_type_list { $1 :: $2 }
;
func_type :
| /* empty */
{ {ins = []; out = None} }
| LPAR PARAM value_type_list RPAR
{ {ins = $3; out = None} }
| LPAR PARAM value_type_list RPAR LPAR RESULT VALUE_TYPE RPAR
{ {ins = $3; out = Some $7} }
| LPAR RESULT VALUE_TYPE RPAR
{ {ins = []; out = Some $3} }
;


Expand Down Expand Up @@ -182,9 +226,8 @@ expr1 :
{ let at1 = ati 1 in
fun c -> return (label c ("return" @@ at1) @@ at1, $2 c) }
| SWITCH labeling expr cases
{ let at1 = ati 1 in
fun c -> let c', l = $2 c in let cs, e = $4 c' in
switch (l, $1 @@ at1, $3 c', List.map (fun a -> a $1) cs, e) }
{ fun c -> let c', l = $2 c in let cs, e = $4 c' in
switch (l, $1, $3 c', List.map (fun a -> a $1) cs, e) }
| CALL var expr_list { fun c -> call ($2 c func, $3 c) }
| CALL_IMPORT var expr_list { fun c -> call_import ($2 c import, $3 c) }
| CALL_INDIRECT var expr expr_list
Expand Down Expand Up @@ -238,35 +281,49 @@ cases :
func_fields :
| expr_list
{ let at = at () in
fun c ->
{params = []; result = None; locals = [];
body = Sugar.func_body ($1 c) @@ at} }
empty_type,
fun c -> let body = Sugar.func_body ($1 c) @@ at in
{ftype = -1 @@ at; locals = []; body} }
| LPAR PARAM value_type_list RPAR func_fields
{ fun c -> anon_locals c $3; let f = $5 c in
{f with params = $3 @ f.params} }
| LPAR PARAM bind_var value_type RPAR func_fields /* Sugar */
{ fun c -> bind_local c $3; let f = $6 c in
{f with params = $4 :: f.params} }
| LPAR RESULT value_type RPAR func_fields
{ let at = at () in
fun c -> let f = $5 c in
match f.result with
| Some _ -> Error.error at "more than one return type"
| None -> {f with result = Some $3} }
{ {(fst $5) with ins = $3 @ (fst $5).ins},
fun c -> anon_locals c $3; (snd $5) c }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note to reviewers: so the return value of func_fields is now a pair, instead of just the function. This was necessary to extract the type during the first pass (when the global scope is built) instead of the second pass (where function bodies are generated). Alternatively, I played a bit with moving the param/result fields out of func_fields and into another production that came before func_fields in func, but I ran into shift/reduce and reduce/reduce conflicts that seemed to require changing the text format to better isolate the signature.

| LPAR PARAM bind_var VALUE_TYPE RPAR func_fields /* Sugar */
{ {(fst $6) with ins = $4 :: (fst $6).ins},
fun c -> bind_local c $3; (snd $6) c }
| LPAR RESULT VALUE_TYPE RPAR func_fields
{ if (fst $5).out <> None then
Error.error (at ()) "more than one return type";
{(fst $5) with out = Some $3},
fun c -> (snd $5) c }
| LPAR LOCAL value_type_list RPAR func_fields
{ fun c -> anon_locals c $3; let f = $5 c in
{f with locals = $3 @ f.locals} }
| LPAR LOCAL bind_var value_type RPAR func_fields /* Sugar */
{ fun c -> bind_local c $3; let f = $6 c in
{f with locals = $4 :: f.locals} }
{ fst $5,
fun c -> anon_locals c $3; let f = (snd $5) c in
{f with locals = $3 @ f.locals} }
| LPAR LOCAL bind_var VALUE_TYPE RPAR func_fields /* Sugar */
{ fst $6,
fun c -> bind_local c $3; let f = (snd $6) c in
{f with locals = $4 :: f.locals} }
;
type_use :
| LPAR TYPE var RPAR { $3 }
;
func :
| LPAR FUNC func_fields RPAR
| LPAR FUNC type_use func_fields RPAR
{ let at = at () in
fun c -> anon_func c; fun () -> $3 (enter_func c) @@ at }
fun c -> anon_func c; let t = explicit_decl c $3 (fst $4) at in
fun () -> {((snd $4) (enter_func c)) with ftype = t} @@ at }
| LPAR FUNC bind_var type_use func_fields RPAR /* Sugar */
{ let at = at () in
fun c -> bind_func c $3; let t = explicit_decl c $4 (fst $5) at in
fun () -> {((snd $5) (enter_func c)) with ftype = t} @@ at }
| LPAR FUNC func_fields RPAR /* Sugar */
{ let at = at () in
fun c -> anon_func c; let t = implicit_decl c (fst $3) at in
fun () -> {((snd $3) (enter_func c)) with ftype = t} @@ at }
| LPAR FUNC bind_var func_fields RPAR /* Sugar */
{ let at = at () in
fun c -> bind_func c $3; fun () -> $4 (enter_func c) @@ at }
fun c -> bind_func c $3; let t = implicit_decl c (fst $4) at in
fun () -> {((snd $4) (enter_func c)) with ftype = t} @@ at }
;


Expand All @@ -290,22 +347,30 @@ memory :
@@ at () }
;

func_params :
| LPAR PARAM value_type_list RPAR { $3 }
;
func_result :
| /* empty */ { None }
| LPAR RESULT value_type RPAR { Some $3 }
type_def :
| LPAR TYPE LPAR FUNC func_type RPAR RPAR
{ fun c -> anon_type c $5 }
| LPAR TYPE bind_var LPAR FUNC func_type RPAR RPAR
{ fun c -> bind_type c $3 $6 }
;

import :
| LPAR IMPORT bind_var TEXT TEXT func_params func_result RPAR
{ let at = at () in fun c -> bind_import c $3;
{module_name = $4; func_name = $5; func_params = $6; func_result = $7 }
@@ at }
| LPAR IMPORT TEXT TEXT func_params func_result RPAR /* Sugar */
{ let at = at () in fun c -> anon_import c;
{module_name = $3; func_name = $4; func_params = $5; func_result = $6 }
@@ at }
| LPAR IMPORT TEXT TEXT type_use RPAR
{ let at = at () in
fun c -> anon_import c; let itype = explicit_decl c $5 empty_type at in
{itype; module_name = $3; func_name = $4} @@ at }
| LPAR IMPORT bind_var TEXT TEXT type_use RPAR /* Sugar */
{ let at = at () in
fun c -> bind_import c $3; let itype = explicit_decl c $6 empty_type at in
{itype; module_name = $4; func_name = $5} @@ at }
| LPAR IMPORT TEXT TEXT func_type RPAR /* Sugar */
{ let at = at () in
fun c -> anon_import c; let itype = implicit_decl c $5 at in
{itype; module_name = $3; func_name = $4} @@ at }
| LPAR IMPORT bind_var TEXT TEXT func_type RPAR /* Sugar */
{ let at = at () in
fun c -> bind_import c $3; let itype = implicit_decl c $6 at in
{itype; module_name = $4; func_name = $5} @@ at }
;

export :
Expand All @@ -316,8 +381,8 @@ export :
module_fields :
| /* empty */
{ fun c ->
{imports = []; exports = []; tables = []; funcs = [];
memory = None} }
{memory = None; types = c.types.tlist; funcs = []; imports = [];
exports = []; tables = []} }
| func module_fields
{ fun c -> let f = $1 c in let m = $2 c in
{m with funcs = f () :: m.funcs} }
Expand All @@ -330,6 +395,8 @@ module_fields :
| LPAR TABLE var_list RPAR module_fields
{ fun c -> let m = $5 c in
{m with tables = ($3 c func @@ ati 3) :: m.tables} }
| type_def module_fields
{ fun c -> $1 c; $2 c }
| memory module_fields
{ fun c -> let m = $2 c in
match m.memory with
Expand Down
Loading