Skip to main content

rustyfi_syntax/
token.rs

1use crate::span::Span;
2
3/// One SATySFi token. Variant names and payloads mirror the token declarations
4/// of the v0.0.6 `parser.mly` (`LETNONREC` = `Let`, `LAMBDA` = `Fun`, ...).
5/// Command payloads keep their sigil, exactly like the OCaml lexer
6/// (`HorzCmd("\\emph")`, `VertCmd("+p")`).
7///
8/// The variants listed in the `token_leaves!` block at the bottom of this file
9/// get a matching leaf struct (`KwLet`, `VarTok`, ...) with a peek → match →
10/// push-back-on-mismatch `Parse`/`Unparse`/`Spanned`. Multi-field and
11/// multi-variant-matching leaves (`LengthTok`, `LiteralTok`, `BinOpTok`,
12/// `VarInHorzTok`, ...) stay hand-written in [`crate::leaf`].
13#[derive(Clone, Debug, PartialEq)]
14pub enum Token {
15    // ---- headers ----
16    HeaderRequire(String),
17    HeaderImport(String),
18    HeaderStage0,
19    HeaderStage1,
20    HeaderPersistent0,
21
22    // ---- identifiers & constants (program mode) ----
23    Var(String),
24    VarWithMod(Vec<String>, String),
25    Constructor(String),
26    TypeVar(String),
27    OpenModule(String), // `Mod.(`
28    /// `M.N.P` — a dotted module path ending in an UPPER segment
29    /// (upstream `LONG_UPPER`, `lexer_v1.mll:357-363`). SATySFi 0.1-only:
30    /// under 0.0.6 this spelling is a lex error ("module path must end
31    /// with a variable name"), unchanged. Payload mirrors
32    /// [`Token::VarWithMod`]: `mods = ["M","N"]`, final segment `"P"` —
33    /// upstream's `(modidents, modident0)` pair carries the same split
34    /// (`parser_v1.mly:407-413` reassembles the chain from it).
35    LongUpper(Vec<String>, String),
36    IntConst(i64),
37    FloatConst(f64),
38    LengthConst(f64, String),
39    /// Backtick string literal. `omit_pre`/`omit_post` mirror the OCaml
40    /// `LITERAL(_, s, pre, post)` flags (whether an adjacent space is trimmed).
41    Literal {
42        body: String,
43        omit_pre: bool,
44        omit_post: bool,
45    },
46
47    // ---- keywords ----
48    //
49    // NOTE: `not` deliberately has NO reserved keyword token here. It lexes
50    // as an ordinary `Var`/`VarTok`, exactly like any other primitive name
51    // (`arabic`, `floor`, ...), so `not expr` works through the existing
52    // `AppExpr` application machinery with no grammar rule of its own. A
53    // reserved `Not` token would have none: nothing in the grammar could
54    // consume it, which is what made the registered `"not"` primitive
55    // (`primitives.rs`) unreachable from real syntax until `picture.satyh`'s
56    // `not (x1r >' x2r)`/`not reversed` surfaced it.
57    Mod,
58    If,
59    Then,
60    Else,
61    Let, // `let` (LETNONREC)
62    LetRec, // `let-rec`
63    LetAnd, // `and`
64    In,
65    Fun, // `fun` (LAMBDA)
66    True,
67    False,
68    Before,
69    While,
70    Do,
71    LetMutable, // `let-mutable`
72    Match,
73    With,
74    When,
75    As,
76    Type,
77    Of,
78    Module,
79    Struct,
80    Sig,
81    Val,
82    End,
83    Direct,
84    Constraint,
85    LetHorz, // `let-inline`
86    LetVert, // `let-block`
87    LetMath, // `let-math`
88    Controls,
89    Cycle,
90    HorzCmdType, // `inline-cmd`
91    VertCmdType, // `block-cmd`
92    MathCmdType, // `math-cmd`
93    Command,
94    Open,
95    /// `rec` — SATySFi 0.1-only keyword (`val rec`/`let rec`); under 0.0.6
96    /// this word stays a plain identifier (see `lexer.rs`'s version-gated
97    /// keyword table).
98    Rec,
99    /// `inline` — SATySFi 0.1-only keyword (`val inline \cmd = ...`).
100    Inline,
101    /// `block` — SATySFi 0.1-only keyword (`val block +cmd = ...`).
102    Block,
103    /// `mutable` — SATySFi 0.1-only keyword (`val mutable x <- e`/`let
104    /// mutable x <- e in ..`); under 0.0.6 this word stays a plain
105    /// identifier (see `lexer.rs`'s version-gated keyword table).
106    Mutable,
107    /// `signature` — SATySFi 0.1-only keyword (`signature S = sig … end`,
108    /// upstream `lexer_v1.mll:348`); a plain identifier under 0.0.6.
109    Signature,
110    /// `include` — SATySFi 0.1-only keyword (`include M` binds /
111    /// `include S` decls, `lexer_v1.mll:335`); a plain identifier under
112    /// 0.0.6.
113    Include,
114    /// `use` — SATySFi 0.1-only keyword (Envelopes packaging headers `use
115    /// package …` / `use … of <path>`, `saphe-split:parser.mly:371-380 @
116    /// b836d512`); a plain identifier under 0.0.6 (no 0.0.6 grammar reserves
117    /// it — see `lexer.rs`'s version-gated keyword table).
118    Use,
119    /// `package` — SATySFi 0.1-only keyword (`use package …`); a plain
120    /// identifier under 0.0.6.
121    Package,
122    /// `math` — SATySFi 0.1-only keyword (`val math <ctx> \cmd … = …`,
123    /// `parser_v1.mly:452-453` dispatch, MATH reserved at :240); a plain
124    /// identifier under 0.0.6 (0.0.6 has no `math` keyword — the word
125    /// survives only as the surface name of `BaseType::MathText`).
126    Math,
127    /// `persistent` — SATySFi 0.1-only keyword, the stage qualifier of a
128    /// `val persistent ~x = e` binding (`parser_v1.mly:420-421`, decl form
129    /// `:602-603`; reserved at `:241`, lexed at `lexer_v1.mll:345`). A plain
130    /// identifier under 0.0.6, which spells the same idea per FILE with a
131    /// `@stage:` header rather than per binding.
132    Persistent,
133
134    // ---- grouping delimiters ----
135    LParen,
136    RParen,
137    BRecord, // `(|`
138    ERecord, // `|)`
139    BList, // `[`
140    EList, // `]`
141    BHorzGrp, // `{` opening inline text
142    EHorzGrp, // `}` closing inline text
143    BVertGrp, // `'<` / `<` opening block text
144    EVertGrp, // `>` closing block text
145    BMathGrp, // `${` / `{` opening math
146    EMathGrp, // `}` closing math
147    BPath, // `<[`
148    EPath, // `]>`
149
150    // ---- punctuation & operators (program mode) ----
151    ListPunct, // `;`
152    Access, // `#`
153    Arrow, // `->`
154    OverwriteEq, // `<-`
155    Bar, // `|`
156    Wildcard, // `_`
157    Colon,
158    Comma,
159    Cons, // `::`
160    /// `:>` — signature coercion/ascription (COERCE, `lexer_v1.mll:280`).
161    /// SATySFi 0.1-only: under 0.0.6 the same two characters lex as
162    /// `Colon` + `BinopGt(">")`, unchanged.
163    Coerce,
164    ExactMinus,
165    DefEq, // `=`
166    ExactTimes, // `*`
167    ExactAmp,   // `&`
168    ExactTilde, // `~`
169    PathCurve,  // `..`
170    PathLine,   // `--`
171    BinopPlus(String),
172    BinopMinus(String),
173    BinopTimes(String),
174    BinopDivides(String),
175    BinopEq(String),
176    BinopLt(String),
177    BinopGt(String),
178    BinopAmp(String),
179    BinopBar(String),
180    BinopHat(String),
181    UnopExclam(String),
182    OptionalType, // `?`
183    OptionalArrow, // `?->`
184    Optional, // `?:`
185    Omission, // `?*`
186    /// `?'r` — a SATySFi 0.1 row variable (upstream `ROWVAR`,
187    /// `lexer_v1.mll:310-311`). `RowVarTok`
188    /// carries the name sans sigil, exactly like [`Token::TypeVar`]/
189    /// `TypeVarTok`. V0_1-only: the lexer (`lexer.rs`'s `'?'` arm) only ever
190    /// mints this under `RustyfiVersion::V0_1`; under `V0_0`, `?'r` stays
191    /// two tokens (`OptionalType` then `TypeVar`), byte-identical to before
192    /// this addition.
193    RowVar(String), // `?'r`
194
195    // ---- commands (payload includes the `\`/`+` sigil) ----
196    HorzCmd(String),
197    HorzCmdWithMod(Vec<String>, String),
198    HorzMacro(String),
199    VertCmd(String),
200    VertCmdWithMod(Vec<String>, String),
201    VertMacro(String),
202    MathCmd(String),
203    MathCmdWithMod(Vec<String>, String),
204
205    // ---- text-mode content ----
206    VarInHorz(Vec<String>, String),
207    VarInVert(Vec<String>, String),
208    VarInMath(Vec<String>, String),
209    Char(String),
210    /// A backtick literal written INSIDE inline text (`` `…` ``). Distinct from
211    /// `Char` because upstream keeps it distinct: it reaches the evaluator as
212    /// `ImInputHorzEmbeddedCodeText` and is dispatched to the context's
213    /// `code_text_command` (`evaluator.cppo.ml:768-779`), which is how a doc
214    /// class sets code spans in a monospace face. Do NOT lex it to a plain
215    /// `Char` run: that erases the distinction irrecoverably, and the literal
216    /// then inherits whatever face surrounds it (italic, inside `\emph`).
217    CodeText(String),
218    Space,
219    Break,
220    Item(usize), // `*`+ with depth
221    Sep, // `|`
222    EndActive, // `;` closing an active area
223
224    // ---- math-mode content ----
225    MathChar(String),
226    Superscript, // `^`
227    Subscript, // `_`
228    Primes(usize),
229
230    Eoi,
231}
232
233impl std::fmt::Display for Token {
234    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235        use Token::*;
236        match self {
237            HeaderRequire(s) => write!(f, "@require: {s}"),
238            HeaderImport(s) => write!(f, "@import: {s}"),
239            HeaderStage0 => write!(f, "@stage: 0"),
240            HeaderStage1 => write!(f, "@stage: 1"),
241            HeaderPersistent0 => write!(f, "@stage: persistent"),
242            Var(s) | Constructor(s) => write!(f, "{s}"),
243            VarWithMod(m, s) | LongUpper(m, s) => write!(f, "{}.{s}", m.join(".")),
244            TypeVar(s) => write!(f, "'{s}"),
245            OpenModule(s) => write!(f, "{s}.("),
246            IntConst(n) => write!(f, "{n}"),
247            FloatConst(x) => write!(f, "{x}"),
248            LengthConst(x, u) => write!(f, "{x}{u}"),
249            Literal { body, .. } => write!(f, "`{body}`"),
250            Mod => write!(f, "mod"),
251            If => write!(f, "if"),
252            Then => write!(f, "then"),
253            Else => write!(f, "else"),
254            Let => write!(f, "let"),
255            LetRec => write!(f, "let-rec"),
256            LetAnd => write!(f, "and"),
257            In => write!(f, "in"),
258            Fun => write!(f, "fun"),
259            True => write!(f, "true"),
260            False => write!(f, "false"),
261            Before => write!(f, "before"),
262            While => write!(f, "while"),
263            Do => write!(f, "do"),
264            LetMutable => write!(f, "let-mutable"),
265            Match => write!(f, "match"),
266            With => write!(f, "with"),
267            When => write!(f, "when"),
268            As => write!(f, "as"),
269            Type => write!(f, "type"),
270            Of => write!(f, "of"),
271            Module => write!(f, "module"),
272            Struct => write!(f, "struct"),
273            Sig => write!(f, "sig"),
274            Val => write!(f, "val"),
275            End => write!(f, "end"),
276            Direct => write!(f, "direct"),
277            Constraint => write!(f, "constraint"),
278            LetHorz => write!(f, "let-inline"),
279            LetVert => write!(f, "let-block"),
280            LetMath => write!(f, "let-math"),
281            Controls => write!(f, "controls"),
282            Cycle => write!(f, "cycle"),
283            HorzCmdType => write!(f, "inline-cmd"),
284            VertCmdType => write!(f, "block-cmd"),
285            MathCmdType => write!(f, "math-cmd"),
286            Command => write!(f, "command"),
287            Open => write!(f, "open"),
288            Rec => write!(f, "rec"),
289            Inline => write!(f, "inline"),
290            Block => write!(f, "block"),
291            Mutable => write!(f, "mutable"),
292            Signature => write!(f, "signature"),
293            Include => write!(f, "include"),
294            Use => write!(f, "use"),
295            Package => write!(f, "package"),
296            Math => write!(f, "math"),
297            Persistent => write!(f, "persistent"),
298            LParen => write!(f, "("),
299            RParen => write!(f, ")"),
300            BRecord => write!(f, "(|"),
301            ERecord => write!(f, "|)"),
302            BList => write!(f, "["),
303            EList => write!(f, "]"),
304            BHorzGrp => write!(f, "{{"),
305            EHorzGrp => write!(f, "}}"),
306            BVertGrp => write!(f, "'<"),
307            EVertGrp => write!(f, ">"),
308            BMathGrp => write!(f, "${{"),
309            EMathGrp => write!(f, "}}"),
310            BPath => write!(f, "<["),
311            EPath => write!(f, "]>"),
312            ListPunct => write!(f, ";"),
313            Access => write!(f, "#"),
314            Arrow => write!(f, "->"),
315            OverwriteEq => write!(f, "<-"),
316            Bar => write!(f, "|"),
317            Wildcard => write!(f, "_"),
318            Colon => write!(f, ":"),
319            Comma => write!(f, ","),
320            Cons => write!(f, "::"),
321            Coerce => write!(f, ":>"),
322            ExactMinus => write!(f, "-"),
323            DefEq => write!(f, "="),
324            ExactTimes => write!(f, "*"),
325            ExactAmp => write!(f, "&"),
326            ExactTilde => write!(f, "~"),
327            PathCurve => write!(f, ".."),
328            PathLine => write!(f, "--"),
329            BinopPlus(s) | BinopMinus(s) | BinopTimes(s) | BinopDivides(s) | BinopEq(s)
330            | BinopLt(s) | BinopGt(s) | BinopAmp(s) | BinopBar(s) | BinopHat(s)
331            | UnopExclam(s) => write!(f, "{s}"),
332            OptionalType => write!(f, "?"),
333            OptionalArrow => write!(f, "?->"),
334            Optional => write!(f, "?:"),
335            Omission => write!(f, "?*"),
336            RowVar(s) => write!(f, "?'{s}"),
337            HorzCmd(s) | VertCmd(s) | MathCmd(s) | HorzMacro(s) | VertMacro(s) => {
338                write!(f, "{s}")
339            }
340            HorzCmdWithMod(m, s) | VertCmdWithMod(m, s) | MathCmdWithMod(m, s) => {
341                let (sigil, name) = s.split_at(1);
342                write!(f, "{sigil}{}.{name}", m.join("."))
343            }
344            VarInHorz(m, s) | VarInVert(m, s) | VarInMath(m, s) => {
345                if m.is_empty() {
346                    write!(f, "#{s}")
347                } else {
348                    write!(f, "#{}.{s}", m.join("."))
349                }
350            }
351            Char(s) => write!(f, "{s}"),
352            CodeText(s) => write!(f, "`{s}`"),
353            Space => write!(f, " "),
354            Break => writeln!(f),
355            Item(n) => write!(f, "{}", "*".repeat(*n)),
356            Sep => write!(f, "|"),
357            EndActive => write!(f, ";"),
358            MathChar(s) => write!(f, "{s}"),
359            Superscript => write!(f, "^"),
360            Subscript => write!(f, "_"),
361            Primes(n) => write!(f, "{}", "'".repeat(*n)),
362            Eoi => Ok(()),
363        }
364    }
365}
366
367/// A token together with its source span: the atom type fed to the syan parser.
368pub type Atom = syan::span::WithSpan<Token, Span>;
369
370// Leaf structs + `Parse`/`Unparse`/`Spanned`, one per listed variant. Stands
371// in for syan's `#[derive(TokenLeaves)]`, which exists only on that crate's
372// api-ergonomics line and is absent from main; see `leaf_macro.rs`.
373token_leaves! {
374    atom = Atom, span = Span, read_span = |a| a.span;
375    (HeaderRequire(String) => HeaderRequireTok, "'@require:'", field = content);
376    (HeaderImport(String) => HeaderImportTok, "'@import:'", field = content);
377    (Var(String) => VarTok, "a variable name", field = name);
378    (Constructor(String) => CtorTok, "a constructor name", field = name);
379    (TypeVar(String) => TypeVarTok, "a type variable", field = name);
380    (OpenModule(String) => OpenModuleTok, "'Mod.('", field = name);
381    (IntConst(i64) => IntTok, "an integer constant", field = value);
382    (FloatConst(f64) => FloatTok, "a float constant", field = value);
383    (If => KwIf, "'if'");
384    (Then => KwThen, "'then'");
385    (Else => KwElse, "'else'");
386    (Let => KwLet, "'let'");
387    (LetRec => KwLetRec, "'let-rec'");
388    (LetAnd => KwAnd, "'and'");
389    (In => KwIn, "'in'");
390    (Fun => KwFun, "'fun'");
391    (True => KwTrue, "'true'");
392    (False => KwFalse, "'false'");
393    (Before => KwBefore, "'before'");
394    (While => KwWhile, "'while'");
395    (Do => KwDo, "'do'");
396    (LetMutable => KwLetMutable, "'let-mutable'");
397    (Match => KwMatch, "'match'");
398    (With => KwWith, "'with'");
399    (When => KwWhen, "'when'");
400    (As => KwAs, "'as'");
401    (Type => KwType, "'type'");
402    (Of => KwOf, "'of'");
403    (Module => KwModule, "'module'");
404    (Struct => KwStruct, "'struct'");
405    (Sig => KwSig, "'sig'");
406    (Val => KwVal, "'val'");
407    (End => KwEnd, "'end'");
408    (Direct => KwDirect, "'direct'");
409    (Constraint => ConstraintTok, "'constraint'");
410    (LetHorz => KwLetHorz, "'let-inline'");
411    (LetVert => KwLetVert, "'let-block'");
412    (LetMath => KwLetMath, "'let-math'");
413    (HorzCmdType => HorzCmdTypeTok, "'inline-cmd'");
414    (VertCmdType => VertCmdTypeTok, "'block-cmd'");
415    (MathCmdType => MathCmdTypeTok, "'math-cmd'");
416    (Command => CommandTok, "'command'");
417    (Open => KwOpen, "'open'");
418    (Rec => KwRec, "'rec'");
419    (Inline => KwInline, "'inline'");
420    (Block => KwBlock, "'block'");
421    (Mutable => KwMutable, "'mutable'");
422    (Signature => KwSignature, "'signature'");
423    (Include => KwInclude, "'include'");
424    (Use => KwUse, "'use'");
425    (Package => KwPackage, "'package'");
426    (Math => KwMath, "'math'");
427    (Persistent => KwPersistent, "'persistent'");
428    (LParen => LParenTok, "'('");
429    (RParen => RParenTok, "')'");
430    (BRecord => BRecordTok, "'(|'");
431    (ERecord => ERecordTok, "'|)'");
432    (BList => BListTok, "'['");
433    (EList => EListTok, "']'");
434    (BHorzGrp => BHorzGrpTok, "'{'");
435    (EHorzGrp => EHorzGrpTok, "'}'");
436    (BVertGrp => BVertGrpTok, "'<'");
437    (EVertGrp => EVertGrpTok, "'>'");
438    (BMathGrp => BMathGrpTok, "'${'");
439    (EMathGrp => EMathGrpTok, "'}'");
440    (ListPunct => ListPunctTok, "';'");
441    (Access => AccessTok, "'#'");
442    (Arrow => ArrowTok, "'->'");
443    (OverwriteEq => OverwriteEqTok, "'<-'");
444    (Bar => BarTok, "'|'");
445    (Wildcard => WildcardTok, "'_'");
446    (Colon => ColonTok, "':'");
447    (Comma => CommaTok, "','");
448    (Cons => ConsTok, "'::'");
449    (Coerce => CoerceTok, "':>'");
450    (ExactMinus => ExactMinusTok, "'-'");
451    (DefEq => DefEqTok, "'='");
452    (ExactTimes => ExactTimesTok, "'*'");
453    (ExactAmp => ExactAmpTok, "'&'");
454    (ExactTilde => ExactTildeTok, "'~'");
455    (UnopExclam(String) => UnopExclamTok, "a '!' operator", field = text);
456    (OptionalType => OptionalTypeTok, "'?'");
457    (OptionalArrow => OptionalArrowTok, "'?->'");
458    (Optional => OptionalTok, "'?:'");
459    (Omission => OmissionTok, "'?*'");
460    (RowVar(String) => RowVarTok, "a row variable (\"?'r\")", field = name);
461    (HorzCmd(String) => HorzCmdTok, "an inline command", field = name);
462    (VertCmd(String) => VertCmdTok, "a block command", field = name);
463    (MathCmd(String) => MathCmdTok, "a math command", field = name);
464    (Char(String) => CharTok, "inline text", field = text);
465    (CodeText(String) => CodeTextTok, "an inline code literal", field = text);
466    (Space => SpaceTok, "a space");
467    (Break => BreakTok, "a line break");
468    (Item(usize) => ItemTok, "an itemize bullet", field = depth);
469    (Sep => SepTok, "'|' separator");
470    (EndActive => EndActiveTok, "';'");
471    (MathChar(String) => MathCharTok, "a math character", field = text);
472    (Superscript => SuperscriptTok, "'^'");
473    (Subscript => SubscriptTok, "'_'");
474    (Primes(usize) => PrimesTok, "a primes mark", field = count);
475    (Eoi => EoiTok, "end of input");
476}