@jesscss/scss-parser 2.0.0-alpha.7 → 2.0.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,25 @@
1
1
  # @jesscss/scss-parser
2
2
 
3
- A [SCSS/Sass](https://sass-lang.com/) parser built on [parseman](https://www.npmjs.com/package/parseman). The grammar is the CSS grammar plus an SCSS delta: `scssGrammar = compose([cssGrammar, <SCSS delta>])`, layered on the shared CSS base in `@jesscss/css-parser`.
3
+ An SCSS grammar for Jess, layered on the CSS base parser **experimental, and not the focus of the Less alpha.**
4
4
 
5
- The goal is **parse coverage**: not every Sass/SCSS feature is necessarily *evaluated*, but the surface syntax should parse. This is the earliest-stage of the four Jess parsers (`2.0.0-alpha.1`).
5
+ > **Status: experimental / roadmap.** Part of [Jess](https://github.com/jesscss/jess), which *is* Less.js v5. Only `.less` rendering ships today. SCSS is **not a goal of this phase** — this parser is early and exists to seed the future **"Sass+"** dialect (roadmap milestone #2, after Less.js). Don't rely on it for production; prefer [`@jesscss/css-parser`](https://www.npmjs.com/package/@jesscss/css-parser) / [`@jesscss/less-parser`](https://www.npmjs.com/package/@jesscss/less-parser). [Report bugs](https://github.com/jesscss/jess/issues); docs live at [jesscss.github.io](https://jesscss.github.io/).
6
+
7
+ ## What it is
8
+
9
+ The SCSS grammar is the shared CSS grammar plus an SCSS delta:
10
+ `scssGrammar = compose([cssGrammar, <SCSS delta>])`, layered on the spec-aligned
11
+ CSS base in `@jesscss/css-parser` and built on
12
+ [parseman](https://www.npmjs.com/package/parseman) — **the fastest
13
+ general-purpose JavaScript parser** in its
14
+ [published benchmarks](https://matthew-dean.github.io/parseman/guide/benchmarks)
15
+ (see `@jesscss/css-parser` for figures and engineering details).
16
+
17
+ The current goal is **parse coverage**: the surface `$`-variable / SCSS syntax
18
+ should parse into a tree, but not every Sass/SCSS feature is necessarily
19
+ *evaluated*. This is the least-mature of the Jess parsers (the `.jess` parser
20
+ trails it), and the seed for the roadmap "Sass+" dialect rather than a shipped
21
+ Sass replacement. The language roadmap is ordered: **Now Less.js → Next Sass+ →
22
+ Final `.jess`.**
6
23
 
7
24
  Two ways to use it:
8
25
 
@@ -129,4 +146,4 @@ type BuildHost = (
129
146
 
130
147
  ## Part of Jess
131
148
 
132
- This package is developed as part of [Jess](https://github.com/jesscss/jess). The core-coupled `.` entry integrates with `@jesscss/core`; the `./cst` and `./grammar` entries are usable on their own. SCSS is the least-mature of the Jess dialects prefer `@jesscss/css-parser` / `@jesscss/less-parser` for production use.
149
+ This package is developed as part of [Jess](https://github.com/jesscss/jess), the Less.js v5 rewrite. SCSS is the least-mature Jess dialect and is not the focus of the Less alpha it seeds the roadmap "Sass+" dialect. For production use, prefer `@jesscss/css-parser` / `@jesscss/less-parser`. Licensed MIT.
@@ -2,7 +2,7 @@ const require_grammar = require("./grammar.cjs");
2
2
  let _jesscss_less_parser_jess = require("@jesscss/less-parser/jess");
3
3
  let _jesscss_css_parser_jess = require("@jesscss/css-parser/jess");
4
4
  let _jesscss_core = require("@jesscss/core");
5
- //#region ../../node_modules/.pnpm/parseman@0.25.0/node_modules/parseman/dist/index.js
5
+ //#region ../../../parser-thing/dist/index.js
6
6
  function union(a, b) {
7
7
  if (a.kind === "any" || b.kind === "any") return { kind: "any" };
8
8
  if (a.kind === "empty") return b;
@@ -88,6 +88,48 @@ function sequenceFirstSet(parsers) {
88
88
  }
89
89
  return fs;
90
90
  }
91
+ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
92
+ if (seen.has(p)) return any();
93
+ seen.add(p);
94
+ const fs = (c) => firstSetOf(c, seen);
95
+ const d = p._def;
96
+ switch (d.tag) {
97
+ case "literal":
98
+ case "regex":
99
+ case "keywords": return p._meta.firstSet;
100
+ case "lazy": try {
101
+ return fs(d.thunk());
102
+ } catch {
103
+ return any();
104
+ }
105
+ case "choice": {
106
+ let out = empty();
107
+ for (const arm of d.parsers) out = union(out, fs(arm));
108
+ return out;
109
+ }
110
+ case "sequence": {
111
+ let out = empty();
112
+ for (const term of d.parsers) {
113
+ out = union(out, fs(term));
114
+ if (!matchesEmpty(term)) return out;
115
+ }
116
+ return out;
117
+ }
118
+ case "oneOrMore":
119
+ case "many":
120
+ case "optional":
121
+ case "transform":
122
+ case "label":
123
+ case "trivia":
124
+ case "token":
125
+ case "node":
126
+ case "grammar":
127
+ case "expect": return fs(d.parser);
128
+ case "sepBy": return fs(d.parser);
129
+ case "skip": return fs(d.main);
130
+ default: return p._meta.firstSet;
131
+ }
132
+ }
91
133
  function mergeRanges(ranges) {
92
134
  if (ranges.length === 0) return [];
93
135
  const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
@@ -178,7 +220,8 @@ function saveCstMark(ctx) {
178
220
  raw: cstRawLen(ctx),
179
221
  tlog: cstTlLen(ctx),
180
222
  leaves: cstLeavesLen(ctx),
181
- fields: ctx._fields?.length ?? 0
223
+ fields: ctx._fields?.length ?? 0,
224
+ errors: ctx._errors?.length ?? 0
182
225
  };
183
226
  }
184
227
  function rollbackBufList(b, keyMulti, keySingle, len) {
@@ -194,6 +237,7 @@ function rollbackBufList(b, keyMulti, keySingle, len) {
194
237
  if (len === 0) b[keySingle] = void 0;
195
238
  }
196
239
  function rollbackCstCapture(ctx, mark) {
240
+ if (ctx._errors && mark.errors !== void 0) ctx._errors.length = mark.errors;
197
241
  const b = ctx._cstBuf;
198
242
  if (b) {
199
243
  rollbackBufList(b, "raw", "rawSingle", mark.raw);
@@ -757,11 +801,74 @@ function firstSetFromRegex(source) {
757
801
  canMatchNewline: firstCanBeNewline(ast)
758
802
  };
759
803
  }
804
+ function firstSetSentinel(fs) {
805
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
806
+ const ranges = fs.ranges;
807
+ return {
808
+ _tag: "firstSetSentinel",
809
+ _meta: {
810
+ firstSet: fs,
811
+ canMatchNewline: false,
812
+ isTrivia: false
813
+ },
814
+ _def: { tag: "unknown" },
815
+ parse(input, pos) {
816
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
817
+ if (code !== void 0) {
818
+ for (const r of ranges) if (code >= r.lo && code <= r.hi) return {
819
+ ok: true,
820
+ value: null,
821
+ span: {
822
+ start: pos,
823
+ end: pos
824
+ }
825
+ };
826
+ }
827
+ return {
828
+ ok: false,
829
+ expected: [],
830
+ span: {
831
+ start: pos,
832
+ end: pos
833
+ }
834
+ };
835
+ }
836
+ };
837
+ }
838
+ function deriveExpected(c) {
839
+ const def = c._def;
840
+ switch (def.tag) {
841
+ case "literal": return [JSON.stringify(def.value)];
842
+ case "regex": return [`/${def.source}/`];
843
+ case "keywords": return def.words.map((w) => JSON.stringify(w));
844
+ case "label": return [def.label];
845
+ case "choice": return def.parsers.flatMap(deriveExpected);
846
+ case "sequence": return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
847
+ case "node":
848
+ case "grammar":
849
+ case "trivia":
850
+ case "token":
851
+ case "optional":
852
+ case "many":
853
+ case "oneOrMore":
854
+ case "transform":
855
+ case "not": return deriveExpected(def.parser);
856
+ case "lazy": {
857
+ const name = c._ruleName;
858
+ try {
859
+ return deriveExpected(def.thunk());
860
+ } catch {
861
+ return name ? [name] : [];
862
+ }
863
+ }
864
+ default: return [];
865
+ }
866
+ }
760
867
  function choice(...args) {
761
868
  const parsers = args.map((a) => "gate" in a ? a.combinator : a);
762
869
  const gates = args.map((a) => "gate" in a ? a.gate : null);
763
870
  const hasGates = gates.some((g) => g !== null);
764
- const disjoint = !hasGates && areDisjoint(parsers.map((p) => p._meta.firstSet));
871
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
765
872
  let combined = { kind: "empty" };
766
873
  for (const p of parsers) combined = union(combined, p._meta.firstSet);
767
874
  const meta = {
@@ -798,15 +905,24 @@ function choice(...args) {
798
905
  const expected = [];
799
906
  if (disjoint && pos < input.length) {
800
907
  const code = input.codePointAt(pos);
801
- let parser2 = code < 128 ? asciiDispatch?.[code] ?? null : null;
802
- if (!parser2) {
803
- for (const p of parsers) if (inFirstSet(code, p._meta.firstSet)) {
804
- parser2 = p;
908
+ let idx = code < 128 ? asciiDispatch[code] : -1;
909
+ if (idx < 0) {
910
+ for (let i = 0; i < parsers.length; i++) if (inFirstSet(code, parsers[i]._meta.firstSet)) {
911
+ idx = i;
805
912
  break;
806
913
  }
807
914
  }
808
- if (parser2) {
809
- const result = parser2.parse(input, pos, ctx);
915
+ if (idx >= 0) {
916
+ const gate = gates[idx];
917
+ if (gate && !gate(ctx.state)) return {
918
+ ok: false,
919
+ expected: deriveExpected(parsers[idx]),
920
+ span: {
921
+ start: pos,
922
+ end: pos
923
+ }
924
+ };
925
+ const result = parsers[idx].parse(input, pos, ctx);
810
926
  if (result.ok) return result;
811
927
  expected.push(...result.expected);
812
928
  return {
@@ -1026,11 +1142,11 @@ function areDisjoint(sets) {
1026
1142
  return true;
1027
1143
  }
1028
1144
  function buildAsciiDispatch(parsers) {
1029
- const table = Array(128).fill(null);
1030
- for (const parser2 of parsers) {
1031
- const fs = parser2._meta.firstSet;
1145
+ const table = Array(128).fill(-1);
1146
+ for (let i = 0; i < parsers.length; i++) {
1147
+ const fs = parsers[i]._meta.firstSet;
1032
1148
  if (fs.kind !== "ranges") continue;
1033
- for (const { lo, hi } of fs.ranges) for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) table[code] = parser2;
1149
+ for (const { lo, hi } of fs.ranges) for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) table[code] = i;
1034
1150
  }
1035
1151
  return table;
1036
1152
  }
@@ -1201,6 +1317,7 @@ function saveTriviaMark(ctx) {
1201
1317
  tlog: m.tlog,
1202
1318
  leaves: m.leaves,
1203
1319
  fields: m.fields,
1320
+ errors: m.errors,
1204
1321
  log: ctx._triviaLog ? ctx._triviaLog.length : 0
1205
1322
  };
1206
1323
  }
@@ -1209,7 +1326,8 @@ function rollbackTrivia(ctx, mark) {
1209
1326
  raw: mark.raw,
1210
1327
  tlog: mark.tlog,
1211
1328
  leaves: mark.leaves,
1212
- fields: mark.fields
1329
+ fields: mark.fields,
1330
+ errors: mark.errors
1213
1331
  });
1214
1332
  if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
1215
1333
  }
@@ -1474,11 +1592,55 @@ function sequence(...parsers) {
1474
1592
  tag: "sequence",
1475
1593
  parsers
1476
1594
  };
1595
+ let followSentinels;
1596
+ function parseTolerant(input, pos, ctx) {
1597
+ followSentinels ??= parsers.map((_, i) => {
1598
+ return firstSetSentinel(parsers.slice(i + 1).reduce((acc, p) => union(acc, firstSetOf(p)), { kind: "empty" }));
1599
+ });
1600
+ const values = def.valueUnused ? void 0 : [];
1601
+ let cur = pos;
1602
+ const inheritedSync = ctx._sync;
1603
+ try {
1604
+ for (let i = 0; i < parsers.length; i++) {
1605
+ ctx._sync = followSentinels[i] ?? inheritedSync;
1606
+ if (ctx.trivia && i > 0) {
1607
+ const mark = saveTriviaMark(ctx);
1608
+ let scanEnd;
1609
+ if (needsDeferredTriviaCommit(ctx)) {
1610
+ const scan = scanTrivia(input, cur, ctx);
1611
+ scan.commit();
1612
+ scanEnd = scan.end;
1613
+ } else scanEnd = advanceTrivia(input, cur, ctx);
1614
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1615
+ if (!result2.ok) return result2;
1616
+ if (result2.span.end > scanEnd) cur = result2.span.end;
1617
+ else rollbackTrivia(ctx, mark);
1618
+ if (values !== void 0) values.push(result2.value);
1619
+ continue;
1620
+ }
1621
+ const result = parsers[i].parse(input, cur, ctx);
1622
+ if (!result.ok) return result;
1623
+ if (values !== void 0) values.push(result.value);
1624
+ cur = result.span.end;
1625
+ }
1626
+ } finally {
1627
+ ctx._sync = inheritedSync;
1628
+ }
1629
+ return {
1630
+ ok: true,
1631
+ value: values ?? void 0,
1632
+ span: {
1633
+ start: pos,
1634
+ end: cur
1635
+ }
1636
+ };
1637
+ }
1477
1638
  return {
1478
1639
  _tag: "sequence",
1479
1640
  _meta: meta,
1480
1641
  _def: def,
1481
1642
  parse(input, pos, ctx) {
1643
+ if (ctx._tolerant) return parseTolerant(input, pos, ctx);
1482
1644
  const values = def.valueUnused ? void 0 : [];
1483
1645
  let cur = pos;
1484
1646
  for (let i = 0; i < parsers.length; i++) {
@@ -2,7 +2,7 @@ import { scssGrammar } from "./grammar.js";
2
2
  import { LessGrammar } from "@jesscss/less-parser/jess";
3
3
  import { runFunctionalParse, spannedComponents } from "@jesscss/css-parser/jess";
4
4
  import { Ampersand, Any, AtRule, AtRuleStatement, Call, Collection, ComplexSelector, Condition, Declaration, Expression, Extend, ExtendFlag, F_VISIBLE, For, Func, INTERPOLATION_PLACEHOLDER, If, Interpolated, InterpolatedSelector, JsImport, List, Log, Mixin, N, Nil, Node, Operation, Paren, Quoted, Reference, Rest, Rules, Ruleset, SelectorCapture, SelectorList, Sequence, StyleImport, Url, VarDeclaration, While, isNode, sourceSpanOf } from "@jesscss/core";
5
- //#region ../../node_modules/.pnpm/parseman@0.25.0/node_modules/parseman/dist/index.js
5
+ //#region ../../../parser-thing/dist/index.js
6
6
  function union(a, b) {
7
7
  if (a.kind === "any" || b.kind === "any") return { kind: "any" };
8
8
  if (a.kind === "empty") return b;
@@ -88,6 +88,48 @@ function sequenceFirstSet(parsers) {
88
88
  }
89
89
  return fs;
90
90
  }
91
+ function firstSetOf(p, seen = /* @__PURE__ */ new Set()) {
92
+ if (seen.has(p)) return any();
93
+ seen.add(p);
94
+ const fs = (c) => firstSetOf(c, seen);
95
+ const d = p._def;
96
+ switch (d.tag) {
97
+ case "literal":
98
+ case "regex":
99
+ case "keywords": return p._meta.firstSet;
100
+ case "lazy": try {
101
+ return fs(d.thunk());
102
+ } catch {
103
+ return any();
104
+ }
105
+ case "choice": {
106
+ let out = empty();
107
+ for (const arm of d.parsers) out = union(out, fs(arm));
108
+ return out;
109
+ }
110
+ case "sequence": {
111
+ let out = empty();
112
+ for (const term of d.parsers) {
113
+ out = union(out, fs(term));
114
+ if (!matchesEmpty(term)) return out;
115
+ }
116
+ return out;
117
+ }
118
+ case "oneOrMore":
119
+ case "many":
120
+ case "optional":
121
+ case "transform":
122
+ case "label":
123
+ case "trivia":
124
+ case "token":
125
+ case "node":
126
+ case "grammar":
127
+ case "expect": return fs(d.parser);
128
+ case "sepBy": return fs(d.parser);
129
+ case "skip": return fs(d.main);
130
+ default: return p._meta.firstSet;
131
+ }
132
+ }
91
133
  function mergeRanges(ranges) {
92
134
  if (ranges.length === 0) return [];
93
135
  const sorted = [...ranges].sort((a, b) => a.lo - b.lo);
@@ -178,7 +220,8 @@ function saveCstMark(ctx) {
178
220
  raw: cstRawLen(ctx),
179
221
  tlog: cstTlLen(ctx),
180
222
  leaves: cstLeavesLen(ctx),
181
- fields: ctx._fields?.length ?? 0
223
+ fields: ctx._fields?.length ?? 0,
224
+ errors: ctx._errors?.length ?? 0
182
225
  };
183
226
  }
184
227
  function rollbackBufList(b, keyMulti, keySingle, len) {
@@ -194,6 +237,7 @@ function rollbackBufList(b, keyMulti, keySingle, len) {
194
237
  if (len === 0) b[keySingle] = void 0;
195
238
  }
196
239
  function rollbackCstCapture(ctx, mark) {
240
+ if (ctx._errors && mark.errors !== void 0) ctx._errors.length = mark.errors;
197
241
  const b = ctx._cstBuf;
198
242
  if (b) {
199
243
  rollbackBufList(b, "raw", "rawSingle", mark.raw);
@@ -757,11 +801,74 @@ function firstSetFromRegex(source) {
757
801
  canMatchNewline: firstCanBeNewline(ast)
758
802
  };
759
803
  }
804
+ function firstSetSentinel(fs) {
805
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
806
+ const ranges = fs.ranges;
807
+ return {
808
+ _tag: "firstSetSentinel",
809
+ _meta: {
810
+ firstSet: fs,
811
+ canMatchNewline: false,
812
+ isTrivia: false
813
+ },
814
+ _def: { tag: "unknown" },
815
+ parse(input, pos) {
816
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
817
+ if (code !== void 0) {
818
+ for (const r of ranges) if (code >= r.lo && code <= r.hi) return {
819
+ ok: true,
820
+ value: null,
821
+ span: {
822
+ start: pos,
823
+ end: pos
824
+ }
825
+ };
826
+ }
827
+ return {
828
+ ok: false,
829
+ expected: [],
830
+ span: {
831
+ start: pos,
832
+ end: pos
833
+ }
834
+ };
835
+ }
836
+ };
837
+ }
838
+ function deriveExpected(c) {
839
+ const def = c._def;
840
+ switch (def.tag) {
841
+ case "literal": return [JSON.stringify(def.value)];
842
+ case "regex": return [`/${def.source}/`];
843
+ case "keywords": return def.words.map((w) => JSON.stringify(w));
844
+ case "label": return [def.label];
845
+ case "choice": return def.parsers.flatMap(deriveExpected);
846
+ case "sequence": return def.parsers.length > 0 ? deriveExpected(def.parsers[0]) : [];
847
+ case "node":
848
+ case "grammar":
849
+ case "trivia":
850
+ case "token":
851
+ case "optional":
852
+ case "many":
853
+ case "oneOrMore":
854
+ case "transform":
855
+ case "not": return deriveExpected(def.parser);
856
+ case "lazy": {
857
+ const name = c._ruleName;
858
+ try {
859
+ return deriveExpected(def.thunk());
860
+ } catch {
861
+ return name ? [name] : [];
862
+ }
863
+ }
864
+ default: return [];
865
+ }
866
+ }
760
867
  function choice(...args) {
761
868
  const parsers = args.map((a) => "gate" in a ? a.combinator : a);
762
869
  const gates = args.map((a) => "gate" in a ? a.gate : null);
763
870
  const hasGates = gates.some((g) => g !== null);
764
- const disjoint = !hasGates && areDisjoint(parsers.map((p) => p._meta.firstSet));
871
+ const disjoint = areDisjoint(parsers.map((p) => p._meta.firstSet)) && parsers.every((p) => !matchesEmpty(p));
765
872
  let combined = { kind: "empty" };
766
873
  for (const p of parsers) combined = union(combined, p._meta.firstSet);
767
874
  const meta = {
@@ -798,15 +905,24 @@ function choice(...args) {
798
905
  const expected = [];
799
906
  if (disjoint && pos < input.length) {
800
907
  const code = input.codePointAt(pos);
801
- let parser2 = code < 128 ? asciiDispatch?.[code] ?? null : null;
802
- if (!parser2) {
803
- for (const p of parsers) if (inFirstSet(code, p._meta.firstSet)) {
804
- parser2 = p;
908
+ let idx = code < 128 ? asciiDispatch[code] : -1;
909
+ if (idx < 0) {
910
+ for (let i = 0; i < parsers.length; i++) if (inFirstSet(code, parsers[i]._meta.firstSet)) {
911
+ idx = i;
805
912
  break;
806
913
  }
807
914
  }
808
- if (parser2) {
809
- const result = parser2.parse(input, pos, ctx);
915
+ if (idx >= 0) {
916
+ const gate = gates[idx];
917
+ if (gate && !gate(ctx.state)) return {
918
+ ok: false,
919
+ expected: deriveExpected(parsers[idx]),
920
+ span: {
921
+ start: pos,
922
+ end: pos
923
+ }
924
+ };
925
+ const result = parsers[idx].parse(input, pos, ctx);
810
926
  if (result.ok) return result;
811
927
  expected.push(...result.expected);
812
928
  return {
@@ -1026,11 +1142,11 @@ function areDisjoint(sets) {
1026
1142
  return true;
1027
1143
  }
1028
1144
  function buildAsciiDispatch(parsers) {
1029
- const table = Array(128).fill(null);
1030
- for (const parser2 of parsers) {
1031
- const fs = parser2._meta.firstSet;
1145
+ const table = Array(128).fill(-1);
1146
+ for (let i = 0; i < parsers.length; i++) {
1147
+ const fs = parsers[i]._meta.firstSet;
1032
1148
  if (fs.kind !== "ranges") continue;
1033
- for (const { lo, hi } of fs.ranges) for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) table[code] = parser2;
1149
+ for (const { lo, hi } of fs.ranges) for (let code = Math.max(0, lo); code <= Math.min(127, hi); code++) table[code] = i;
1034
1150
  }
1035
1151
  return table;
1036
1152
  }
@@ -1201,6 +1317,7 @@ function saveTriviaMark(ctx) {
1201
1317
  tlog: m.tlog,
1202
1318
  leaves: m.leaves,
1203
1319
  fields: m.fields,
1320
+ errors: m.errors,
1204
1321
  log: ctx._triviaLog ? ctx._triviaLog.length : 0
1205
1322
  };
1206
1323
  }
@@ -1209,7 +1326,8 @@ function rollbackTrivia(ctx, mark) {
1209
1326
  raw: mark.raw,
1210
1327
  tlog: mark.tlog,
1211
1328
  leaves: mark.leaves,
1212
- fields: mark.fields
1329
+ fields: mark.fields,
1330
+ errors: mark.errors
1213
1331
  });
1214
1332
  if (ctx._triviaLog) ctx._triviaLog.length = mark.log;
1215
1333
  }
@@ -1474,11 +1592,55 @@ function sequence(...parsers) {
1474
1592
  tag: "sequence",
1475
1593
  parsers
1476
1594
  };
1595
+ let followSentinels;
1596
+ function parseTolerant(input, pos, ctx) {
1597
+ followSentinels ??= parsers.map((_, i) => {
1598
+ return firstSetSentinel(parsers.slice(i + 1).reduce((acc, p) => union(acc, firstSetOf(p)), { kind: "empty" }));
1599
+ });
1600
+ const values = def.valueUnused ? void 0 : [];
1601
+ let cur = pos;
1602
+ const inheritedSync = ctx._sync;
1603
+ try {
1604
+ for (let i = 0; i < parsers.length; i++) {
1605
+ ctx._sync = followSentinels[i] ?? inheritedSync;
1606
+ if (ctx.trivia && i > 0) {
1607
+ const mark = saveTriviaMark(ctx);
1608
+ let scanEnd;
1609
+ if (needsDeferredTriviaCommit(ctx)) {
1610
+ const scan = scanTrivia(input, cur, ctx);
1611
+ scan.commit();
1612
+ scanEnd = scan.end;
1613
+ } else scanEnd = advanceTrivia(input, cur, ctx);
1614
+ const result2 = parsers[i].parse(input, scanEnd, ctx);
1615
+ if (!result2.ok) return result2;
1616
+ if (result2.span.end > scanEnd) cur = result2.span.end;
1617
+ else rollbackTrivia(ctx, mark);
1618
+ if (values !== void 0) values.push(result2.value);
1619
+ continue;
1620
+ }
1621
+ const result = parsers[i].parse(input, cur, ctx);
1622
+ if (!result.ok) return result;
1623
+ if (values !== void 0) values.push(result.value);
1624
+ cur = result.span.end;
1625
+ }
1626
+ } finally {
1627
+ ctx._sync = inheritedSync;
1628
+ }
1629
+ return {
1630
+ ok: true,
1631
+ value: values ?? void 0,
1632
+ span: {
1633
+ start: pos,
1634
+ end: cur
1635
+ }
1636
+ };
1637
+ }
1477
1638
  return {
1478
1639
  _tag: "sequence",
1479
1640
  _meta: meta,
1480
1641
  _def: def,
1481
1642
  parse(input, pos, ctx) {
1643
+ if (ctx._tolerant) return parseTolerant(input, pos, ctx);
1482
1644
  const values = def.valueUnused ? void 0 : [];
1483
1645
  let cur = pos;
1484
1646
  for (let i = 0; i < parsers.length; i++) {