UNPKG

apg-lite

Version:

lightweight JavaScript APG parser

1 lines 16.7 kB
const Parser=function(){const e=identifiers,t=utilities,s=this,a="parser.js: Parser(): ";s.ast=void 0,s.stats=void 0,s.trace=void 0,s.callbacks=[];let n,r,o,i,h,c,l,d=0,p=0,T=0,u=0,g=0,m=new function(){this.state=e.ACTIVE,this.phraseLength=0,this.refresh=()=>{this.state=e.ACTIVE,this.phraseLength=0}};s.parse=(f,A,b,E)=>{const M=`${a}parse(): `;d=0,p=0,T=0,u=0,g=0,n=void 0,r=void 0,o=void 0,i=void 0,m.refresh(),h=void 0,c=void 0,l=void 0,i=t.stringToChars(b),n=f.rules,r=f.udts;const $=A.toLowerCase();let L;for(const e in n)if(n.hasOwnProperty(e)&&$===n[e].lower){L=n[e].index;break}if(void 0===L)throw new Error(`${M}start rule name '${startRule}' not recognized`);(()=>{const e=`${a}initializeCallbacks(): `;let t,o;for(h=[],c=[],t=0;t<n.length;t+=1)h[t]=void 0;for(t=0;t<r.length;t+=1)c[t]=void 0;const i=[];for(t=0;t<n.length;t+=1)i.push(n[t].lower);for(t=0;t<r.length;t+=1)i.push(r[t].lower);for(const a in s.callbacks)if(s.callbacks.hasOwnProperty(a)){if(t=i.indexOf(a.toLowerCase()),t<0)throw new Error(`${e}syntax callback '${a}' not a rule or udt name`);if(o=s.callbacks[a]?s.callbacks[a]:void 0,"function"!=typeof o&&void 0!==o)throw new Error(`${e}syntax callback[${a}] must be function reference or falsy)`);t<n.length?h[t]=o:c[t-n.length]=o}})(),s.trace&&s.trace.init(n,r,i),s.stats&&s.stats.init(n,r),s.ast&&s.ast.init(n,r,i),l=E,o=[{type:e.RNM,index:L}],w(0,0),o=void 0;let C=!1;switch(m.state){case e.ACTIVE:throw new Error(`${M}final state should never be 'ACTIVE'`);case e.NOMATCH:C=!1;break;case e.EMPTY:case e.MATCH:C=m.phraseLength===i.length;break;default:throw new Error("unrecognized state")}return{success:C,state:m.state,stateName:e.idName(m.state),length:i.length,matched:m.phraseLength,maxMatched:g,maxTreeDepth:T,nodeHits:u}};const f=(t,s,n,r)=>{if(s.phraseLength>n){let e=`${a}opRNM(${t.name}): callback function error: `;throw e+=`sysData.phraseLength: ${s.phraseLength}`,e+=` must be <= remaining chars: ${n}`,new Error(e)}switch(s.state){case e.ACTIVE:if(!r)throw new Error(`${a}opRNM(${t.name}): callback function return error. ACTIVE state not allowed.`);break;case e.EMPTY:s.phraseLength=0;break;case e.MATCH:0===s.phraseLength&&(s.state=e.EMPTY);break;case e.NOMATCH:s.phraseLength=0;break;default:throw new Error(`${a}opRNM(${t.name}): callback function return error. Unrecognized return state: ${s.state}`)}},A=(t,h)=>{let p,T,u;const g=o[t],f=r[g.index];m.UdtIndex=f.index,d||(u=s.ast&&s.ast.udtDefined(g.index),u&&(T=n.length+g.index,p=s.ast.getLength(),s.ast.down(T,f.name)));const A=i.length-h;c[g.index](m,i,h,l),((t,s,n)=>{if(s.phraseLength>n){let e=`${a}opUDT(${t.name}): callback function error: `;throw e+=`sysData.phraseLength: ${s.phraseLength}`,e+=` must be <= remaining chars: ${n}`,new Error(e)}switch(s.state){case e.ACTIVE:throw new Error(`${a}opUDT(${t.name}) ACTIVE state return not allowed.`);case e.EMPTY:if(!t.empty)throw new Error(`${a}opUDT(${t.name}) may not return EMPTY.`);s.phraseLength=0;break;case e.MATCH:if(0===s.phraseLength){if(!t.empty)throw new Error(`${a}opUDT(${t.name}) may not return EMPTY.`);s.state=e.EMPTY}break;case e.NOMATCH:s.phraseLength=0;break;default:throw new Error(`${a}opUDT(${t.name}): callback function return error. Unrecognized return state: ${s.state}`)}})(f,m,A),d||u&&(m.state===e.NOMATCH?s.ast.setLength(p):s.ast.up(T,f.name,h,m.phraseLength))},w=(t,r)=>{const c=`${a}opExecute(): `,b=o[t];switch(u+=1,p>T&&(T=p),p+=1,m.refresh(),s.trace&&s.trace.down(b,r),b.type){case e.ALT:((t,s)=>{const a=o[t];for(let t=0;t<a.children.length&&(w(a.children[t],s),m.state===e.NOMATCH);t+=1);})(t,r);break;case e.CAT:((t,a)=>{let n,r,i,h;const c=o[t];s.ast&&(r=s.ast.getLength()),n=!0,i=a,h=0;for(let t=0;t<c.children.length;t+=1){if(w(c.children[t],i),m.state===e.NOMATCH){n=!1;break}i+=m.phraseLength,h+=m.phraseLength}n?(m.state=0===h?e.EMPTY:e.MATCH,m.phraseLength=h):(m.state=e.NOMATCH,m.phraseLength=0,s.ast&&s.ast.setLength(r))})(t,r);break;case e.REP:((t,a)=>{let n,r,h,c;const l=o[t];if(0===l.max)return m.state=e.EMPTY,void(m.phraseLength=0);for(r=a,h=0,c=0,s.ast&&(n=s.ast.getLength());!(r>=i.length)&&(w(t+1,r),m.state!==e.NOMATCH)&&m.state!==e.EMPTY&&(c+=1,h+=m.phraseLength,r+=m.phraseLength,c!==l.max););m.state===e.EMPTY||c>=l.min?(m.state=0===h?e.EMPTY:e.MATCH,m.phraseLength=h):(m.state=e.NOMATCH,m.phraseLength=0,s.ast&&s.ast.setLength(n))})(t,r);break;case e.RNM:((t,a)=>{let r,c,p;const T=o[t],u=n[T.index],g=h[u.index];if(d||(c=s.ast&&s.ast.ruleDefined(T.index),c&&(r=s.ast.getLength(),s.ast.down(T.index,n[T.index].name))),g){const t=i.length-a;g(m,i,a,l),f(u,m,t,!0),m.state===e.ACTIVE&&(p=o,o=u.opcodes,w(0,a),o=p,g(m,i,a,l),f(u,m,t,!1))}else p=o,o=u.opcodes,w(0,a,m),o=p;d||c&&(m.state===e.NOMATCH?s.ast.setLength(r):s.ast.up(T.index,u.name,a,m.phraseLength))})(t,r);break;case e.TRG:((t,s)=>{const a=o[t];m.state=e.NOMATCH,s<i.length&&a.min<=i[s]&&i[s]<=a.max&&(m.state=e.MATCH,m.phraseLength=1)})(t,r);break;case e.TBS:((t,s)=>{const a=o[t],n=a.string.length;if(m.state=e.NOMATCH,s+n<=i.length){for(let e=0;e<n;e+=1)if(i[s+e]!==a.string[e])return;m.state=e.MATCH,m.phraseLength=n}})(t,r);break;case e.TLS:((t,s)=>{let a;const n=o[t];m.state=e.NOMATCH;const r=n.string.length;if(0!==r){if(s+r<=i.length){for(let e=0;e<r;e+=1)if(a=i[s+e],a>=65&&a<=90&&(a+=32),a!==n.string[e])return;m.state=e.MATCH,m.phraseLength=r}}else m.state=e.EMPTY})(t,r);break;case e.UDT:A(t,r);break;case e.AND:((t,s)=>{switch(d+=1,w(t+1,s),d-=1,m.phraseLength=0,m.state){case e.EMPTY:case e.MATCH:m.state=e.EMPTY;break;case e.NOMATCH:m.state=e.NOMATCH;break;default:throw new Error(`opAND: invalid state ${m.state}`)}})(t,r);break;case e.NOT:((t,s)=>{switch(d+=1,w(t+1,s),d-=1,m.phraseLength=0,m.state){case e.EMPTY:case e.MATCH:m.state=e.NOMATCH;break;case e.NOMATCH:m.state=e.EMPTY;break;default:throw new Error(`opNOT: invalid state ${m.state}`)}})(t,r);break;default:throw new Error(`${c}unrecognized operator`)}d||r+m.phraseLength>g&&(g=r+m.phraseLength),s.stats&&s.stats.collect(b,m),s.trace&&s.trace.up(b,m.state,r,m.phraseLength),p-=1}},Ast=function(){const e=identifiers,t=utilities,s=this;let a,n,r,o=0;const i=[],h=[],c=[];function l(e){let t="";for(;e-- >0;)t+=" ";return t}s.callbacks=[],s.init=(e,t,l)=>{let d;h.length=0,c.length=0,o=0,a=e,n=t,r=l;const p=[];for(d=0;d<a.length;d+=1)p.push(a[d].lower);for(d=0;d<n.length;d+=1)p.push(n[d].lower);for(o=a.length+n.length,d=0;d<o;d+=1)i[d]=void 0;for(const e in s.callbacks)if(s.callbacks.hasOwnProperty(e)){const t=e.toLowerCase();if(d=p.indexOf(t),d<0)throw new Error(`parser.js: Ast()): init: node '${e}' not a rule or udt name`);i[d]=s.callbacks[e]}},s.ruleDefined=e=>!!i[e],s.udtDefined=e=>!!i[a.length+e],s.down=(t,s)=>{const a=c.length;return h.push(a),c.push({name:s,thisIndex:a,thatIndex:void 0,state:e.SEM_PRE,callbackIndex:t,phraseIndex:void 0,phraseLength:void 0,stack:h.length}),a},s.up=(t,s,a,n)=>{const r=c.length,o=h.pop();return c.push({name:s,thisIndex:r,thatIndex:o,state:e.SEM_POST,callbackIndex:t,phraseIndex:a,phraseLength:n,stack:h.length}),c[o].thatIndex=r,c[o].phraseIndex=a,c[o].phraseLength=n,r},s.translate=t=>{let s,a;for(let n=0;n<c.length;n+=1)a=c[n],s=i[a.callbackIndex],s&&(a.state===e.SEM_PRE?s(e.SEM_PRE,r,a.phraseIndex,a.phraseLength,t):s&&s(e.SEM_POST,r,a.phraseIndex,a.phraseLength,t))},s.setLength=e=>{c.length=e,h.length=e>0?c[e-1].stack:0},s.getLength=()=>c.length,s.toXml=()=>{let s="",a=0;return s+='<?xml version="1.0" encoding="utf-8"?>\n',s+=`<root nodes="${c.length/2}" characters="${r.length}">\n`,s+="\x3c!-- input string --\x3e\n",s+=l(a+2),s+=t.charsToString(r),s+="\n",c.forEach((n=>{n.state===e.SEM_PRE?(a+=1,s+=l(a),s+=`<node name="${n.name}" index="${n.phraseIndex}" length="${n.phraseLength}">\n`,s+=l(a+2),s+=t.charsToString(r,n.phraseIndex,n.phraseLength),s+="\n"):(s+=l(a),s+=`</node>\x3c!-- name="${n.name}" --\x3e\n`,a-=1)})),s+="</root>\n",s}},Trace=function(){const e=identifiers,t=utilities,s="parser.js: Trace(): ";let a,n,r,o="",i=0;const h=this,c=e=>{let t="",s=0;if(e>=0)for(;e--;)s+=1,5===s?(t+="|",s=0):t+=".";return t};h.init=(e,t,s)=>{n=e,r=t,a=s};const l=a=>{let o;switch(a.type){case e.ALT:o="ALT";break;case e.CAT:o="CAT";break;case e.REP:o=a.max===1/0?`REP(${a.min},inf)`:`REP(${a.min},${a.max})`;break;case e.RNM:o=`RNM(${n[a.index].name})`;break;case e.TRG:o=`TRG(${a.min},${a.max})`;break;case e.TBS:o=a.string.length>6?`TBS(${t.charsToString(a.string,0,3)}...)`:`TBS(${t.charsToString(a.string,0,6)})`;break;case e.TLS:o=a.string.length>6?`TLS(${t.charsToString(a.string,0,3)}...)`:`TLS(${t.charsToString(a.string,0,6)})`;break;case e.UDT:o=`UDT(${r[a.index].name})`;break;case e.AND:o="AND";break;case e.NOT:o="NOT";break;default:throw new Error(`${s}Trace: opName: unrecognized opcode`)}return o};h.down=(e,s)=>{const n=c(i),r=Math.min(100,a.length-s);let h=t.charsToString(a,s,r);r<a.length-s&&(h+="..."),h=`${n}|-|[${l(e)}]${h}\n`,o+=h,i+=1},h.up=(n,r,h,d)=>{const p=`${s}trace.up: `;i-=1;const T=c(i);let u,g,m;switch(r){case e.EMPTY:m="|E|",g="''";break;case e.MATCH:m="|M|",u=Math.min(100,d),g=u<d?`'${t.charsToString(a,h,u)}...'`:`'${t.charsToString(a,h,u)}'`;break;case e.NOMATCH:m="|N|",g="";break;default:throw new Error(`${p} unrecognized state`)}g=`${T}${m}[${l(n)}]${g}\n`,o+=g},h.displayTrace=()=>o},Stats=function(){const e=identifiers;let t,s,a;const n=[],r=[],o=[];this.init=(e,a)=>{t=e,s=a,p()},this.collect=(t,s)=>{T(a,s.state,s.phraseLength),T(n[t.type],s.state,s.phraseLength),t.type===e.RNM&&T(r[t.index],s.state,s.phraseLength),t.type===e.UDT&&T(o[t.index],s.state,s.phraseLength)},this.displayStats=()=>{let t="";const s={match:0,empty:0,nomatch:0,total:0},a=(e,t,a,n,r)=>{s.match+=t,s.empty+=a,s.nomatch+=n,s.total+=r;return`${e} | ${i(t)} | ${i(a)} | ${i(n)} | ${i(r)} |\n`};return t+=" OPERATOR STATS\n",t+=" | MATCH | EMPTY | NOMATCH | TOTAL |\n",t+=a(" ALT",n[e.ALT].match,n[e.ALT].empty,n[e.ALT].nomatch,n[e.ALT].total),t+=a(" CAT",n[e.CAT].match,n[e.CAT].empty,n[e.CAT].nomatch,n[e.CAT].total),t+=a(" REP",n[e.REP].match,n[e.REP].empty,n[e.REP].nomatch,n[e.REP].total),t+=a(" RNM",n[e.RNM].match,n[e.RNM].empty,n[e.RNM].nomatch,n[e.RNM].total),t+=a(" TRG",n[e.TRG].match,n[e.TRG].empty,n[e.TRG].nomatch,n[e.TRG].total),t+=a(" TBS",n[e.TBS].match,n[e.TBS].empty,n[e.TBS].nomatch,n[e.TBS].total),t+=a(" TLS",n[e.TLS].match,n[e.TLS].empty,n[e.TLS].nomatch,n[e.TLS].total),t+=a(" UDT",n[e.UDT].match,n[e.UDT].empty,n[e.UDT].nomatch,n[e.UDT].total),t+=a(" AND",n[e.AND].match,n[e.AND].empty,n[e.AND].nomatch,n[e.AND].total),t+=a(" NOT",n[e.NOT].match,n[e.NOT].empty,n[e.NOT].nomatch,n[e.NOT].total),t+=a("TOTAL",s.match,s.empty,s.nomatch,s.total),t},this.displayHits=e=>{let t="";const s=(e,t,s,n,r)=>{a.match+=e,a.empty+=t,a.nomatch+=s,a.total+=n;return`| ${i(e)} | ${i(t)} | ${i(s)} | ${i(n)} | ${r}\n`};"string"==typeof e&&"a"===e.toLowerCase()[0]?(r.sort(h),o.sort(h),t+=" RULES/UDTS ALPHABETICALLY\n"):"string"==typeof e&&"i"===e.toLowerCase()[0]?(r.sort(l),o.sort(l),t+=" RULES/UDTS BY INDEX\n"):(r.sort(c),o.sort(c),t+=" RULES/UDTS BY HIT COUNT\n"),t+="| MATCH | EMPTY | NOMATCH | TOTAL | NAME\n";for(let e=0;e<r.length;e+=1){let a=r[e];a.total&&(t+=s(a.match,a.empty,a.nomatch,a.total,a.name))}for(let e=0;e<o.length;e+=1){let a=o[e];a.total&&(t+=s(a.match,a.empty,a.nomatch,a.total,a.name))}return t};const i=e=>e<10?` ${e}`:e<100?` ${e}`:e<1e3?` ${e}`:e<1e4?` ${e}`:e<1e5?` ${e}`:e<1e6?` ${e}`:`${e}`,h=(e,t)=>e.lower<t.lower?-1:e.lower>t.lower?1:0,c=(e,t)=>e.total<t.total?1:e.total>t.total?-1:h(e,t),l=(e,t)=>e.index<t.index?-1:e.index>t.index?1:0,d=function(){this.empty=0,this.match=0,this.nomatch=0,this.total=0},p=()=>{n.length=0,a=new d,n[e.ALT]=new d,n[e.CAT]=new d,n[e.REP]=new d,n[e.RNM]=new d,n[e.TRG]=new d,n[e.TBS]=new d,n[e.TLS]=new d,n[e.UDT]=new d,n[e.AND]=new d,n[e.NOT]=new d,r.length=0;for(let e=0;e<t.length;e+=1)r.push({empty:0,match:0,nomatch:0,total:0,name:t[e].name,lower:t[e].lower,index:t[e].index});if(s.length>0){o.length=0;for(let e=0;e<s.length;e+=1)o.push({empty:0,match:0,nomatch:0,total:0,name:s[e].name,lower:s[e].lower,index:s[e].index})}},T=(t,s)=>{switch(t.total+=1,s){case e.EMPTY:t.empty+=1;break;case e.MATCH:t.match+=1;break;case e.NOMATCH:t.nomatch+=1;break;default:throw new Error(`parser.js: Stats(): collect(): incStat(): unrecognized state: ${s}`)}}},utilities={stringToChars:e=>[...e].map((e=>e.codePointAt(0))),charsToString:(e,t,s)=>{let a=e;for(;!(void 0===t||t<0);){if(void 0===s){a=e.slice(t);break}if(s<=0)return"";a=e.slice(t,t+s);break}return String.fromCodePoint(...a)}},identifiers={ALT:1,CAT:2,REP:3,RNM:4,TRG:5,TBS:6,TLS:7,UDT:11,AND:12,NOT:13,ACTIVE:100,MATCH:101,EMPTY:102,NOMATCH:103,SEM_PRE:200,SEM_POST:201,SEM_OK:300,idName:e=>{switch(e){case identifiers.ALT:return"ALT";case identifiers.CAT:return"CAT";case identifiers.REP:return"REP";case identifiers.RNM:return"RNM";case identifiers.TRG:return"TRG";case identifiers.TBS:return"TBS";case identifiers.TLS:return"TLS";case identifiers.UDT:return"UDT";case identifiers.AND:return"AND";case identifiers.NOT:return"NOT";case identifiers.ACTIVE:return"ACTIVE";case identifiers.EMPTY:return"EMPTY";case identifiers.MATCH:return"MATCH";case identifiers.NOMATCH:return"NOMATCH";case identifiers.SEM_PRE:return"SEM_PRE";case identifiers.SEM_POST:return"SEM_POST";case identifiers.SEM_OK:return"SEM_OK";default:return"UNRECOGNIZED STATE"}}},Grammar=function(){this.grammarObject="grammarObject",this.rules=[],this.rules[0]={name:"S",lower:"s",index:0,isBkr:!1},this.rules[1]={name:"AB",lower:"ab",index:1,isBkr:!1},this.rules[2]={name:"BC",lower:"bc",index:2,isBkr:!1},this.rules[3]={name:"a",lower:"a",index:3,isBkr:!1},this.rules[4]={name:"b",lower:"b",index:4,isBkr:!1},this.rules[5]={name:"c",lower:"c",index:5,isBkr:!1},this.udts=[],this.rules[0].opcodes=[],this.rules[0].opcodes[0]={type:2,children:[1,6,8,9]},this.rules[0].opcodes[1]={type:12},this.rules[0].opcodes[2]={type:2,children:[3,4]},this.rules[0].opcodes[3]={type:4,index:1},this.rules[0].opcodes[4]={type:13},this.rules[0].opcodes[5]={type:4,index:4},this.rules[0].opcodes[6]={type:3,min:0,max:1/0},this.rules[0].opcodes[7]={type:4,index:3},this.rules[0].opcodes[8]={type:4,index:2},this.rules[0].opcodes[9]={type:13},this.rules[0].opcodes[10]={type:4,index:5},this.rules[1].opcodes=[],this.rules[1].opcodes[0]={type:2,children:[1,2,4]},this.rules[1].opcodes[1]={type:4,index:3},this.rules[1].opcodes[2]={type:3,min:0,max:1},this.rules[1].opcodes[3]={type:4,index:1},this.rules[1].opcodes[4]={type:4,index:4},this.rules[2].opcodes=[],this.rules[2].opcodes[0]={type:2,children:[1,2,4]},this.rules[2].opcodes[1]={type:4,index:4},this.rules[2].opcodes[2]={type:3,min:0,max:1},this.rules[2].opcodes[3]={type:4,index:2},this.rules[2].opcodes[4]={type:4,index:5},this.rules[3].opcodes=[],this.rules[3].opcodes[0]={type:6,string:[97]},this.rules[4].opcodes=[],this.rules[4].opcodes[0]={type:6,string:[98]},this.rules[5].opcodes=[],this.rules[5].opcodes[0]={type:6,string:[99]},this.toString=function(){let e="";return e+="; It is known that the language the language L = {a^nb^nc^n | n >= 1} is not context free.\n",e+="; However, with the use of the look ahead operators we can construct a grammar\n",e+="; that accepts those phrases and only those phrases.\n",e+="S = &(AB !b) *a BC !c\n",e+="AB = a [AB] b\n",e+="BC = b [BC] c\n",e+='a = %s"a"\n',e+='b = %s"b"\n',e+='c = %s"c"\n','; It is known that the language the language L = {a^nb^nc^n | n >= 1} is not context free.\n; However, with the use of the look ahead operators we can construct a grammar\n; that accepts those phrases and only those phrases.\nS = &(AB !b) *a BC !c\nAB = a [AB] b\nBC = b [BC] c\na = %s"a"\nb = %s"b"\nc = %s"c"\n'}},doTrace=function(){const e=new Parser,t=new Grammar;e.stats=new Stats,e.trace=new Trace;const s=$("#string").val();$("#tabs-stats").html(""),$("#tabs-trace").html(""),$("#values").html("");const a=e.parse(t,"S",s);$("#result").html("Parser Result<br>"+JSON.stringify(a)),values="\nClick the Stats tab to see the operator and rule name hit count statistics.\n",values+="Click the Trace tab to see a full trace of the parser through the parse tree.\n",$("#values").html(values),$("#tabs-stats").html(e.stats.displayStats()),$("#tabs-stats").append("<br>"),$("#tabs-stats").append(e.stats.displayHits()),$("#tabs-stats").append("<br>"),$("#tabs-stats").append(e.stats.displayHits("a")),$("#tabs-stats").append("<br>"),$("#tabs-stats").append(e.stats.displayHits("i"));let n=e.trace.displayTrace();$("#tabs-trace").html(n)};$(document).ready((()=>{const e=new Grammar;$("#grammar-bnf").html(e.toString()),$("#parse").click(doTrace),$("#string").val("aaabbbccc"),$("#tabs").tabs()}));