diff --git a/regression/Makefile b/regression/Makefile index 6e6305d44..16acdfb23 100644 --- a/regression/Makefile +++ b/regression/Makefile @@ -9,7 +9,7 @@ check: $(TESTS) $(TESTS): %: %.expr # @$(RC) $< && cat $@.input | ./$@ > $@.log && diff $@.log orig/$@.log @cat $@.input | $(RC) -i $< > $@.log && diff $@.log orig/$@.log -# @cat $@.input | $(RC) -s $< > $@.log && diff $@.log orig/$@.log + @cat $@.input | $(RC) -s $< > $@.log && diff $@.log orig/$@.log clean: rm -f test*.log *.s *~ $(TESTS) diff --git a/src/Driver.ml b/src/Driver.ml index f2c8d9938..9e96848e9 100644 --- a/src/Driver.ml +++ b/src/Driver.ml @@ -42,7 +42,7 @@ let main = let output = if interpret then Language.eval prog input - else failwith "Not implemented yet" (*SM.run (SM.compile prog) input*) + else SM.run (SM.compile prog) input in List.iter (fun i -> Printf.printf "%d\n" i) output | `Fail er -> Printf.eprintf "Syntax error: %s\n" er diff --git a/src/SM.ml b/src/SM.ml index 35302f53b..ce6587d7f 100644 --- a/src/SM.ml +++ b/src/SM.ml @@ -11,15 +11,18 @@ open Language (* store a variable from the stack *) | ST of string (* a label *) | LABEL of string (* unconditional jump *) | JMP of string -(* conditional jump *) | CJMP of string * string with show +(* conditional jump *) | CJMP of string * string +(* begins procedure definition *) | BEGIN of string list * string list +(* end procedure definition *) | END +(* calls a procedure *) | CALL of string with show (* The type for the stack machine program *) type prg = insn list - -(* The type for the stack machine configuration: a stack and a configuration from statement + +(* The type for the stack machine configuration: control stack, stack and configuration from statement interpreter *) -type config = int list * Stmt.config +type config = (prg * State.t) list * int list * Stmt.config (* Stack machine interpreter @@ -28,19 +31,31 @@ type config = int list * Stmt.config 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 ) *) -let rec eval env ((stack, ((st, i, o) as c)) as conf) = function +let rec eval env ((cstack, stack, ((st, i, o) as c)) as conf) = function | [] -> conf | insn :: prg' -> (match insn with - | BINOP op -> let y::x::stack' = stack in eval env (Expr.to_func op x y :: stack', c) prg' - | READ -> let z::i' = i in eval env (z::stack, (st, i', o)) prg' - | WRITE -> let z::stack' = stack in eval env (stack', (st, i, o @ [z])) prg' - | CONST i -> eval env (i::stack, c) prg' - | LD x -> eval env (State.eval st x :: stack, c) prg' - | ST x -> let z::stack' = stack in eval env (stack', (State.update x z st, i, o)) prg' - | LABEL _ -> eval env conf prg' - | JMP l -> eval env conf (env#labeled l) - | CJMP (c, l) -> let x::stack' = stack in eval env conf (if (c = "z" && x = 0) || (c = "nz" && x <> 0) then env#labeled l else prg') + | BINOP op -> let y::x::stack' = stack in eval env (cstack, Expr.to_func op x y :: stack', c) prg' + | READ -> let z::i' = i in eval env (cstack, z::stack, (st, i', o)) prg' + | WRITE -> let z::stack' = stack in eval env (cstack, stack', (st, i, o @ [z])) prg' + | CONST i -> eval env (cstack, i::stack, c) prg' + | LD x -> eval env (cstack, State.eval st x :: stack, c) prg' + | ST x -> let z::stack' = stack in eval env (cstack, stack', (State.update x z st, i, o)) prg' + | LABEL _ -> eval env conf prg' + | JMP l -> eval env conf (env#labeled l) + | CJMP (c, l) -> let x::stack' = stack in eval env conf (if (c = "z" && x = 0) || (c = "nz" && x <> 0) then env#labeled l else prg') + | CALL f -> eval env ((prg', st)::cstack, stack, c) (env#labeled f) + | BEGIN (args, locals) -> let rec combine acc args stack = + match args, stack with + | [], _ -> List.rev acc, stack + | a::args', s::stack' -> combine ((a, s)::acc) args' stack' + in + let state', stack' = combine [] args stack in + eval env (cstack, stack', (List.fold_left (fun s (x, v) -> State.update x v s) (State.push_scope st (args @ locals)) state', i, o)) prg' + | END -> (match cstack with + | (prg', st')::cstack' -> eval env (cstack', stack, (State.drop_scope st st', i, o)) prg' + | [] -> conf + ) ) (* Top-level evaluation @@ -57,53 +72,73 @@ let run p i = | _ :: tl -> make_map m tl in let m = make_map M.empty p in - let (_, (_, _, o)) = eval (object method labeled l = M.find l m end) ([], (State.empty, i, [])) p in o + let (_, _, (_, _, o)) = eval (object method labeled l = M.find l m end) ([], [], (State.empty, i, [])) p in o (* Stack machine compiler - val compile : Language.Stmt.t -> prg + val compile : Language.t -> prg Takes a program in the source language and returns an equivalent program for the stack machine *) -let compile p = +let compile (defs, p) = + let label s = "L" ^ s in let rec expr = function | Expr.Var x -> [LD x] | Expr.Const n -> [CONST n] | Expr.Binop (op, x, y) -> expr x @ expr y @ [BINOP op] in - let rec compile' l env = function + let rec compile_stmt l env = function | Stmt.Read x -> env, false, [READ; ST x] | Stmt.Write e -> env, false, expr e @ [WRITE] | Stmt.Assign (x, e) -> env, false, expr e @ [ST x] | Stmt.Skip -> env, false, [] | Stmt.Seq (s1, s2) -> let l2, env = env#get_label in - let env, flag1, s1 = compile' l2 env s1 in - let env, flag2, s2 = compile' l env s2 in + let env, flag1, s1 = compile_stmt l2 env s1 in + let env, flag2, s2 = compile_stmt l env s2 in env, flag2, s1 @ (if flag1 then [LABEL l2] else []) @ s2 | Stmt.If (c, s1, s2) -> let l2, env = env#get_label in - let env, flag1, s1 = compile' l env s1 in - let env, flag2, s2 = compile' l env s2 in + let env, flag1, s1 = compile_stmt l env s1 in + let env, flag2, s2 = compile_stmt l env s2 in env, true, expr c @ [CJMP ("z", l2)] @ s1 @ (if flag1 then [] else [JMP l]) @ [LABEL l2] @ s2 @ (if flag2 then [] else [JMP l]) | Stmt.While (c, s) -> let loop, env = env#get_label in let cond, env = env#get_label in - let env, _, s = compile' cond env s in + let env, _, s = compile_stmt cond env s in env, false, [JMP cond; LABEL loop] @ s @ [LABEL cond] @ expr c @ [CJMP ("nz", loop)] | Stmt.Repeat (s, c) -> let loop , env = env#get_label in let check, env = env#get_label in - let env , flag, body = compile' check env s in + let env , flag, body = compile_stmt check env s in env, false, [LABEL loop] @ body @ (if flag then [LABEL check] else []) @ (expr c) @ [CJMP ("z", loop)] + + | Stmt.Call (f, args) -> let args_code = List.concat @@ List.map expr (List.rev args) in + env, false, args_code @ [CALL (label f)] + in + let compile_def env (name, (args, locals, stmt)) = + let lend, env = env#get_label in + let env, flag, code = compile_stmt lend env stmt in + env, + [LABEL name; BEGIN (args, locals)] @ + code @ + (if flag then [LABEL lend] else []) @ + [END] in let env = object - val label = 0 - method get_label = "L_" ^ string_of_int label, {< label = label + 1 >} + val ls = 0 + method get_label = (label @@ string_of_int ls), {< ls = ls + 1 >} end in + let env, def_code = + List.fold_left + (fun (env, code) (name, others) -> let env, code' = compile_def env (label name, others) in env, code'::code) + (env, []) + defs + in let lend, env = env#get_label in - let _, flag, code = compile' lend env p in - if flag then code @ [LABEL lend] else code + let _, flag, code = compile_stmt lend env p in + (LABEL "main" :: if flag then code @ [LABEL lend] else code) @ [END] @ (List.concat def_code) +