@minelang-ts/parser 0.0.1
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/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +500 -0
- package/dist/index.d.ts +500 -0
- package/dist/index.js +3 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Maysara Elshewehy (https://github.com/maysara-elshewehy)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!-- ╔═══════════════════════════ BEG ════════════════════════════╗ -->
|
|
2
|
+
|
|
3
|
+
<br>
|
|
4
|
+
<div align="center">
|
|
5
|
+
<p>
|
|
6
|
+
<img src="./assets/img/logo.png" alt="logo" style="" height="60" />
|
|
7
|
+
</p>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div align="center">
|
|
11
|
+
<img data="version" src="https://img.shields.io/badge/v-0.0.1-black"/>
|
|
12
|
+
<a href="https://github.com/minelang-ts"><img src="https://img.shields.io/badge/@-minelang--ts-black"/></a>
|
|
13
|
+
<img src="https://img.shields.io/badge/coverage-99.91%25-brightgreen" alt="Test Coverage" />
|
|
14
|
+
</div>
|
|
15
|
+
<br>
|
|
16
|
+
|
|
17
|
+
<!-- ╚════════════════════════════════════════════════════════════╝ -->
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
<!-- ╔═══════════════════════════ DOC ════════════════════════════╗ -->
|
|
22
|
+
|
|
23
|
+
- ## Note 📝
|
|
24
|
+
|
|
25
|
+
> All repositories related to `minelang-ts`, **like this repository**, are temporary repositories created for the purpose of simulating the final form of the Mine language.
|
|
26
|
+
>
|
|
27
|
+
> This means that they will later be ignored or deleted entirely after the real version of Mine is created using Mine itself rather than TypeScript. Until that happens, I will not provide any guarantees for the use of these repositories, since I will not spend much time on them because, as I said, they are temporary.
|
|
28
|
+
|
|
29
|
+
<!-- ╚════════════════════════════════════════════════════════════╝ -->
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
<!-- ╔═══════════════════════════ END ════════════════════════════╗ -->
|
|
34
|
+
|
|
35
|
+
<br>
|
|
36
|
+
|
|
37
|
+
<div align="center"><img src="./assets/img/line.png" alt="logo" style="" width="50%" /></div>
|
|
38
|
+
|
|
39
|
+
<br>
|
|
40
|
+
|
|
41
|
+
<div align="center">
|
|
42
|
+
<a href="https://github.com/maysara-elshewehy"><img src="https://img.shields.io/badge/by-Maysara-black"/></a>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<!-- ╚════════════════════════════════════════════════════════════╝ -->
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use strict';var h=class s{constructor(e,t,r,n){this.span={start:-99,end:-99};this.status="unset";this.source=null;this.mode="unset";this.errors=[];this.status=e,this.source=t,this.mode=r,this.span=n;}static create(e,t,r,n){return new s(e,t,r,n)}static createAsToken(e,t,r){return s.create(e,{source_kind:"token-source",type:t?.type??"unset",text:t?.text??void 0,span:r},"token",r)}static createAsOptional(e,t,r){return s.create(e,{source_kind:"optional-source",result:t},"optional",r)}static createAsChoice(e,t,r,n){return s.create(e,{source_kind:"choice-source",atIndex:r,result:t},"choice",n)}static createAsRepeat(e,t,r,n=false){return s.create(e,{source_kind:"repeat-source",endsWithSep:n,result:t??[]},"repeat",r)}static createAsSequence(e,t,r){return s.create(e,{source_kind:"sequence-source",result:t??[]},"seq",r)}static createAsPratt(e,t,r){return s.create(e,{source_kind:"pratt-source",result:t},"pratt",r)}static createAsCustom(e,t,r,n){return s.create(e,{source_kind:"custom-source",name:t,data:r},"custom",n)}isPassed(){return this.status==="passed"}isFailed(){return this.status==="failed"}isUnset(){return this.status==="unset"}isToken(){return this.mode==="token"}isOptional(){return this.mode==="optional"}isChoice(){return this.mode==="choice"}isRepeat(){return this.mode==="repeat"}isSequence(){return this.mode==="seq"}isPratt(){return this.mode==="pratt"}isFullyPassed(){return !(!this.isPassed()||this.isOptional()&&!this.isOptionalPassed())}isOptionalPassed(){return this.isOptional()&&this.source.result!==null}isCustom(e){return this.mode!=="custom"?false:e?this.source.name===e:true}getTokenType(){return this.isToken()?this.source.type:void 0}getTokenSpan(){return this.isToken()?this.source.span:void 0}getOptionalResult(){return this.isOptionalPassed()?this.source.result:void 0}getChoiceIndex(){return this.isChoice()?this.source.atIndex:void 0}getChoiceResult(){return this.isChoice()?this.source.result:void 0}getRepeatCount(){return this.isRepeat()?this.source.result.length:void 0}getRepeatResult(){return this.isRepeat()?this.source.result:void 0}isRepeatEndsWithSep(){return this.isRepeat()?this.source.endsWithSep:void 0}getSequenceCount(){return this.isSequence()?this.source.result.length:void 0}getSequenceResult(){return this.isSequence()?this.source.result:void 0}getPrattResult(){return this.isPratt()?this.source.result:void 0}getCustomData(){return this.isCustom()?this.source.data:void 0}getCustomName(){return this.isCustom()?this.source.name:void 0}getTokenValue(){if(!this.isToken())return;let e=this.source.text;return e===void 0?null:e}getTokenData(){if(!this.isToken())return;let e=this.source;return {type:e.type,text:e.text,span:e.span}}clone(){let e=new s(this.status,this.source,this.mode,this.span);return e.errors=[...this.errors],e}hasErrors(){return this.errors.length>0}withError(e){return this.errors.push(e),this}};var p={LEXICAL_ERROR:"LEXICAL_ERROR",TOKEN_EXPECTED_EOF:"TOKEN_EXPECTED_EOF",TOKEN_MISMATCH:"TOKEN_MISMATCH",RULE_FAILED:"RULE_FAILED",BUILD_FUNCTION_FAILED:"BUILD_FUNCTION_FAILED",REPEAT_MIN_NOT_MET:"REPEAT_MIN_NOT_MET",SEQUENCE_FAILED:"SEQUENCE_FAILED",CUSTOM_ERROR:"CUSTOM_ERROR",CHOICE_ALL_FAILED:"CHOICE_ALL_FAILED",PRATT_NO_PREFIX:"PRATT_NO_PREFIX",FATAL_ERROR:"FATAL_ERROR",UNKNOWN_ERROR:"UNKNOWN_ERROR",RECOVERY_CUSTOM:"RECOVERY_CUSTOM"},E={};var d=h.create("failed",null,"unset",{start:-1,end:-1}),g=class{constructor(e,t){this.tokens=[];this.index=0;this.errors=[];this.ast=[];this.stats={tokensProcessed:0,rulesApplied:0,errorsRecovered:0,parseTimeMs:0};this._compiled=new Map;this._ruleIndex=new Map;this._laTable=new Map;this._memo=new Map;this._depth=0;this._silentDepth=0;this._rootStart=0;this._startTime=0;this._debugLevel="off";this._ignoredSet=new Set;this.lastHandledRule="unknown";this.lastVisitedIndex=0;this.ruleStack=[];this.rules=new Map(e.map(i=>[i.name,i])),this.settings=this._normalizeSettings(t),this._debugLevel=this.settings.debug,this._ignoredSet=new Set(this.settings.ignored);let r=0;for(let i of this.rules.keys())this._ruleIndex.set(i,r++);let n=this._validateGrammar();if(n.length)throw new Error(`Grammar validation failed:
|
|
2
|
+
${n.join(`
|
|
3
|
+
`)}`);this._buildLookaheadSets();for(let[i,o]of this.rules)this._compiled.set(i,this._compilePattern(o.pattern,o));}parse(e){if(this._reset(e),this._startTime=Date.now(),!e.length)return {ast:[],errors:[]};let t=e.find(a=>a.type==="error");if(t)return {ast:[],errors:[this._mkError(p.LEXICAL_ERROR,`Unexpected token '${t.text}'`,t.span,0,0,"lexer")]};let r=this.rules.get(this.settings.startRule);if(!r)throw new Error(`Start rule '${this.settings.startRule}' not found`);let n=this._compiled.get(this.settings.startRule),i=this.settings.errorRecovery.maxErrors;this.settings.errorRecovery.mode==="resilient";try{for(this._skipIgnored();this.index<this.tokens.length&&!(i>0&&this.errors.length>=i);){let a=this.index;this._rootStart=a;let u=n(!1);if(u.isPassed()){let l=r.options?.build?this._safeBuild(r.options.build,u):u;l&&this.ast.push(l);}if(this.index===a)break;this._skipIgnored();}}catch(a){this._handleFatal(a);}return this.stats.parseTimeMs=Date.now()-this._startTime,{ast:this.ast,errors:this.errors,statistics:this.stats}}dispose(){this._memo.clear(),this._compiled.clear(),this.tokens=[],this.ast=[],this.errors=[];}isNextToken(e,t){let r=new Set([...this._ignoredSet,...t??[]]);for(let n=this.index;n<this.tokens.length;n++){let i=this.tokens[n];if(i.type===e)return true;if(!r.has(i.type))break}return false}isPrevToken(e,t=-1,r){let n=new Set([...this._ignoredSet,...r??[]]),i=t<0?this.index:t;for(let o=i-1;o>=0;o--){let a=this.tokens[o];if(a.type===e)return true;if(!n.has(a.type))break}return false}isPrevRule(e){return this.lastHandledRule===e}_buildLookaheadSets(){for(let t of this.rules.keys())this._laTable.set(t,new Set);let e=true;for(;e;){e=false;for(let[t,r]of this.rules){let n=this._laTable.get(t),i=this._firstOfPattern(r.pattern);for(let o of i)n.has(o)||(n.add(o),e=true);}}}_firstOfPattern(e){let t=new Set;switch(e.type){case "token":t.add(e.name);break;case "rule":{let r=this._laTable.get(e.name);if(r)for(let n of r)t.add(n);break}case "seq":for(let r of e.patterns??[]){for(let n of this._firstOfPattern(r))t.add(n);if(r.type!=="optional")break}break;case "choice":for(let r of e.patterns??[])for(let n of this._firstOfPattern(r))t.add(n);break;case "optional":case "repeat":case "conditional":case "not":case "lookahead":if(e.pattern)for(let r of this._firstOfPattern(e.pattern))t.add(r);break;case "action":break;case "pratt":if(e.table)for(let r of e.table.prefix.keys())t.add(r);break}return t}_compilePattern(e,t){switch(e.type){case "token":return this._compileToken(e,t);case "rule":return this._compileRule(e,t);case "seq":return this._compileSeq(e,t);case "choice":return this._compileChoice(e,t);case "repeat":return this._compileRepeat(e,t);case "optional":return this._compileOptional(e,t);case "pratt":return this._compilePratt(e,t);case "conditional":return this._compileConditional(e,t);case "action":return this._compileAction(e,t);case "not":return this._compileNot(e,t);case "lookahead":return this._compileLookahead(e,t);default:throw new Error(`Unknown pattern type: ${e.type}`)}}_compileToken(e,t){let r=e.name,n=e.value;return i=>{if(this.lastHandledRule=t?.name??r,this.lastVisitedIndex=this.index,this.index>=this.tokens.length){if(i)return d;throw this._mkError(p.TOKEN_EXPECTED_EOF,`Expected '${r}', got EOF`,this._span(),0,this.index,this.lastHandledRule)}let o=this.tokens[this.index];if(o.type===r){if(n!==void 0&&o.text!==n){if(i)return d;throw this._mkError(p.TOKEN_MISMATCH,`Expected '${r}' with value '${n}', got '${o.text}'`,o.span,0,this.index,this.lastHandledRule)}return this.index++,this.stats.tokensProcessed++,h.createAsToken("passed",o,o.span)}if(i)return d;let a=this._mkError(p.TOKEN_MISMATCH,`Expected '${r}', got '${o.type}'`,o.span,0,this.index,this.lastHandledRule);throw this._customErrorOr(t,a)}}_compileRule(e,t){let r=e.name;return n=>{let i=this.rules.get(r);if(!i)throw new Error(`Rule '${r}' not found`);let o=this._compiled.get(r);if(!o)throw new Error(`Rule '${r}' not compiled`);this.ruleStack.push(r),this.stats.rulesApplied++;let u=(this._ruleIndex.get(r)??0)<<16|this.index,l=this._getMemo(u);if(l)return this.ruleStack.pop(),this.index=l.endIndex,l.result??d;let c=this.index,f=this.errors.length,m=o(n);if(!m.isFullyPassed()){if(this.index=c,this.ruleStack.pop(),n)return d;let _=this._mkError(p.RULE_FAILED,`Rule '${r}' failed`,this._span(),0,this.lastVisitedIndex,r);throw this._customErrorOr(t,_)}let R=m;if(i.options?.build){let _=this._safeBuild(i.options.build,m);_&&(R=_);}return this._setMemo(u,R,this.index,f),this.ruleStack.pop(),R}}_compileSeq(e,t){let r=(e.patterns??[]).map(n=>this._compilePattern(n,t));return n=>{let i=this.index,o=[];for(let a=0;a<r.length;a++){this._skipIgnored(t?.options?.ignored);let u=r[a](n);if(!u.isPassed()){if(this.index=i,n)return d;let l=this._mkError(p.SEQUENCE_FAILED,`Sequence failed at element ${a+1}/${r.length}`,this._span(),a,this.lastVisitedIndex,this.lastHandledRule);throw this._customErrorOr(t,l)}o.push(u);}return h.createAsSequence("passed",o,this._spanOf(o))}}_compileChoice(e,t){let r=e.patterns??[],n=r.map(o=>this._firstOfPattern(o)),i=r.map(o=>this._compilePattern(o,t));return o=>{let a=this.index;if(this._silentDepth++,this.index<this.tokens.length){let c=this.tokens[this.index].type,f=-1,m=false;for(let R=0;R<n.length;R++)if(n[R].has(c)){if(f>=0){m=true;break}f=R;}if(!m&&f>=0){this._silentDepth--;let R=i[f](o);if(R.isFullyPassed())return h.createAsChoice("passed",R,f,R.span);this.index=a,this._silentDepth++;}}let u=null;for(let c=0;c<i.length;c++){this.index=a;let f=i[c](true);if(f.isFullyPassed())return this._silentDepth--,h.createAsChoice("passed",f,c,f.span);let m=this.lastVisitedIndex-a;(!u||m>u.index-a)&&(u={index:this.lastVisitedIndex,err:null,altIdx:c});}if(this._silentDepth--,this.index=a,o)return d;let l=this._mkError(p.CHOICE_ALL_FAILED,`Expected one of: ${r.map(c=>this._patStr(c)).join(" | ")}`,this._span(),u?.altIdx??0,this.lastVisitedIndex,this.lastHandledRule);throw this._customErrorOr(t,l)}}_compileOptional(e,t){let r=this._compilePattern(e.pattern,t);return n=>{let i=this.index;this._silentDepth++;let o=r(true);return this._silentDepth--,o.isFullyPassed()?h.createAsOptional("passed",o,o.span):(this.index=i,h.createAsOptional("passed",null,this._span()))}}_compileRepeat(e,t){let r=e.min??0,n=e.max??1/0,i=this._compilePattern(e.pattern,t),o=e.separator?this._compilePattern(e.separator,t):null;return a=>{let u=[],l=false,c=this.index;for(;u.length<n&&this.index<this.tokens.length;){let f=this.index;this._silentDepth++;let m=i(true);if(this._silentDepth--,!m.isFullyPassed()){this.index=f,l=false;break}if(u.push(m),l=false,this.index===f)break;if(o&&u.length<n&&this.index<this.tokens.length){let R=this.index;this._silentDepth++;let _=o(true);if(this._silentDepth--,!_.isFullyPassed()){this.index=R;break}l=true;}}if(u.length<r){if(this.index=c,a)return d;throw this._mkError(p.REPEAT_MIN_NOT_MET,`Expected at least ${r} occurrences, got ${u.length}`,this._span(),0,this.index,this.lastHandledRule)}return h.createAsRepeat("passed",u,this._spanOf(u),l)}}_compilePratt(e,t){let r=e.table;return n=>{if(this.index>=this.tokens.length){if(n)return d;throw this._mkError(p.PRATT_NO_PREFIX,"Expected an expression",this._span(),0,this.index,"pratt")}let i=this.tokens[this.index],o=r.prefix.get(i.type);if(!o){if(n)return d;throw this._mkError(p.PRATT_NO_PREFIX,`Unexpected token '${i.type}' in expression`,i.span,0,this.index,"pratt")}this.index++;let a=o.parse(this,i);if(!a.isPassed())return n?d:a;for(;this.index<this.tokens.length&&(this._skipIgnored(),!(this.index>=this.tokens.length));){let u=this.tokens[this.index],l=r.infix.get(u.type);if(!l||l.lbp<=0||(this.index++,a=l.parse(this,a,u),!a.isPassed()))break}return a}}_compileConditional(e,t){let r=this._compilePattern(e.pattern,t),n=e.predicate;return i=>{let o=this.index,a;if(i?(this._silentDepth++,a=r(true),this._silentDepth--):a=r(false),!a.isFullyPassed())return this.index=o,i?d:a;try{let u={parser:this,result:a,index:this.index,depth:this._depth,ruleStack:[...this.ruleStack]};if(n(u))return h.createAsCustom("passed","conditional",a,a.span);{if(this.index=o,i)return d;let c=this._mkError(p.RULE_FAILED,"Conditional predicate returned false",this._span(),0,this.lastVisitedIndex,this.lastHandledRule);throw this._customErrorOr(t,c)}}catch(u){if(this.index=o,i)return d;let l=u instanceof Error?u.message:String(u),c=this._mkError(p.RULE_FAILED,`Conditional predicate threw: ${l}`,this._span(),0,this.lastVisitedIndex,this.lastHandledRule);throw this._customErrorOr(t,c)}}}_compileAction(e,t){let r=e.fn;return n=>{try{r(this);}catch(i){let o=i instanceof Error?i.message:String(i);throw this._mkError(p.RULE_FAILED,`Action function threw: ${o}`,this._span(),0,this.index,"action")}return h.createAsCustom("passed","action",null,this._span())}}_compileNot(e,t){let r=this._compilePattern(e.pattern,t);return n=>{let i=this.index;this._silentDepth++;let o=r(true);if(this._silentDepth--,o.isFullyPassed()){if(this.index=i,n)return d;let a=this._mkError(p.RULE_FAILED,"NOT pattern failed - inner pattern matched",this._span(),0,i,this.lastHandledRule);throw this._customErrorOr(t,a)}else return this.index=i,h.createAsCustom("passed","not",null,this._span())}}_compileLookahead(e,t){let r=this._compilePattern(e.pattern,t);return n=>{let i=this.index;this._silentDepth++;let o=r(true);if(this._silentDepth--,this.index=i,o.isFullyPassed())return h.createAsCustom("passed","lookahead",null,this._span());{if(n)return d;let a=this._mkError(p.RULE_FAILED,"Lookahead pattern failed",this._span(),0,i,this.lastHandledRule);throw this._customErrorOr(t,a)}}}_getMemo(e){let t=this._memo.get(e);return t?t.errorCount!==this.errors.length?(this._memo.delete(e),null):t:null}_setMemo(e,t,r,n){this._memo.set(e,{result:t,endIndex:r,errorCount:this.errors.length});}_mkError(e,t,r,n,i,o,a){return {code:e,msg:t,span:r,failedAt:n,tokenIndex:i,startIndex:this._rootStart,prevRule:o,prevInnerRule:a??this.ruleStack.at(-1)??"unknown"}}_customErrorOr(e,t){if(!e?.options?.errors)return t;for(let r of e.options.errors){let n=false;if(typeof r.cond=="number")n=t.failedAt===r.cond;else if(typeof r.cond=="function")try{n=r.cond(this,{failedAt:t.failedAt,tokenIndex:t.tokenIndex});}catch{}if(n)return this._mkError(r.code??p.CUSTOM_ERROR,r.msg,t.span,t.failedAt,t.tokenIndex,t.prevRule,t.prevInnerRule)}return t}_addError(e){if(this._silentDepth>0)return;let t=this.settings.errorRecovery.maxErrors;t>0&&this.errors.length>=t||this.settings.errorRecovery.mode==="strict"&&this.errors.length>0||this.errors.some(r=>r.span?.start===e.span?.start)||this.errors.push(e);}_handleFatal(e){e&&typeof e=="object"&&"msg"in e&&"code"in e?this._addError(e):e instanceof Error&&this._addError(this._mkError(p.FATAL_ERROR,e.message,this._span(),0,this.index,this.lastHandledRule));}_safeBuild(e,t){try{return e(t,this)}catch(r){let n=this._mkError(p.BUILD_FUNCTION_FAILED,r instanceof Error?r.message:String(r),this._span(),0,this.index,this.lastHandledRule);return this._addError(n),t}}_span(){if(!this.tokens.length)return {start:0,end:0};if(this.index>=this.tokens.length){let e=this.tokens[this.tokens.length-1];return {start:e.span.end,end:e.span.end}}return this.tokens[this.index].span}_spanOf(e){return e.length?{start:e[0].span.start,end:e[e.length-1].span.end}:this._span()}_skipIgnored(e){let t=e?new Set([...this._ignoredSet,...e]):this._ignoredSet;for(;this.index<this.tokens.length&&t.has(this.tokens[this.index].type);)this.index++,this.stats.tokensProcessed++;}_patStr(e){switch(e.type){case "token":return e.name;case "rule":return e.name;case "seq":return `(${(e.patterns??[]).map(t=>this._patStr(t)).join(" ")})`;case "choice":return (e.patterns??[]).map(t=>this._patStr(t)).join(" | ");case "optional":return `${this._patStr(e.pattern)}?`;case "repeat":return `${this._patStr(e.pattern)}*`;case "conditional":return `${this._patStr(e.pattern)}.if(...)`;case "action":return "action(...)";case "not":return `!${this._patStr(e.pattern)}`;case "lookahead":return `lookahead(${this._patStr(e.pattern)})`;case "pratt":return "expr";default:return e.type}}_validateGrammar(){let e=[],t=new Set(this.rules.keys()),r=(n,i)=>{n.type==="rule"&&!t.has(n.name)&&e.push(`Rule '${i}' references undefined rule '${n.name}'`);for(let o of [n.pattern,...n.patterns??[]])o&&r(o,i);n.separator&&r(n.separator,i);};for(let[n,i]of this.rules)r(i.pattern,n);return this.rules.has(this.settings.startRule)||e.push(`Start rule '${this.settings.startRule}' is not defined`),e}_normalizeSettings(e){return {startRule:e?.startRule??"root",errorRecovery:{mode:e?.errorRecovery?.mode??"strict",maxErrors:e?.errorRecovery?.maxErrors??1},ignored:e?.ignored??["ws"],debug:e?.debug??"off",maxDepth:e?.maxDepth??1e3}}_reset(e){this.tokens=e,this.index=0,this.errors=[],this.ast=[],this._depth=0,this._silentDepth=0,this._rootStart=0,this.ruleStack=[],this._memo.clear(),this.stats={tokensProcessed:0,rulesApplied:0,errorsRecovered:0,parseTimeMs:0};}};function w(s){Object.assign(E,s);}function F(s,e,t){let r=new g(e,t);try{return r.parse(s)}finally{r.dispose();}}function L(s,e,t={}){return {name:s,pattern:e,options:{name:false,...t}}}function M(s,e){if(!s)throw new Error("token(): name must be a non-empty string");return {type:"token",name:s,value:e,silent:false}}function k(s){return {type:"optional",pattern:s,silent:false}}function D(...s){if(!s.length)throw new Error("choice(): at least one pattern required");return {type:"choice",patterns:s,silent:false}}function T(s,e=0,t=1/0,r){if(e<0)throw new Error("repeat(): min cannot be negative");if(t<e)throw new Error("repeat(): max cannot be less than min");return {type:"repeat",pattern:s,min:e,max:t,separator:r,silent:false}}function H(s,e){return T(s,1,1/0,e)}function x(s,e){return T(s,0,1/0,e)}function N(s,e){return O(T(s,0,1,e))}function y(...s){if(!s.length)throw new Error("seq(): at least one pattern required");return {type:"seq",patterns:s,silent:false}}function U(s,e){if(!s)throw new Error("rule(): name must be a non-empty string");return {type:"rule",name:s,params:e,silent:false}}function O(s){return {...s,silent:true}}function $(s){return {...s,silent:false}}function q(s){return {type:"pratt",table:s,silent:false}}function S(s,e){return {type:"conditional",pattern:s,predicate:e,silent:false}}function V(s,e){return S(s,e)}function B(s,e){return S(s,e)}function K(s){return {type:"action",fn:s,silent:false}}function X(s){return {type:"not",pattern:s,silent:false}}function I(s){return {type:"lookahead",pattern:s,silent:false}}function j(s){return I(s)}function W(s){return {prefix:new Map(Object.entries(s.prefix??{})),infix:new Map(Object.entries(s.infix??{}))}}function z(s,e,t){let{min:r=0,trailingOk:n=false}=t??{},i;return r===0?n?i=k(y(T(s,1,1/0),x(y(e,s)),k(e))):i=k(y(T(s,1,1/0),x(y(e,s)))):r===1?n?i=y(T(s,1,1/0),x(y(e,s)),k(e)):i=y(T(s,1,1/0),x(y(e,s))):n?i=y(T(s,r,1/0),x(y(e,s)),k(e)):i=y(T(s,r,1/0),x(y(e,s))),i}function G(s,e,t){return y(e,s,t)}function Q(s,e,t){return y(s,e,t)}function Y(s,e,t){return {cond:s,msg:e,code:t??p.RECOVERY_CUSTOM}}var J={skipUntil(s){return {type:"skipUntil",tokens:Array.isArray(s)?s:[s]}}};exports.ERRORS=p;exports.Parser=g;exports.Result=h;exports.action=K;exports.between=Q;exports.buildPrattTable=W;exports.choice=D;exports.conditional=S;exports.createRule=L;exports.delimited=z;exports.error=Y;exports.errorRecoveryStrategies=J;exports.globalTokenMap=E;exports.ifCondition=B;exports.lookahead=I;exports.loud=$;exports.not=X;exports.oneOrMore=H;exports.optional=k;exports.parse=F;exports.peek=j;exports.pratt=q;exports.registerTokenMap=w;exports.repeat=T;exports.rule=U;exports.seq=y;exports.silent=O;exports.surrounded=G;exports.token=M;exports.when=V;exports.zeroOrMore=x;exports.zeroOrOne=N;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import { Span, Token } from '@minelang-ts/lexer';
|
|
2
|
+
export { Span, Token } from '@minelang-ts/lexer';
|
|
3
|
+
|
|
4
|
+
type ResultStatus = 'unset' | 'failed' | 'passed';
|
|
5
|
+
type ResultMode = 'unset' | 'token' | 'optional' | 'choice' | 'repeat' | 'seq' | 'pratt' | 'custom';
|
|
6
|
+
interface TokenSource {
|
|
7
|
+
source_kind: 'token-source';
|
|
8
|
+
type: string;
|
|
9
|
+
text?: string;
|
|
10
|
+
span?: Span;
|
|
11
|
+
}
|
|
12
|
+
interface OptionalSource {
|
|
13
|
+
source_kind: 'optional-source';
|
|
14
|
+
result: Result | null;
|
|
15
|
+
}
|
|
16
|
+
interface ChoiceSource {
|
|
17
|
+
source_kind: 'choice-source';
|
|
18
|
+
atIndex: number;
|
|
19
|
+
result: Result | null;
|
|
20
|
+
}
|
|
21
|
+
interface RepeatSource {
|
|
22
|
+
source_kind: 'repeat-source';
|
|
23
|
+
endsWithSep: boolean;
|
|
24
|
+
result: Result[];
|
|
25
|
+
}
|
|
26
|
+
interface SequenceSource {
|
|
27
|
+
source_kind: 'sequence-source';
|
|
28
|
+
result: Result[];
|
|
29
|
+
}
|
|
30
|
+
interface PrattSource {
|
|
31
|
+
source_kind: 'pratt-source';
|
|
32
|
+
result: Result[];
|
|
33
|
+
}
|
|
34
|
+
interface CustomSource {
|
|
35
|
+
source_kind: 'custom-source';
|
|
36
|
+
name: string;
|
|
37
|
+
data: unknown;
|
|
38
|
+
}
|
|
39
|
+
type ResultSource = TokenSource | OptionalSource | ChoiceSource | RepeatSource | SequenceSource | PrattSource | CustomSource | null;
|
|
40
|
+
declare class Result {
|
|
41
|
+
span: Span;
|
|
42
|
+
status: ResultStatus;
|
|
43
|
+
source: ResultSource;
|
|
44
|
+
mode: ResultMode;
|
|
45
|
+
errors: ParseError[];
|
|
46
|
+
constructor(status: ResultStatus, source: ResultSource | null, mode: ResultMode, span: Span);
|
|
47
|
+
static create(status: ResultStatus, source: ResultSource | null, mode: ResultMode, span: Span): Result;
|
|
48
|
+
static createAsToken(status: ResultStatus, source: Token | null, span: Span): Result;
|
|
49
|
+
static createAsOptional(status: ResultStatus, source: Result | null, span: Span): Result;
|
|
50
|
+
static createAsChoice(status: ResultStatus, source: Result | null, index: number, span: Span): Result;
|
|
51
|
+
static createAsRepeat(status: ResultStatus, source: Result[] | null, span: Span, endsWithSep?: boolean): Result;
|
|
52
|
+
static createAsSequence(status: ResultStatus, source: Result[] | null, span: Span): Result;
|
|
53
|
+
static createAsPratt(status: ResultStatus, source: Result[], span: Span): Result;
|
|
54
|
+
static createAsCustom(status: ResultStatus, name: string, data: unknown, span: Span): Result;
|
|
55
|
+
isPassed(): boolean;
|
|
56
|
+
isFailed(): boolean;
|
|
57
|
+
isUnset(): boolean;
|
|
58
|
+
isToken(): boolean;
|
|
59
|
+
isOptional(): boolean;
|
|
60
|
+
isChoice(): boolean;
|
|
61
|
+
isRepeat(): boolean;
|
|
62
|
+
isSequence(): boolean;
|
|
63
|
+
isPratt(): boolean;
|
|
64
|
+
isFullyPassed(): boolean;
|
|
65
|
+
isOptionalPassed(): boolean;
|
|
66
|
+
isCustom(tag?: string): boolean;
|
|
67
|
+
getTokenType(): string | undefined;
|
|
68
|
+
getTokenSpan(): Span | undefined;
|
|
69
|
+
getOptionalResult(): Result | null | undefined;
|
|
70
|
+
getChoiceIndex(): number | undefined;
|
|
71
|
+
getChoiceResult(): Result | null | undefined;
|
|
72
|
+
getRepeatCount(): number | undefined;
|
|
73
|
+
getRepeatResult(): Result[] | undefined;
|
|
74
|
+
isRepeatEndsWithSep(): boolean | undefined;
|
|
75
|
+
getSequenceCount(): number | undefined;
|
|
76
|
+
getSequenceResult(): Result[] | undefined;
|
|
77
|
+
getPrattResult(): Result[] | undefined;
|
|
78
|
+
getCustomData(): unknown | undefined;
|
|
79
|
+
getCustomName(): string | undefined;
|
|
80
|
+
getTokenValue(): string | null | undefined;
|
|
81
|
+
getTokenData(): Token | undefined;
|
|
82
|
+
clone(): Result;
|
|
83
|
+
hasErrors(): boolean;
|
|
84
|
+
withError(e: ParseError): Result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare class Parser {
|
|
88
|
+
rules: Map<string, Rule>;
|
|
89
|
+
settings: ParserSettings;
|
|
90
|
+
tokens: Token[];
|
|
91
|
+
index: number;
|
|
92
|
+
errors: ParseError[];
|
|
93
|
+
ast: Result[];
|
|
94
|
+
stats: ParseStatistics;
|
|
95
|
+
private _compiled;
|
|
96
|
+
private _ruleIndex;
|
|
97
|
+
private _laTable;
|
|
98
|
+
private _memo;
|
|
99
|
+
private _depth;
|
|
100
|
+
private _silentDepth;
|
|
101
|
+
private _rootStart;
|
|
102
|
+
private _startTime;
|
|
103
|
+
private _debugLevel;
|
|
104
|
+
private _ignoredSet;
|
|
105
|
+
lastHandledRule: string;
|
|
106
|
+
lastVisitedIndex: number;
|
|
107
|
+
ruleStack: string[];
|
|
108
|
+
constructor(rules: Rule[], settings?: ParserSettings);
|
|
109
|
+
parse(tokens: Token[]): ParseResult;
|
|
110
|
+
dispose(): void;
|
|
111
|
+
isNextToken(kind: string, extra?: string[]): boolean;
|
|
112
|
+
isPrevToken(kind: string, from?: number, extra?: string[]): boolean;
|
|
113
|
+
isPrevRule(name: string): boolean;
|
|
114
|
+
private _buildLookaheadSets;
|
|
115
|
+
private _firstOfPattern;
|
|
116
|
+
private _compilePattern;
|
|
117
|
+
private _compileToken;
|
|
118
|
+
private _compileRule;
|
|
119
|
+
private _compileSeq;
|
|
120
|
+
private _compileChoice;
|
|
121
|
+
private _compileOptional;
|
|
122
|
+
private _compileRepeat;
|
|
123
|
+
private _compilePratt;
|
|
124
|
+
private _compileConditional;
|
|
125
|
+
private _compileAction;
|
|
126
|
+
private _compileNot;
|
|
127
|
+
private _compileLookahead;
|
|
128
|
+
private _getMemo;
|
|
129
|
+
private _setMemo;
|
|
130
|
+
private _mkError;
|
|
131
|
+
private _customErrorOr;
|
|
132
|
+
private _addError;
|
|
133
|
+
private _handleFatal;
|
|
134
|
+
private _safeBuild;
|
|
135
|
+
private _span;
|
|
136
|
+
private _spanOf;
|
|
137
|
+
private _skipIgnored;
|
|
138
|
+
private _patStr;
|
|
139
|
+
private _validateGrammar;
|
|
140
|
+
private _normalizeSettings;
|
|
141
|
+
private _reset;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface MiniToken {
|
|
145
|
+
kind: string;
|
|
146
|
+
value: string | null;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Conditional context passed to predicate function.
|
|
150
|
+
* Provides access to parser state for conditional decision-making.
|
|
151
|
+
*/
|
|
152
|
+
interface ConditionalContext {
|
|
153
|
+
parser: Parser;
|
|
154
|
+
result: Result | null;
|
|
155
|
+
index: number;
|
|
156
|
+
depth: number;
|
|
157
|
+
ruleStack: string[];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Predicate function for conditional pattern execution.
|
|
161
|
+
* Return true to continue with the pattern, false to fail.
|
|
162
|
+
*/
|
|
163
|
+
type ConditionalPredicate = (context: ConditionalContext) => boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Action function executed for side effects during parsing.
|
|
166
|
+
* Called during pattern execution, can access and modify parser state.
|
|
167
|
+
*/
|
|
168
|
+
type ActionFunction = (parser: Parser) => void;
|
|
169
|
+
interface Pattern {
|
|
170
|
+
type: 'token' | 'rule' | 'repeat' | 'choice' | 'seq' | 'optional' | 'pratt' | 'conditional' | 'action' | 'not' | 'lookahead';
|
|
171
|
+
silent: boolean;
|
|
172
|
+
value?: string;
|
|
173
|
+
name?: string;
|
|
174
|
+
min?: number;
|
|
175
|
+
max?: number;
|
|
176
|
+
patterns?: Pattern[];
|
|
177
|
+
separator?: Pattern;
|
|
178
|
+
pattern?: Pattern;
|
|
179
|
+
table?: PrattTable;
|
|
180
|
+
predicate?: ConditionalPredicate;
|
|
181
|
+
fn?: ActionFunction;
|
|
182
|
+
params?: Record<string, any>;
|
|
183
|
+
}
|
|
184
|
+
interface PrefixHandler {
|
|
185
|
+
/** How tightly this prefix binds its right operand. */
|
|
186
|
+
bp: number;
|
|
187
|
+
parse: (parser: Parser, token: Token) => Result;
|
|
188
|
+
}
|
|
189
|
+
interface InfixHandler {
|
|
190
|
+
/** Precedence level - left binding power. */
|
|
191
|
+
lbp: number;
|
|
192
|
+
/** Right binding power (lbp - 1 for right-associative). Defaults to lbp. */
|
|
193
|
+
rbp?: number;
|
|
194
|
+
parse: (parser: Parser, left: Result, token: Token) => Result;
|
|
195
|
+
}
|
|
196
|
+
interface PrattTable {
|
|
197
|
+
prefix: Map<string, PrefixHandler>;
|
|
198
|
+
infix: Map<string, InfixHandler>;
|
|
199
|
+
}
|
|
200
|
+
interface ErrorHandler {
|
|
201
|
+
cond: number | ((parser: Parser, opt: {
|
|
202
|
+
failedAt: number;
|
|
203
|
+
tokenIndex: number;
|
|
204
|
+
force?: boolean;
|
|
205
|
+
prevRule?: string;
|
|
206
|
+
prevInnerRule?: string;
|
|
207
|
+
}) => boolean);
|
|
208
|
+
msg: string;
|
|
209
|
+
code?: string;
|
|
210
|
+
}
|
|
211
|
+
interface RecoveryStrategy {
|
|
212
|
+
type: 'skipUntil' | 'synchronize';
|
|
213
|
+
tokens?: string[];
|
|
214
|
+
token?: string;
|
|
215
|
+
}
|
|
216
|
+
type BuildFunction = (matches: Result, parser: Parser) => Result;
|
|
217
|
+
interface Rule {
|
|
218
|
+
name: string;
|
|
219
|
+
pattern: Pattern;
|
|
220
|
+
options?: {
|
|
221
|
+
build?: BuildFunction;
|
|
222
|
+
errors?: ErrorHandler[];
|
|
223
|
+
recovery?: RecoveryStrategy;
|
|
224
|
+
ignored?: string[];
|
|
225
|
+
silent?: boolean;
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
type Rules = Rule[];
|
|
229
|
+
interface ParseStatistics {
|
|
230
|
+
tokensProcessed: number;
|
|
231
|
+
rulesApplied: number;
|
|
232
|
+
errorsRecovered: number;
|
|
233
|
+
parseTimeMs: number;
|
|
234
|
+
}
|
|
235
|
+
interface ParseError {
|
|
236
|
+
msg: string;
|
|
237
|
+
code: string;
|
|
238
|
+
span: Span;
|
|
239
|
+
failedAt: number;
|
|
240
|
+
tokenIndex: number;
|
|
241
|
+
startIndex: number;
|
|
242
|
+
prevRule: string;
|
|
243
|
+
prevInnerRule?: string;
|
|
244
|
+
}
|
|
245
|
+
interface ParseResult {
|
|
246
|
+
ast: Result[];
|
|
247
|
+
errors: ParseError[];
|
|
248
|
+
statistics?: ParseStatistics;
|
|
249
|
+
}
|
|
250
|
+
type DebugLevel = 'off' | 'errors' | 'rules' | 'patterns' | 'tokens' | 'verbose';
|
|
251
|
+
interface ParserSettings {
|
|
252
|
+
startRule: string;
|
|
253
|
+
errorRecovery?: {
|
|
254
|
+
mode?: 'strict' | 'resilient';
|
|
255
|
+
maxErrors?: number;
|
|
256
|
+
};
|
|
257
|
+
ignored?: string[];
|
|
258
|
+
debug?: DebugLevel;
|
|
259
|
+
maxDepth?: number;
|
|
260
|
+
}
|
|
261
|
+
declare const ERRORS: {
|
|
262
|
+
readonly LEXICAL_ERROR: "LEXICAL_ERROR";
|
|
263
|
+
readonly TOKEN_EXPECTED_EOF: "TOKEN_EXPECTED_EOF";
|
|
264
|
+
readonly TOKEN_MISMATCH: "TOKEN_MISMATCH";
|
|
265
|
+
readonly RULE_FAILED: "RULE_FAILED";
|
|
266
|
+
readonly BUILD_FUNCTION_FAILED: "BUILD_FUNCTION_FAILED";
|
|
267
|
+
readonly REPEAT_MIN_NOT_MET: "REPEAT_MIN_NOT_MET";
|
|
268
|
+
readonly SEQUENCE_FAILED: "SEQUENCE_FAILED";
|
|
269
|
+
readonly CUSTOM_ERROR: "CUSTOM_ERROR";
|
|
270
|
+
readonly CHOICE_ALL_FAILED: "CHOICE_ALL_FAILED";
|
|
271
|
+
readonly PRATT_NO_PREFIX: "PRATT_NO_PREFIX";
|
|
272
|
+
readonly FATAL_ERROR: "FATAL_ERROR";
|
|
273
|
+
readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
|
|
274
|
+
readonly RECOVERY_CUSTOM: "RECOVERY_CUSTOM";
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* Global registry mapping string literals to token kinds.
|
|
278
|
+
* Example: {'let': 'LET', 'if': 'IF', '{': 'LBRACE'}
|
|
279
|
+
* When registered, string patterns like seq('let', 'IDENT') will work.
|
|
280
|
+
*/
|
|
281
|
+
declare const globalTokenMap: Record<string, string>;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Register a global token map for string shorthand patterns.
|
|
285
|
+
* After registration, you can use 'let', 'if', etc. directly in patterns.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* registerTokenMap({
|
|
289
|
+
* 'let': 'LET',
|
|
290
|
+
* 'if': 'IF',
|
|
291
|
+
* '{': 'LBRACE',
|
|
292
|
+
* '}': 'RBRACE',
|
|
293
|
+
* '=': 'EQ',
|
|
294
|
+
* });
|
|
295
|
+
*
|
|
296
|
+
* // Now you can write:
|
|
297
|
+
* seq('let', 'IDENT', '=', rule('expr'))
|
|
298
|
+
*/
|
|
299
|
+
declare function registerTokenMap(map: Record<string, string>): void;
|
|
300
|
+
declare function parse(tokens: Token[], rules: Rules, settings?: ParserSettings): ParseResult;
|
|
301
|
+
declare function createRule(name: string, pattern: Pattern, options?: Rule['options']): Rule;
|
|
302
|
+
declare function token(name: string, value?: string): Pattern;
|
|
303
|
+
declare function optional(pattern: Pattern): Pattern;
|
|
304
|
+
declare function choice(...patterns: Pattern[]): Pattern;
|
|
305
|
+
declare function repeat(pattern: Pattern, min?: number, max?: number, separator?: Pattern): Pattern;
|
|
306
|
+
declare function oneOrMore(pattern: Pattern, separator?: Pattern): Pattern;
|
|
307
|
+
declare function zeroOrMore(pattern: Pattern, separator?: Pattern): Pattern;
|
|
308
|
+
declare function zeroOrOne(pattern: Pattern, separator?: Pattern): Pattern;
|
|
309
|
+
declare function seq(...patterns: Pattern[]): Pattern;
|
|
310
|
+
declare function rule(name: string): Pattern;
|
|
311
|
+
declare function rule(name: string, params: Record<string, unknown>): Pattern;
|
|
312
|
+
declare function silent<T extends Pattern>(pattern: T): T;
|
|
313
|
+
declare function loud<T extends Pattern>(pattern: T): T;
|
|
314
|
+
/**
|
|
315
|
+
* Build a Pratt expression parser inline in a rule.
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* const expr = pratt({
|
|
319
|
+
* prefix: new Map([
|
|
320
|
+
* ['NUM', { bp: 0, parse: (_, tok) => Result.createAsToken('passed', tok, tok.span) }],
|
|
321
|
+
* ['MINUS', { bp: 70, parse: (p, tok) => {
|
|
322
|
+
* const right = p.parse(...) // handled via sub-rule
|
|
323
|
+
* return Result.createAsPratt('passed', [right], right.span)
|
|
324
|
+
* }}],
|
|
325
|
+
* ]),
|
|
326
|
+
* infix: new Map([
|
|
327
|
+
* ['PLUS', { lbp: 50, parse: (_, left, tok) => ... }],
|
|
328
|
+
* ['STAR', { lbp: 60, parse: (_, left, tok) => ... }],
|
|
329
|
+
* ['STAR2', { lbp: 70, rbp: 69, parse: ... }], // right-associative
|
|
330
|
+
* ]),
|
|
331
|
+
* });
|
|
332
|
+
*/
|
|
333
|
+
declare function pratt(table: PrattTable): Pattern;
|
|
334
|
+
/**
|
|
335
|
+
* Adds conditional execution to a pattern.
|
|
336
|
+
* The predicate receives parser context and decides whether to continue.
|
|
337
|
+
*
|
|
338
|
+
* @param pattern - The pattern to conditionally execute
|
|
339
|
+
* @param predicate - Function that receives parser context and returns true to continue
|
|
340
|
+
* @returns Conditional pattern that evaluates predicate after inner pattern matches
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* // Only match if we're not too deep in the parse tree
|
|
344
|
+
* pattern.if(ctx => ctx.depth < 10)
|
|
345
|
+
*
|
|
346
|
+
* // Only match if next token after match is LBRACE
|
|
347
|
+
* token('IF').if(ctx => ctx.parser.isNextToken('LBRACE'))
|
|
348
|
+
*
|
|
349
|
+
* // Inspect full parser state
|
|
350
|
+
* rule('expr').if(ctx => {
|
|
351
|
+
* return ctx.parser.errors.length === 0 && ctx.ruleStack.length < 5
|
|
352
|
+
* })
|
|
353
|
+
*/
|
|
354
|
+
declare function conditional(pattern: Pattern, predicate: ConditionalPredicate): Pattern;
|
|
355
|
+
/**
|
|
356
|
+
* Shorthand for conditional() - more natural syntax
|
|
357
|
+
* @see conditional
|
|
358
|
+
*/
|
|
359
|
+
declare function when(pattern: Pattern, predicate: ConditionalPredicate): Pattern;
|
|
360
|
+
/**
|
|
361
|
+
* Method-chaining style: pattern.if(predicate)
|
|
362
|
+
* Extends Pattern type with .if() method for fluent API
|
|
363
|
+
*
|
|
364
|
+
* @note This is implemented as a utility function, not directly on Pattern
|
|
365
|
+
* Use: conditional(pattern, predicate) or when(pattern, predicate)
|
|
366
|
+
*/
|
|
367
|
+
declare function ifCondition(pattern: Pattern, predicate: ConditionalPredicate): Pattern;
|
|
368
|
+
/**
|
|
369
|
+
* Action pattern - executes a side effect during parsing.
|
|
370
|
+
* Useful for scope management, state tracking, and stateful parsing.
|
|
371
|
+
*
|
|
372
|
+
* @param fn - Function called during execution with access to parser state
|
|
373
|
+
* @returns Action pattern that always succeeds
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* // Track parse state
|
|
377
|
+
* seq(
|
|
378
|
+
* token('LBRACE'),
|
|
379
|
+
* action(p => p.symbolTable?.pushScope()),
|
|
380
|
+
* zeroOrMore(rule('stmt')),
|
|
381
|
+
* token('RBRACE'),
|
|
382
|
+
* action(p => p.symbolTable?.popScope())
|
|
383
|
+
* )
|
|
384
|
+
*
|
|
385
|
+
* // Count custom metric
|
|
386
|
+
* action(p => p.stats.customCounter = (p.stats.customCounter ?? 0) + 1)
|
|
387
|
+
*/
|
|
388
|
+
declare function action(fn: ActionFunction): Pattern;
|
|
389
|
+
/**
|
|
390
|
+
* NOT pattern - succeeds if the inner pattern FAILS.
|
|
391
|
+
* Useful for negative lookahead and validation checks.
|
|
392
|
+
*
|
|
393
|
+
* @param pattern - The pattern to negate
|
|
394
|
+
* @returns NOT pattern
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* // Match any character except EOF
|
|
398
|
+
* not(token('EOF'))
|
|
399
|
+
*
|
|
400
|
+
* // In a choice: match IDENT that is not a keyword
|
|
401
|
+
* choice(
|
|
402
|
+
* not(choice(token('IF'), token('WHILE'), token('FOR'))),
|
|
403
|
+
* token('IDENT')
|
|
404
|
+
* )
|
|
405
|
+
*/
|
|
406
|
+
declare function not(pattern: Pattern): Pattern;
|
|
407
|
+
/**
|
|
408
|
+
* Lookahead pattern - checks if inner pattern would match WITHOUT consuming tokens.
|
|
409
|
+
* Common alias: peek()
|
|
410
|
+
*
|
|
411
|
+
* @param pattern - The pattern to lookahead check
|
|
412
|
+
* @returns Lookahead pattern
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* // Only match expr if followed by RPAREN
|
|
416
|
+
* seq(
|
|
417
|
+
* lookahead(seq(rule('expr'), token('RPAREN'))),
|
|
418
|
+
* rule('expr')
|
|
419
|
+
* )
|
|
420
|
+
*
|
|
421
|
+
* // Safe token inspection
|
|
422
|
+
* seq(
|
|
423
|
+
* token('IDENT'),
|
|
424
|
+
* when(lookahead(token('LPAREN')), ctx => ctx.parser.isNextToken('LPAREN'))
|
|
425
|
+
* )
|
|
426
|
+
*/
|
|
427
|
+
declare function lookahead(pattern: Pattern): Pattern;
|
|
428
|
+
/**
|
|
429
|
+
* Alias for lookahead() - more concise syntax.
|
|
430
|
+
* @see lookahead
|
|
431
|
+
*/
|
|
432
|
+
declare function peek(pattern: Pattern): Pattern;
|
|
433
|
+
/** Convenience: build a PrattTable from plain objects. */
|
|
434
|
+
declare function buildPrattTable(spec: {
|
|
435
|
+
prefix?: Record<string, PrefixHandler>;
|
|
436
|
+
infix?: Record<string, InfixHandler>;
|
|
437
|
+
}): PrattTable;
|
|
438
|
+
/**
|
|
439
|
+
* Delimited list pattern - matches items separated by a delimiter.
|
|
440
|
+
* Commonly used for comma-separated values, arguments, etc.
|
|
441
|
+
*
|
|
442
|
+
* @param item - The pattern for each list item
|
|
443
|
+
* @param sep - The separator pattern (usually token(','))
|
|
444
|
+
* @param options - {min?: number, trailingOk?: boolean}
|
|
445
|
+
* @returns Composed pattern for delimited list
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* // Comma-separated identifiers (0+)
|
|
449
|
+
* delimited(token('IDENT'), token('COMMA'))
|
|
450
|
+
*
|
|
451
|
+
* // Function arguments (1+ with optional trailing)
|
|
452
|
+
* delimited(rule('expr'), token('COMMA'), {min: 1, trailingOk: true})
|
|
453
|
+
*
|
|
454
|
+
* // Array elements
|
|
455
|
+
* between('[', delimited(rule('expr'), token('COMMA')), ']')
|
|
456
|
+
*/
|
|
457
|
+
declare function delimited(item: Pattern, sep: Pattern, options?: {
|
|
458
|
+
min?: number;
|
|
459
|
+
trailingOk?: boolean;
|
|
460
|
+
}): Pattern;
|
|
461
|
+
/**
|
|
462
|
+
* Surrounded pattern - matches content between open and close delimiters.
|
|
463
|
+
* Alias for between(open, content, close).
|
|
464
|
+
*
|
|
465
|
+
* @param content - The pattern for interior content
|
|
466
|
+
* @param open - Opening delimiter
|
|
467
|
+
* @param close - Closing delimiter
|
|
468
|
+
* @returns Composed pattern: open, content, close
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* // Parenthesized expression
|
|
472
|
+
* surrounded(rule('expr'), token('LPAREN'), token('RPAREN'))
|
|
473
|
+
*
|
|
474
|
+
* // Generic syntax with string shorthand
|
|
475
|
+
* surrounded(rule('typeList'), '(', ')')
|
|
476
|
+
*/
|
|
477
|
+
declare function surrounded(content: Pattern, open: Pattern, close: Pattern): Pattern;
|
|
478
|
+
/**
|
|
479
|
+
* Between pattern - matches content between left and right patterns.
|
|
480
|
+
* Alias for surrounded but with more explicit name.
|
|
481
|
+
*
|
|
482
|
+
* @param left - Left sentinel pattern
|
|
483
|
+
* @param content - Interior content pattern
|
|
484
|
+
* @param right - Right sentinel pattern
|
|
485
|
+
* @returns Composed pattern: left, content, right
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* // Array literal
|
|
489
|
+
* between(token('LBRACK'), delimited(rule('expr'), token('COMMA')), token('RBRACK'))
|
|
490
|
+
*
|
|
491
|
+
* // Function call
|
|
492
|
+
* between('(', delimited(rule('param'), ','), ')')
|
|
493
|
+
*/
|
|
494
|
+
declare function between(left: Pattern, content: Pattern, right: Pattern): Pattern;
|
|
495
|
+
declare function error(cond: ErrorHandler['cond'], msg: string, code?: string): ErrorHandler;
|
|
496
|
+
declare const errorRecoveryStrategies: {
|
|
497
|
+
skipUntil(tokens: string | string[]): RecoveryStrategy;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
export { type BuildFunction, type ConditionalContext, type ConditionalPredicate, type DebugLevel, ERRORS, type ErrorHandler, type InfixHandler, type MiniToken, type ParseError, type ParseResult, Parser, type ParserSettings, type Pattern, type PrattTable, type PrefixHandler, type RecoveryStrategy, Result, type Rule, type Rules, action, between, buildPrattTable, choice, conditional, createRule, delimited, error, errorRecoveryStrategies, globalTokenMap, ifCondition, lookahead, loud, not, oneOrMore, optional, parse, peek, pratt, registerTokenMap, repeat, rule, seq, silent, surrounded, token, when, zeroOrMore, zeroOrOne };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import { Span, Token } from '@minelang-ts/lexer';
|
|
2
|
+
export { Span, Token } from '@minelang-ts/lexer';
|
|
3
|
+
|
|
4
|
+
type ResultStatus = 'unset' | 'failed' | 'passed';
|
|
5
|
+
type ResultMode = 'unset' | 'token' | 'optional' | 'choice' | 'repeat' | 'seq' | 'pratt' | 'custom';
|
|
6
|
+
interface TokenSource {
|
|
7
|
+
source_kind: 'token-source';
|
|
8
|
+
type: string;
|
|
9
|
+
text?: string;
|
|
10
|
+
span?: Span;
|
|
11
|
+
}
|
|
12
|
+
interface OptionalSource {
|
|
13
|
+
source_kind: 'optional-source';
|
|
14
|
+
result: Result | null;
|
|
15
|
+
}
|
|
16
|
+
interface ChoiceSource {
|
|
17
|
+
source_kind: 'choice-source';
|
|
18
|
+
atIndex: number;
|
|
19
|
+
result: Result | null;
|
|
20
|
+
}
|
|
21
|
+
interface RepeatSource {
|
|
22
|
+
source_kind: 'repeat-source';
|
|
23
|
+
endsWithSep: boolean;
|
|
24
|
+
result: Result[];
|
|
25
|
+
}
|
|
26
|
+
interface SequenceSource {
|
|
27
|
+
source_kind: 'sequence-source';
|
|
28
|
+
result: Result[];
|
|
29
|
+
}
|
|
30
|
+
interface PrattSource {
|
|
31
|
+
source_kind: 'pratt-source';
|
|
32
|
+
result: Result[];
|
|
33
|
+
}
|
|
34
|
+
interface CustomSource {
|
|
35
|
+
source_kind: 'custom-source';
|
|
36
|
+
name: string;
|
|
37
|
+
data: unknown;
|
|
38
|
+
}
|
|
39
|
+
type ResultSource = TokenSource | OptionalSource | ChoiceSource | RepeatSource | SequenceSource | PrattSource | CustomSource | null;
|
|
40
|
+
declare class Result {
|
|
41
|
+
span: Span;
|
|
42
|
+
status: ResultStatus;
|
|
43
|
+
source: ResultSource;
|
|
44
|
+
mode: ResultMode;
|
|
45
|
+
errors: ParseError[];
|
|
46
|
+
constructor(status: ResultStatus, source: ResultSource | null, mode: ResultMode, span: Span);
|
|
47
|
+
static create(status: ResultStatus, source: ResultSource | null, mode: ResultMode, span: Span): Result;
|
|
48
|
+
static createAsToken(status: ResultStatus, source: Token | null, span: Span): Result;
|
|
49
|
+
static createAsOptional(status: ResultStatus, source: Result | null, span: Span): Result;
|
|
50
|
+
static createAsChoice(status: ResultStatus, source: Result | null, index: number, span: Span): Result;
|
|
51
|
+
static createAsRepeat(status: ResultStatus, source: Result[] | null, span: Span, endsWithSep?: boolean): Result;
|
|
52
|
+
static createAsSequence(status: ResultStatus, source: Result[] | null, span: Span): Result;
|
|
53
|
+
static createAsPratt(status: ResultStatus, source: Result[], span: Span): Result;
|
|
54
|
+
static createAsCustom(status: ResultStatus, name: string, data: unknown, span: Span): Result;
|
|
55
|
+
isPassed(): boolean;
|
|
56
|
+
isFailed(): boolean;
|
|
57
|
+
isUnset(): boolean;
|
|
58
|
+
isToken(): boolean;
|
|
59
|
+
isOptional(): boolean;
|
|
60
|
+
isChoice(): boolean;
|
|
61
|
+
isRepeat(): boolean;
|
|
62
|
+
isSequence(): boolean;
|
|
63
|
+
isPratt(): boolean;
|
|
64
|
+
isFullyPassed(): boolean;
|
|
65
|
+
isOptionalPassed(): boolean;
|
|
66
|
+
isCustom(tag?: string): boolean;
|
|
67
|
+
getTokenType(): string | undefined;
|
|
68
|
+
getTokenSpan(): Span | undefined;
|
|
69
|
+
getOptionalResult(): Result | null | undefined;
|
|
70
|
+
getChoiceIndex(): number | undefined;
|
|
71
|
+
getChoiceResult(): Result | null | undefined;
|
|
72
|
+
getRepeatCount(): number | undefined;
|
|
73
|
+
getRepeatResult(): Result[] | undefined;
|
|
74
|
+
isRepeatEndsWithSep(): boolean | undefined;
|
|
75
|
+
getSequenceCount(): number | undefined;
|
|
76
|
+
getSequenceResult(): Result[] | undefined;
|
|
77
|
+
getPrattResult(): Result[] | undefined;
|
|
78
|
+
getCustomData(): unknown | undefined;
|
|
79
|
+
getCustomName(): string | undefined;
|
|
80
|
+
getTokenValue(): string | null | undefined;
|
|
81
|
+
getTokenData(): Token | undefined;
|
|
82
|
+
clone(): Result;
|
|
83
|
+
hasErrors(): boolean;
|
|
84
|
+
withError(e: ParseError): Result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare class Parser {
|
|
88
|
+
rules: Map<string, Rule>;
|
|
89
|
+
settings: ParserSettings;
|
|
90
|
+
tokens: Token[];
|
|
91
|
+
index: number;
|
|
92
|
+
errors: ParseError[];
|
|
93
|
+
ast: Result[];
|
|
94
|
+
stats: ParseStatistics;
|
|
95
|
+
private _compiled;
|
|
96
|
+
private _ruleIndex;
|
|
97
|
+
private _laTable;
|
|
98
|
+
private _memo;
|
|
99
|
+
private _depth;
|
|
100
|
+
private _silentDepth;
|
|
101
|
+
private _rootStart;
|
|
102
|
+
private _startTime;
|
|
103
|
+
private _debugLevel;
|
|
104
|
+
private _ignoredSet;
|
|
105
|
+
lastHandledRule: string;
|
|
106
|
+
lastVisitedIndex: number;
|
|
107
|
+
ruleStack: string[];
|
|
108
|
+
constructor(rules: Rule[], settings?: ParserSettings);
|
|
109
|
+
parse(tokens: Token[]): ParseResult;
|
|
110
|
+
dispose(): void;
|
|
111
|
+
isNextToken(kind: string, extra?: string[]): boolean;
|
|
112
|
+
isPrevToken(kind: string, from?: number, extra?: string[]): boolean;
|
|
113
|
+
isPrevRule(name: string): boolean;
|
|
114
|
+
private _buildLookaheadSets;
|
|
115
|
+
private _firstOfPattern;
|
|
116
|
+
private _compilePattern;
|
|
117
|
+
private _compileToken;
|
|
118
|
+
private _compileRule;
|
|
119
|
+
private _compileSeq;
|
|
120
|
+
private _compileChoice;
|
|
121
|
+
private _compileOptional;
|
|
122
|
+
private _compileRepeat;
|
|
123
|
+
private _compilePratt;
|
|
124
|
+
private _compileConditional;
|
|
125
|
+
private _compileAction;
|
|
126
|
+
private _compileNot;
|
|
127
|
+
private _compileLookahead;
|
|
128
|
+
private _getMemo;
|
|
129
|
+
private _setMemo;
|
|
130
|
+
private _mkError;
|
|
131
|
+
private _customErrorOr;
|
|
132
|
+
private _addError;
|
|
133
|
+
private _handleFatal;
|
|
134
|
+
private _safeBuild;
|
|
135
|
+
private _span;
|
|
136
|
+
private _spanOf;
|
|
137
|
+
private _skipIgnored;
|
|
138
|
+
private _patStr;
|
|
139
|
+
private _validateGrammar;
|
|
140
|
+
private _normalizeSettings;
|
|
141
|
+
private _reset;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface MiniToken {
|
|
145
|
+
kind: string;
|
|
146
|
+
value: string | null;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Conditional context passed to predicate function.
|
|
150
|
+
* Provides access to parser state for conditional decision-making.
|
|
151
|
+
*/
|
|
152
|
+
interface ConditionalContext {
|
|
153
|
+
parser: Parser;
|
|
154
|
+
result: Result | null;
|
|
155
|
+
index: number;
|
|
156
|
+
depth: number;
|
|
157
|
+
ruleStack: string[];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Predicate function for conditional pattern execution.
|
|
161
|
+
* Return true to continue with the pattern, false to fail.
|
|
162
|
+
*/
|
|
163
|
+
type ConditionalPredicate = (context: ConditionalContext) => boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Action function executed for side effects during parsing.
|
|
166
|
+
* Called during pattern execution, can access and modify parser state.
|
|
167
|
+
*/
|
|
168
|
+
type ActionFunction = (parser: Parser) => void;
|
|
169
|
+
interface Pattern {
|
|
170
|
+
type: 'token' | 'rule' | 'repeat' | 'choice' | 'seq' | 'optional' | 'pratt' | 'conditional' | 'action' | 'not' | 'lookahead';
|
|
171
|
+
silent: boolean;
|
|
172
|
+
value?: string;
|
|
173
|
+
name?: string;
|
|
174
|
+
min?: number;
|
|
175
|
+
max?: number;
|
|
176
|
+
patterns?: Pattern[];
|
|
177
|
+
separator?: Pattern;
|
|
178
|
+
pattern?: Pattern;
|
|
179
|
+
table?: PrattTable;
|
|
180
|
+
predicate?: ConditionalPredicate;
|
|
181
|
+
fn?: ActionFunction;
|
|
182
|
+
params?: Record<string, any>;
|
|
183
|
+
}
|
|
184
|
+
interface PrefixHandler {
|
|
185
|
+
/** How tightly this prefix binds its right operand. */
|
|
186
|
+
bp: number;
|
|
187
|
+
parse: (parser: Parser, token: Token) => Result;
|
|
188
|
+
}
|
|
189
|
+
interface InfixHandler {
|
|
190
|
+
/** Precedence level - left binding power. */
|
|
191
|
+
lbp: number;
|
|
192
|
+
/** Right binding power (lbp - 1 for right-associative). Defaults to lbp. */
|
|
193
|
+
rbp?: number;
|
|
194
|
+
parse: (parser: Parser, left: Result, token: Token) => Result;
|
|
195
|
+
}
|
|
196
|
+
interface PrattTable {
|
|
197
|
+
prefix: Map<string, PrefixHandler>;
|
|
198
|
+
infix: Map<string, InfixHandler>;
|
|
199
|
+
}
|
|
200
|
+
interface ErrorHandler {
|
|
201
|
+
cond: number | ((parser: Parser, opt: {
|
|
202
|
+
failedAt: number;
|
|
203
|
+
tokenIndex: number;
|
|
204
|
+
force?: boolean;
|
|
205
|
+
prevRule?: string;
|
|
206
|
+
prevInnerRule?: string;
|
|
207
|
+
}) => boolean);
|
|
208
|
+
msg: string;
|
|
209
|
+
code?: string;
|
|
210
|
+
}
|
|
211
|
+
interface RecoveryStrategy {
|
|
212
|
+
type: 'skipUntil' | 'synchronize';
|
|
213
|
+
tokens?: string[];
|
|
214
|
+
token?: string;
|
|
215
|
+
}
|
|
216
|
+
type BuildFunction = (matches: Result, parser: Parser) => Result;
|
|
217
|
+
interface Rule {
|
|
218
|
+
name: string;
|
|
219
|
+
pattern: Pattern;
|
|
220
|
+
options?: {
|
|
221
|
+
build?: BuildFunction;
|
|
222
|
+
errors?: ErrorHandler[];
|
|
223
|
+
recovery?: RecoveryStrategy;
|
|
224
|
+
ignored?: string[];
|
|
225
|
+
silent?: boolean;
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
type Rules = Rule[];
|
|
229
|
+
interface ParseStatistics {
|
|
230
|
+
tokensProcessed: number;
|
|
231
|
+
rulesApplied: number;
|
|
232
|
+
errorsRecovered: number;
|
|
233
|
+
parseTimeMs: number;
|
|
234
|
+
}
|
|
235
|
+
interface ParseError {
|
|
236
|
+
msg: string;
|
|
237
|
+
code: string;
|
|
238
|
+
span: Span;
|
|
239
|
+
failedAt: number;
|
|
240
|
+
tokenIndex: number;
|
|
241
|
+
startIndex: number;
|
|
242
|
+
prevRule: string;
|
|
243
|
+
prevInnerRule?: string;
|
|
244
|
+
}
|
|
245
|
+
interface ParseResult {
|
|
246
|
+
ast: Result[];
|
|
247
|
+
errors: ParseError[];
|
|
248
|
+
statistics?: ParseStatistics;
|
|
249
|
+
}
|
|
250
|
+
type DebugLevel = 'off' | 'errors' | 'rules' | 'patterns' | 'tokens' | 'verbose';
|
|
251
|
+
interface ParserSettings {
|
|
252
|
+
startRule: string;
|
|
253
|
+
errorRecovery?: {
|
|
254
|
+
mode?: 'strict' | 'resilient';
|
|
255
|
+
maxErrors?: number;
|
|
256
|
+
};
|
|
257
|
+
ignored?: string[];
|
|
258
|
+
debug?: DebugLevel;
|
|
259
|
+
maxDepth?: number;
|
|
260
|
+
}
|
|
261
|
+
declare const ERRORS: {
|
|
262
|
+
readonly LEXICAL_ERROR: "LEXICAL_ERROR";
|
|
263
|
+
readonly TOKEN_EXPECTED_EOF: "TOKEN_EXPECTED_EOF";
|
|
264
|
+
readonly TOKEN_MISMATCH: "TOKEN_MISMATCH";
|
|
265
|
+
readonly RULE_FAILED: "RULE_FAILED";
|
|
266
|
+
readonly BUILD_FUNCTION_FAILED: "BUILD_FUNCTION_FAILED";
|
|
267
|
+
readonly REPEAT_MIN_NOT_MET: "REPEAT_MIN_NOT_MET";
|
|
268
|
+
readonly SEQUENCE_FAILED: "SEQUENCE_FAILED";
|
|
269
|
+
readonly CUSTOM_ERROR: "CUSTOM_ERROR";
|
|
270
|
+
readonly CHOICE_ALL_FAILED: "CHOICE_ALL_FAILED";
|
|
271
|
+
readonly PRATT_NO_PREFIX: "PRATT_NO_PREFIX";
|
|
272
|
+
readonly FATAL_ERROR: "FATAL_ERROR";
|
|
273
|
+
readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
|
|
274
|
+
readonly RECOVERY_CUSTOM: "RECOVERY_CUSTOM";
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* Global registry mapping string literals to token kinds.
|
|
278
|
+
* Example: {'let': 'LET', 'if': 'IF', '{': 'LBRACE'}
|
|
279
|
+
* When registered, string patterns like seq('let', 'IDENT') will work.
|
|
280
|
+
*/
|
|
281
|
+
declare const globalTokenMap: Record<string, string>;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Register a global token map for string shorthand patterns.
|
|
285
|
+
* After registration, you can use 'let', 'if', etc. directly in patterns.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* registerTokenMap({
|
|
289
|
+
* 'let': 'LET',
|
|
290
|
+
* 'if': 'IF',
|
|
291
|
+
* '{': 'LBRACE',
|
|
292
|
+
* '}': 'RBRACE',
|
|
293
|
+
* '=': 'EQ',
|
|
294
|
+
* });
|
|
295
|
+
*
|
|
296
|
+
* // Now you can write:
|
|
297
|
+
* seq('let', 'IDENT', '=', rule('expr'))
|
|
298
|
+
*/
|
|
299
|
+
declare function registerTokenMap(map: Record<string, string>): void;
|
|
300
|
+
declare function parse(tokens: Token[], rules: Rules, settings?: ParserSettings): ParseResult;
|
|
301
|
+
declare function createRule(name: string, pattern: Pattern, options?: Rule['options']): Rule;
|
|
302
|
+
declare function token(name: string, value?: string): Pattern;
|
|
303
|
+
declare function optional(pattern: Pattern): Pattern;
|
|
304
|
+
declare function choice(...patterns: Pattern[]): Pattern;
|
|
305
|
+
declare function repeat(pattern: Pattern, min?: number, max?: number, separator?: Pattern): Pattern;
|
|
306
|
+
declare function oneOrMore(pattern: Pattern, separator?: Pattern): Pattern;
|
|
307
|
+
declare function zeroOrMore(pattern: Pattern, separator?: Pattern): Pattern;
|
|
308
|
+
declare function zeroOrOne(pattern: Pattern, separator?: Pattern): Pattern;
|
|
309
|
+
declare function seq(...patterns: Pattern[]): Pattern;
|
|
310
|
+
declare function rule(name: string): Pattern;
|
|
311
|
+
declare function rule(name: string, params: Record<string, unknown>): Pattern;
|
|
312
|
+
declare function silent<T extends Pattern>(pattern: T): T;
|
|
313
|
+
declare function loud<T extends Pattern>(pattern: T): T;
|
|
314
|
+
/**
|
|
315
|
+
* Build a Pratt expression parser inline in a rule.
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* const expr = pratt({
|
|
319
|
+
* prefix: new Map([
|
|
320
|
+
* ['NUM', { bp: 0, parse: (_, tok) => Result.createAsToken('passed', tok, tok.span) }],
|
|
321
|
+
* ['MINUS', { bp: 70, parse: (p, tok) => {
|
|
322
|
+
* const right = p.parse(...) // handled via sub-rule
|
|
323
|
+
* return Result.createAsPratt('passed', [right], right.span)
|
|
324
|
+
* }}],
|
|
325
|
+
* ]),
|
|
326
|
+
* infix: new Map([
|
|
327
|
+
* ['PLUS', { lbp: 50, parse: (_, left, tok) => ... }],
|
|
328
|
+
* ['STAR', { lbp: 60, parse: (_, left, tok) => ... }],
|
|
329
|
+
* ['STAR2', { lbp: 70, rbp: 69, parse: ... }], // right-associative
|
|
330
|
+
* ]),
|
|
331
|
+
* });
|
|
332
|
+
*/
|
|
333
|
+
declare function pratt(table: PrattTable): Pattern;
|
|
334
|
+
/**
|
|
335
|
+
* Adds conditional execution to a pattern.
|
|
336
|
+
* The predicate receives parser context and decides whether to continue.
|
|
337
|
+
*
|
|
338
|
+
* @param pattern - The pattern to conditionally execute
|
|
339
|
+
* @param predicate - Function that receives parser context and returns true to continue
|
|
340
|
+
* @returns Conditional pattern that evaluates predicate after inner pattern matches
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* // Only match if we're not too deep in the parse tree
|
|
344
|
+
* pattern.if(ctx => ctx.depth < 10)
|
|
345
|
+
*
|
|
346
|
+
* // Only match if next token after match is LBRACE
|
|
347
|
+
* token('IF').if(ctx => ctx.parser.isNextToken('LBRACE'))
|
|
348
|
+
*
|
|
349
|
+
* // Inspect full parser state
|
|
350
|
+
* rule('expr').if(ctx => {
|
|
351
|
+
* return ctx.parser.errors.length === 0 && ctx.ruleStack.length < 5
|
|
352
|
+
* })
|
|
353
|
+
*/
|
|
354
|
+
declare function conditional(pattern: Pattern, predicate: ConditionalPredicate): Pattern;
|
|
355
|
+
/**
|
|
356
|
+
* Shorthand for conditional() - more natural syntax
|
|
357
|
+
* @see conditional
|
|
358
|
+
*/
|
|
359
|
+
declare function when(pattern: Pattern, predicate: ConditionalPredicate): Pattern;
|
|
360
|
+
/**
|
|
361
|
+
* Method-chaining style: pattern.if(predicate)
|
|
362
|
+
* Extends Pattern type with .if() method for fluent API
|
|
363
|
+
*
|
|
364
|
+
* @note This is implemented as a utility function, not directly on Pattern
|
|
365
|
+
* Use: conditional(pattern, predicate) or when(pattern, predicate)
|
|
366
|
+
*/
|
|
367
|
+
declare function ifCondition(pattern: Pattern, predicate: ConditionalPredicate): Pattern;
|
|
368
|
+
/**
|
|
369
|
+
* Action pattern - executes a side effect during parsing.
|
|
370
|
+
* Useful for scope management, state tracking, and stateful parsing.
|
|
371
|
+
*
|
|
372
|
+
* @param fn - Function called during execution with access to parser state
|
|
373
|
+
* @returns Action pattern that always succeeds
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* // Track parse state
|
|
377
|
+
* seq(
|
|
378
|
+
* token('LBRACE'),
|
|
379
|
+
* action(p => p.symbolTable?.pushScope()),
|
|
380
|
+
* zeroOrMore(rule('stmt')),
|
|
381
|
+
* token('RBRACE'),
|
|
382
|
+
* action(p => p.symbolTable?.popScope())
|
|
383
|
+
* )
|
|
384
|
+
*
|
|
385
|
+
* // Count custom metric
|
|
386
|
+
* action(p => p.stats.customCounter = (p.stats.customCounter ?? 0) + 1)
|
|
387
|
+
*/
|
|
388
|
+
declare function action(fn: ActionFunction): Pattern;
|
|
389
|
+
/**
|
|
390
|
+
* NOT pattern - succeeds if the inner pattern FAILS.
|
|
391
|
+
* Useful for negative lookahead and validation checks.
|
|
392
|
+
*
|
|
393
|
+
* @param pattern - The pattern to negate
|
|
394
|
+
* @returns NOT pattern
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* // Match any character except EOF
|
|
398
|
+
* not(token('EOF'))
|
|
399
|
+
*
|
|
400
|
+
* // In a choice: match IDENT that is not a keyword
|
|
401
|
+
* choice(
|
|
402
|
+
* not(choice(token('IF'), token('WHILE'), token('FOR'))),
|
|
403
|
+
* token('IDENT')
|
|
404
|
+
* )
|
|
405
|
+
*/
|
|
406
|
+
declare function not(pattern: Pattern): Pattern;
|
|
407
|
+
/**
|
|
408
|
+
* Lookahead pattern - checks if inner pattern would match WITHOUT consuming tokens.
|
|
409
|
+
* Common alias: peek()
|
|
410
|
+
*
|
|
411
|
+
* @param pattern - The pattern to lookahead check
|
|
412
|
+
* @returns Lookahead pattern
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* // Only match expr if followed by RPAREN
|
|
416
|
+
* seq(
|
|
417
|
+
* lookahead(seq(rule('expr'), token('RPAREN'))),
|
|
418
|
+
* rule('expr')
|
|
419
|
+
* )
|
|
420
|
+
*
|
|
421
|
+
* // Safe token inspection
|
|
422
|
+
* seq(
|
|
423
|
+
* token('IDENT'),
|
|
424
|
+
* when(lookahead(token('LPAREN')), ctx => ctx.parser.isNextToken('LPAREN'))
|
|
425
|
+
* )
|
|
426
|
+
*/
|
|
427
|
+
declare function lookahead(pattern: Pattern): Pattern;
|
|
428
|
+
/**
|
|
429
|
+
* Alias for lookahead() - more concise syntax.
|
|
430
|
+
* @see lookahead
|
|
431
|
+
*/
|
|
432
|
+
declare function peek(pattern: Pattern): Pattern;
|
|
433
|
+
/** Convenience: build a PrattTable from plain objects. */
|
|
434
|
+
declare function buildPrattTable(spec: {
|
|
435
|
+
prefix?: Record<string, PrefixHandler>;
|
|
436
|
+
infix?: Record<string, InfixHandler>;
|
|
437
|
+
}): PrattTable;
|
|
438
|
+
/**
|
|
439
|
+
* Delimited list pattern - matches items separated by a delimiter.
|
|
440
|
+
* Commonly used for comma-separated values, arguments, etc.
|
|
441
|
+
*
|
|
442
|
+
* @param item - The pattern for each list item
|
|
443
|
+
* @param sep - The separator pattern (usually token(','))
|
|
444
|
+
* @param options - {min?: number, trailingOk?: boolean}
|
|
445
|
+
* @returns Composed pattern for delimited list
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* // Comma-separated identifiers (0+)
|
|
449
|
+
* delimited(token('IDENT'), token('COMMA'))
|
|
450
|
+
*
|
|
451
|
+
* // Function arguments (1+ with optional trailing)
|
|
452
|
+
* delimited(rule('expr'), token('COMMA'), {min: 1, trailingOk: true})
|
|
453
|
+
*
|
|
454
|
+
* // Array elements
|
|
455
|
+
* between('[', delimited(rule('expr'), token('COMMA')), ']')
|
|
456
|
+
*/
|
|
457
|
+
declare function delimited(item: Pattern, sep: Pattern, options?: {
|
|
458
|
+
min?: number;
|
|
459
|
+
trailingOk?: boolean;
|
|
460
|
+
}): Pattern;
|
|
461
|
+
/**
|
|
462
|
+
* Surrounded pattern - matches content between open and close delimiters.
|
|
463
|
+
* Alias for between(open, content, close).
|
|
464
|
+
*
|
|
465
|
+
* @param content - The pattern for interior content
|
|
466
|
+
* @param open - Opening delimiter
|
|
467
|
+
* @param close - Closing delimiter
|
|
468
|
+
* @returns Composed pattern: open, content, close
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* // Parenthesized expression
|
|
472
|
+
* surrounded(rule('expr'), token('LPAREN'), token('RPAREN'))
|
|
473
|
+
*
|
|
474
|
+
* // Generic syntax with string shorthand
|
|
475
|
+
* surrounded(rule('typeList'), '(', ')')
|
|
476
|
+
*/
|
|
477
|
+
declare function surrounded(content: Pattern, open: Pattern, close: Pattern): Pattern;
|
|
478
|
+
/**
|
|
479
|
+
* Between pattern - matches content between left and right patterns.
|
|
480
|
+
* Alias for surrounded but with more explicit name.
|
|
481
|
+
*
|
|
482
|
+
* @param left - Left sentinel pattern
|
|
483
|
+
* @param content - Interior content pattern
|
|
484
|
+
* @param right - Right sentinel pattern
|
|
485
|
+
* @returns Composed pattern: left, content, right
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* // Array literal
|
|
489
|
+
* between(token('LBRACK'), delimited(rule('expr'), token('COMMA')), token('RBRACK'))
|
|
490
|
+
*
|
|
491
|
+
* // Function call
|
|
492
|
+
* between('(', delimited(rule('param'), ','), ')')
|
|
493
|
+
*/
|
|
494
|
+
declare function between(left: Pattern, content: Pattern, right: Pattern): Pattern;
|
|
495
|
+
declare function error(cond: ErrorHandler['cond'], msg: string, code?: string): ErrorHandler;
|
|
496
|
+
declare const errorRecoveryStrategies: {
|
|
497
|
+
skipUntil(tokens: string | string[]): RecoveryStrategy;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
export { type BuildFunction, type ConditionalContext, type ConditionalPredicate, type DebugLevel, ERRORS, type ErrorHandler, type InfixHandler, type MiniToken, type ParseError, type ParseResult, Parser, type ParserSettings, type Pattern, type PrattTable, type PrefixHandler, type RecoveryStrategy, Result, type Rule, type Rules, action, between, buildPrattTable, choice, conditional, createRule, delimited, error, errorRecoveryStrategies, globalTokenMap, ifCondition, lookahead, loud, not, oneOrMore, optional, parse, peek, pratt, registerTokenMap, repeat, rule, seq, silent, surrounded, token, when, zeroOrMore, zeroOrOne };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var h=class s{constructor(e,t,r,n){this.span={start:-99,end:-99};this.status="unset";this.source=null;this.mode="unset";this.errors=[];this.status=e,this.source=t,this.mode=r,this.span=n;}static create(e,t,r,n){return new s(e,t,r,n)}static createAsToken(e,t,r){return s.create(e,{source_kind:"token-source",type:t?.type??"unset",text:t?.text??void 0,span:r},"token",r)}static createAsOptional(e,t,r){return s.create(e,{source_kind:"optional-source",result:t},"optional",r)}static createAsChoice(e,t,r,n){return s.create(e,{source_kind:"choice-source",atIndex:r,result:t},"choice",n)}static createAsRepeat(e,t,r,n=false){return s.create(e,{source_kind:"repeat-source",endsWithSep:n,result:t??[]},"repeat",r)}static createAsSequence(e,t,r){return s.create(e,{source_kind:"sequence-source",result:t??[]},"seq",r)}static createAsPratt(e,t,r){return s.create(e,{source_kind:"pratt-source",result:t},"pratt",r)}static createAsCustom(e,t,r,n){return s.create(e,{source_kind:"custom-source",name:t,data:r},"custom",n)}isPassed(){return this.status==="passed"}isFailed(){return this.status==="failed"}isUnset(){return this.status==="unset"}isToken(){return this.mode==="token"}isOptional(){return this.mode==="optional"}isChoice(){return this.mode==="choice"}isRepeat(){return this.mode==="repeat"}isSequence(){return this.mode==="seq"}isPratt(){return this.mode==="pratt"}isFullyPassed(){return !(!this.isPassed()||this.isOptional()&&!this.isOptionalPassed())}isOptionalPassed(){return this.isOptional()&&this.source.result!==null}isCustom(e){return this.mode!=="custom"?false:e?this.source.name===e:true}getTokenType(){return this.isToken()?this.source.type:void 0}getTokenSpan(){return this.isToken()?this.source.span:void 0}getOptionalResult(){return this.isOptionalPassed()?this.source.result:void 0}getChoiceIndex(){return this.isChoice()?this.source.atIndex:void 0}getChoiceResult(){return this.isChoice()?this.source.result:void 0}getRepeatCount(){return this.isRepeat()?this.source.result.length:void 0}getRepeatResult(){return this.isRepeat()?this.source.result:void 0}isRepeatEndsWithSep(){return this.isRepeat()?this.source.endsWithSep:void 0}getSequenceCount(){return this.isSequence()?this.source.result.length:void 0}getSequenceResult(){return this.isSequence()?this.source.result:void 0}getPrattResult(){return this.isPratt()?this.source.result:void 0}getCustomData(){return this.isCustom()?this.source.data:void 0}getCustomName(){return this.isCustom()?this.source.name:void 0}getTokenValue(){if(!this.isToken())return;let e=this.source.text;return e===void 0?null:e}getTokenData(){if(!this.isToken())return;let e=this.source;return {type:e.type,text:e.text,span:e.span}}clone(){let e=new s(this.status,this.source,this.mode,this.span);return e.errors=[...this.errors],e}hasErrors(){return this.errors.length>0}withError(e){return this.errors.push(e),this}};var p={LEXICAL_ERROR:"LEXICAL_ERROR",TOKEN_EXPECTED_EOF:"TOKEN_EXPECTED_EOF",TOKEN_MISMATCH:"TOKEN_MISMATCH",RULE_FAILED:"RULE_FAILED",BUILD_FUNCTION_FAILED:"BUILD_FUNCTION_FAILED",REPEAT_MIN_NOT_MET:"REPEAT_MIN_NOT_MET",SEQUENCE_FAILED:"SEQUENCE_FAILED",CUSTOM_ERROR:"CUSTOM_ERROR",CHOICE_ALL_FAILED:"CHOICE_ALL_FAILED",PRATT_NO_PREFIX:"PRATT_NO_PREFIX",FATAL_ERROR:"FATAL_ERROR",UNKNOWN_ERROR:"UNKNOWN_ERROR",RECOVERY_CUSTOM:"RECOVERY_CUSTOM"},E={};var d=h.create("failed",null,"unset",{start:-1,end:-1}),g=class{constructor(e,t){this.tokens=[];this.index=0;this.errors=[];this.ast=[];this.stats={tokensProcessed:0,rulesApplied:0,errorsRecovered:0,parseTimeMs:0};this._compiled=new Map;this._ruleIndex=new Map;this._laTable=new Map;this._memo=new Map;this._depth=0;this._silentDepth=0;this._rootStart=0;this._startTime=0;this._debugLevel="off";this._ignoredSet=new Set;this.lastHandledRule="unknown";this.lastVisitedIndex=0;this.ruleStack=[];this.rules=new Map(e.map(i=>[i.name,i])),this.settings=this._normalizeSettings(t),this._debugLevel=this.settings.debug,this._ignoredSet=new Set(this.settings.ignored);let r=0;for(let i of this.rules.keys())this._ruleIndex.set(i,r++);let n=this._validateGrammar();if(n.length)throw new Error(`Grammar validation failed:
|
|
2
|
+
${n.join(`
|
|
3
|
+
`)}`);this._buildLookaheadSets();for(let[i,o]of this.rules)this._compiled.set(i,this._compilePattern(o.pattern,o));}parse(e){if(this._reset(e),this._startTime=Date.now(),!e.length)return {ast:[],errors:[]};let t=e.find(a=>a.type==="error");if(t)return {ast:[],errors:[this._mkError(p.LEXICAL_ERROR,`Unexpected token '${t.text}'`,t.span,0,0,"lexer")]};let r=this.rules.get(this.settings.startRule);if(!r)throw new Error(`Start rule '${this.settings.startRule}' not found`);let n=this._compiled.get(this.settings.startRule),i=this.settings.errorRecovery.maxErrors;this.settings.errorRecovery.mode==="resilient";try{for(this._skipIgnored();this.index<this.tokens.length&&!(i>0&&this.errors.length>=i);){let a=this.index;this._rootStart=a;let u=n(!1);if(u.isPassed()){let l=r.options?.build?this._safeBuild(r.options.build,u):u;l&&this.ast.push(l);}if(this.index===a)break;this._skipIgnored();}}catch(a){this._handleFatal(a);}return this.stats.parseTimeMs=Date.now()-this._startTime,{ast:this.ast,errors:this.errors,statistics:this.stats}}dispose(){this._memo.clear(),this._compiled.clear(),this.tokens=[],this.ast=[],this.errors=[];}isNextToken(e,t){let r=new Set([...this._ignoredSet,...t??[]]);for(let n=this.index;n<this.tokens.length;n++){let i=this.tokens[n];if(i.type===e)return true;if(!r.has(i.type))break}return false}isPrevToken(e,t=-1,r){let n=new Set([...this._ignoredSet,...r??[]]),i=t<0?this.index:t;for(let o=i-1;o>=0;o--){let a=this.tokens[o];if(a.type===e)return true;if(!n.has(a.type))break}return false}isPrevRule(e){return this.lastHandledRule===e}_buildLookaheadSets(){for(let t of this.rules.keys())this._laTable.set(t,new Set);let e=true;for(;e;){e=false;for(let[t,r]of this.rules){let n=this._laTable.get(t),i=this._firstOfPattern(r.pattern);for(let o of i)n.has(o)||(n.add(o),e=true);}}}_firstOfPattern(e){let t=new Set;switch(e.type){case "token":t.add(e.name);break;case "rule":{let r=this._laTable.get(e.name);if(r)for(let n of r)t.add(n);break}case "seq":for(let r of e.patterns??[]){for(let n of this._firstOfPattern(r))t.add(n);if(r.type!=="optional")break}break;case "choice":for(let r of e.patterns??[])for(let n of this._firstOfPattern(r))t.add(n);break;case "optional":case "repeat":case "conditional":case "not":case "lookahead":if(e.pattern)for(let r of this._firstOfPattern(e.pattern))t.add(r);break;case "action":break;case "pratt":if(e.table)for(let r of e.table.prefix.keys())t.add(r);break}return t}_compilePattern(e,t){switch(e.type){case "token":return this._compileToken(e,t);case "rule":return this._compileRule(e,t);case "seq":return this._compileSeq(e,t);case "choice":return this._compileChoice(e,t);case "repeat":return this._compileRepeat(e,t);case "optional":return this._compileOptional(e,t);case "pratt":return this._compilePratt(e,t);case "conditional":return this._compileConditional(e,t);case "action":return this._compileAction(e,t);case "not":return this._compileNot(e,t);case "lookahead":return this._compileLookahead(e,t);default:throw new Error(`Unknown pattern type: ${e.type}`)}}_compileToken(e,t){let r=e.name,n=e.value;return i=>{if(this.lastHandledRule=t?.name??r,this.lastVisitedIndex=this.index,this.index>=this.tokens.length){if(i)return d;throw this._mkError(p.TOKEN_EXPECTED_EOF,`Expected '${r}', got EOF`,this._span(),0,this.index,this.lastHandledRule)}let o=this.tokens[this.index];if(o.type===r){if(n!==void 0&&o.text!==n){if(i)return d;throw this._mkError(p.TOKEN_MISMATCH,`Expected '${r}' with value '${n}', got '${o.text}'`,o.span,0,this.index,this.lastHandledRule)}return this.index++,this.stats.tokensProcessed++,h.createAsToken("passed",o,o.span)}if(i)return d;let a=this._mkError(p.TOKEN_MISMATCH,`Expected '${r}', got '${o.type}'`,o.span,0,this.index,this.lastHandledRule);throw this._customErrorOr(t,a)}}_compileRule(e,t){let r=e.name;return n=>{let i=this.rules.get(r);if(!i)throw new Error(`Rule '${r}' not found`);let o=this._compiled.get(r);if(!o)throw new Error(`Rule '${r}' not compiled`);this.ruleStack.push(r),this.stats.rulesApplied++;let u=(this._ruleIndex.get(r)??0)<<16|this.index,l=this._getMemo(u);if(l)return this.ruleStack.pop(),this.index=l.endIndex,l.result??d;let c=this.index,f=this.errors.length,m=o(n);if(!m.isFullyPassed()){if(this.index=c,this.ruleStack.pop(),n)return d;let _=this._mkError(p.RULE_FAILED,`Rule '${r}' failed`,this._span(),0,this.lastVisitedIndex,r);throw this._customErrorOr(t,_)}let R=m;if(i.options?.build){let _=this._safeBuild(i.options.build,m);_&&(R=_);}return this._setMemo(u,R,this.index,f),this.ruleStack.pop(),R}}_compileSeq(e,t){let r=(e.patterns??[]).map(n=>this._compilePattern(n,t));return n=>{let i=this.index,o=[];for(let a=0;a<r.length;a++){this._skipIgnored(t?.options?.ignored);let u=r[a](n);if(!u.isPassed()){if(this.index=i,n)return d;let l=this._mkError(p.SEQUENCE_FAILED,`Sequence failed at element ${a+1}/${r.length}`,this._span(),a,this.lastVisitedIndex,this.lastHandledRule);throw this._customErrorOr(t,l)}o.push(u);}return h.createAsSequence("passed",o,this._spanOf(o))}}_compileChoice(e,t){let r=e.patterns??[],n=r.map(o=>this._firstOfPattern(o)),i=r.map(o=>this._compilePattern(o,t));return o=>{let a=this.index;if(this._silentDepth++,this.index<this.tokens.length){let c=this.tokens[this.index].type,f=-1,m=false;for(let R=0;R<n.length;R++)if(n[R].has(c)){if(f>=0){m=true;break}f=R;}if(!m&&f>=0){this._silentDepth--;let R=i[f](o);if(R.isFullyPassed())return h.createAsChoice("passed",R,f,R.span);this.index=a,this._silentDepth++;}}let u=null;for(let c=0;c<i.length;c++){this.index=a;let f=i[c](true);if(f.isFullyPassed())return this._silentDepth--,h.createAsChoice("passed",f,c,f.span);let m=this.lastVisitedIndex-a;(!u||m>u.index-a)&&(u={index:this.lastVisitedIndex,err:null,altIdx:c});}if(this._silentDepth--,this.index=a,o)return d;let l=this._mkError(p.CHOICE_ALL_FAILED,`Expected one of: ${r.map(c=>this._patStr(c)).join(" | ")}`,this._span(),u?.altIdx??0,this.lastVisitedIndex,this.lastHandledRule);throw this._customErrorOr(t,l)}}_compileOptional(e,t){let r=this._compilePattern(e.pattern,t);return n=>{let i=this.index;this._silentDepth++;let o=r(true);return this._silentDepth--,o.isFullyPassed()?h.createAsOptional("passed",o,o.span):(this.index=i,h.createAsOptional("passed",null,this._span()))}}_compileRepeat(e,t){let r=e.min??0,n=e.max??1/0,i=this._compilePattern(e.pattern,t),o=e.separator?this._compilePattern(e.separator,t):null;return a=>{let u=[],l=false,c=this.index;for(;u.length<n&&this.index<this.tokens.length;){let f=this.index;this._silentDepth++;let m=i(true);if(this._silentDepth--,!m.isFullyPassed()){this.index=f,l=false;break}if(u.push(m),l=false,this.index===f)break;if(o&&u.length<n&&this.index<this.tokens.length){let R=this.index;this._silentDepth++;let _=o(true);if(this._silentDepth--,!_.isFullyPassed()){this.index=R;break}l=true;}}if(u.length<r){if(this.index=c,a)return d;throw this._mkError(p.REPEAT_MIN_NOT_MET,`Expected at least ${r} occurrences, got ${u.length}`,this._span(),0,this.index,this.lastHandledRule)}return h.createAsRepeat("passed",u,this._spanOf(u),l)}}_compilePratt(e,t){let r=e.table;return n=>{if(this.index>=this.tokens.length){if(n)return d;throw this._mkError(p.PRATT_NO_PREFIX,"Expected an expression",this._span(),0,this.index,"pratt")}let i=this.tokens[this.index],o=r.prefix.get(i.type);if(!o){if(n)return d;throw this._mkError(p.PRATT_NO_PREFIX,`Unexpected token '${i.type}' in expression`,i.span,0,this.index,"pratt")}this.index++;let a=o.parse(this,i);if(!a.isPassed())return n?d:a;for(;this.index<this.tokens.length&&(this._skipIgnored(),!(this.index>=this.tokens.length));){let u=this.tokens[this.index],l=r.infix.get(u.type);if(!l||l.lbp<=0||(this.index++,a=l.parse(this,a,u),!a.isPassed()))break}return a}}_compileConditional(e,t){let r=this._compilePattern(e.pattern,t),n=e.predicate;return i=>{let o=this.index,a;if(i?(this._silentDepth++,a=r(true),this._silentDepth--):a=r(false),!a.isFullyPassed())return this.index=o,i?d:a;try{let u={parser:this,result:a,index:this.index,depth:this._depth,ruleStack:[...this.ruleStack]};if(n(u))return h.createAsCustom("passed","conditional",a,a.span);{if(this.index=o,i)return d;let c=this._mkError(p.RULE_FAILED,"Conditional predicate returned false",this._span(),0,this.lastVisitedIndex,this.lastHandledRule);throw this._customErrorOr(t,c)}}catch(u){if(this.index=o,i)return d;let l=u instanceof Error?u.message:String(u),c=this._mkError(p.RULE_FAILED,`Conditional predicate threw: ${l}`,this._span(),0,this.lastVisitedIndex,this.lastHandledRule);throw this._customErrorOr(t,c)}}}_compileAction(e,t){let r=e.fn;return n=>{try{r(this);}catch(i){let o=i instanceof Error?i.message:String(i);throw this._mkError(p.RULE_FAILED,`Action function threw: ${o}`,this._span(),0,this.index,"action")}return h.createAsCustom("passed","action",null,this._span())}}_compileNot(e,t){let r=this._compilePattern(e.pattern,t);return n=>{let i=this.index;this._silentDepth++;let o=r(true);if(this._silentDepth--,o.isFullyPassed()){if(this.index=i,n)return d;let a=this._mkError(p.RULE_FAILED,"NOT pattern failed - inner pattern matched",this._span(),0,i,this.lastHandledRule);throw this._customErrorOr(t,a)}else return this.index=i,h.createAsCustom("passed","not",null,this._span())}}_compileLookahead(e,t){let r=this._compilePattern(e.pattern,t);return n=>{let i=this.index;this._silentDepth++;let o=r(true);if(this._silentDepth--,this.index=i,o.isFullyPassed())return h.createAsCustom("passed","lookahead",null,this._span());{if(n)return d;let a=this._mkError(p.RULE_FAILED,"Lookahead pattern failed",this._span(),0,i,this.lastHandledRule);throw this._customErrorOr(t,a)}}}_getMemo(e){let t=this._memo.get(e);return t?t.errorCount!==this.errors.length?(this._memo.delete(e),null):t:null}_setMemo(e,t,r,n){this._memo.set(e,{result:t,endIndex:r,errorCount:this.errors.length});}_mkError(e,t,r,n,i,o,a){return {code:e,msg:t,span:r,failedAt:n,tokenIndex:i,startIndex:this._rootStart,prevRule:o,prevInnerRule:a??this.ruleStack.at(-1)??"unknown"}}_customErrorOr(e,t){if(!e?.options?.errors)return t;for(let r of e.options.errors){let n=false;if(typeof r.cond=="number")n=t.failedAt===r.cond;else if(typeof r.cond=="function")try{n=r.cond(this,{failedAt:t.failedAt,tokenIndex:t.tokenIndex});}catch{}if(n)return this._mkError(r.code??p.CUSTOM_ERROR,r.msg,t.span,t.failedAt,t.tokenIndex,t.prevRule,t.prevInnerRule)}return t}_addError(e){if(this._silentDepth>0)return;let t=this.settings.errorRecovery.maxErrors;t>0&&this.errors.length>=t||this.settings.errorRecovery.mode==="strict"&&this.errors.length>0||this.errors.some(r=>r.span?.start===e.span?.start)||this.errors.push(e);}_handleFatal(e){e&&typeof e=="object"&&"msg"in e&&"code"in e?this._addError(e):e instanceof Error&&this._addError(this._mkError(p.FATAL_ERROR,e.message,this._span(),0,this.index,this.lastHandledRule));}_safeBuild(e,t){try{return e(t,this)}catch(r){let n=this._mkError(p.BUILD_FUNCTION_FAILED,r instanceof Error?r.message:String(r),this._span(),0,this.index,this.lastHandledRule);return this._addError(n),t}}_span(){if(!this.tokens.length)return {start:0,end:0};if(this.index>=this.tokens.length){let e=this.tokens[this.tokens.length-1];return {start:e.span.end,end:e.span.end}}return this.tokens[this.index].span}_spanOf(e){return e.length?{start:e[0].span.start,end:e[e.length-1].span.end}:this._span()}_skipIgnored(e){let t=e?new Set([...this._ignoredSet,...e]):this._ignoredSet;for(;this.index<this.tokens.length&&t.has(this.tokens[this.index].type);)this.index++,this.stats.tokensProcessed++;}_patStr(e){switch(e.type){case "token":return e.name;case "rule":return e.name;case "seq":return `(${(e.patterns??[]).map(t=>this._patStr(t)).join(" ")})`;case "choice":return (e.patterns??[]).map(t=>this._patStr(t)).join(" | ");case "optional":return `${this._patStr(e.pattern)}?`;case "repeat":return `${this._patStr(e.pattern)}*`;case "conditional":return `${this._patStr(e.pattern)}.if(...)`;case "action":return "action(...)";case "not":return `!${this._patStr(e.pattern)}`;case "lookahead":return `lookahead(${this._patStr(e.pattern)})`;case "pratt":return "expr";default:return e.type}}_validateGrammar(){let e=[],t=new Set(this.rules.keys()),r=(n,i)=>{n.type==="rule"&&!t.has(n.name)&&e.push(`Rule '${i}' references undefined rule '${n.name}'`);for(let o of [n.pattern,...n.patterns??[]])o&&r(o,i);n.separator&&r(n.separator,i);};for(let[n,i]of this.rules)r(i.pattern,n);return this.rules.has(this.settings.startRule)||e.push(`Start rule '${this.settings.startRule}' is not defined`),e}_normalizeSettings(e){return {startRule:e?.startRule??"root",errorRecovery:{mode:e?.errorRecovery?.mode??"strict",maxErrors:e?.errorRecovery?.maxErrors??1},ignored:e?.ignored??["ws"],debug:e?.debug??"off",maxDepth:e?.maxDepth??1e3}}_reset(e){this.tokens=e,this.index=0,this.errors=[],this.ast=[],this._depth=0,this._silentDepth=0,this._rootStart=0,this.ruleStack=[],this._memo.clear(),this.stats={tokensProcessed:0,rulesApplied:0,errorsRecovered:0,parseTimeMs:0};}};function w(s){Object.assign(E,s);}function F(s,e,t){let r=new g(e,t);try{return r.parse(s)}finally{r.dispose();}}function L(s,e,t={}){return {name:s,pattern:e,options:{name:false,...t}}}function M(s,e){if(!s)throw new Error("token(): name must be a non-empty string");return {type:"token",name:s,value:e,silent:false}}function k(s){return {type:"optional",pattern:s,silent:false}}function D(...s){if(!s.length)throw new Error("choice(): at least one pattern required");return {type:"choice",patterns:s,silent:false}}function T(s,e=0,t=1/0,r){if(e<0)throw new Error("repeat(): min cannot be negative");if(t<e)throw new Error("repeat(): max cannot be less than min");return {type:"repeat",pattern:s,min:e,max:t,separator:r,silent:false}}function H(s,e){return T(s,1,1/0,e)}function x(s,e){return T(s,0,1/0,e)}function N(s,e){return O(T(s,0,1,e))}function y(...s){if(!s.length)throw new Error("seq(): at least one pattern required");return {type:"seq",patterns:s,silent:false}}function U(s,e){if(!s)throw new Error("rule(): name must be a non-empty string");return {type:"rule",name:s,params:e,silent:false}}function O(s){return {...s,silent:true}}function $(s){return {...s,silent:false}}function q(s){return {type:"pratt",table:s,silent:false}}function S(s,e){return {type:"conditional",pattern:s,predicate:e,silent:false}}function V(s,e){return S(s,e)}function B(s,e){return S(s,e)}function K(s){return {type:"action",fn:s,silent:false}}function X(s){return {type:"not",pattern:s,silent:false}}function I(s){return {type:"lookahead",pattern:s,silent:false}}function j(s){return I(s)}function W(s){return {prefix:new Map(Object.entries(s.prefix??{})),infix:new Map(Object.entries(s.infix??{}))}}function z(s,e,t){let{min:r=0,trailingOk:n=false}=t??{},i;return r===0?n?i=k(y(T(s,1,1/0),x(y(e,s)),k(e))):i=k(y(T(s,1,1/0),x(y(e,s)))):r===1?n?i=y(T(s,1,1/0),x(y(e,s)),k(e)):i=y(T(s,1,1/0),x(y(e,s))):n?i=y(T(s,r,1/0),x(y(e,s)),k(e)):i=y(T(s,r,1/0),x(y(e,s))),i}function G(s,e,t){return y(e,s,t)}function Q(s,e,t){return y(s,e,t)}function Y(s,e,t){return {cond:s,msg:e,code:t??p.RECOVERY_CUSTOM}}var J={skipUntil(s){return {type:"skipUntil",tokens:Array.isArray(s)?s:[s]}}};export{p as ERRORS,g as Parser,h as Result,K as action,Q as between,W as buildPrattTable,D as choice,S as conditional,L as createRule,z as delimited,Y as error,J as errorRecoveryStrategies,E as globalTokenMap,B as ifCondition,I as lookahead,$ as loud,X as not,H as oneOrMore,k as optional,F as parse,j as peek,q as pratt,w as registerTokenMap,T as repeat,U as rule,y as seq,O as silent,G as surrounded,M as token,V as when,x as zeroOrMore,N as zeroOrOne};
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@minelang-ts/parser",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Mine programming language parser.",
|
|
5
|
+
"keywords": ["mine", "programming", "language", "parser"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": {
|
|
8
|
+
"email": "maysara.elshewehy@gmail.com",
|
|
9
|
+
"name": "Maysara Elshewehy",
|
|
10
|
+
"url": "https://github.com/maysara-elshewehy"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"homepage": "https://github.com/minelang-ts/parser#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/minelang-ts/parser/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/minelang-ts/parser.git"
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": ["dist"],
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"require": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"bun": ">=1.3.3"
|
|
33
|
+
},
|
|
34
|
+
"pkg": {
|
|
35
|
+
"type": "pkg"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"bun": "^1.3.14"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@minelang-ts/lexer": "^0.0.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@eslint/js": "^10.0.1",
|
|
46
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
47
|
+
"@types/bun": "^1.3.14",
|
|
48
|
+
"@types/node": "^26.0.1",
|
|
49
|
+
"bun-plugin-dts": "^0.4.0",
|
|
50
|
+
"bun-types": "^1.3.14",
|
|
51
|
+
"ts-node": "^10.9.2",
|
|
52
|
+
"tsup": "^8.5.1",
|
|
53
|
+
"typescript": "^6.0.3",
|
|
54
|
+
"typescript-eslint": "^8.62.1"
|
|
55
|
+
}
|
|
56
|
+
}
|