open Printf
open MIPSOps
open Primitive
open PP
open Integer
exception RuntimeError
type value =
| VBool of bool
| VInt of int32
| VArray of value array
| VNil
let asBool (v : value) : bool =
match v with
| VBool b ->
b
| _ ->
assert false
let asInt (v : value) : int32 =
match v with
| VInt i ->
i
| _ ->
assert false
let asArray (v : value) : value array =
match v with
| VArray a ->
a
| VNil ->
fprintf stderr "Runtime error -- access to nil array.\n";
raise RuntimeError
| _ ->
assert false
let default (t : typ) : value =
match t with
| TypBool ->
VBool false
| TypInt ->
VInt 0l
| TypArray _ ->
VNil
let allocate typ =
ref (default typ)
let interpret_primitive (p : primitive) (actuals : value list) =
match p, actuals with
| Write, [ v ] ->
fprintf stdout "%ld%!" (asInt v);
None
| Writeln, [ v ] ->
fprintf stdout "%ld\n%!" (asInt v);
None
| Readln, [] ->
let line = input_line stdin in
begin try
Some (VInt (Int32.of_string line))
with Failure "int of string" ->
fprintf stderr "readln: error: \"%s\" is not the representation of an integer.\n%!" line;
raise RuntimeError
end
| _ ->
assert false
type environment =
value ref StringMap.t
let lookup (genv : environment) (env : environment) (x : string) : value ref =
try
StringMap.find x env
with Not_found ->
try
StringMap.find x genv
with Not_found ->
assert false
type definitions =
PP.procedure StringMap.t
let rec interpret_call
(defs : definitions)
(genv : environment)
(env : environment)
(callee : callee)
(actuals : expression list)
: value option =
let actuals = List.map (interpret_expression defs genv env) actuals in
match callee with
| CPrimitiveFunction p ->
interpret_primitive p actuals
| CUserFunction f ->
let proc =
try
StringMap.find f defs
with Not_found ->
assert false
in
let env =
List.fold_right2 (fun (formal, _) actual env ->
StringMap.add formal (ref actual) env
) proc.formals actuals StringMap.empty
in
let env =
Option.fold (fun typ env -> StringMap.add f (allocate typ) env) proc.result env
in
let env =
StringMap.addm (StringMap.map allocate proc.locals) env
in
interpret_instruction defs genv env proc.body;
Option.map (fun _ -> !(StringMap.find f env)) proc.result
and interpret_expression
(defs : definitions)
(genv : environment)
(env : environment)
(e : expression)
: value =
match e with
| EConst (ConstBool b) ->
VBool b
| EConst (ConstInt i) ->
VInt i
| EGetVar x ->
!(lookup genv env x)
| EUnOp (UOpNeg, e) ->
VInt (- (asInt (interpret_expression defs genv env e)))
| EBinOp (op, e1, e2) ->
begin
let i1 = asInt (interpret_expression defs genv env e1) in
let i2 = asInt (interpret_expression defs genv env e2) in
match op with
| OpAdd ->
VInt (i1 + i2)
| OpSub ->
VInt (i1 - i2)
| OpMul ->
VInt (i1 * i2)
| OpDiv ->
VInt (i1 / i2)
| OpLt ->
VBool (i1 < i2)
| OpLe ->
VBool (i1 <= i2)
| OpGt ->
VBool (i1 > i2)
| OpGe ->
VBool (i1 >= i2)
| OpEq ->
VBool (i1 = i2)
| OpNe ->
VBool (i1 <> i2)
end
| EFunCall (callee, es) ->
begin match interpret_call defs genv env callee es with
| Some result ->
result
| None ->
assert false
end
| EArrayGet (ea, ei) ->
let a = asArray (interpret_expression defs genv env ea) in
let i = asInt (interpret_expression defs genv env ei) in
begin try
Array.get a i
with Invalid_argument _ ->
fprintf stderr "Runtime error -- array index out of bounds (%ld / %ld).\n"
i (Array.length a);
raise RuntimeError
end
| EArrayAlloc (typ, en) ->
let n = asInt (interpret_expression defs genv env en) in
begin try
VArray (Array.make n (default typ))
with Invalid_argument _ ->
fprintf stderr "Runtime error -- negative array length (%ld).\n" n;
raise RuntimeError
end
and interpret_condition
(defs : definitions)
(genv : environment)
(env : environment)
(c : condition)
: bool =
match c with
| CExpression e ->
asBool (interpret_expression defs genv env e)
| CNot c ->
not (interpret_condition defs genv env c)
| CAnd (c1, c2) ->
interpret_condition defs genv env c1 && interpret_condition defs genv env c2
| COr (c1, c2) ->
interpret_condition defs genv env c1 || interpret_condition defs genv env c2
and interpret_instruction
(defs : definitions)
(genv : environment)
(env : environment)
(i : instruction)
: unit =
match i with
| IProcCall (callee, es) ->
begin match interpret_call defs genv env callee es with
| None ->
()
| Some _ ->
assert false
end
| ISetVar (x, e) ->
(lookup genv env x) := (interpret_expression defs genv env e)
| IArraySet (ea, ei, ev) ->
let a = asArray (interpret_expression defs genv env ea) in
let i = asInt (interpret_expression defs genv env ei) in
let v = interpret_expression defs genv env ev in
begin try
Array.set a i v
with Invalid_argument _ ->
fprintf stderr "Runtime error -- array index out of bounds (%ld / %ld).\n"
i (Array.length a);
raise RuntimeError
end
| ISeq is ->
List.iter (interpret_instruction defs genv env) is
| IIf (cond, i1, i2) ->
interpret_instruction defs genv env
(if interpret_condition defs genv env cond then i1 else i2)
| IWhile (cond, body) ->
while interpret_condition defs genv env cond do
interpret_instruction defs genv env body
done
let interpret (p : program) : unit =
let genv =
StringMap.map allocate p.globals
in
interpret_instruction p.defs genv StringMap.empty p.main