(** 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 (* 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 } type program = { structs : structure list; unions : union list; globals : global list; (* in declaration order *) 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