The slot after a defn's parameters is unconditionally a type. Parse.decl no
longer takes a set of type names, and is_type_form, qualified_type, types_in,
declared_types and prelude_types are gone with the pre-pass that fed them.
What they were for: (Option f64) and (Some 1) are the same s-expression, so the
parser decided which it had by looking the head up in a set of the file's own
type names. Sound -- one top-level namespace means a name cannot be both a type
and a value -- and brittle, because the set had to be complete. It was wrong
twice in one day, the second time parsing (defn f [] (Rune {.code 65}) (bar))
as a function returning a Rune with a one-form body, silently, in every file in
the language.
Two things fall out. A type the parser could not have known -- a struct
declared further down the file, rl/Vector2 behind an unresolved alias, a
prelude type -- never needed recognising, only placing. And a mistyped type is
a mistyped type: (defn f [] f65 0.0) reaches the resolver's near-miss check and
says did you mean f64, where it used to be read as the first form of the body
and reported as an unknown name.
Unit is written (). The old spelling is refused with a message naming the new
one, the rule the colon-to-dot change followed. Internally it is still
Tname "Unit" and Types.Unit, so the resolver, the shim and the emitter did not
change; Cimport still builds Tname "Unit" for C's void without going through
the parser. Types.to_string prints () though -- that printer prints what a
person would write for every other type it knows, [i32], {K V}, (Ptr T), and
Unit was the odd one out once the source spelling moved.
Dropping prelude_types removes one of the two reasons Macro.reduce may only
drop defns: the memoised set a bootstrap build could have poisoned is gone, so
the remaining reason is the plain one.
168 lines
8.0 KiB
OCaml
168 lines
8.0 KiB
OCaml
(** 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
|
|
(* The [string option] is a loop label: [(while :outer c ...)]. A keyword in
|
|
that position is unambiguous because a loop condition is never one. *)
|
|
| While of string option * expr * expr list
|
|
| Return of expr option
|
|
(* Leaving a loop, and starting its next iteration. The [string option] is
|
|
the label of the loop meant, and [None] means the innermost. Neither is a
|
|
goto: the checker resolves the name against the loops this form is
|
|
lexically inside, so control can only leave a loop it is already in —
|
|
Odin's restriction, and what keeps it safe. *)
|
|
| Break of string option
|
|
| Continue of string 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 *)
|
|
(* (array 4 rl/Vector2) — a zeroed fixed array, given its count and its
|
|
element type. [n T] is the ordinary *type* syntax and already works
|
|
everywhere a type is expected; a [let] binding is the one position with no
|
|
type slot, so there [4 rl/Vector2] reads as a two-element [Arr] literal and
|
|
fails on an unknown name. This is that position's answer, and it says what
|
|
it does rather than looking like a vector of two things. *)
|
|
| ArrayOf of texpr (* the whole array type, built by Parse *)
|
|
(* 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 option * string * expr * expr list (* (dotimes :o [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 (); only declare omits it *)
|
|
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
|