Output and functions
These notes are very sparse right now–they will be updated soon.
Adding output to the interpreter
let output_channel = ref stdout let rec interp_exp env (exp : s_exp) : value = match exp with | Lst (Sym "do" :: exps) when List.length exps > 0 -> exps |> List.rev_map (interp_exp env) |> List.hd | Lst [Sym "print"; e] -> interp_exp env e |> string_of_value |> output_string !output_channel ; Boolean true | Lst [Sym "newline"] -> output_string !output_channel "\n" ; Boolean true
Adding output to the compiler
int main(int argc, char **argv) { void *heap = (void *)malloc(4096); entry(heap); return 0; } void print_newline() { printf("\n"); }
let rec compile_exp (tab : int symtab) (stack_index : int) (exp : s_exp) : directive list = match exp with | Lst (Sym "do" :: exps) when List.length exps > 0 -> List.concat_map (compile_exp tab stack_index) exps | Lst [Sym "print"; e] -> compile_exp tab stack_index e @ [ Mov (stack_address stack_index, Reg Rdi) ; Mov (Reg Rdi, Reg Rax) ; Add (Reg Rsp, Imm (align_stack_index stack_index)) ; Call "print_value" ; Sub (Reg Rsp, Imm (align_stack_index stack_index)) ; Mov (Reg Rdi, stack_address stack_index) ; Mov (Reg Rax, operand_of_bool true) ] | Lst [Sym "newline"] -> [ Mov (stack_address stack_index, Reg Rdi) ; Add (Reg Rsp, Imm (align_stack_index stack_index)) ; Call "print_newline" ; Sub (Reg Rsp, Imm (align_stack_index stack_index)) ; Mov (Reg Rdi, stack_address stack_index) ; Mov (Reg Rax, operand_of_bool true) ] (* ... *) let compile (program : s_exp) : string = [ Global "entry" ; Extern "error" ; Extern "read_num" ; Extern "print_value" ; Extern "print_newline" ; Label "entry" ] @ compile_exp Symtab.empty (-8) program @ [Ret] |> List.map string_of_directive |> String.concat "\n"
Updating difftest infrastructure
let interp (program : string) : unit = interp_exp Symtab.empty (parse program) |> ignore let interp_io (program : string) (input : string) = let input_pipe_ex, input_pipe_en = Unix.pipe () in let output_pipe_ex, output_pipe_en = Unix.pipe () in input_channel := Unix.in_channel_of_descr input_pipe_ex ; set_binary_mode_in !input_channel false ; output_channel := Unix.out_channel_of_descr output_pipe_en ; set_binary_mode_out !output_channel false ; let write_input_channel = Unix.out_channel_of_descr input_pipe_en in set_binary_mode_out write_input_channel false ; let read_output_channel = Unix.in_channel_of_descr output_pipe_ex in set_binary_mode_in read_output_channel false ; output_string write_input_channel input ; close_out write_input_channel ; interp program ; close_out !output_channel ; let r = input_all read_output_channel in input_channel := stdin ; output_channel := stdout ; r let interp_err (program : string) (input : string) : string = try interp_io program input with BadExpression _ -> "ERROR"
let compile_and_run_io (program : string) (input : string) : string = compile_to_file program ; ignore (Unix.system "nasm program.s -f macho64 -o program.o") ; ignore (Unix.system "gcc program.o runtime.o -o program") ; let inp, outp = Unix.open_process "./program" in output_string outp input ; close_out outp ; let r = input_all inp in close_in inp ; r let compile_and_run_err (program : string) (input : string) : string = try compile_and_run_io program input with BadExpression _ -> "ERROR" let difftest (examples : (string * string) list) = let results = List.map (fun (ex, i) -> (compile_and_run_err ex i, Interp.interp_err ex i)) examples in List.for_all (fun (r1, r2) -> r1 = r2) results let test () = difftest [("(print (read-num))", "1")]
Functions
Interpreter:
let rec interp_exp (defns : defn list) (env : value symtab) (exp : s_exp) : value = (* ... *) | Lst (Sym f :: args) when is_defn defns f -> let defn = get_defn defns f in if List.length args = List.length defn.args then let vals = List.map (interp_exp defns env) args in let fenv = List.combine (defn.args, vals) |> Symtab.of_list in interp_exp defns fenv defn.body else raise (BadExpression exp) let interp (program : string) : unit = let defns, body = parse_many program |> get_defns_and_body in interp_exp defns Symtab.empty body |> ignore