More Functions

These notes are very sparse right now–they will be updated soon.

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

Compiler:

let rec compile_exp (defns : defn list) (tab : int symtab) (stack_index : int)
    (exp : s_exp) : directive list =
  (* ... *)
  | 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 stack_base = align_stack_index (stack_index + 8) in
        let compiled_args =
          args
          |> List.mapi (fun i arg ->
                 compile_exp defns tab (stack_base - ((i + 2) * 8)) arg
                 @ [Mov (stack_address (stack_base - ((i + 2) * 8)), Reg Rax)])
          |> List.concat
        in
        compiled_args
        @ [ Add (Reg Rsp, Imm stack_base)
          ; Call (defn_label f)
          ; Sub (Reg Rsp, Imm stack_base) ]
      else raise (BadExpression exp)

let compile_defn defns {name; args; body} =
  let ftab =
    args
    |> List.mapi (fun i arg -> (arg, -8 * (i + 1)))
    |> Symtab.of_list
  in
  [Label (defn_label name)]
  @ compile_exp defns ftab (-8 * (List.length args + 1)) body
  @ [Ret]

let compile (program : s_exp list) : string =
  let defns, body = defns_and_body program in
  [ Global "entry"
  ; Extern "error"
  ; Extern "read_num"
  ; Extern "print_value"
  ; Extern "print_newline" ]
  @ [Label "entry"]
  @ compile_exp defns Symtab.empty (-8) body
  @ [Ret]
  @ List.concat_map (compile_defn defns) defns
  |> List.map string_of_directive
  |> String.concat "\n"

let compile_to_file (program : string) : unit =
  let file = open_out "program.s" in
  output_string file (compile (parse_many program)) ;
  close_out file