Expand description
The recursive expression/pattern/type/text grammar. Program expressions
embed inline/block text ({…}, '<…>), text embeds commands, and
command arguments re-enter program expressions.
Recursion structure. Grammatically this is one big knot, but at the
type level every recursion edge except three self-loops is routed through
the stream-erasing leaf wrappers defined above (ExprErased,
PatErased, TyErased) — see their doc comment for the measured
compile-time blowup that forced this. The #[recurse] macro therefore
sees exactly three singleton SCCs, each a directly self-referential root:
Expr(its own variants’Box<Expr>children —nxletnesting likeif … then if … else …runs on the engine);PatBot(CtorApplied’sBox<PatBot>argument —Some Some x);TypeExpr(Fun’s right-recursiveBox<TypeExpr>codomain).
Every sub-cycle trivially passes through its root. All other nesting
(command arguments, parenthesized/tuple bodies, record/list elements,
match-arm bodies, …) recurses at runtime through the erasers’
hand-written Parse impls, which is unbounded by construction.
A command’s arguments are still represented as one application-chain
Expr (CmdTail::Args) rather than a dedicated argument list — faithful
to the OCaml AST, where command arguments are a curried UTApply chain
anyway. Elaboration flattens that chain back into the argument list.
Structs§
- Access
Seg - One
#labelfield-access segment (nxbot ACCESS var). - AndBinding
- An
and name param* = valuecontinuation of alet-rec. - AppExpr
nxun/nxapp/nxunsubflattened: an optional leading unary minus, an optional leading!/!!/… deref (UNOP_EXCLAM,nxunsub), an atomic head with any#labelfield accesses (nxbot ACCESS var, left-recursive inparser.mly— flattened to a postfixVechere, the same technique asPatCons’s::), and an application-chain tail (nxapp nxunsub/nxapp CONSTRUCTOR/nxapp OPTIONAL nxunsub/nxapp OMISSION, left-folded during elaboration).EXACT_AMP/EXACT_TILDE(&/~) are the staging prefixes, carried instage(seeStagePrefix). First-class command references (command \cmd, upstream’snxapp: COMMAND hcmd) are modeled one level down, asAtomic::Command— see its doc comment for the rationale.- AsClause
- The
as namesuffix of a pattern. - BarArm
- A
| pat [when guard] -> bodycontinuation of a match’s arm list. - Before
Tail - The
before bodysuffix of anOpChain(nxbfr). - Comma
Expr - A
, exprcontinuation inside a parenthesized tuple. - Comma
Pattern - A
, patcontinuation inside a parenthesized tuple pattern. - ConsSeg
- One
:: patbotcontinuation of a cons pattern. - CstOpt
ArgEntry - One
label = exprentry of aCstOptArgsbundle — a FULL expression (?(bias = 1 + n)), routed throughsuper::ExprErasedso this satellite never joinsExpr’s SCC. - CstOpt
Args - A SATySFi 0.1
?(l = e, …)optional-argument bundle (forAppArg::Bundled/AppArg::BundledCtor): the?sigil, then a parenthesized,-separated list oflabel = exprentries. - CstOpt
Binder Entry - One
label = binderentry of aCstOptBindersbundle (the last,is optional;=is upstream’sEXACT_EQ, reusingDefEqTok). - CstOpt
Binders - A SATySFi 0.1
?(l = x, …)optional-parameter binder bundle (forExpr::FunRows): the?sigil, then a parenthesized,-separated list oflabel = binderentries. - CstRecord
Open Field - One
l : ty,field of aTypeAtom::RecordOpen(last,optional). - CstRecord
Open Inner - A
TypeAtom::RecordOpen’s group content: one or more,-separated fields (nonempty enforced at lowering, matching the closed form), then a mandatory| ?'rrow-variable tail. - CstType
OptDom ?(l = ty, …)— the closed labeled-optional-domain prefix ofTypeExpr::OptRowFun. No row-variable-tail field: row-tailed optional domains need signature-level row quantification (parser_v1.mly’srowquant/quant) — not implemented here;cst_v1’s ownTypeOptDomInnerV1models the tail at parse level and rejects it with aLowerErrorbefore ever reaching here (v1/lower.rs).- CstType
OptEntry - One
label : ty,entry of aCstTypeOptDom(last,optional — matching the 0.1 lowering convention this file’s other additive nodes use, e.g.CstOptArgEntry, rather than the frozen grammar’s;). - Guard
- A match arm’s
when condguard. - List
Item - One list element
expr;(the last;is optional). - Match
Arm - One
pat [when guard] -> bodymatch arm. The pattern and body sit behind the stream-erasing wrappers (deref to reach the inner nodes). - Math
Elem Cst mathtop: one math element, i.e. amathbotbase with any postfix^/_/'script combos (mathtop’s seven alternatives, flattened to aVecin source order — the sameOps/OpChaindeferred- precedence technique, since combos 3–6 interleave sub/superscript application order in a way elaboration is better placed to resolve).- OpChain
- A flattened binary-operator chain:
head (op rhs)*, left-folded (with correct per-operator precedence/associativity) during elaboration.beforeisnxbfr’s postfix (e1 before e2), attached here rather than modeled at its own precedence level:nxbfrsits betweennxifandnxlambda, i.e. abovenxlor/OpChain’s own level, soparser.mly’s left operand is actuallynxlambda(which also coversFun/Overwrite) — attaching toOpChainalone misses(fun x -> e1) before e2/(x <- e1) before e2as the left operand; such input is rejected here (a documented simplification, not a silent misparse).bodyis threaded throughExprErased(not boxed directly) to keepExpra singleton SCC: a directBox<Expr>field onOpChainwould makeOpChainitself part ofExpr’s SCC (a second, non-Expr-variant self-loop edge), which is exactly the multi-type-cycle shape the module doc warns about. - OpRhs
- One
op rhscontinuation of anOpChain. - OptArrow
Dom - One
ty ?->leading domain of aTypeExpr::Fun’s optional-argument prefix (parser.mly’stxfuncopts, 880-882). - Paren
Body - The parenthesized-expression group’s content: one expression, plus
any
, exprcontinuations (present only for a tuple). - PatCons
pattr: apatbot, followed by any number of:: patbotsegments.parser.mlywrites this as right recursion (patbot :: pattr, always fine, unlike left recursion) but it is flattened to aVechere (the same right-fold-at-elaboration technique asOpChain,::being right-associative) so thatPatConsneed not be self-referential: aPatCons/ConsRestpair of mutually-referencing wrapper structs would form a 2-cycle with no self-loop of its own, which the#[recurse]depth engine rejects (“a sub-cycle running entirely through non-root types”).- PatList
Item - One list-pattern element
pat;(the last;is optional). - Pattern
patas: a pattern, plus an optionalas namebinding.- Pattern
Paren Body - The parenthesized-pattern group’s content: one pattern, plus any
, patcontinuations (present only for a tuple pattern). - RecAscription
- A
let-recbinding’s optional: tyascription (seeRecBinding’s doc comment). A direct (non-erased)TypeExprfield:RecBindingis already inside this#[recurse]module (embedded directly byExpr::LetRecIn, not through an eraser), and connecting it straight toTypeExpr— one of the module’s three self-recursive SCC roots — is exactly the same kind of cross-root DAG edgeRecBinding.params: Vec<PatBot>already makes to thePatBotroot;TypeExprnever refers back toExpr/PatBot/RecBinding, so no new cycle results. - RecBinding
- One
name [: ty] [|] patbot* = value [| patbot* = value]*clause GROUP of alet-rec(also reused, from outside this module, by top-levellet-rec).ascriptionisparser.mly’s rarerCOLON tytype-annotated form (recdecargpart’sCOLON ty BARalternative), e.g. the bundleditemize.satyh’slet-rec listing-item : context -> int -> bool -> bool -> itemize -> block-boxes | ctx depth is-first is-last (Item(...)) = ... Parsed but not enforced — there is no enforcement pass for value-level ascriptions (only moduleval/directsignature items reachtypecheck.rs’scommand_scheme/sig machinery) — so it is a parse-and-ignore stand-in whose only job is making verbatim upstream source parse.paramsispatbot*(recdecargpart’s plainargpatsform, optionally preceded by aleading_bar—recdecargpart’sBAR argpatlstalternative, used both for the OCaml-style “every clause, including the first, gets a|” layout the bundled packages write, e.g.list.satyg’slet-rec map\n | f [] = []\n | f (x :: xs) = .., and for theCOLON ty BARform above, whose single clause is only reachable via a leading|).extraholds any further| patbot* = valuecontinuation clauses (nxrecdecpar) — SATySFi’s multi-clause pattern-matching function-definition sugar. Every clause in the group must bind the same number of parameters (checked at elaboration — upstream’sIllegalArgumentLength— not here); the (possibly plural) clauses desugar to one curried function that matches a tuple of fresh parameters against each clause’s patterns in turn — seeelaborate.rs’srec_clause_value. - RecClause
- A
| patbot* = valuecontinuation clause of a multi-clauselet-recbinding (seeRecBinding’s doc comment). - Record
Field - One record field
label = expr;(the last;is optional). - Star
Type - A
* tycontinuation of aTypeProd. - TypeApp
txapp— a postfix type applicationarg1 arg2 … argN ctor, upstream’s N-ary chain flattened into a greedy atom run (the wayOpChain/PatConsflatten their own left-recursions).headis always present; whenrestis non-empty the LAST atom is the type constructor (a bare orMod.-qualified name —list/option/result/Eq.t/implicit) and every atom before it (includinghead) is one of its arguments. This is unambiguous because SATySFi always parenthesizes a nested single-argument application (('a list) list,('a t) implicit— never'a list list), so a flat run of atoms can only be one constructor applied to the preceding arguments:- Type
CmdArg Item - One
;-separated element of aTypeAtom::Cmd’s bracketed argument list: a mandatoryty, or an optionalty?(parser.mly’stxlist, 955-960) — routed throughsuper::TyErasedrather than the narrowerTypeAppupstream uses, both to stay a DAG leaf (a directTypeAppfield here would closeTypeAtom -> Cmd -> ... -> TypeApp -> TypeAtom, a fresh cycle through non-root types — seeAppArgErased’s doc comment for the identical hazard) and per this port’s usual permissive-superset simplification. - Type
CmdOpt Field - One
label : ty,field of aTypeCmdArgItem::opt_labelsbundle (the last,is optional, matching this port’s other 0.1-additive comma-separated satellite fields —CstOptBinderEntry,CstTypeOptEntry). - Type
Prod txprod: one or more*-separatedTypeApps (a product type), or just a single one if there’s no*at all — flattened to aVec(the same deferred-fold technique asOpChain/PatCons) rather than modeled as its own right-recursive rule, keepingTypeExpra singleton SCC.- Type
Record Field - One
l : ty;field of aTypeAtom::Record(txrecord,parser.mly:962-965) — sibling ofsuper::RecordKindField, but (unlike that struct, defined outside the#[recurse]module and so free to hold a directast::TypeExprfield) this one lives insideTypeAtom’s own SCC, so the field type is routed throughsuper::TyErasedinstead — a directast::TypeExprfield here would close a fresh cycle back throughTypeAtomitself (the same hazardTypeCmdArgItem’s doc comment explains).
Enums§
- AppArg
- One application-chain argument: an optional-argument value (
?: arg), an omitted optional argument (?*), a plain atomic value (with its own optional!prefix and#accesssuffixes, mirroringAppExpr’s head position — eachnxunsub/nxbotin thenxappchain is independent), or a bare constructor applied nullarily (nxapp CONSTRUCTOR). - Atomic
nxbot(plus the ctor-head case usually found innxun): an atomic expression.- Block
Elem - One block-text element (
vxbot). - CmdTail
- A command’s arguments (
narg* sargsin parser.mly, upstream’s own dedicated grammar — not a reuse of the general application chain likeAppExpr). Either a bare;(no arguments) or a flat, non-empty sequence ofAppArgs: each is?: value(a supplied optionalnarg),?*(an omitted optionalnarg), or a plain (possibly!/#access-decorated) atomic value —(expr),(|record|),[list],{inline},<block>, a bare ctor, etc. — covering bothnarg’s mandatory forms andsargs’s group forms uniformly (this port’s usual simplification:AppArg::Atom’sAtomicalready spans every shape upstream splits acrossnarg/sargs). Optional/omittednargs may lead (\ref?:(x){text},\ref?*{text}) since every element is independently oneAppArg— anExpr-based encoding could not, its head being a plain atom. - CmdType
Kind - The command-type keyword closing a
TypeAtom::Cmd’s bracketed list. - Expr
nxlet: a let/if/match/lambda-headed expression, falling through to the flattened operator chain (Ops,OpChain) at the bottom. Variant order is parse priority;Opshas no distinguishing leading keyword, so it must stay last.- Inline
Elem - One inline-text element (
ih/ihtext/ihcmdin parser.mly). - MathArg
matharg(parser.mly:1138-1146 + narg 1201-1210): one math-mode command argument — a mandatory body, a?:-supplied optional (UTOptionalArgument), or?*(UTOmission). The six body shapes live once inMathArgBody; Optional/Omission/Plain are first-token-disjoint.- Math
ArgBody - The six body shapes shared by mandatory and
?:-optional math args: a math/inline/block group, or a!-escaped program-mode value. The lexer already switches mode on the escape sigil (!(/![/!(|/!{/!<all emit ordinaryLParen/BList/BRecord/BHorzGrp/BVertGrptokens — seelexer.rs’slex_math), so at the token level the escapes are indistinguishable fromAtomic’s ownParen/List/Recordshapes; reusing those bodies directly here (rather than going through a fullExprErased, which would also happily swallow a followingmathargbracket group as a trailing application argument) keeps eachmathargexactly one bracket group. NOTBox<MathArg>: a direct self-loop on a non-root type is what#[recurse]rejects, and upstream’s grammar is non-recursive here anyway. - MathBot
mathbot.- Math
Group Arg mathgroup: a script’s operand is either a bracketed math group or a baremathbot.- Math
Script - One postfix script combo of a
MathElemCst(mathtop’sSUPERSCRIPT/SUBSCRIPT/PRIMESsuffixes, one at a time). - Param
- One curried parameter of an ordinary (non-
let-rec)let, or of alet-inline/let-block/let-mathcommand binding — upstream’sargnonterminal (nxnonrecdec’sargpart/cmdarglst,parser.mly:622-624:arg: patbot | OPTIONAL defedvar): a full pattern, or the def-site optional-parameter marker?:name(parser.mly’sOPTIONAL vartok), e.g.stdja.satyh’slet document record ?:configopt inner = ..andannot.satyh’slet-inline ctx \href ?:borderopt uri inner = ... Upstream’slet-rec/funargument grammar (recdecargpart/argpats—RecBinding/AndBinding/Expr::Fun) has no such alternative, only plainletand the three command-binding forms do — all four keepVec<Param>(super::TopLet,Expr::LetIn,super::TopBinding::LetInline/LetBlock/LetMath,Expr::LetMathIn). Elaborated (elaborate.rs) by wideningOptionaltoPatBot::Var(params_to_patbots) before the ordinary pattern-currying machinery runs (plainlet’srec_clause_value, or a command binding’scurry_cmd_params) — the?:marker carries no further semantics of its own in this port (typecheck.rs’scommand_schemedoc comment: optionality is inferred structurally, not from this marker); for a command binding, the maximal leading run of?:-marked params is additionally counted byelaborate.rs’sleading_optional_countand recorded into the binding’sScope::optional_arity, so a marker-less call site can auto-omit those slots (seecmd_args/math_bot’sCmdarm). - PatBot
patbot, plus the constructor-pattern formspattradds inparser.mly(folded in here to keepPatConsa plain struct).- Record
Body (| … |)’s content: either a plain field list, or a record updatebase with l = e; …(nxrecordsynt’s third alternative).Updateis tried first (it backtracks cleanly toFields— parsingbaseas an expression stops right before a barelabel = expr’s=, since=isn’t a valid expression continuation, so thewithkeyword check fails fast andFieldspicks it up).baseisnxbotinparser.mly(an atomic expression); routed throughExprErasedhere instead, which is strictly more permissive (accepts any expression as the base, not just an atomic one) — a deliberate simplification, and also the only way to reference it without adding a second, non-Grouprecursion edge intoAtomic(which — likeAppExpr/OpChain— is not itself part ofExpr’s SCC, and should stay that way).- Stage
Prefix - A staging prefix on a
nxunsuboperand:&ebuilds code for the next stage,~esplices the result of a previous-stage computation (parser.mly:796-797,UTNext/UTPrev). - Type
Atom - An atomic type expression.
- Type
Expr - A minimal type-expression grammar for
typedeclarations and signature (val .. : ty) annotations (txfunc/txprod/txapppre/txapp/txbot, simplified). Function arrows (right-associative, with an optional-argument?->prefix chain — seeOptArrowDom), 2+-way product types (*,TypeProd), a SINGLE-argument postfix type-constructor application ('a option,'a list; seeTypeApp), command-argument-list types ([ty; ty?; ..] inline-cmd/block-cmd/math-cmd; seeTypeAtom::Cmd), parenthesized grouping, closed record types ((| l : ty; … |); seeTypeAtom::Record), bare/qualified names, and type variables are supported; N-ary applied constructors are not — such input is rejected with a parse error. Self-recursive only throughFun’s codomain (right recursion); parenthesized nesting goes through thesuper::TyErasedleaf.