mpri-funcprog-project/src/Finish.ml

330 lines
9.1 KiB
OCaml

(* The source calculus. *)
module S = Top
(* The target calculus. *)
module T = C
(* -------------------------------------------------------------------------- *)
(* [interval i j f] constructs the list [[f i; f (i + 1); ...; f (j - 1)]]. *)
let rec interval i j (f : int -> 'a) : 'a list =
if i < j then
f i :: interval (i + 1) j f
else
[]
(* -------------------------------------------------------------------------- *)
(* [index xs] constructs a list of pairs, where each element of [xs] is paired
with its index. Indices are 0-based. *)
let index (xs : 'a list) : (int * 'a) list =
let n = List.length xs in
let indices = interval 0 n (fun i -> i) in
List.combine indices xs
(* -------------------------------------------------------------------------- *)
(* The number of fields of a block, not counting its tag. *)
let block_num_fields b =
match b with
| S.Con (_, vs) ->
List.length vs
(* -------------------------------------------------------------------------- *)
(* A simple-minded way of ensuring that every atom is printed as a
distinct string is to concatenate the atom's hint and identity,
with an underscore in between. This is guaranteed to rule out
collisions. *)
let var (x : S.variable) : T.ident =
Printf.sprintf "%s_%d" (Atom.hint x) (Atom.identity x)
let evar (x : S.variable) : T.expr =
T.Name (var x)
(* -------------------------------------------------------------------------- *)
(* Predefined C types and functions. *)
(* A universal type: every value is translated to a C value of type [univ].
This is a union type (i.e., an untagged sum) of integers and pointers to
memory blocks. *)
let univ : T.type_spec =
T.Named "univ"
(* The type of integers. *)
let int : T.type_spec =
T.Named "int"
(* The type [char] appears in the type of [main]. *)
let char : T.type_spec =
T.Named "char"
let answer : T.type_spec =
int
(* Our functions never actually return, since they are tail recursive.
We use [int] as their return type, since this is the return type of
[main]. *)
let exit : T.expr =
T.Name "exit"
let printf : T.expr =
T.Name "printf"
(* -------------------------------------------------------------------------- *)
(* [declare x init] constructs a local variable declaration for a variable [x]
of type [univ]. [x] is optionally initialized according to [init]. *)
let declare (x : S.variable) (init : T.init option) : T.declaration =
univ, None, [ T.Ident (var x), init ]
(* -------------------------------------------------------------------------- *)
(* Macro invocations. *)
let macro m es : T.expr =
(* We disguise a macro invocation as a procedure call. *)
T.Call (T.Name m, es)
(* -------------------------------------------------------------------------- *)
(* Integer literals; conversions between [univ] and [int]. *)
let iconst i : T.expr =
T.Constant (Constant.Int64, string_of_int i)
let to_int v : T.expr =
macro "TO_INT" [ v ]
(* This is an unsafe conversion, of course. *)
let from_int v : T.expr =
macro "FROM_INT" [ v ]
(* -------------------------------------------------------------------------- *)
(* The translation of values. *)
let finish_op = function
| S.OpAdd ->
T.K.Add
| S.OpSub ->
T.K.Sub
| S.OpMul ->
T.K.Mult
| S.OpDiv ->
T.K.Div
let rec finish_value (v : S.value) : T.expr =
match v with
| S.VVar x ->
evar x
| S.VLit i ->
from_int (iconst i)
| S.VBinOp (v1, op, v2) ->
from_int (
T.Op2 (
finish_op op,
to_int (finish_value v1),
to_int (finish_value v2)
)
)
let finish_values vs =
List.map finish_value vs
(* -------------------------------------------------------------------------- *)
(* A macro for allocating a memory block. *)
let alloc b : T.expr =
T.Call (T.Name "ALLOC", [ iconst (block_num_fields b) ])
(* -------------------------------------------------------------------------- *)
(* Macros for reading and initializing the tag of a memory block. *)
let read_tag (v : S.value) : T.expr =
macro "GET_TAG" [ finish_value v ]
let set_tag (x : S.variable) (tag : S.tag) : T.stmt =
T.Expr (macro "SET_TAG" [ evar x; iconst tag ])
(* -------------------------------------------------------------------------- *)
(* Macros for reading and setting a field in a memory block. *)
let read_field (v : S.value) (i : int) : T.expr =
(* [i] is a 0-based field index. *)
macro "GET_FIELD" [ finish_value v; iconst i ]
let read_field (v : S.value) (i, x) (t : T.stmt list) : T.stmt list =
(* [x] is a variable, which is declared and initialized with
the content of the [i]th field of the block [v]. *)
T.DeclStmt (declare x (Some (T.InitExpr (read_field v i)))) ::
t
let read_fields (v : S.value) xs (t : T.stmt list) : T.stmt list =
(* [xs] are variables, which are declared and initialized with
the contents of the fields of the block [v]. *)
List.fold_right (read_field v) (index xs) t
let set_field x i (v : S.value) : T.stmt =
T.Expr (macro "SET_FIELD" [ evar x; iconst i; finish_value v ])
(* -------------------------------------------------------------------------- *)
(* A sequence of instructions for initializing a memory block. *)
let init_block (x : S.variable) (b : S.block) : T.stmt list =
match b with
| S.Con (tag, vs) ->
T.Comment "Initializing a memory block:" ::
set_tag x tag ::
List.mapi (set_field x) vs
(* -------------------------------------------------------------------------- *)
(* Function calls, as expressions and as statements. *)
let ecall f args : T.expr =
T.Call (f, args)
let scall f args : T.stmt =
T.Expr (ecall f args)
(* -------------------------------------------------------------------------- *)
(* The translation of terms. *)
let rec finish_term (t : S.term) : C.stmt =
match t with
| S.Exit ->
T.Compound [
scall exit [ iconst 0 ]
]
| S.TailCall (f, vs) ->
T.Return (Some (ecall (evar f) (finish_values vs)))
| S.Print (v, t) ->
T.Compound [
scall printf [ T.Literal "%d\\n"; to_int (finish_value v) ];
finish_term t
]
| S.LetVal (x, v1, t2) ->
T.Compound [
T.DeclStmt (declare x (Some (T.InitExpr (finish_value v1))));
finish_term t2
]
| S.LetBlo (x, b1, t2) ->
T.Compound (
T.DeclStmt (declare x (Some (T.InitExpr (alloc b1)))) ::
init_block x b1 @
[ finish_term t2 ]
)
| S.IfZero (expr, tIf, tElse) ->
T.IfElse (
T.Op2(T.K.Eq, to_int @@ finish_value expr,
T.Constant(T.K.Int32, "0")),
finish_term tIf,
finish_term tElse)
| S.Swi (v, bs) ->
T.Switch (
read_tag v,
finish_branches v bs,
default
)
and default : T.stmt =
(* This default [switch] branch should never be taken. *)
T.Compound [
scall printf [ T.Literal "Oops! A nonexistent case has been taken.\\n" ];
scall exit [ iconst 42 ];
]
and finish_branches v bs =
List.map (finish_branch v) bs
and finish_branch v (S.Branch (tag, xs, t)) : T.expr * T.stmt =
iconst tag,
T.Compound (read_fields v xs [finish_term t])
(* -------------------------------------------------------------------------- *)
(* Function declarations. *)
(* We distinguish the function [main], whose type is imposed by the C standard,
and ordinary functions, whose parameters have type [univ]. *)
(* A parameter of an ordinary function has type [univ]. *)
let param (x : S.variable) : T.param =
univ, T.Ident (var x)
(* A declaration of an ordinary function. *)
let declare_ordinary_function f xs : T.declaration =
answer, None, [ T.Function (None, T.Ident (var f), List.map param xs), None ]
(* The declaration of the main function. *)
let declare_main_function : T.declaration =
let params = [
int, T.Ident "argc";
char, T.Pointer (T.Pointer (T.Ident "argv"))
] in
int, None, [ T.Function (None, T.Ident "main", params), None ]
(* -------------------------------------------------------------------------- *)
(* A function definition. *)
type decl_or_fun =
T.declaration_or_function
let define (decl : T.declaration) (t : S.term) : decl_or_fun =
T.Function (
[], (* no comments *)
false, (* not inlined *)
decl,
T.Compound [finish_term t]
)
let define_ordinary_function (S.Fun (f, xs, t)) : decl_or_fun =
define (declare_ordinary_function f xs) t
let define_main_function (t : S.term) : decl_or_fun =
define declare_main_function t
(* -------------------------------------------------------------------------- *)
(* Because all functions are mutually recursive, their definitions must be
preceded with their prototypes. *)
let prototype (f : decl_or_fun) : decl_or_fun =
match f with
| T.Function (_, _, declaration, _) ->
T.Decl ([], declaration)
| T.Decl _ ->
assert false
let prototypes (fs : decl_or_fun list) : decl_or_fun list =
List.map prototype fs @
fs
(* -------------------------------------------------------------------------- *)
(* The translation of a complete program. *)
let finish_program (S.Prog (decls, main) : S.program) : T.program =
prototypes (
define_main_function main ::
List.map define_ordinary_function decls
)