(** The AST: syntax with special forms recognised, before typing. Sugar is gone by this point. [when], [unless], [cond] and [and]/[or] are desugared into [If] and [Do]; they are compiler special forms until macros arrive at milestone 5, so there is nothing to preserve for a macroexpander to see yet. Types here are *surface* type expressions, not resolved types. [Ptr] and [Option] are still just names; the checker resolves them. *) (* ── Type expressions ──────────────────────────────────────────────── *) type texpr = { t : texpr_kind; tloc : Loc.t } and texpr_kind = | Tname of string (* i32 bool Cursor string *) | Tslice of texpr (* [u8] ptr+len *) | Tarray of len * texpr (* [4 f32] [rows [cols u32]] *) | Tmap of texpr * texpr (* {string i32} *) | Tapp of string * texpr list (* (Ptr Cursor) (Option f64) *) | Tfn of texpr list * texpr (* (Fn [a a] bool) *) (* An array length is an integer or a compile-time constant's name. *) and len = | Lint of int64 | Lname of string (* ── Expressions ───────────────────────────────────────────────────── *) type expr = { e : expr_kind; loc : Loc.t } and expr_kind = | Int of int64 | Float of float | Byte of int | Str of string | Kw of string (* :space — coerced at typed call sites *) | Quote of string (* 'skip-form — restart names *) | Var of string | Do of expr list | Let of binding list * expr list | If of expr * expr * expr option | While of expr * expr list | Return of expr option | Set of place * expr | Field of expr * string (* (.pos c) — auto-derefs one level *) | Call of expr * expr list | Match of expr * arm list | Struct of string * (string * expr) list (* (Cursor {.src s}) *) | Arr of expr list (* [0xE6B800FF ...] — a fixed array value *) (* These bind names or alter control flow, so none of them can be a call. *) | Fn of string list * expr list (* (fn [x y] ...) — non-escaping *) | Dotimes of string * expr * expr list (* (dotimes [i n] ...) *) | Defer of expr list (* runs on scope exit *) | Unwrap of unwrap * expr (* (some x) / (try x) *) (* (handler-bind [(Type [c] body ...) ...] body ...) — spec-conditions.md. A clause binds a name for the condition, so this cannot be a call. *) | HandlerBind of hclause list * expr list | Signal of sigkind * expr (* (signal c) / (error c) *) (* (restart-case body (name [p T] body ...) ...) and (invoke-restart 'name arg ...). Both alter control flow, so neither can be a call, and a clause binds its parameters — §3. *) | RestartCase of expr * rclause list | InvokeRestart of string * expr list (* Two ways to signal, because they are two different things — §1 and §2. [signal] returns Unit whatever it finds; [error] has type Never and, with nothing transferring, the program stops. *) and sigkind = Ssignal | Serror and hclause = { hty : texpr; hname : string; hbody : expr list; hloc : Loc.t } (* [rparams] are §3's inline annotations, the same name/type pairs a [defn] takes. They are bound in the clause body and filled in by whatever invoked the restart, which is why their count and types are checked at run time (§3): a restart is found by name on a dynamic stack. *) and rclause = { rname : string; rparams : field list; rbody : expr list; rloc : Loc.t } (* Inline name/type pairs, as in [defn], [let] and [defstruct]. Here because a restart clause's parameters are one, and a clause is part of an expression. *) and field = { fname : string; fty : texpr; floc : Loc.t } (* Two unwrap operators, because they are two different things — plan.org. *) and unwrap = Usome | Utry and binding = { bname : string; bty : texpr option; bval : expr; bloc : Loc.t } (* The fixed list of assignable forms — spec-memory.md. Not setf. *) and place = | Pvar of string | Pfield of expr * string (* (set (.hp e) v) *) | Pindex of expr * expr list (* (set (at grid r c) v) *) | Pderef of expr (* (set (deref p) v) *) and arm = { pat : pattern; body : expr list; aloc : Loc.t } and pattern = | Pctor of string * string list (* (Some e) (Rect w h) None *) | Pwild (* _ :else *) (* ── Declarations ──────────────────────────────────────────────────── *) type fn = { name : string; params : field list; ret : texpr option; (* None means Unit *) fbody : expr list; nloc : Loc.t; } type decl = { d : decl_kind; dloc : Loc.t } and decl_kind = | Package of string | Import of string * string (* alias, path *) | Defalias of string * texpr | Defstruct of string * field list | Defunion of string * variant list | Defn of fn (* No body, so no [defn]: a foreign function, and the string is the C symbol it is actually called by (plan.org, Types — [declare] is kept only where there is no body). *) | Declare of fn * string (* The same, but written in the C library's own terms — structs by value, strings as strings. [Shim] generates the C that flattens it and rewrites this into a [Declare] plus an ordinary [Defn], so nothing downstream sees one. Two forms and not one because [(declare f [p string] ...)] already means "the symbol takes ptr+len", which is the opposite of what this means. *) | DeclareC of fn * string (* Inline name/value pairs, as everywhere else. The members are what a keyword at a call site resolves against. *) | Defenum of string * (string * int64) list (* value is optional: ZII. `uninit` opts out and is recorded as Uninit. *) | Defvar of string * texpr option * init | Defconst of string * texpr option * expr and variant = { vname : string; vfields : field list; vloc : Loc.t } and init = Zeroed | Uninit | Init of expr (* Every top-level name a declaration introduces, whatever kind it is. There is one top-level namespace, so this is both the set [Load] renames on an import and the set [Check] refuses to see twice — one definition, so the two cannot drift apart. *) let declared_name (d : decl) = match d.d with | Defenum (n, _) | Defalias (n, _) | Defstruct (n, _) | Defunion (n, _) | Defvar (n, _, _) | Defconst (n, _, _) -> Some n | Declare (fn, _) | DeclareC (fn, _) | Defn fn -> Some fn.name | Package _ | Import _ -> None