121 lines
4.6 KiB
OCaml
121 lines
4.6 KiB
OCaml
(** The typed IR: what the checker produces and what every backend consumes.
|
|
|
|
Three backends share this — the tree-walking interpreter, dev redefinition
|
|
and the release AOT build (plan.org, Compilation) — so everything a backend
|
|
would otherwise have to re-derive is resolved here and nowhere else:
|
|
|
|
- names are gone. A local is a slot index into the frame, a global is a
|
|
name, and a call names its callee directly. No environment lookup.
|
|
- field access is an index, not a string, and any auto-deref the source
|
|
relied on is an explicit [Deref] node.
|
|
- literals have a machine type. There is no untyped 1 past this point.
|
|
- a struct literal lists every field in declaration order, with the omitted
|
|
ones filled in as [Zero] — ZII is settled here rather than at runtime.
|
|
- sugar is already gone from the AST; what is left is the small set below. *)
|
|
|
|
type prim =
|
|
(* arithmetic and comparison, per machine type — the operands carry their own
|
|
kind at runtime, so one constructor covers every width *)
|
|
| Add | Sub | Mul | Div | Rem
|
|
| Eq | Ne | Lt | Le | Gt | Ge
|
|
| Not
|
|
(* bitwise, integers only. [Shr] is arithmetic on a signed type and logical
|
|
on an unsigned one, which is what the operand's own kind already says. *)
|
|
| BitAnd | BitOr | BitXor | Shl | Shr
|
|
(* containers: fixed arrays and slices only at milestone 2 *)
|
|
| Len | At | Slice
|
|
(* the milestone-2 host primitives, plan.org. The four conversions are
|
|
*text*: bytes->f64 parses "12.5", f64->bytes renders it — that is what
|
|
calc-me's tokenizer and the prelude's printers each need. *)
|
|
| Bytes | BytesToF64 | BytesToI64 | F64ToBytes | I64ToBytes
|
|
| WriteStdout | Exit | Argv
|
|
| Cast of Types.t
|
|
|
|
type expr = { e : expr_kind; ty : Types.t; loc : Loc.t }
|
|
|
|
and expr_kind =
|
|
| Int of int64 * Types.ikind
|
|
| Float of float * Types.fkind
|
|
| Bool of bool
|
|
| Str of string
|
|
| Unit
|
|
| Zero of Types.t (* ZII: all-bytes-zero of this type *)
|
|
| Uninit of Types.t (* the explicit opt-out *)
|
|
| Local of int (* slot index into the frame *)
|
|
| Global of string
|
|
| Prim of prim * expr list
|
|
| Call of string * expr list (* direct call; no first-class fns yet *)
|
|
| Do of expr list
|
|
| Let of (int * expr) list * expr list
|
|
| If of expr * expr * expr
|
|
| While of expr * expr list
|
|
| Return of expr option
|
|
| Set of place * expr
|
|
| Field of expr * int (* target is already a struct value *)
|
|
| Addr of place
|
|
| Deref of expr
|
|
| Make of string * expr list (* struct literal, every field, in order *)
|
|
| Arr of expr list (* fixed-array literal *)
|
|
| Some_ of expr
|
|
| None_
|
|
| Match of expr * arm list
|
|
(* (some x): unwrap Some, else early-return None from the enclosing function.
|
|
An early return, not an expression that can fail — hence its own node. *)
|
|
| UnwrapSome of expr
|
|
|
|
and place =
|
|
| Plocal of int
|
|
| Pglobal of string
|
|
| Pfield of expr * int
|
|
| Pindex of expr * expr list
|
|
| Pkey of expr * expr
|
|
| Pderef of expr
|
|
|
|
(* [binds] are the slots the pattern's fields are bound to, in field order. *)
|
|
and arm = { acase : string option; binds : int list; abody : expr list }
|
|
|
|
type field = { fname : string; fty : Types.t }
|
|
|
|
type structure = { sname : string; fields : field list }
|
|
|
|
type variant = { vname : string; vfields : field list }
|
|
|
|
type union = { uname : string; cases : variant list }
|
|
|
|
type fn = {
|
|
name : string;
|
|
params : Types.t list; (* bound to slots 0 .. n-1, in order *)
|
|
slots : Types.t array; (* the frame: one entry per slot *)
|
|
ret : Types.t;
|
|
body : expr list;
|
|
floc : Loc.t;
|
|
}
|
|
|
|
type global = { gname : string; gty : Types.t; ginit : expr; gconst : bool }
|
|
|
|
(* A foreign function: no body, and [esym] is the symbol the linker sees. The
|
|
aggregate calling convention is not modelled here — a C shim flattens every
|
|
struct that crosses the boundary, so clang classifies it per target and
|
|
nothing in the backend has to know x86-64 from arm64 from wasm32. *)
|
|
type extern = {
|
|
ename : string; (* the Flan name, e.g. rl/init-window *)
|
|
esym : string; (* the C symbol *)
|
|
eparams : Types.t list;
|
|
eret : Types.t;
|
|
}
|
|
|
|
type program = {
|
|
structs : structure list;
|
|
unions : union list;
|
|
globals : global list; (* in declaration order *)
|
|
externs : extern list;
|
|
fns : fn list;
|
|
}
|
|
|
|
let field_index (s : structure) name =
|
|
let rec go i = function
|
|
| [] -> None
|
|
| f :: rest -> if String.equal f.fname name then Some i else go (i + 1) rest
|
|
in
|
|
go 0 s.fields
|