type instruction =
Push
| Pop
| Read
| Print
| PrintLn
| Swap
| Stop
| Return
| Cons
| Car
| Cdr
| IsNull
| Nil
| GetElem
| SetElem
| And
| Or
| Add
| Sub
| Mul
| Div
| DivI
| RemI
| Concat
| Neg
| Frac
| Int
| Lt
| Gt
| Lte
| Gte
| Eq
| Neq
| ConstUninit
| Apply
| ConstInt of int
| ConstFloat of float
| ConstString of string
| PushSf of string
| Assign of int
| Acc of int
| Rev of int
| MakeBlock of int
| MakeBlockFilled of int
| AllocFields of int
| GetField of int
| SetField of int
| Call of string
| Jmp of string
| Jz of string
| Jnz of string | RPC of string
| Label of string
| Comment of string
| Annotation of string
| GetElemStatic of int
| SetElemStatic of int
| MakeBlockStatic of int * int
type ir_instruction =
Instruction of instruction
| GetVar of string
| SetVar of string
| NoteVar of string * int
| NoteFunction of string
| If of ir_buffer * ir_buffer * ir_buffer
| For of ir_buffer * ir_buffer * ir_buffer * ir_buffer
| While of ir_buffer * ir_buffer
| DoWhile of ir_buffer * ir_buffer
| Break
| Continue
| BeginScope
| EndScope
| EndScopeSf
| EndScopeSfRPC
| DeclareGlobal of string
and ir_buffer = ir_instruction list
let concat s_list = String.concat "" s_list
exception Ircompile_error of string
let init_buffer = ref []
let raise_compile_error s_list =
raise (Ircompile_error (concat s_list))
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 label_id = ref (-1)
let make_label str =
let _ = label_id := !label_id + 1 in
concat ["_";string_of_int !label_id;"_";str]
let rec note_arg_list lst index = match lst with
(_,varname)::rest -> (NoteVar (varname,index))::(note_arg_list rest (index+1))
| [] -> []
let rec compile_datatype_init dt = match dt with
(Ast.SynIntType
| Ast.SynFloatType
| Ast.SynStringType
| Ast.SynBoolType
| Ast.SynVoidType
| Ast.SynRefType _) -> [Instruction ConstUninit]
| Ast.SynEnumOrStructType (name,field_types) ->
if List.length !field_types > 0 then
let n_fields = List.length !field_types in
let fields_result = compile_datatypes_pushing !field_types in
fields_result
@ [Instruction (Rev n_fields)]
@ [Instruction (MakeBlockStatic (1,n_fields))]
else
[Instruction ConstUninit]
| Ast.SynArrayType (t_arr,vprod) ->
let dt_code = compile_datatype_init t_arr in
let vprod_code = compile_value_producer vprod in
dt_code
@ [Instruction Push]
@ vprod_code
@ [Instruction (MakeBlockFilled 0)]
| Ast.SynListType _ ->
[Instruction Nil]
| Ast.SynArrowType (_,_) -> [Instruction (ConstString "")]
and compile_datatypes_pushing dt_list = match dt_list with
dt::rest -> (compile_datatype_init dt)@[Instruction Push]@(compile_datatypes_pushing rest)
| [] -> []
and compile_expression expr = match expr with
Ast.SynVarDeclare vdecl -> compile_variable_declaration vdecl
| Ast.SynEnumDeclare edecl -> []
| Ast.SynVarAssign vasgn -> compile_variable_assignment vasgn
| Ast.SynCond cond -> compile_conditional cond
| Ast.SynLoop loop -> compile_loop loop
| Ast.SynFunctionCall funcall -> compile_function_call funcall
| Ast.SynReturnStatement ret -> compile_return ret
| Ast.SynBreak -> [Break]
| Ast.SynContinue -> [Continue]
and compile_expression_list expr_list = match expr_list with
cur::rest ->
let curbuf = compile_expression cur in
let restbuf = compile_expression_list rest in
curbuf@restbuf
| [] -> []
and compile_variable_declaration vdecl = match vdecl with
Ast.SynVarDeclareNoInit (dt, varname) ->
let dt_init = compile_datatype_init dt in
dt_init @ [SetVar varname]
| Ast.SynVarDeclareWithInit(_, varname, vprod) ->
let valbuf = compile_value_producer vprod in
valbuf@[SetVar varname]
and compile_conditional cond = match cond with
Ast.SynIf (vp,expr_list) ->
let vp_result = compile_value_producer vp in
let expr_result = compile_expression_list expr_list in
[If (vp_result, expr_result, [])]
| Ast.SynIfCase (vp,expr_list,continued_cond) ->
let vp_result = compile_value_producer vp in
let expr_result = compile_expression_list expr_list in
let continued_result = compile_continued_conditional continued_cond in
[If (vp_result, expr_result, continued_result)]
and compile_continued_conditional cond = match cond with
Ast.SynFinalElse expr_list -> compile_expression_list expr_list
| Ast.SynElse cond -> compile_conditional cond
and compile_loop loop = match loop with
Ast.SynWhile (vprod,lexpr_list) ->
let vp_result = compile_value_producer vprod in
let lexpr_result = compile_expression_list lexpr_list in
[While (vp_result,lexpr_result)]
| Ast.SynFor (init,test,step,body) ->
let init_result = compile_expression init in
let test_result = compile_value_producer test in
let step_result = compile_expression step in
let body_result = compile_expression_list body in
[For (init_result, test_result, step_result, body_result)]
| Ast.SynDoWhile (lexpr_list, vprod) ->
let vp_result = compile_value_producer vprod in
let lexpr_result = compile_expression_list lexpr_list in
[While (lexpr_result,vp_result)]
and compile_value value = match value with
Ast.SynIntValue i -> [Instruction (ConstInt i)]
| Ast.SynFloatValue f -> [Instruction (ConstFloat f)]
| Ast.SynBoolValue b -> [Instruction (ConstInt (if b then 1 else 0))]
| Ast.SynStringValue s -> [Instruction (ConstString s)]
and compile_value_producer vprod = match vprod with
Ast.SynValue value -> compile_value value
| Ast.SynFunctionCallValue funcall -> compile_function_call funcall
| Ast.SynVarIdentifier vident -> compile_get_variable_identifier vident
| Ast.SynBinop (vp1,op,vp2) -> compile_binary_operation vp1 op vp2
| Ast.SynPrefixUnop (op,vp) -> compile_prefix_unary_operation op vp
| Ast.SynParenthesized vprod -> compile_value_producer vprod
| Ast.SynArrayValueProducer vprod_list ->
let n = List.length vprod_list in
let values_result = compile_value_producer_list_pushing vprod_list in
values_result
@ [Instruction (Rev n);
Instruction (MakeBlockStatic (0,n))]
| Ast.SynListValueProducer lvprod -> compile_list_value_producer lvprod
and compile_value_producer_list_pushing vp_list = match vp_list with
cur::rest ->
let cur_result = compile_value_producer cur in
let rest_result= compile_value_producer_list_pushing rest in
cur_result
@ [Instruction Push]
@ rest_result
| [] -> []
and compile_list_value_producer lvprod = match lvprod with
Ast.SynListNil _ -> [Instruction Nil]
| Ast.SynListList vprod_list ->
let result = compile_value_producer_list_pushing vprod_list in
result
@ [Instruction Nil]
@ (repeat_list [Instruction Cons] (List.length vprod_list))
| Ast.SynListCons (vp1,vp2) ->
let result_car = compile_value_producer vp1 in
let result_cdr = compile_value_producer vp2 in
result_car
@ [Instruction Push]
@ result_cdr
@ [Instruction Cons]
and compile_variable_assignment vasgn = match vasgn with
Ast.SynVarAssignment (vident,vprod) ->
let vp_result = compile_value_producer vprod in
let setvar_result = compile_set_variable_identifier vident in
vp_result @ setvar_result
| Ast.SynVarModify (vident, op, vprod) ->
let vp1, vp2 = Ast.SynVarIdentifier vident, vprod
in
let op_result = compile_value_producer
(Ast.SynBinop (vp1, op, vp2)) in
let setvar_result = compile_set_variable_identifier vident in
op_result @ setvar_result
and compile_set_variable_identifier vident =
match vident with
Ast.SynVarName s -> [SetVar s]
| Ast.SynStructOrEnumValue (vid1,s2,ftype) ->
(match !ftype with
Ast.VIdStructField index ->
let getstruct = compile_get_variable_identifier vid1 in
[Instruction Push]
@ getstruct
@ [Instruction (SetElemStatic index)]
| Ast.VIdEnumValue _ ->
raise_compile_error ["Unexpected compile error: cannot set an enum value."]
| Ast.VIdUnspecified ->
raise_compile_error ["Unexpected compile error: field index ";s2;" was not determined by type checker."])
| Ast.SynArrayCell (vid,vprod) ->
let vid_result = compile_get_variable_identifier vid in
let vprod_result = compile_value_producer vprod in
[Instruction Push]
@ vprod_result
@ [Instruction Push]
@ vid_result
@ [Instruction SetElem]
and compile_get_variable_identifier vident =
match vident with
Ast.SynVarName s -> [GetVar s]
| Ast.SynStructOrEnumValue (vid1,s2,ftype) ->
(match !ftype with
Ast.VIdStructField index ->
let getstruct = compile_get_variable_identifier vid1 in
getstruct @ [Instruction (GetElemStatic index)]
| Ast.VIdEnumValue index ->
[Instruction (ConstInt index)]
| Ast.VIdUnspecified ->
raise_compile_error ["Unexpected compile error: field index ";s2;" was not determined by type checker."])
| Ast.SynArrayCell (vid,vprod) ->
let vid_result = compile_get_variable_identifier vid in
let vprod_result = compile_value_producer vprod in
vprod_result
@ [Instruction Push]
@ vid_result
@ [Instruction GetElem]
and compile_binary_operation vp1 op vp2 =
let vp1_result = compile_value_producer vp1 in
let vp2_result = compile_value_producer vp2 in
let op_command = match op with
Ast.SynBinopAnd -> [Instruction And]
| Ast.SynBinopOr -> [Instruction Or]
| Ast.SynBinopAdd -> [Instruction Add]
| Ast.SynBinopSub -> [Instruction Sub]
| Ast.SynBinopMul -> [Instruction Mul]
| Ast.SynBinopDiv -> [Instruction Div]
| Ast.SynBinopIDiv ->[Instruction DivI]
| Ast.SynBinopMod -> [Instruction RemI]
| Ast.SynBinopConcat -> [Instruction Concat]
| Ast.SynBinopCons -> [Instruction Cons]
| Ast.SynCompLt -> [Instruction Lt]
| Ast.SynCompGt -> [Instruction Gt]
| Ast.SynCompLte ->[Instruction Lte]
| Ast.SynCompGte ->[Instruction Gte]
| Ast.SynCompEq -> [Instruction Eq]
| Ast.SynCompNeq ->[Instruction Neq]
in
vp2_result
@ [Instruction Push]
@ vp1_result
@ op_command
and compile_prefix_unary_operation op vp =
let vp_result = compile_value_producer vp in
let op_command = match op with
Ast.SynUnopNot -> [Instruction Push;
Instruction (ConstInt 1);
Instruction Add;
Instruction Push;
Instruction (ConstInt 2);
Instruction Swap;
Instruction RemI]
| Ast.SynUnopCar -> [Instruction Car]
| Ast.SynUnopCdr -> [Instruction Cdr]
| Ast.SynUnopTrunc -> [Instruction Int]
| Ast.SynUnopDeref -> [Instruction (GetElemStatic 0)]
| Ast.SynUnopNeg -> [Instruction Neg]
| Ast.SynUnopNull -> [Instruction IsNull]
in
vp_result @ op_command
and compile_function_call funcall = match funcall with
Ast.SynLocalCall (fname,vprod_list,isvar) ->
let n_args = List.length vprod_list in
let args_result = compile_value_producer_list_pushing vprod_list in
let ret_label = make_label (concat ["returnfrom_local_";fname]) in
let call_instruction =
if !isvar then
[GetVar fname;
Instruction Apply]
else
[Instruction (Call fname)]
in
[Instruction (Comment (concat ["Local call to ";fname]));
Instruction (PushSf ret_label);
BeginScope]
@ args_result
@ [Instruction (Rev n_args)]
@ call_instruction
@ [EndScopeSf;
Instruction (Label ret_label)]
| Ast.SynRemoteCall (fname,vprod_list,ret_var) ->
let n_args = List.length vprod_list in
let args_result = compile_value_producer_list_pushing vprod_list in
let ret_label = make_label (concat ["returnfrom_remote_withresult_";fname]) in
let done_label = make_label (concat ["done_interpreting_remote_results_";fname]) in
let vident_set_result = compile_set_variable_identifier ret_var in
[Instruction (Comment (concat ["Remote call with result to ";fname]));
Instruction (PushSf ret_label);
BeginScope]
@ args_result
@ [Instruction (Rev n_args);
Instruction (RPC fname);
EndScopeSfRPC;
Instruction (Label ret_label);
Instruction (Jz done_label);
Instruction Swap]
@ vident_set_result
@ [Instruction Swap;
Instruction (Label done_label);
Instruction Swap;
Instruction (Acc 0);
Instruction Pop]
| Ast.SynRemoteCallNoResult (fname, vprod_list) ->
let n_args = List.length vprod_list in
let args_result = compile_value_producer_list_pushing vprod_list in
let ret_label = make_label (concat ["returnfrom_local_";fname]) in
[Instruction (Comment (concat ["Remote call with no result to ";fname]));
Instruction (PushSf ret_label);
BeginScope]
@ args_result
@ [Instruction (Rev n_args);
Instruction (RPC fname);
EndScopeSfRPC;
Instruction (Label ret_label);
Instruction Swap;
Instruction (Acc 0);
Instruction Pop]
and compile_return ret = match ret with
Ast.SynVoidReturn ->
[Instruction ConstUninit; Instruction Return]
| Ast.SynValueReturn vprod ->
let vp_result = compile_value_producer vprod in
vp_result@[Instruction Return]
let rec compile_function_definition name arg_list expr_list =
let label = make_label name in
let args = note_arg_list arg_list 0 in
let body_code = compile_expression_list expr_list in
[NoteFunction name;
Instruction (Annotation (concat ["function ";name;" ";string_of_int (List.length arg_list)]));
Instruction (Label label);
BeginScope]
@ args
@ body_code
@ [Instruction (Comment (concat ["end function ";name]));
Instruction ConstUninit;
Instruction Return;
EndScope]
let rec compile_global_expression expr = match expr with
Ast.SynFunctionDeclare (dt, name, arglist) -> [NoteFunction name]
| Ast.SynRemotableFunctionDeclare (dt, name, arglist) -> []
| Ast.SynFunctionDefine ((dt, name, arglist), expr_list) ->
compile_function_definition name arglist expr_list
| Ast.SynTemplatedDeclare (_,expr) -> compile_global_expression expr
| Ast.SynTemplatedDefine (_,expr) -> compile_global_expression expr
| Ast.SynStructDeclare sdecl -> []
| Ast.SynGlobalEnumDeclare edecl -> []
| Ast.SynGlobalVarDeclare vdecl ->
(match vdecl with
Ast.SynVarDeclareNoInit (dt,v) ->
let dt_result = compile_datatype_init dt in
let _ = init_buffer := !init_buffer @ dt_result @ [SetVar v] in
[DeclareGlobal v]
| _ -> raise_compile_error ["Unexpected compile error: cannot initialize a variable in the global scope."])
| Ast.SynStateMachine _ ->
raise_compile_error ["Unexpected compile error: source-to-source compilation of state machine failed."]
| Ast.SynInclude data ->
match !data with
Ast.IncludeAst ast -> compile_global_expression_list ast
| Ast.IncludeFileName s ->
raise_compile_error ["Unexpected compile error: include ";s;" was not typechecked properly."]
and compile_global_expression_list src_prog =
match src_prog with
cur::rest ->
let curbuf = compile_global_expression cur in
let restbuf = compile_global_expression_list rest in
curbuf@restbuf
| [] -> []
and compile_ir src_prog =
try
let _ = init_buffer := [] in
let init_label = make_label "INIT" in
let program_result = compile_global_expression_list src_prog in
[Instruction (Annotation (concat ["init ";init_label]))]
@ program_result
@ [Instruction (Annotation (concat ["function ";init_label;" 0"]));
Instruction (Label (init_label))]
@ !init_buffer
@ [Instruction Return]
with
Ircompile_error s ->
let _ = output_string stderr s in
let _ = output_string stderr "\n" in
let _ = flush stderr in
raise (Ircompile_error s)
let string_of_instruction inst = match inst with
Push -> "Push"
| Pop -> "Pop"
| Read -> "Read"
| Print -> "Print"
| PrintLn -> "PrintLn"
| Swap -> "Swap"
| Stop -> "Stop"
| Return -> "Return"
| Cons -> "Cons"
| Car -> "Car"
| Cdr -> "Cdr"
| IsNull -> "IsNull"
| Nil -> "Nil"
| GetElem -> "GetElem"
| SetElem -> "SetElem"
| And -> "And"
| Or -> "Or"
| Add -> "Add"
| Sub -> "Sub"
| Mul -> "Mul"
| Div -> "Div"
| DivI -> "DivI"
| RemI -> "RemI"
| Concat -> "Concat"
| Neg -> "Neg"
| Frac -> "Frac"
| Int -> "Int"
| Lt -> "Lt"
| Gt -> "Gt"
| Lte -> "Lte"
| Gte -> "Gte"
| Eq -> "Eq"
| Neq -> "Neq"
| ConstUninit -> "Const"
| ConstInt i -> concat ["Const ";string_of_int i]
| ConstFloat f -> concat ["Const ";string_of_float f]
| ConstString s -> concat ["Const \"";s;"\""]
| PushSf s -> concat ["PushSf ";s]
| Assign i -> concat ["Assign ";string_of_int i]
| Acc i -> concat ["Acc ";string_of_int i]
| Rev i -> concat ["Rev ";string_of_int i]
| MakeBlock i -> concat ["MakeBlock ";string_of_int i]
| AllocFields i -> concat ["-allocfields, ";string_of_int i]
| GetField i -> concat ["GetField ";string_of_int i]
| SetField i -> concat ["SetField ";string_of_int i]
| Call s -> concat ["Call ";s]
| Jmp s -> concat ["Jmp ";s]
| Jz s -> concat ["Jz ";s]
| Jnz s -> concat ["Jnz ";s]
| RPC s -> concat ["RPC ";s]
| Label s -> concat [s;": "]
| Comment s -> concat ["//";s]
| Annotation s -> concat ["-";s]
| GetElemStatic i -> concat ["GetElem ";string_of_int i]
| SetElemStatic i -> concat ["SetElem ";string_of_int i]
| MakeBlockStatic (i,j) -> concat ["MakeBlockStatic ";string_of_int i;" ";string_of_int j]
| MakeBlockFilled n -> concat ["MakeFilledBlock ";string_of_int n]
| Apply -> "Apply"
let rec string_of_ir_instruction inst = match inst with
Instruction i -> string_of_instruction i
| GetVar s -> concat ["GetVar ";s]
| SetVar s -> concat ["SetVar ";s]
| NoteVar (s,i) -> concat ["NoteVar ";s;" ";string_of_int i]
| If (test,body,elsebody) -> concat
["IF\n";
string_of_ir_buffer test;
"\nTHEN\n";
string_of_ir_buffer body;
"\nELSE\n";
string_of_ir_buffer elsebody;
"\nENDIF\n"]
| For (init,test,step,body) -> concat
["FOR\nINIT\n";
string_of_ir_buffer init;
"\nTEST IF TRUE\n";
string_of_ir_buffer test;
"\nSTEP\n";
string_of_ir_buffer step;
"\nDO\n";
string_of_ir_buffer body;
"\nENDFOR\n"]
| While (test,body) -> concat
["WHILE\nTEST IF TRUE\n";
string_of_ir_buffer test;
"\nDO\n";
string_of_ir_buffer body;
"\nENDWHILE\n"]
| DoWhile (body,test) -> concat
["WHILE\nTEST IF TRUE\n";
string_of_ir_buffer test;
"\nDO\n";
string_of_ir_buffer body;
"\nENDWHILE\n"]
| Break -> "Break"
| Continue -> "Continue"
| DeclareGlobal s -> concat ["DeclareGlobal ";s]
| EndScope -> "EndScope"
| EndScopeSf -> "EndScopeSf"
| BeginScope -> "BeginScope"
| EndScopeSfRPC -> "EndScopeSfRPC"
| NoteFunction s -> concat ["NoteFunction ";s]
and string_of_ir_buffer buf = match buf with
cur::rest -> concat [(string_of_ir_instruction cur);"\n";(string_of_ir_buffer rest)]
| [] -> ""
and print_ir_buffer chan buf =
output_string chan (string_of_ir_buffer buf)