lama_byterun/src/SM.ml

1150 lines
54 KiB
OCaml
Raw Normal View History

2018-02-20 01:28:29 +03:00
open GT
2018-02-25 19:14:25 +03:00
open Language
2018-11-06 00:21:38 +03:00
(* The type for patters *)
2021-09-28 03:02:05 +03:00
@type patt = StrCmp | String | Array | Sexp | Boxed | UnBoxed | Closure with show, enum
2020-09-10 09:07:38 +03:00
(* The type for local scopes tree *)
@type scope = {
blab : string;
elab : string;
names : (string * int) list;
subs : scope list;
} with show
let show_scope = show(scope);;
2018-02-20 01:28:29 +03:00
(* The type for the stack machine instructions *)
@type insn =
2018-11-05 20:17:11 +03:00
(* binary operator *) | BINOP of string
(* put a constant on the stack *) | CONST of int
(* put a string on the stack *) | STRING of string
(* create an S-expression *) | SEXP of string * int
(* load a variable to the stack *) | LD of Value.designation
(* load a variable address to the stack *) | LDA of Value.designation
(* store a value into a variable *) | ST of Value.designation
2019-04-11 17:31:45 +03:00
(* store a value into a reference *) | STI
2021-10-03 17:10:21 +03:00
(* store a value into array/sexp/string *) | STA
(* takes an element of array/string/sexp *) | ELEM
2018-11-05 20:17:11 +03:00
(* a label *) | LABEL of string
(* a forwarded label *) | FLABEL of string
2020-09-10 09:07:38 +03:00
(* a scope label *) | SLABEL of string
2018-11-05 20:17:11 +03:00
(* unconditional jump *) | JMP of string
(* conditional jump *) | CJMP of string * string
2020-09-10 09:07:38 +03:00
(* begins procedure definition *) | BEGIN of string * int * int * Value.designation list * string list * scope list
2018-11-05 20:17:11 +03:00
(* end procedure definition *) | END
2019-12-29 01:12:40 +03:00
(* create a closure *) | CLOSURE of string * Value.designation list
(* proto closure *) | PROTO of string * string
(* proto closure to a possible constant *) | PPROTO of string * string
2020-03-23 00:49:20 +03:00
(* proto call *) | PCALLC of int * bool
(* calls a closure *) | CALLC of int * bool
(* calls a function/procedure *) | CALL of string * int * bool
2019-04-10 22:15:08 +03:00
(* returns from a function *) | RET
2018-11-05 20:17:11 +03:00
(* drops the top element off *) | DROP
(* duplicates the top element *) | DUP
(* swaps two top elements *) | SWAP
(* checks the tag and arity of S-expression *) | TAG of string * int
2018-11-06 00:21:38 +03:00
(* checks the tag and size of array *) | ARRAY of int
(* checks various patterns *) | PATT of patt
2019-12-26 00:17:34 +03:00
(* match failure (location, leave a value *) | FAIL of Loc.t * bool
2019-11-24 02:30:32 +03:00
(* external definition *) | EXTERN of string
(* public definition *) | PUBLIC of string
2021-10-03 17:10:21 +03:00
(* import clause *) | IMPORT of string
2020-09-04 00:25:07 +03:00
(* line info *) | LINE of int
2018-05-04 02:59:23 +03:00
with show
2018-04-25 01:06:18 +03:00
(* The type for the stack machine program *)
@type prg = insn list with show
2021-09-28 03:02:05 +03:00
module ByteCode =
struct
module M = Map.Make (String)
module S = Set.Make (String)
module StringTab =
struct
type t = {mutable smap : int M.t; buffer: Buffer.t; mutable index : int}
let create () = {smap = M.empty; buffer = Buffer.create 255; index = 0}
let add st s =
try let i = M.find s st.smap in i
with Not_found ->
let i = st.index in
Buffer.add_string st.buffer s;
Buffer.add_char st.buffer (Char.chr 0);
st.smap <- M.add s i st.smap;
st.index <- st.index + String.length s + 1;
i
end
exception Found of int
let opnum =
let optab = ["+"; "-"; "*"; "/"; "%"; "<"; "<="; ">"; ">="; "=="; "!="; "&&"; "!!"] in
fun s ->
try
ignore @@ List.fold_left (fun i op -> if s = op then raise (Found i) else i+1) 1 optab;
failwith (Printf.sprintf "ERROR: undefined binary operator '%s'" s)
with
Found i -> i
(* Below are the the numbers of occurrencies of SM instructions for the stdlib+lama compiler itself
7328 SLABEL
5351 CALL
5321 DROP
4437 LABEL
4331 LD
4213 DUP
3979 EXTERN
3525 CONST
2503 LINE
2281 JMP
1400 ST
1122 CJMP
922 END
922 BEGIN
790 SEXP
770 CLOSURE
519 TAG
493 STRING
354 FAIL
349 CALLC
339 BINOP
289 ARRAY
270 PUBLIC
87 PATT
39 STA
16 FLABEL
*)
let compile cmd insns =
let word_size = 4 in
let code = Buffer.create 256 in
let st = StringTab.create () in
let lmap = Stdlib.ref M.empty in
let pubs = Stdlib.ref S.empty in
2021-10-03 17:10:21 +03:00
let imports = Stdlib.ref S.empty in
2021-09-28 03:02:05 +03:00
let globals = Stdlib.ref M.empty in
let glob_count = Stdlib.ref 0 in
let fixups = Stdlib.ref [] in
let add_lab l = lmap := M.add l (Buffer.length code) !lmap in
let add_public l = pubs := S.add l !pubs in
2021-10-03 17:10:21 +03:00
let add_import l = imports := S.add l !imports in
2021-09-28 03:02:05 +03:00
let add_fixup l = fixups := (Buffer.length code, l) :: !fixups in
let add_bytes = List.iter (fun x -> Buffer.add_char code @@ Char .chr x) in
let add_ints = List.iter (fun x -> Buffer.add_int32_ne code @@ Int32.of_int x) in
let add_strings = List.iter (fun x -> Buffer.add_int32_ne code @@ Int32.of_int @@ StringTab.add st x) in
let add_designations n =
let b x =
match n with
None -> x
| Some b -> b * 16 + x
in
List.iter (function
| Value.Global s ->
let i =
try M.find s !globals
with Not_found ->
let i = !glob_count in
incr glob_count;
globals := M.add s i !globals;
i
in
add_bytes [b 0]; add_ints [i]
| Value.Local n -> add_bytes [b 1]; add_ints [n]
| Value.Arg n -> add_bytes [b 2]; add_ints [n]
| Value.Access n -> add_bytes [b 3]; add_ints [n]
)
in
let insn_code = function
(* 0x0s *) | BINOP s -> add_bytes [opnum s]
(* 0x10 n:32 *) | CONST n -> add_bytes [1*16 + 0]; add_ints [n]
(* 0x11 s:32 *) | STRING s -> add_bytes [1*16 + 1]; add_strings [s]
(* 0x12 s:32 n:32 *) | SEXP (s, n) -> add_bytes [1*16 + 2]; add_strings [s]; add_ints [n]
(* 0x13 *) | STI -> add_bytes [1*16 + 3]
(* 0x14 *) | STA -> add_bytes [1*16 + 4]
| LABEL s
| FLABEL s
| SLABEL s -> add_lab s
(* 0x15 l:32 *) | JMP s -> add_bytes [1*16 + 5]; add_fixup s; add_ints [0]
(* 0x16 *) | END -> add_bytes [1*16 + 6]
(* 0x17 *) | RET -> add_bytes [1*16 + 7]
(* 0x18 *) | DROP -> add_bytes [1*16 + 8]
(* 0x19 *) | DUP -> add_bytes [1*16 + 9]
(* 0x1a *) | SWAP -> add_bytes [1*16 + 10]
2021-10-03 17:10:21 +03:00
(* 0x1b *) | ELEM -> add_bytes [1*16 + 11]
2021-09-28 03:02:05 +03:00
(* 0x2d n:32 *) | LD d -> add_designations (Some 2) [d]
(* 0x3d n:32 *) | LDA d -> add_designations (Some 3) [d]
(* 0x4d n:32 *) | ST d -> add_designations (Some 4) [d]
(* 0x50 l:32 *) | CJMP ("z" , s) -> add_bytes [5*16 + 0]; add_fixup s; add_ints [0]
(* 0x51 l:32 *) | CJMP ("nz", s) -> add_bytes [5*16 + 1]; add_fixup s; add_ints [0]
2021-10-03 17:10:21 +03:00
(* 0x70 *) | CALL ("Lread", _, _) -> add_bytes [7*16 + 0]
(* 0x71 *) | CALL ("Lwrite", _, _) -> add_bytes [7*16 + 1]
(* 0x72 *) | CALL ("Llength", _, _) -> add_bytes [7*16 + 2]
2021-10-29 14:52:35 +03:00
(* 0x73 *) | CALL ("Lstring", _, _) -> add_bytes [7*16 + 3]
(* 0x74 *) | CALL (".array", n, _) -> add_bytes [7*16 + 4]; add_ints [n]
2021-10-03 17:10:21 +03:00
2021-09-28 03:02:05 +03:00
(* 0x52 n:32 n:32 *) | BEGIN (_, a, l, [], _, _) -> add_bytes [5*16 + 2]; add_ints [a; l] (* with no closure *)
(* 0x53 n:32 n:32 *) | BEGIN (_, a, l, _, _, _) -> add_bytes [5*16 + 3]; add_ints [a; l] (* with a closure *)
(* 0x54 l:32 n:32 d*:32 *) | CLOSURE (s, ds) -> add_bytes [5*16 + 4]; add_fixup s; add_ints [0; List.length ds]; add_designations None ds
(* 0x55 n:32 *) | CALLC (n, tail) -> add_bytes [5*16 + 5]; add_ints [n]
(* 0x56 l:32 n:32 *) | CALL (fn, n, tail) -> add_bytes [5*16 + 6]; add_fixup fn; add_ints [0; n]
(* 0x57 s:32 n:32 *) | TAG (s, n) -> add_bytes [5*16 + 7]; add_strings [s]; add_ints [n]
(* 0x58 n:32 *) | ARRAY n -> add_bytes [5*16 + 8]; add_ints [n]
(* 0x59 n:32 n:32 *) | FAIL ((l, c), _) -> add_bytes [5*16 + 9]; add_ints [l; c]
(* 0x5a n:32 *) | LINE n -> add_bytes [5*16 + 10]; add_ints [n]
(* 0x6p *) | PATT p -> add_bytes [6*16 + enum(patt) p]
2021-10-03 17:10:21 +03:00
2021-09-28 03:02:05 +03:00
| EXTERN s -> ()
| PUBLIC s -> add_public s
2021-10-03 17:10:21 +03:00
| IMPORT s -> add_import s
2021-09-28 03:02:05 +03:00
in
List.iter insn_code insns;
add_bytes [255];
let code = Buffer.to_bytes code in
List.iter
(fun (ofs, l) ->
Bytes.set_int32_ne code ofs (Int32.of_int @@ try M.find l !lmap with Not_found -> failwith (Printf.sprintf "ERROR: undefined label '%s'" l))
)
!fixups;
let pubs = List.map
(fun l ->
Int32.of_int @@ StringTab.add st l,
(Int32.of_int @@ try M.find l !lmap with Not_found -> failwith (Printf.sprintf "ERROR: undefined label '%s'" l))
) @@ S.elements !pubs
in
let st = Buffer.to_bytes st.StringTab.buffer in
let file = Buffer.create 1024 in
Buffer.add_int32_ne file (Int32.of_int @@ Bytes.length st);
Buffer.add_int32_ne file (Int32.of_int @@ !glob_count);
Buffer.add_int32_ne file (Int32.of_int @@ List.length pubs);
List.iter (fun (n, o) -> Buffer.add_int32_ne file n; Buffer.add_int32_ne file o) pubs;
Buffer.add_bytes file st;
Buffer.add_bytes file code;
let f = open_out_bin (Printf.sprintf "%s.bc" cmd#basename) in
Buffer.output_buffer f file;
close_out f
end
2019-12-12 17:42:45 +03:00
let show_prg p =
let b = Buffer.create 512 in
List.iter (fun i -> Buffer.add_string b (show(insn) i); Buffer.add_string b "\n") p;
Buffer.contents b;;
(* Values *)
2019-12-28 01:47:26 +03:00
@type value = (string, value array) Value.t with show
(* Local state of the SM *)
2019-12-28 01:47:26 +03:00
@type local = { args : value array; locals : value array; closure : value array } with show
(* Global state of the SM *)
@type global = (string, value) arrow
2018-03-27 22:53:31 +03:00
(* Control stack *)
@type control = (prg * local) list with show
(* Data stack *)
@type stack = value list with show
(* The type for the stack machine configuration: control stack, stack, global and local states,
input and output streams
2018-04-25 01:06:18 +03:00
*)
type config = control * stack * global * local * int list * int list
2018-02-20 01:28:29 +03:00
(* Stack machine interpreter
val eval : env -> config -> prg -> config
2018-02-20 01:28:29 +03:00
Takes an environment, a configuration and a program, and returns a configuration as a result. The
environment is used to locate a label to jump to (via method env#labeled <label_name>)
2018-04-03 07:21:59 +03:00
*)
2018-04-25 01:06:18 +03:00
let split n l =
let rec unzip (taken, rest) = function
| 0 -> (List.rev taken, rest)
| n -> let h::tl = rest in unzip (h::taken, tl) (n-1)
in
unzip ([], l) n
let update glob loc z = function
| Value.Global x -> State.bind x z glob
| Value.Local i -> loc.locals.(i) <- z; glob
| Value.Arg i -> loc.args.(i) <- z; glob
2019-12-28 01:47:26 +03:00
| Value.Access i -> loc.closure.(i) <- z; glob
let print_stack memo s =
Printf.eprintf "Memo %!";
List.iter (fun v -> Printf.eprintf "%s " @@ show(value) v) s;
Printf.eprintf "\n%!"
2019-10-15 01:54:57 +03:00
let show_insn = show insn
let rec eval env (((cstack, stack, glob, loc, i, o) as conf) : config) = function
| [] -> conf
2019-10-15 01:54:57 +03:00
| insn :: prg' ->
(*
Printf.eprintf "eval\n";
Printf.eprintf " insn=%s\n" (show_insn insn);
Printf.eprintf " stack=%s\n" (show(list) (show(value)) stack);
Printf.eprintf "end\n";
*)
2020-09-04 00:25:07 +03:00
(match insn with
2021-10-04 23:42:17 +03:00
| IMPORT _ | PUBLIC _ | EXTERN _ | LINE _ -> eval env conf prg'
2020-09-04 00:25:07 +03:00
| BINOP "==" -> let y::x::stack' = stack in
let z =
match x, y with
| Value.Int _, Value.Int _ -> Value.of_int @@ Expr.to_func "==" (Value.to_int x) (Value.to_int y)
| Value.Int _, _ | _, Value.Int _ -> Value.of_int 0
| _ -> failwith "unexpected operands in comparison: %s vs. %s\n"
(show(Value.t) (fun _ -> "<not supported>") (fun _ -> "<not supported>") x)
(show(Value.t) (fun _ -> "<not supported>") (fun _ -> "<not supported>") y)
in
eval env (cstack, z :: stack', glob, loc, i, o) prg'
| BINOP op -> let y::x::stack' = stack in eval env (cstack, (Value.of_int @@ Expr.to_func op (Value.to_int x) (Value.to_int y)) :: stack', glob, loc, i, o) prg'
| CONST n -> eval env (cstack, (Value.of_int n)::stack, glob, loc, i, o) prg'
| STRING s -> eval env (cstack, (Value.of_string @@ Bytes.of_string s)::stack, glob, loc, i, o) prg'
2018-05-01 02:57:09 +03:00
| SEXP (s, n) -> let vs, stack' = split n stack in
eval env (cstack, (Value.sexp s @@ List.rev vs)::stack', glob, loc, i, o) prg'
2021-10-03 17:10:21 +03:00
| ELEM -> let a :: b :: stack' = stack in
eval env (env#builtin ".elem" [a; b] (cstack, stack', glob, loc, i, o)) prg'
| LD x -> eval env (cstack, (match x with
| Value.Global x -> glob x
| Value.Local i -> loc.locals.(i)
| Value.Arg i -> loc.args.(i)
2019-12-28 01:47:26 +03:00
| Value.Access i -> loc.closure.(i)) :: stack, glob, loc, i, o) prg'
| LDA x -> eval env (cstack, (Value.Var x) :: stack, glob, loc, i, o) prg'
| ST x -> let z::stack' = stack in
eval env (cstack, z::stack', update glob loc z x, loc, i, o) prg'
| STI -> let z::(Value.Var r)::stack' = stack in
eval env (cstack, z::stack', update glob loc z r, loc, i, o) prg'
| STA -> let z::j::stack' = stack in
(match j with
| Value.Var r -> eval env (cstack, z::stack', update glob loc z r, loc, i, o) prg'
| Value.Int _ ->
let x :: stack' = stack' in
Value.update_elem x (Value.to_int j) z;
eval env (cstack, z::stack', glob, loc, i, o) prg'
)
| SLABEL _ | LABEL _ | FLABEL _ -> eval env conf prg'
2018-04-25 01:06:18 +03:00
| JMP l -> eval env conf (env#labeled l)
| CJMP (c, l) -> let x::stack' = stack in
eval env (cstack, stack', glob, loc, i, o) (if (c = "z" && Value.to_int x = 0) || (c = "nz" && Value.to_int x <> 0) then env#labeled l else prg')
2019-10-11 17:25:58 +03:00
2019-12-29 01:12:40 +03:00
| CLOSURE (name, dgs) -> let closure =
2019-10-11 17:25:58 +03:00
Array.of_list @@
2019-12-28 01:37:59 +03:00
List.map (
function
| Value.Arg i -> loc.args.(i)
| Value.Local i -> loc.locals.(i)
2019-12-28 01:47:26 +03:00
| Value.Access i -> loc.closure.(i)
2019-12-28 01:37:59 +03:00
| _ -> invalid_arg "wrong value in CLOSURE")
dgs
2019-10-11 17:25:58 +03:00
in
2019-12-28 01:47:26 +03:00
eval env (cstack, (Value.Closure ([], name, closure)) :: stack, glob, loc, i, o) prg'
2020-03-23 00:49:20 +03:00
| CALL (f, n, _) -> let args, stack' = split n stack in
2019-10-14 19:44:33 +03:00
if env#is_label f
2019-12-29 01:12:40 +03:00
then eval env ((prg', loc)::cstack, stack', glob, {args = Array.of_list (List.rev args); locals = [||]; closure = [||]}, i, o) (env#labeled f)
2019-10-14 19:44:33 +03:00
else eval env (env#builtin f args ((cstack, stack', glob, loc, i, o) : config)) prg'
2020-03-23 00:49:20 +03:00
| CALLC (n, _) -> let vs, stack' = split (n+1) stack in
let f::args = List.rev vs in
(match f with
2019-10-11 17:25:58 +03:00
| Value.Builtin f ->
eval env (env#builtin f (List.rev args) ((cstack, stack', glob, loc, i, o) : config)) prg'
| Value.Closure (_, f, closure) ->
eval env ((prg', loc)::cstack, stack', glob, {args = Array.of_list args; locals = [||]; closure = closure}, i, o) (env#labeled f)
| _ -> invalid_arg "not a closure (or a builtin) in CALL: %s\n" @@ show(value) f
)
2020-09-10 09:07:38 +03:00
| BEGIN (_, _, locals, _, _, _) -> eval env (cstack, stack, glob, {loc with locals = Array.init locals (fun _ -> Value.Empty)}, i, o) prg'
2019-04-10 22:15:08 +03:00
| END -> (match cstack with
2020-01-05 03:33:17 +03:00
| (prg', loc')::cstack' -> eval env (cstack', (*Value.Empty ::*) stack, glob, loc', i, o) prg'
| [] -> conf
2019-04-10 22:15:08 +03:00
)
| RET -> (match cstack with
| (prg', loc')::cstack' -> eval env (cstack', stack, glob, loc', i, o) prg'
| [] -> conf
2018-04-25 01:06:18 +03:00
)
2019-04-10 22:15:08 +03:00
| DROP -> eval env (cstack, List.tl stack, glob, loc, i, o) prg'
| DUP -> eval env (cstack, List.hd stack :: stack, glob, loc, i, o) prg'
2018-05-04 02:59:23 +03:00
| SWAP -> let x::y::stack' = stack in
eval env (cstack, y::x::stack', glob, loc, i, o) prg'
2018-11-05 20:17:11 +03:00
| TAG (t, n) -> let x::stack' = stack in
eval env (cstack, (Value.of_int @@ match x with Value.Sexp (t', a) when t' = t && Array.length a = n -> 1 | _ -> 0) :: stack', glob, loc, i, o) prg'
2018-11-06 00:21:38 +03:00
| ARRAY n -> let x::stack' = stack in
eval env (cstack, (Value.of_int @@ match x with Value.Array a when Array.length a = n -> 1 | _ -> 0) :: stack', glob, loc, i, o) prg'
2018-11-06 00:21:38 +03:00
| PATT StrCmp -> let x::y::stack' = stack in
eval env (cstack, (Value.of_int @@ match x, y with (Value.String xs, Value.String ys) when xs = ys -> 1 | _ -> 0) :: stack', glob, loc, i, o) prg'
2018-11-06 00:21:38 +03:00
| PATT Array -> let x::stack' = stack in
eval env (cstack, (Value.of_int @@ match x with Value.Array _ -> 1 | _ -> 0) :: stack', glob, loc, i, o) prg'
2018-11-06 00:21:38 +03:00
| PATT String -> let x::stack' = stack in
eval env (cstack, (Value.of_int @@ match x with Value.String _ -> 1 | _ -> 0) :: stack', glob, loc, i, o) prg'
2018-11-06 00:21:38 +03:00
| PATT Sexp -> let x::stack' = stack in
eval env (cstack, (Value.of_int @@ match x with Value.Sexp _ -> 1 | _ -> 0) :: stack', glob, loc, i, o) prg'
2018-11-06 00:21:38 +03:00
| PATT Boxed -> let x::stack' = stack in
eval env (cstack, (Value.of_int @@ match x with Value.Int _ -> 0 | _ -> 1) :: stack', glob, loc, i, o) prg'
2018-11-06 00:21:38 +03:00
| PATT UnBoxed -> let x::stack' = stack in
eval env (cstack, (Value.of_int @@ match x with Value.Int _ -> 1 | _ -> 0) :: stack', glob, loc, i, o) prg'
2019-10-11 17:25:58 +03:00
| PATT Closure -> let x::stack' = stack in
eval env (cstack, (Value.of_int @@ match x with Value.Closure _ -> 1 | _ -> 0) :: stack', glob, loc, i, o) prg'
2019-12-26 00:17:34 +03:00
| FAIL (l, _) -> let x::_ = stack in
2019-12-24 03:59:05 +03:00
raise (Failure (Printf.sprintf "matching value %s failure at %s" (show(value) x) (show(Loc.t) l)))
)
2018-02-20 01:28:29 +03:00
2018-02-25 15:02:30 +03:00
(* Top-level evaluation
2018-03-06 18:46:57 +03:00
val run : prg -> int list -> int list
2018-02-25 15:02:30 +03:00
2018-03-07 14:54:02 +03:00
Takes a program, an input stream, and returns an output stream this program calculates
2018-02-25 15:02:30 +03:00
*)
2019-10-16 01:13:52 +03:00
module M = Map.Make (String)
class indexer prg =
let rec make_env m = function
| [] -> m
| (LABEL l) :: tl
| (FLABEL l) :: tl -> make_env (M.add l tl m) tl
| _ :: tl -> make_env m tl
in
2019-10-16 01:13:52 +03:00
let m = make_env M.empty prg in
object
method is_label l = M.mem l m
method labeled l = M.find l m
end
let run p i =
let module M = Map.Make (String) in
2019-10-16 01:13:52 +03:00
let glob = State.undefined in
let (_, _, _, _, i, o) =
2018-04-25 01:06:18 +03:00
eval
object
2019-10-16 01:13:52 +03:00
inherit indexer p
method builtin f args ((cstack, stack, glob, loc, i, o) as conf : config) =
2018-04-25 01:06:18 +03:00
let f = match f.[0] with 'L' -> String.sub f 1 (String.length f - 1) | _ -> f in
let (st, i, o, r) = Language.Builtin.eval (State.I, i, o, []) (List.map Obj.magic @@ List.rev args) f in
(cstack, (match r with [r] -> (Obj.magic r)::stack | _ -> Value.Empty :: stack), glob, loc, i, o)
end
2019-12-28 01:47:26 +03:00
([], [], (List.fold_left (fun s (name, value) -> State.bind name value s) glob (Builtin.bindings ())), {locals=[||]; args=[||]; closure=[||]}, i, [])
2018-04-25 01:06:18 +03:00
p
in
o
2018-02-25 15:02:30 +03:00
2018-02-20 01:28:29 +03:00
(* Stack machine compiler
2018-03-27 03:13:00 +03:00
val compile : Language.t -> prg
2018-02-20 01:28:29 +03:00
Takes a program in the source language and returns an equivalent program for the
stack machine
*)
let label s = "L" ^ s
let scope_label i s = label s ^ "_" ^ string_of_int i
2019-09-29 02:35:04 +03:00
let check_name_and_add names name mut =
if List.exists (fun (n, _) -> n = name) names
2020-01-14 05:36:03 +03:00
then report_error ~loc:(Loc.get name) (Printf.sprintf "name \"%s\" is already defined in the scope" (Subst.subst name))
else (name, mut) :: names
2019-10-13 04:57:41 +03:00
;;
@type funscope = {
st : Value.designation State.t;
arg_index : int;
local_index : int;
acc_index : int;
nlocals : int;
2020-09-10 09:07:38 +03:00
closure : Value.designation list;
scopes : scope list;
2019-10-13 04:57:41 +03:00
} with show
@type fundef = {
name : string;
args : string list;
body : Expr.t;
scope : funscope;
} with show
@type context =
| Top of fundef list
| Item of fundef * fundef list * context
with show
2019-12-29 01:12:40 +03:00
2019-10-13 04:57:41 +03:00
let init_scope st = {
st = st;
arg_index = 0;
acc_index = 0;
local_index = 0;
nlocals = 0;
2020-09-10 09:07:38 +03:00
closure = [];
scopes = [];
2019-10-13 04:57:41 +03:00
}
2019-12-28 01:59:04 +03:00
let to_fundef name args body st = {
2019-10-13 04:57:41 +03:00
name = name;
args = args;
body = body;
scope = init_scope st;
}
2019-12-28 01:59:04 +03:00
let from_fundef fd = (fd.name, fd.args, fd.body, fd.scope.st)
2019-10-13 04:57:41 +03:00
let open_scope c fd =
match c with
| Top _ -> Item (fd, [], c)
| Item (p, fds, up) ->
Item (fd, [], Item ({p with scope = fd.scope}, fds, up))
2019-10-13 05:29:06 +03:00
let close_scope (Item (f, [], c)) = c
2019-10-13 04:57:41 +03:00
let add_fun c fd =
2019-10-13 05:29:06 +03:00
match c with
2019-10-13 04:57:41 +03:00
| Top fds -> Top (fd :: fds)
| Item (parent, fds, up) -> Item (parent, fd :: fds, up)
2019-10-13 05:29:06 +03:00
let rec pick = function
| Item (parent, fd :: fds, up) ->
Item (parent, fds, up), Some fd
| Top (fd :: fds) ->
Top fds, Some fd
| c -> c, None
2019-10-13 04:57:41 +03:00
let top = function Item (p, _, _) -> Some p | _ -> None
2019-10-13 05:29:06 +03:00
let rec propagate_acc (Item (p, fds, up) as item) name =
match State.eval p.scope.st name with
| Value.Access n when n = ~-1 ->
let index = p.scope.acc_index in
let up', loc = propagate_acc up name in
Item ({p with
scope = {p.scope with
st = State.update name (Value.Access index) p.scope.st;
acc_index = p.scope.acc_index + 1;
closure = loc :: p.scope.closure
}}, fds, up'), Value.Access index
| other -> item, other
2019-12-29 01:12:40 +03:00
module FC = Map.Make (struct type t = string * string let compare = Pervasives.compare end)
class funinfo =
object (self : 'self)
val funtree = (Pervasives.ref M.empty : string M.t ref)
val closures = (Pervasives.ref M.empty : Value.designation list M.t ref)
val functx = (Pervasives.ref FC.empty : Value.designation list FC.t ref)
method show_funinfo =
Printf.sprintf "funtree: %s\nclosures: %s\ncontexts: %s\n"
(show(list) (fun (x, y) -> x ^ ": " ^ y) @@ M.bindings !funtree)
(show(list) (fun (x, y) -> x ^ ": " ^ show(list) (show(Value.designation)) y) @@ M.bindings !closures)
(show(list) (fun ((x, y), v) -> "(" ^ x ^ ", " ^ y ^ ")" ^ show(list) (show(Value.designation)) v) @@ FC.bindings !functx)
method lookup_closure p = FC.find p !functx
method register_call f c = functx := FC.add (f, c) [] !functx; self
method register_fun f p = funtree := M.add f p !funtree; self
method register_closure f c = closures := M.add f c !closures; self
method private get_parent f = M.find f !funtree
method get_closure f = M.find f !closures
2019-12-29 01:12:40 +03:00
method private propagate_for_call (f, c) =
try
let fp = self#get_parent f in
let rec find_path current =
if fp = current
then []
else find_path (self#get_parent current) @ [current]
in
let path = find_path c in
let changed = Pervasives.ref false in
let rec propagate_downwards current_closure = function
| [] -> current_closure
| f :: tl ->
let fclosure = self#get_closure f in
let delta = Pervasives.ref fclosure in
let index = Pervasives.ref (List.length fclosure) in
let added = Pervasives.ref false in
let add_to_closure loc =
added := true;
delta := !delta @ [loc];
let loc' = Value.Access !index in
incr index;
loc'
in
let next_closure =
List.map
(fun loc ->
let rec find_index i = function
| [] -> raise Not_found
| loc' :: tl ->
if loc' = loc
then Value.Access i
else find_index (i+1) tl
in
try find_index 0 fclosure with Not_found -> add_to_closure loc
)
current_closure
in
if !added then (
changed := true;
closures := M.add f !delta !closures
);
propagate_downwards next_closure tl
in
let closure = propagate_downwards (self#get_closure f) path in
functx := FC.add (f, c) closure !functx;
!changed
with Not_found -> false
method propagate_closures =
while List.fold_left (fun flag (call, _) -> flag || self#propagate_for_call call) false @@ FC.bindings !functx
do () done;
self
end
class env cmd imports =
2019-09-29 02:35:04 +03:00
object (self : 'self)
2019-10-11 17:25:58 +03:00
val label_index = 0
val scope_index = 0
val lam_index = 0
2019-10-13 04:57:41 +03:00
val scope = init_scope State.I
val fundefs = Top []
2019-11-24 02:30:32 +03:00
val decls = []
2019-12-29 01:12:40 +03:00
val funinfo = new funinfo
2020-09-04 00:25:07 +03:00
val line = None
val end_label = ""
2019-12-29 01:12:40 +03:00
method show_funinfo = funinfo#show_funinfo
method get_closure p = try funinfo#lookup_closure p with Not_found -> []
method get_fun_closure f = funinfo#get_closure f
2019-12-29 01:12:40 +03:00
method propagate_closures = {< funinfo = funinfo#propagate_closures >}
method register_call f = {< funinfo = funinfo#register_call f self#current_function >}
method register_fun f = {< funinfo = funinfo#register_fun f self#current_function >}
2019-12-29 01:12:40 +03:00
method register_closure f = {< funinfo = funinfo#register_closure f self#closure >}
method current_function =
match fundefs with Top _ -> "main" | Item (fd, _, _) -> fd.name
method private import_imports =
let paths = cmd#get_include_paths in
let env = List.fold_left
(fun env import ->
let _, intfs = Interface.find import paths in
List.fold_left
(fun env -> function
2020-12-11 01:22:25 +03:00
| `Variable name -> env#add_name name `Extern Mut
| `Fun name -> env#add_fun_name name `Extern
| _ -> env
)
env
intfs
)
self
2019-12-18 18:44:01 +03:00
imports
in
env
2019-11-24 02:30:32 +03:00
method global_scope = scope_index = 0
method get_label = (label @@ string_of_int label_index), {< label_index = label_index + 1 >}
method get_end_label =
let lab = label @@ string_of_int label_index in
lab, {< end_label = lab; label_index = label_index + 1 >}
method end_label = end_label
2019-10-13 04:57:41 +03:00
method nargs = scope.arg_index
method nlocals = scope.nlocals
2019-11-24 02:30:32 +03:00
method get_decls =
2019-11-29 23:56:03 +03:00
let opt_label = function true -> label | _ -> fun x -> "global_" ^ x in
2019-11-25 15:26:00 +03:00
List.flatten @@
List.map
(function
2019-11-29 23:56:03 +03:00
| (name, `Extern, f) -> [EXTERN (opt_label f name)]
| (name, `Public, f) -> [PUBLIC (opt_label f name)]
| (name, `PublicExtern, f) -> [PUBLIC (opt_label f name); EXTERN (opt_label f name)]
| _ -> invalid_arg "must not happen"
2019-11-25 15:26:00 +03:00
) @@
2019-11-29 23:56:03 +03:00
List.filter (function (_, `Local, _) -> false | _ -> true) decls
2019-11-24 02:30:32 +03:00
2020-09-10 09:07:38 +03:00
method push_scope (blab : string) (elab : string) =
2020-12-11 01:22:25 +03:00
(*Printf.printf "push: Scope local index = %d\n" scope.local_index;*)
match scope.st with
| State.I ->
{<
scope_index = scope_index + 1;
scope = {
scope with
st = State.G ([], State.undefined)
}
>} # import_imports
| _ ->
{< scope_index = scope_index + 1;
scope = {
scope with
2020-09-10 09:07:38 +03:00
st = State.L ([], State.undefined, scope.st);
scopes = {blab = blab; elab = elab; names = []; subs = []} :: scope.scopes
}
>}
method pop_scope =
2019-10-13 04:57:41 +03:00
match scope.st with
2019-10-13 05:29:06 +03:00
| State.I -> {< scope = {scope with st = State.I} >}
| State.G _ -> {< scope = {scope with st = State.I} >}
2019-10-13 04:57:41 +03:00
| State.L (xs, _, x) ->
2019-10-13 05:29:06 +03:00
{<
scope = {
scope with
st = x;
2020-12-11 01:22:25 +03:00
local_index = ((*Printf.printf "pop: Scope local index = %d\n" (scope.local_index - List.length xs);*) scope.local_index - List.length (List.filter (fun (_, x) -> x <> FVal) xs) (*xs*));
2020-09-10 09:07:38 +03:00
scopes = match scope.scopes with
[_] -> scope.scopes
| hs :: ps :: tl -> {ps with subs = hs :: ps.subs} :: tl
2019-10-13 05:29:06 +03:00
}
>}
2019-10-13 04:57:41 +03:00
2020-09-10 09:07:38 +03:00
method open_fun_scope blab elab (name, args, body, st') =
2019-12-28 01:37:59 +03:00
{<
2019-10-13 04:57:41 +03:00
fundefs = open_scope fundefs {
name = name;
args = args;
body = body;
2019-12-28 01:59:04 +03:00
scope = {scope with st = st'};
2019-10-13 05:29:06 +03:00
};
2019-10-13 04:57:41 +03:00
scope = init_scope (
let rec readdress_to_closure = function
| State.L (xs, st, tl) ->
State.L (xs, (fun name -> match st name with Value.Fun _ as x -> x | _ -> Value.Access (~-1)), readdress_to_closure tl)
2019-10-13 04:57:41 +03:00
| st -> st
in
readdress_to_closure st'
2019-10-13 05:29:06 +03:00
);
2020-09-10 09:07:38 +03:00
>} # push_scope blab elab
2019-10-13 04:57:41 +03:00
method close_fun_scope =
2020-09-10 09:07:38 +03:00
(*Printf.printf "Scopes: %s\n" @@ show(GT.list) show_scope scope.scopes;*)
let scopes = scope.scopes in
2019-10-13 04:57:41 +03:00
let fundefs' = close_scope fundefs in
match top fundefs' with
2020-09-10 09:07:38 +03:00
| Some fd -> {< fundefs = fundefs'; scope = fd.scope >} # pop_scope, scopes
| None -> {< fundefs = fundefs' >} # pop_scope, scopes
2019-10-13 04:57:41 +03:00
method add_arg (name : string) = {<
2019-10-13 04:57:41 +03:00
scope = {
scope with
st = (match scope.st with
| State.I | State.G _ ->
invalid_arg "wrong scope in add_arg"
| State.L (names, s, p) ->
2020-12-11 01:22:25 +03:00
State.L (check_name_and_add names name Mut, State.bind name (Value.Arg scope.arg_index) s, p)
2019-10-13 04:57:41 +03:00
);
arg_index = scope.arg_index + 1
}
>}
2019-11-24 02:30:32 +03:00
method check_scope m name =
match m with
| `Local -> ()
| _ ->
2020-01-14 05:36:03 +03:00
report_error (Printf.sprintf "external/public definitions (\"%s\") not allowed in local scopes" (Subst.subst name))
2019-11-24 02:30:32 +03:00
2020-12-11 01:22:25 +03:00
method add_name (name : string) (m : [`Local | `Extern | `Public | `PublicExtern]) (mut : Language.k) = {<
2019-11-29 23:56:03 +03:00
decls = (name, m, false) :: decls;
2019-10-13 04:57:41 +03:00
scope = {
scope with
st = (match scope.st with
| State.I ->
invalid_arg "uninitialized scope"
| State.G (names, s) ->
State.G ((match m with `Extern | `PublicExtern -> names | _ -> check_name_and_add names name mut), State.bind name (Value.Global name) s)
2019-10-13 04:57:41 +03:00
| State.L (names, s, p) ->
2019-11-24 02:30:32 +03:00
self#check_scope m name;
2020-12-11 01:22:25 +03:00
State.L (check_name_and_add names name mut, State.bind name (Value.Local ((*Printf.printf "Var: %s -> %d\n" name scope.local_index;*) scope.local_index)) s, p) (* !! *)
2019-10-13 04:57:41 +03:00
);
local_index = (match scope.st with State.L _ -> scope.local_index + 1 | _ -> scope.local_index);
2020-09-10 09:07:38 +03:00
nlocals = (match scope.st with State.L _ -> max (scope.local_index + 1) scope.nlocals | _ -> scope.nlocals);
scopes = match scope.scopes with
ts :: tl -> {ts with names = (name, scope.local_index) :: ts.names} :: tl
| _ -> scope.scopes
2019-10-13 04:57:41 +03:00
}
>}
method fun_internal_name (name : string) =
2019-10-13 04:57:41 +03:00
(match scope.st with State.G _ -> label | _ -> scope_label scope_index) name
2019-11-25 15:26:00 +03:00
method add_fun_name (name : string) (m : [`Local | `Extern | `Public | `PublicExtern]) =
let name' = self#fun_internal_name name in
let st' =
2019-10-13 04:57:41 +03:00
match scope.st with
| State.I ->
invalid_arg "uninitialized scope"
| State.G (names, s) ->
2020-12-11 01:22:25 +03:00
State.G ((match m with `Extern | `PublicExtern -> names | _ -> check_name_and_add names name FVal), State.bind name (Value.Fun name') s)
| State.L (names, s, p) ->
2019-11-24 02:30:32 +03:00
self#check_scope m name;
2020-12-11 01:22:25 +03:00
State.L (check_name_and_add names name FVal, State.bind name (Value.Fun name') s, p)
in
{<
2019-11-29 23:56:03 +03:00
decls = (name, m, true) :: decls;
2019-10-13 04:57:41 +03:00
scope = {scope with st = st'}
>}
2019-10-11 17:25:58 +03:00
method add_lambda (args : string list) (body : Expr.t) =
let name' = self#fun_internal_name (Printf.sprintf "lambda_%d" lam_index) in
2019-12-29 01:12:40 +03:00
{< fundefs = add_fun fundefs (to_fundef name' args body scope.st); lam_index = lam_index + 1 >} # register_fun name', name'
2019-10-11 17:25:58 +03:00
2019-11-25 15:26:00 +03:00
method add_fun (name : string) (args : string list) (m : [`Local | `Extern | `Public | `PublicExtern]) (body : Expr.t) =
let name' = self#fun_internal_name name in
2019-11-24 02:30:32 +03:00
match m with
| `Extern -> self
| _ ->
2019-12-29 01:12:40 +03:00
{<
2019-12-28 01:59:04 +03:00
fundefs = add_fun fundefs (to_fundef name' args body scope.st)
2019-12-29 01:12:40 +03:00
>} # register_fun name'
2020-01-11 16:38:25 +03:00
method lookup name =
2019-10-13 04:57:41 +03:00
match State.eval scope.st name with
2019-10-11 17:25:58 +03:00
| Value.Access n when n = ~-1 ->
2019-10-13 04:57:41 +03:00
let index = scope.acc_index in
2019-10-13 05:29:06 +03:00
let fundefs', loc = propagate_acc fundefs name in
2019-10-13 04:57:41 +03:00
{<
2019-10-13 05:29:06 +03:00
fundefs = fundefs';
scope = {
2019-10-13 04:57:41 +03:00
scope with
2020-01-11 16:38:25 +03:00
st = State.update name (Value.Access index) scope.st;
2019-10-13 04:57:41 +03:00
acc_index = scope.acc_index + 1;
2019-10-13 05:29:06 +03:00
closure = loc :: scope.closure
2019-10-13 04:57:41 +03:00
}
>}, Value.Access index
2019-10-11 17:25:58 +03:00
| other -> self, other
method next_definition =
2019-10-13 04:57:41 +03:00
match pick fundefs with
| fds, None -> None
| fds, Some fd -> Some ({< fundefs = fds >}, from_fundef fd)
2019-10-11 17:25:58 +03:00
2019-12-29 01:12:40 +03:00
method closure = List.rev scope.closure
2020-09-04 00:25:07 +03:00
method gen_line name =
match Loc.get name with
| None -> self, []
| Some (l, _) ->
match line with
| None -> {< line = Some l >}, [LINE l]
| Some l' when l' <> l -> {< line = Some l >}, [LINE l]
| _ -> self, []
2019-09-29 02:35:04 +03:00
end
2019-11-29 23:56:03 +03:00
let compile cmd ((imports, infixes), p) =
2019-04-07 23:42:20 +03:00
let rec pattern env lfalse = function
2019-04-02 19:51:46 +03:00
| Pattern.Wildcard -> env, false, [DROP]
| Pattern.Named (_, p) -> pattern env lfalse p
| Pattern.Const c -> env, true, [CONST c; BINOP "=="; CJMP ("z", lfalse)]
| Pattern.String s -> env, true, [STRING s; PATT StrCmp; CJMP ("z", lfalse)]
| Pattern.ArrayTag -> env, true, [PATT Array; CJMP ("z", lfalse)]
| Pattern.StringTag -> env, true, [PATT String; CJMP ("z", lfalse)]
| Pattern.SexpTag -> env, true, [PATT Sexp; CJMP ("z", lfalse)]
| Pattern.UnBoxed -> env, true, [PATT UnBoxed; CJMP ("z", lfalse)]
| Pattern.Boxed -> env, true, [PATT Boxed; CJMP ("z", lfalse)]
2019-10-11 17:25:58 +03:00
| Pattern.ClosureTag -> env, true, [PATT Closure; CJMP ("z", lfalse)]
2019-04-02 19:51:46 +03:00
| Pattern.Array ps ->
2018-11-06 00:21:38 +03:00
let lhead, env = env#get_label in
let ldrop, env = env#get_label in
let tag = [DUP; ARRAY (List.length ps); CJMP ("nz", lhead); LABEL ldrop; DROP; JMP lfalse; LABEL lhead] in
let code, env = pattern_list lhead ldrop env ps in
env, true, tag @ code @ [DROP]
2019-04-02 19:51:46 +03:00
| Pattern.Sexp (t, ps) ->
2018-11-06 00:21:38 +03:00
let lhead, env = env#get_label in
2018-05-11 02:40:52 +03:00
let ldrop, env = env#get_label in
2018-11-06 00:21:38 +03:00
let tag = [DUP; TAG (t, List.length ps); CJMP ("nz", lhead); LABEL ldrop; DROP; JMP lfalse; LABEL lhead] in
let code, env = pattern_list lhead ldrop env ps in
env, true, tag @ code @ [DROP]
and pattern_list lhead ldrop env ps =
let _, env, code =
List.fold_left
(fun (i, env, code) p ->
let env, _, pcode = pattern env ldrop p in
2021-10-03 17:10:21 +03:00
i+1, env, ([DUP; CONST i; ELEM (*CALL (".elem", 2, false)*) ] @ pcode) :: code
2018-11-06 00:21:38 +03:00
)
(0, env, [])
ps
in
List.flatten (List.rev code), env
and bindings env p =
2018-05-11 02:40:52 +03:00
let bindings =
2019-04-02 19:51:46 +03:00
transform(Pattern.t)
2018-12-03 14:48:23 +03:00
(fun fself ->
2019-06-03 13:42:37 +03:00
object inherit [int list, _, (string * int list) list] @Pattern.t
method c_Wildcard path _ = []
method c_Named path _ s p = [s, path] @ fself path p
method c_Sexp path _ x ps = List.concat @@ List.mapi (fun i p -> fself (path @ [i]) p) ps
method c_UnBoxed _ _ = []
method c_StringTag _ _ = []
method c_String _ _ _ = []
method c_SexpTag _ _ = []
method c_Const _ _ _ = []
method c_Boxed _ _ = []
method c_ArrayTag _ _ = []
method c_ClosureTag _ _ = []
method c_Array path _ ps = List.concat @@ List.mapi (fun i p -> fself (path @ [i]) p) ps
2018-12-03 14:48:23 +03:00
end)
2018-05-11 02:40:52 +03:00
[]
p
2018-05-04 02:59:23 +03:00
in
let env, code =
List.fold_left
(fun (env, acc) (name, path) ->
2020-12-11 01:22:25 +03:00
(*Printf.printf "Bindings..\n";*)
let env = env#add_name name `Local Mut in
2020-01-11 16:38:25 +03:00
let env, dsg = env#lookup name in
2020-12-11 01:22:25 +03:00
(*Printf.printf "End Bindings..\n";*)
env,
([DUP] @
2021-10-03 17:10:21 +03:00
List.concat (List.map (fun i -> [CONST i; ELEM (* CALL (".elem", 2, false)*)]) path) @
2019-10-11 17:25:58 +03:00
[ST dsg; DROP]) :: acc
2018-05-11 02:40:52 +03:00
)
(env, [])
2018-05-11 02:40:52 +03:00
(List.rev bindings)
in
env, (List.flatten code) @ [DROP]
2019-04-07 23:42:20 +03:00
and add_code (env, flag, s) l f s' = env, f, s @ (if flag then [LABEL l] else []) @ s'
2020-03-23 00:49:20 +03:00
and compile_list tail l env = function
2019-04-07 23:42:20 +03:00
| [] -> env, false, []
2020-03-23 00:49:20 +03:00
| [e] -> compile_expr tail l env e
2019-04-07 23:42:20 +03:00
| e::es ->
let les, env = env#get_label in
2020-03-23 00:49:20 +03:00
let env, flag1, s1 = compile_expr false les env e in
let env, flag2, s2 = compile_list tail l env es in
2019-04-07 23:42:20 +03:00
add_code (env, flag1, s1) les flag2 s2
2020-03-23 00:49:20 +03:00
and compile_expr tail l env = function
2019-10-11 17:25:58 +03:00
| Expr.Lambda (args, b) ->
2020-09-04 23:45:57 +03:00
let env, lines = List.fold_left (fun (env, acc) name -> let env, ln = env#gen_line name in env, acc @ ln) (env, []) args in
let env, name = env#add_lambda args b in
env#register_call name, false, lines @ [PROTO (name, env#current_function)]
2019-10-11 17:25:58 +03:00
| Expr.Scope (ds, e) ->
2020-09-10 09:07:38 +03:00
let blab, env = env#get_label in
let elab, env = env#get_label in
let env = env#push_scope blab elab in
let env, e, funs =
2019-09-29 02:35:04 +03:00
List.fold_left
(fun (env, e, funs) ->
2019-09-29 02:35:04 +03:00
function
2019-11-24 02:30:32 +03:00
| name, (m, `Fun (args, b)) -> env#add_fun_name name m, e, (name, args, m, b) :: funs
2020-12-11 01:22:25 +03:00
| name, (m, `Variable None) -> env#add_name name m Mut, e, funs
| name, (m, `Variable (Some v)) -> env#add_name name m Mut, Expr.Seq (Expr.Ignore (Expr.Assign (Expr.Ref name, v)), e), funs
2019-09-29 02:35:04 +03:00
)
(env, e, [])
(List.rev ds)
2019-09-29 02:35:04 +03:00
in
2019-11-24 02:30:32 +03:00
let env = List.fold_left (fun env (name, args, m, b) -> env#add_fun name args m b) env funs in
2020-03-23 00:49:20 +03:00
let env, flag, code = compile_expr tail l env e in
2020-09-10 09:07:38 +03:00
env#pop_scope, flag, [SLABEL blab] @ code @ [SLABEL elab]
2019-09-29 02:35:04 +03:00
2019-04-10 22:15:08 +03:00
| Expr.Unit -> env, false, [CONST 0]
| Expr.Ignore s -> let ls, env = env#get_label in
2020-03-23 00:49:20 +03:00
add_code (compile_expr tail ls env s) ls false [DROP]
2019-04-10 22:15:08 +03:00
2020-03-23 00:49:20 +03:00
| Expr.ElemRef (x, i) -> compile_list tail l env [x; i]
2020-09-04 00:25:07 +03:00
| Expr.Var x -> let env, line = env#gen_line x in
let env, acc = env#lookup x in
2020-12-11 01:22:25 +03:00
(*Printf.printf "Looking up %s -> %s\n" x (show(Value.designation) acc);*)
2020-09-04 00:25:07 +03:00
(match acc with Value.Fun name -> env#register_call name, false, line @ [PROTO (name, env#current_function)] | _ -> env, false, line @ [LD acc])
| Expr.Ref x -> let env, line = env#gen_line x in
let env, acc = env#lookup x in env, false, line @ [LDA acc]
2019-04-07 23:42:20 +03:00
| Expr.Const n -> env, false, [CONST n]
| Expr.String s -> env, false, [STRING s]
| Expr.Binop (op, x, y) -> let lop, env = env#get_label in
2020-03-23 00:49:20 +03:00
add_code (compile_list false lop env [x; y]) lop false [BINOP op]
2019-10-14 19:44:33 +03:00
| Expr.Call (f, args) -> let lcall, env = env#get_label in
(match f with
| Expr.Var name ->
let env, line = env#gen_line name in
let env, acc = env#lookup name in
2019-10-14 19:44:33 +03:00
(match acc with
| Value.Fun name ->
let env = env#register_call name in
2020-03-23 00:49:20 +03:00
let env, f, code = add_code (compile_list false lcall env args) lcall false [PCALLC (List.length args, tail)] in
env, f, line @ (PPROTO (name, env#current_function) :: code)
2019-10-14 19:44:33 +03:00
| _ ->
2020-03-23 00:49:20 +03:00
add_code (compile_list false lcall env (f :: args)) lcall false [CALLC (List.length args, tail)]
)
2019-10-14 19:44:33 +03:00
2020-03-23 00:49:20 +03:00
| _ -> add_code (compile_list false lcall env (f :: args)) lcall false [CALLC (List.length args, tail)]
2019-10-14 19:44:33 +03:00
)
2019-04-07 23:42:20 +03:00
| Expr.Array xs -> let lar, env = env#get_label in
2020-03-23 00:49:20 +03:00
add_code (compile_list false lar env xs) lar false [CALL (".array", List.length xs, tail)]
2019-04-07 23:42:20 +03:00
| Expr.Sexp (t, xs) -> let lsexp, env = env#get_label in
2020-03-23 00:49:20 +03:00
add_code (compile_list false lsexp env xs) lsexp false [SEXP (t, List.length xs)]
2019-04-07 23:42:20 +03:00
| Expr.Elem (a, i) -> let lelem, env = env#get_label in
2021-10-03 17:10:21 +03:00
add_code (compile_list false lelem env [a; i]) lelem false [ELEM (* CALL (".elem", 2, tail) *)]
2019-04-07 23:42:20 +03:00
| Expr.Assign (Expr.Ref x, e) -> let lassn, env = env#get_label in
let env , line = env#gen_line x in
let env , acc = env#lookup x in
add_code (compile_expr false lassn env e) lassn false (line @ [ST acc])
2019-04-11 17:31:45 +03:00
2019-04-07 23:42:20 +03:00
| Expr.Assign (x, e) -> let lassn, env = env#get_label in
add_code (compile_list false lassn env [x; e]) lassn false [match x with Expr.Ref _ -> STI | _ -> STA] (*Expr.ElemRef _ -> STA | _ -> STI]*)
2019-04-07 23:42:20 +03:00
| Expr.Skip -> env, false, []
2020-03-23 00:49:20 +03:00
| Expr.Seq (s1, s2) -> compile_list tail l env [s1; s2]
2019-04-07 23:42:20 +03:00
| Expr.If (c, s1, s2) -> let le, env = env#get_label in
let l2, env = env#get_label in
2020-03-23 00:49:20 +03:00
let env, fe , se = compile_expr false le env c in
let env, flag1, s1 = compile_expr tail l env s1 in
let env, flag2, s2 = compile_expr tail l env s2 in
2019-04-07 23:42:20 +03:00
env, true, se @ (if fe then [LABEL le] else []) @ [CJMP ("z", l2)] @ s1 @ (if flag1 then [] else [JMP l]) @ [LABEL l2] @ s2 @ (if flag2 then [] else [JMP l])
2019-04-07 23:42:20 +03:00
| Expr.While (c, s) -> let lexp, env = env#get_label in
let loop, env = env#get_label in
let cond, env = env#get_label in
2020-03-23 00:49:20 +03:00
let env, fe, se = compile_expr false lexp env c in
let env, _ , s = compile_expr false cond env s in
env, false, [JMP cond; FLABEL loop] @ s @ [LABEL cond] @ se @ (if fe then [LABEL lexp] else []) @ [CJMP ("nz", loop)]
| Expr.DoWhile (s, c) -> let lexp , env = env#get_label in
2019-04-07 23:42:20 +03:00
let loop , env = env#get_label in
let check, env = env#get_label in
2020-03-23 00:49:20 +03:00
let env, fe , se = compile_expr false lexp env c in
let env, flag, body = compile_expr false check env s in
env, false, [LABEL loop] @ body @ (if flag then [LABEL check] else []) @ se @ (if fe then [LABEL lexp] else []) @ [CJMP ("nz", loop)]
2019-03-11 15:24:03 +03:00
| Expr.Leave -> env, false, []
2019-10-15 01:54:57 +03:00
2019-12-26 00:17:34 +03:00
| Expr.Case (e, brs, loc, atr) ->
2019-12-24 03:59:05 +03:00
let n = List.length brs - 1 in
let lfail, env = env#get_label in
let lexp , env = env#get_label in
2020-03-23 00:49:20 +03:00
let env , fe , se = compile_expr false lexp env e in
2019-12-24 03:59:05 +03:00
let env , _, _, code, fail =
2018-05-04 02:59:23 +03:00
List.fold_left
2018-11-05 18:21:41 +03:00
(fun ((env, lab, i, code, continue) as acc) (p, s) ->
if continue
then
let (lfalse, env), jmp =
if i = n
2019-12-24 03:59:05 +03:00
then (lfail, env), []
2018-11-05 18:21:41 +03:00
else env#get_label, [JMP l]
in
let env, lfalse', pcode = pattern env lfalse p in
2020-09-10 09:07:38 +03:00
let blab, env = env#get_label in
let elab, env = env#get_label in
let env = env#push_scope blab elab in
let env, bindcode = bindings env p in
2020-03-23 00:49:20 +03:00
let env, l' , scode = compile_expr tail l env s in
let env = env#pop_scope in
2020-09-10 09:07:38 +03:00
(env, Some lfalse, i+1, ((match lab with None -> [SLABEL blab] | Some l -> [SLABEL blab; LABEL l; DUP]) @ pcode @ bindcode @ scode @ jmp @ [SLABEL elab]) :: code, lfalse')
2018-11-05 18:21:41 +03:00
else acc
2018-05-04 02:59:23 +03:00
)
2018-11-05 18:21:41 +03:00
(env, None, 0, [], true) brs
2018-05-04 02:59:23 +03:00
in
2019-12-26 00:17:34 +03:00
env, true, se @ (if fe then [LABEL lexp] else []) @ [DUP] @ (List.flatten @@ List.rev code) @ [JMP l] @ if fail then [LABEL lfail; FAIL (loc, atr != Expr.Void); JMP l] else []
in
2019-12-28 01:59:04 +03:00
let rec compile_fundef env ((name, args, stmt, st) as fd) =
(* Printf.eprintf "Compile fundef: %s, state=%s\n" name (show(State.t) (show(Value.designation)) st); *)
(* Printf.eprintf "st (inner) = %s\n" (try show(Value.designation) @@ State.eval st "inner" with _ -> " not found"); *)
2020-09-10 09:07:38 +03:00
let blab, env = env#get_label in
let elab, env = env#get_label in
let env = env#open_fun_scope blab elab fd in
(*Printf.eprintf "Lookup: %s\n%!" (try show(Value.designation) @@ snd (env#lookup "inner") with _ -> "no inner..."); *)
let env = List.fold_left (fun env arg -> env#add_arg arg) env args in
let lend, env = env#get_end_label in
2020-03-23 00:49:20 +03:00
let env, flag, code = compile_expr true lend env stmt in
2019-10-13 04:57:41 +03:00
let env, funcode = compile_fundefs [] env in
(*Printf.eprintf "Function: %s, closure: %s\n%!" name (show(list) (show(Value.designation)) env#closure);*)
2019-12-29 01:12:40 +03:00
let env = env#register_closure name in
2020-09-10 09:07:38 +03:00
let nargs, nlocals, closure = env#nargs, env#nlocals, env#closure in
let env, scopes = env#close_fun_scope in
2019-12-29 01:12:40 +03:00
let code =
2020-09-10 09:07:38 +03:00
([LABEL name; BEGIN (name, nargs, nlocals, closure, args, scopes); SLABEL blab] @
2019-12-29 01:12:40 +03:00
code @
[LABEL lend; SLABEL elab; END]) :: funcode
2019-12-29 01:12:40 +03:00
in
2020-09-10 09:07:38 +03:00
env, code
2019-10-13 04:57:41 +03:00
and compile_fundefs acc env =
match env#next_definition with
2019-10-13 04:57:41 +03:00
| None -> env, acc
| Some (env, def) ->
let env, code = compile_fundef env def in
2019-10-13 04:57:41 +03:00
compile_fundefs (acc @ code) env
in
let fix_closures env prg =
let rec inner state = function
| [] -> []
2020-09-10 09:07:38 +03:00
| BEGIN (f, na, l, c, a, s) :: tl -> BEGIN (f, na, l, (try env#get_fun_closure f with Not_found -> c), a, s) :: inner state tl
| PROTO (f, c) :: tl -> CLOSURE (f, env#get_closure (f, c)) :: inner state tl
| PPROTO (f, c) :: tl ->
(match env#get_closure (f, c) with
| [] -> inner (Some f :: state) tl
| closure -> CLOSURE (f, closure) :: inner (None :: state) tl
)
2020-03-23 00:49:20 +03:00
| PCALLC (n, tail) :: tl ->
(match state with
2020-03-23 00:49:20 +03:00
| None :: state' -> CALLC (n, tail) :: inner state' tl
| Some f :: state' -> CALL (f, n, tail) :: inner state' tl
)
| insn :: tl -> insn :: inner state tl
in
inner [] prg
in
let env = new env cmd imports in
let lend, env = env#get_label in
2020-03-23 00:49:20 +03:00
let env, flag, code = compile_expr false lend env p in
2019-11-24 02:30:32 +03:00
let code = if flag then code @ [LABEL lend] else code in
let topname = cmd#topname in
2020-09-10 09:07:38 +03:00
let env, prg = compile_fundefs [[LABEL topname; BEGIN (topname, (if topname = "main" then 2 else 0), env#nlocals, [], [], [])] @ code @ [END]] env in
2021-10-03 17:10:21 +03:00
let prg = (List.map (fun i -> IMPORT i) imports) @ [PUBLIC topname] @ env#get_decls @ List.flatten prg in
(*Printf.eprintf "Before propagating closures:\n";
2019-12-29 01:12:40 +03:00
Printf.eprintf "%s\n%!" env#show_funinfo;
*)
2019-12-29 01:12:40 +03:00
let env = env#propagate_closures in
(*
2019-12-29 01:12:40 +03:00
Printf.eprintf "After propagating closures:\n";
Printf.eprintf "%s\n%!" env#show_funinfo;
*)
(*Printf.eprintf "Before fix:\n%s\n" (show_prg prg); *)
2019-12-29 01:12:40 +03:00
let prg = fix_closures env prg in
2019-12-12 17:42:45 +03:00
cmd#dump_SM prg;
2019-10-13 05:29:06 +03:00
prg