open Bmdirc
type instruction = Bmdirc.instruction
type location =
StackLoc of int
| GlobalLoc of int
| FunctionLoc of string
type loop_info =
LoopBreakContinue of string * string
| LoopNoInfo
type environment =
EnvTopLevel of int * (string * location) list
| EnvFrame of int * loop_info * (string * location) list * environment
exception Compile_error of string
let raise_compile_error slist = raise (Compile_error (String.concat "" slist))
let make_env () = (EnvTopLevel (0,[]))
let rec get_cur_defs env = match env with
EnvTopLevel (_,dlist) -> dlist
| EnvFrame (_,_,dlist,parent) -> dlist @ (get_cur_defs parent)
let get_loc v env = List.assoc v (get_cur_defs env)
let add_loc (v,loc) env = match env with
EnvTopLevel (n,dlist) -> EnvTopLevel (n,((v,loc)::dlist))
| EnvFrame (n,info,dlist,parent) -> EnvFrame (n,info,((v,loc)::dlist),parent)
let isdef v env = List.mem_assoc v (get_cur_defs env)
let rec offset_locs i env =
let helper (x,y) = match y with
StackLoc loc -> (x,StackLoc (loc+i))
| z -> x,z
in
match env with
EnvTopLevel (n,vlist) ->
EnvTopLevel (n+i, List.map helper vlist)
| EnvFrame (n,info, vlist,parent) ->
EnvFrame (n+i,info,List.map helper vlist, offset_locs i parent)
let add_frame env = EnvFrame (0,LoopNoInfo,[],env)
let remove_frame env = match env with
EnvTopLevel _ -> raise_compile_error ["Unexpected compilation error: attempted to pop toplevel compilation scope."]
| EnvFrame (n,_,_,parent) -> offset_locs (-n) parent
let rec repeat_list lst n =
if n = 0 then []
else if n = 1 then lst
else if n > 1 then lst @ (repeat_list lst (n-1))
else raise (Invalid_argument "repeat_list must have a nonnegative argument")
let get_restore_code env = match env with
EnvTopLevel _ -> []
| EnvFrame (n,_,_,_) -> repeat_list [Pop] n
let set_loop_info info env = match env with
EnvTopLevel _ -> raise_compile_error ["Unexpected compilation error: attempted to set loop information in top level."]
| EnvFrame (n,_,defs,parent) -> EnvFrame (n,info,defs,parent)
let rec get_break_label env = match env with
EnvTopLevel _ -> raise_compile_error ["Unexpected compilation error: attempted to break from a loop outside a loop."]
| EnvFrame (_,info,_,parent) ->
match info with
LoopNoInfo -> get_break_label parent
| LoopBreakContinue (b,_) -> b
let rec get_breakcontinue_restore env = match env with
EnvTopLevel _ -> raise_compile_error ["Unexpected compilation error: attempted to break from a loop outside a loop."]
| EnvFrame (n,info,_,parent) ->
match info with
LoopNoInfo -> (repeat_list [Pop] n) @ get_breakcontinue_restore parent
| LoopBreakContinue (_,_) -> repeat_list [Pop] n
let rec get_continue_label env = match env with
EnvTopLevel _ -> raise_compile_error ["Unexpected compilation error: attempted to continue a loop outside a loop."]
| EnvFrame (_,info,_,parent) ->
match info with
LoopNoInfo -> get_continue_label parent
| LoopBreakContinue (_,c) -> c
let make_label str = Bmdirc.make_label str
let get_stack_offset inst = match inst with
Push -> 1
| Pop -> -1
| Read -> 0
| Print -> 0
| PrintLn -> 0
| Swap -> 0
| Stop -> 0
| Return -> 0
| Cons -> -1
| Car -> 0
| Cdr -> 0
| IsNull -> 0
| Nil -> 0
| GetElem -> -1
| SetElem -> -2
| And -> -1
| Or -> -1
| Add -> -1
| Sub -> -1
| Mul -> -1
| Div -> -1
| DivI -> -1
| RemI -> -1
| Concat -> -1
| Neg -> 0
| Frac -> 0
| Int -> 0
| Lt -> -1
| Gt -> -1
| Lte -> -1
| Gte -> -1
| Eq -> -1
| Neq -> -1
| ConstUninit -> 0
| ConstInt _ -> 0
| ConstFloat _ -> 0
| ConstString _ -> 0
| PushSf _ -> 2
| Assign _ -> 0
| Acc _ -> 0
| Rev _ -> 0
| MakeBlock _ -> 0
| AllocFields _ -> 0
| GetField _ -> 0
| SetField _ -> 0
| Call _ -> 0
| Apply -> 0
| Jmp _ -> 0
| Jz _ -> 0
| Jnz _ -> 0
| RPC _ -> 0
| Label _ -> 0
| Comment _ -> 0
| Annotation _ -> 0
| GetElemStatic _ -> 0
| SetElemStatic _ -> -1
| MakeBlockStatic (_,n) -> -n
| MakeBlockFilled _ -> -1
let rec n_globals env =
let rec helper lst = match lst with
(_,GlobalLoc _)::rest -> 1 + (helper rest)
| _::rest -> helper rest
| [] -> 0
in
match env with
EnvTopLevel (_,vlist) -> helper vlist
| EnvFrame (_,_,vlist,parent) -> (helper vlist) + (n_globals parent)
let rec compile_ir_instruction inst env = match inst with
Instruction i -> [i], offset_locs (get_stack_offset i) env
| GetVar v ->
if isdef v env then
(match get_loc v env with
StackLoc loc ->
[Acc loc], env
| GlobalLoc loc ->
[GetField loc], env
| FunctionLoc funname ->
[ConstString funname], env)
else
raise_compile_error ["Error in instruction GetVar, ";v;" was not defined."]
| SetVar v ->
if isdef v env then
(match get_loc v env with
StackLoc loc ->
[Assign loc], env
| GlobalLoc loc ->
[SetField loc], env
| FunctionLoc name ->
raise_compile_error ["Unexpected compile error: attempted to re-define function ";name;"."])
else
[Push], add_loc (v,StackLoc 0) (offset_locs 1 env)
| NoteVar (v,i) -> [], add_loc (v,StackLoc i) env
| NoteFunction name -> [], add_loc (name,FunctionLoc name) env
| If (test,thenbody,elsebody) ->
let else_label = make_label "else" in
let endif_label = make_label "endif" in
let test_result,_ = compile_ir_instruction_list test env in
let then_result,then_env = compile_ir_instruction_list thenbody (add_frame env) in
let else_result,else_env = compile_ir_instruction_list elsebody (add_frame env) in
let restore_code_then = get_restore_code then_env in
let restore_code_else = get_restore_code else_env in
[Comment "if"]
@ test_result
@ [Jz else_label]
@ then_result
@ restore_code_then
@ [Jmp endif_label;
Label else_label]
@ else_result
@ restore_code_else
@ [Label endif_label]
@ [Comment "endif"], env
| For (init, test, step, body) ->
let test_label = make_label "for_test" in
let endfor_label = make_label "for_end" in
let init_result,init_env = compile_ir_instruction_list init (add_frame env) in
let restore_init = get_restore_code init_env in
let test_result,_ = compile_ir_instruction_list test init_env in
let env_for_body = set_loop_info (LoopBreakContinue (endfor_label,test_label)) (add_frame init_env) in
let body_result,body_env = compile_ir_instruction_list body env_for_body in
let restore_body = get_restore_code body_env in
let step_result,step_env = compile_ir_instruction_list step (add_frame init_env) in
let restore_step = get_restore_code step_env in
[Comment "for"]
@ init_result
@ [Label test_label]
@ test_result
@ [Jz endfor_label]
@ body_result
@ restore_body
@ step_result
@ restore_step
@ [Jmp test_label]
@ [Label endfor_label]
@ restore_init
@ [Comment "end for"], env
| While (test, body) ->
let test_label = make_label "while" in
let wend_label = make_label "wend" in
let test_result,_ = compile_ir_instruction_list test env in
let env_for_body = set_loop_info (LoopBreakContinue (wend_label,test_label)) (add_frame env) in
let body_result,body_env = compile_ir_instruction_list body env_for_body in
let restore_body = get_restore_code body_env in
[Comment "while"]
@ [Label test_label]
@ test_result
@ [Jz wend_label]
@ body_result
@ restore_body
@ [Jmp test_label]
@ [Label wend_label]
@ [Comment "wend"], env
| DoWhile (body,test) ->
let do_label = make_label "do" in
let end_label = make_label "end_dowhile" in
let test_result,_ = compile_ir_instruction_list test env in
let env_for_body = set_loop_info (LoopBreakContinue (end_label,do_label)) (add_frame env) in
let body_result,body_env = compile_ir_instruction_list body env_for_body in
let restore_body = get_restore_code body_env in
[Comment "dowhile"]
@ [Label do_label]
@ body_result
@ restore_body
@ [Comment "dowhile_test"]
@ test_result
@ [Jnz do_label]
@ [Label end_label]
@ [Comment "end dowhile"], env
| Break ->
let restore_code = get_breakcontinue_restore env in
restore_code @ [Jmp (get_break_label env)], env
| Continue ->
let restore_code = get_breakcontinue_restore env in
restore_code @ [Jmp (get_continue_label env)], env
| BeginScope -> [], add_frame env
| EndScope -> [], remove_frame env
| EndScopeSf -> [], offset_locs (-2) (remove_frame env)
| EndScopeSfRPC -> [], offset_locs (-1) (remove_frame env)
| DeclareGlobal s ->
let n = (n_globals env) in
[], add_loc (s,GlobalLoc n) env
and compile_ir_instruction_list ir_buf env = match ir_buf with
cur::rest ->
let cur_result,new_env = compile_ir_instruction cur env in
let rest_result,final_env = compile_ir_instruction_list rest new_env in
cur_result @ rest_result, final_env
| [] -> [], env
let compile_buffer ir_buf =
try
let start_env = make_env () in
let result,result_env = compile_ir_instruction_list ir_buf start_env in
(AllocFields (n_globals result_env))::result
with
Compile_error s ->
let _ = output_string stderr s in
let _ = output_string stderr "\n" in
let _ = flush stderr in
raise (Compile_error s)
let rec string_of_buffer buf = match buf with
cur::rest -> String.concat "" [(Bmdirc.string_of_instruction cur);"\n";(string_of_buffer rest)]
| [] -> ""
let print_buffer chan buf =
output_string chan (string_of_buffer buf)