Expand description
The SATySFi 0.1.0 (dev-0-1-0) surface grammar — a fork of
crate::cst, not a version-gate of it (gating one shared cst.rs would
mean hand-writing Parse for nearly every node, destroying the derive
idiom and risking 0.0.6 on every 0.1 edit). crate::cst stays frozen:
this module imports only its token-Atom-generic, non-recursive helpers
(crate::cst::Header, crate::cst::ParseFileError) and re-declares
everything else — including its own *ErasedV1 eraser leaves and its own
copy of render_parse_error — so that touching cst_v1.rs never touches
cst.rs.
Scope. SATySFi 0.1’s grammar adds a whole ML-style module system
(bind/modexpr/sigexpr/decl) on top of an expr/pattern/type layer
that is structurally close to 0.0.6’s, plus five surface deltas:
,-separated lists/records (not ;), EXACT_EQ for both definitional
and record = (reusing DefEqTok — no new = leaf), mandatory
match … with … end, per-binding staging instead of a whole-file
@stage: header, and no when/while/before at all. This module
builds:
FileV1—header* expr EOIorheader* module Name option(sig_annot) = struct bind* end EOI.Bind— every arm of upstreambind:val/val inline/val block,val math(math-split — seeBind::ValueMath),val rec … and …/val mutable/type … and …,module … = modexpr/signature … = sigexpr/include modexpr.ast::ModExpr/ast::SigExpr/ast::Decl— the full module/signature grammar: functor literals/ application, module paths/aliases,:>coercion,sig … endwith everydeclform,with typerefinement,include.- A copy of
crate::cst::ast’s expr/pattern/type layer with the 0.1 deltas applied (see each type’s doc comment for the exact delta), including the fulllet rec … and … in/let mutable … inexpression forms, a widenedTypeExprgrammar (products, prefix application),inline […]/block […]/math […]command types (parser_v1.mly:730-735) andLONG_LOWERqualified type paths (:720-728,742-743;TypeApp::AppliedLong/TypeAtom::LongName).
Grammar shipped with placeholder semantics only. Every
module/signature construct beyond the struct-literal
ModExpr::Struct body PARSES and round-trips, but lowers to a precise
LowerError (v1/lower.rs) rather than real semantics — see that
module’s doc comment for the placeholder set and the seal rule.
Deliberately NOT built: macro binds/decls
and row quantifiers (rowquant). Staging DOES parse — the
operand prefixes &e/~e (ast::StagePrefix,
parser_v1.mly:870-873) and the per-binding qualifier of val ~x/val persistent ~x (BindStageV1, :417-421 and the decl form
:600-603), with persistent a 0.1-only keyword token.
The #[recurse] SCC story (five roots). Five
singleton, directly self-referential roots — the same shape
crate::cst::ast uses, for the same reason (see its module doc comment
for the measured compile-time blowup a naive transcription hits):
ast::Expr(its variants’ ownBox<Expr>children);ast::PatBot(CtorApplied’sBox<PatBot>argument);ast::TypeExpr(Fun’s right-recursiveBox<TypeExpr>codomain);ast::ModExpr(Functor.body’sBox<ModExpr>self-loop);ast::SigExpr(Functor.dom/Functor.cod’sBox<SigExpr>self-loop — encoded left-recursion-safe,withis bot+suffix, neverWith { base: Box<SigExpr> }; seeast::SigExpr’s own doc comment).
Every other recursion edge is routed through the erasers declared below
(ExprErasedV1, PatErasedV1, PatBotErasedV1, TyErasedV1,
MathErasedV1, ModExprErasedV1, SigExprErasedV1,
TypeBindsErasedV1), keeping each SCC a singleton and the wrapped
grammar’s recursion reborrowing one stream type (syan pins it, not us).
ast::Decl is a satellite, not a root: it has no Box<Self> anywhere
and no type inside the #[recurse] module ever names it — it is reached
only through the hand-written StructDeclV1 connector (an opaque leaf
to the SCC analysis, mirroring StructBindV1), so SigExpr ↔ Decl
never forms a rootless static sub-cycle.
Re-exports§
pub use crate::cst::Header;pub use crate::cst::BindName;pub use ast::AscribedInnerV1;pub use ast::OptParamEntryV1;pub use ast::OptParamsV1;pub use ast::Param;pub use ast::ParamBody;
Modules§
- ast
- The recursive expression/pattern/type/text grammar for SATySFi 0.1.
A copy of
crate::cst::astwith the deltas documented on each type; see the module doc comment for the SCC/root story.
Structs§
- BarVariant
DefV1 - A
| UPPER [OF typ]continuation (variants’separated_nonempty_ list(BAR, variant),parser_v1.mly:545-548). - Bind
Stage V1 - The stage qualifier of a
valbind orvaldecl:~alone is stage 0,persistent ~is the persistent stage (parser_v1.mly:417-421for binds,:600-603for decls). No prefix at all is stage 1 — the document stage, where an ordinaryvallives — which is why the field holding this is anOption. - Expr
Erased V1 - An
ast::Exprbehind a stream-erasing parse (see above). - Math
Erased V1 - An
ast::MathElemCstbehind a stream-erasing parse (see above). - ModExpr
Erased V1 - An
ast::ModExprbehind a stream-erasing parse. Carries the OUTSIDE→INSIDE edgeBind::Module.body → ModExpr— the one edge of theBind → ModExpr → StructBindV1 → Bindruntime cycle not already erased by the connector (the erasers are for the INSIDE types, not forBind). - OfType
V1 - The
of typpayload suffix. - PatBot
Erased V1 - An
ast::PatBotbehind a stream-erasing parse (see above). Kept separate fromPatErasedV1for the same reasoncrate::cst’sPatErased/PatBotErasedsplit exists: a constructor pattern’s argument is apatbot, not a fullpatas. - PatErased
V1 - An
ast::Patternbehind a stream-erasing parse (see above). - PatNon
VarErased V1 - An
ast::Patternthat is not a bare variable — upstream 0.1’s ownpattern_non_var(parser_v1.mly:796), and the 0.1 twin ofcrate::cst::PatNonVarErased, whose doc comment carries the whole story. - Scripts
Param V1 scripts_param(parser_v1.mly:532-534):WITH sub=LOWER sup=LOWER—val math’s optionalwith sub supsuffix, binding the two script-callback parameters directly rather than synthesizing the hidden%math-attach-scriptswrapper.- SigAnnot
V1 COERCE sigexpr— a signature annotation:> S(sig_annot,parser_v1.mly:555-557). 0.1’s annotation sigil is:>(COERCE,lexer_v1.mll:280), NOT 0.0.6’s: sig … end(crate::cst::SigAnnot,cst.rs:295-303) —module M : S = …is a 0.1 parse error (pinned in tests). The signature body goes throughSigExprErasedV1:SigAnnotV1lives outside the#[recurse]module, so this is a cross-boundary edge into theSigExprroot (see the module doc comment’s SCC story).- SigExpr
Erased V1 - An
ast::SigExprbehind a stream-erasing parse. Used bySigAnnotV1andBind::Signature(outside → the SigExpr root). - Struct
Bind V1 - One declaration inside a
module … = struct … endbody.Bind’s own alternatives are exactly what a struct body may contain (bind*), so this simply re-parses aBind— but not by namingBindas a field type directly:Bindlives outside the#[recurse]module (below), soBind -> ModExpr -> Vec<StructBindV1> -> Bindwould be a self-recursive cycle through a plain#[derive(Parse)], which (without the#[recurse]engine to back it) is anE0275hazard (an unbounded recursive trait-bound obligation) — exactlycrate::cst::StructDecl’s own rationale (cst.rs:262-269). Hand-writingParse/Unparsehere — the same trick as theerased_leaf_v1!macro below — sidesteps that: the impl has no recursive where-bound for the compiler to try to satisfy, it just callsBind::parsethrough the stream-erasing adapter at runtime. - Struct
Decl V1 - One declaration inside a
sig … endbody (list(decl),parser_v1.mly:591) —StructBindV1’s twin, hand-writtenParse/Unparsefor the sameE0275reason.ast::Decllives INSIDE the#[recurse]module andSigExpr → SigBotV1 → StructDeclV1 → Decl → SigExpris a runtime cycle; namingast::Declas a plain derived field ofast::SigBotV1would re-enter the module’s own SCC analysis. As an opaque leaf it closes that cycle at RUNTIME while keeping both SCCs singletons. NOTE: named aftercrate::cst::StructDecl(the mechanism), even though it carries a sig-decl, not a struct binding. - TyErased
V1 - An
ast::TypeExprbehind a stream-erasing parse (see above). - Type
AndV1 - An
and bind_type_singlecontinuation (bind_type’sseparated_nonempty_list(AND, …),parser_v1.mly:535-537). - Type
Bind Single V1 - One
bind_type_single(parser_v1.mly:539-544). 0.1 delta fromcrate::cst::TypeDecl: the type parameters come AFTER the name (type t 'a = …,tyident LOWER; tyvars list(TYPEVAR)), where 0.0.6 writes them before (type 'a t = …,cst.rs:401-408) — the lowering reorders the fields. Noconstraintsuffix exists in 0.1’s production. - Type
Binds Erased V1 - A
TypeBindsV1behind a stream-erasing parse. Unlike every other eraser this one targets an OUTSIDE type:SigExpr::WithType/Decl::Type(inside) must reachbind_type, whoseTypeBindSingleV1re-enters the module through plain-derivedast::TypeExprfields — an inside→outside-plain-derive→inside-root chain with no precedent incst.rs’s discipline. Erasing at the boundary keeps the re-entry cheap (one stream type, monomorphized once), exactly like every other cross-boundary edge. - Type
Binds V1 - One whole
bind_typechain —bind_type_single (AND bind_type_single)*(parser_v1.mly:535-537) — grouped into a single struct so the sig layer (ast::SigExpr::WithType,ast::Decl::Type) can reference the chain through ONE eraser (TypeBindsErasedV1).Bind::Typekeeps its flattenedfirst/andsfields unchanged (avoiding call-site churn); the two spellings are the same grammar. - Variant
DefV1 - One
UPPER [OF typ]variant (parser_v1.mly:549-553).
Enums§
- Bind
- Every arm of
bind(parser_v1.mly:415-440) — upstream’s own nonterminal name (helper types likeStructBindV1/TypeBindSingleV1keep aV1suffix). Every value arm’s=isEXACT_EQ(DefEqTok) and body is anast::Expr.nameis acrate::cst::BindNamewherever upstream’sbound_identifierreaches it (Value, and the rec clauses insideast::RecClauseV1);ValueMutableandValueInline/ValueBlock’sctxstay plainVarToks — upstream’sMUTABLE LOWER …/ctx-variable productions are a plainLOWER, notbound_identifier(seecrate::cst::BindName’s doc comment for the ordered-choice-safety argument). - FileV1
- A whole 0.1
.saty/.satyhfile (main, upstreamparser_v1.mly:364- 368): a header list followed by either a library (main_lib) or a document expression. Unlike 0.0.6’scrate::cst::File(a flat prelude of top-levellets with an optional trailingin body), 0.1 has no flat top-level binding sequence at all: a document body is just anast::Expr(everyletchains its ownin), and a library is exactly onemodule … = struct … end. - Header
V1 - A 0.1 header element — the UNION of BOTH packaging generations’ header
forms (Axis B).
Legacyisdev-0-1-0’s@require:/@import:(byte-identical to 0.0.6’s, reusingHeader); the threeUse*forms aresaphe-split’sheaderelem(parser.mly:371-380 @ b836d512). Which family is legal is aLoadModequestion the loader answers (rustyfi_loader), not a grammar question — this ONEV0_1grammar accepts both so the mode error can be raised at load time with a better message than a lex error would give. - Type
Body V1 - The right-hand side of one type bind: a variant’s constructor list
(
EXACT_EQ BAR? variants,parser_v1.mly:540-541,545-553) or a transparent synonym (EXACT_EQ typ,:542-543). Variant-first is unambiguous for the same reason ascrate::cst::TypeDeclBody(cst.rs:410-418): a variant list isBarTok/CtorTok-headed and noast::TypeExprcan start with either.
Functions§
- parse_
file_ v1 - Lex (
crate::lexer::lex_with_versionundercrate::version::RustyfiVersion::V0_1) and parse a whole 0.1.saty/.satyhsource file. Mirrorscrate::cst::parse_file’s two-step shape exactly, sharing itscrate::cst::ParseFileError(no new error type).