UNPKG

tscc-compiler

Version:

An LALR(1) compiler compiler written in Typescript

279 lines (269 loc) 7.12 kB
import { PatternException as E } from './pattern-exception.js'; import { Action,Arc,EndAction,State } from './state.js'; import { CharSet } from './char-set'; function stackReader(str: string, strs?: {[s: string]: string}){ var stack = [{ sptr: 0,s: str,name: '' }]; var top = stack[0]; function checkNested(name: string){ for(var i = 0;i < stack.length;i++){ if(stack[i].name === name){ throw new E('cannot use pattern "' + name + '" which leads to loop reference'); } } } return { next: function(){ top.sptr++; if(top.sptr >= top.s.length){ stack.length > 1 && stack.pop(); top = stack[stack.length - 1]; } }, peek: function(){ return top.s[top.sptr] || null; }, pushTo: function(name){ var nn = strs ? strs[name] : null; if(!nn){ throw new E('undefined name "' + name + '"'); } checkNested(name); stack.push({ sptr: 0,s: '(' + nn + ')',name: name }); top = stack[stack.length - 1]; } }; } /** * compile a regular expression into e-NFA * @param {string} input * @param {number} stateCount * @param {Object.<string,string>|undefined} regs * @returns {{result: State,stateCount: number,tail: State}} */ export function compile<T>(input: string, regs: {[s: string]: string} = {}){ var reader = stackReader(input,regs); var c = reader.peek(); function nc(){ reader.next(); c = reader.peek(); } function notEof(reason?: string){ if(c === null){ throw new E('unexpected end of string' + (reason ? ', ' + reason : '')); } } function ns(){ var s = new State<T>(); // s.index = stateCount++; return s; } function eof(){ return c === null; } function expect(c1){ if(c !== c1){ throw new E('unexpected character "' + c + '",expecting "' + c1 + '"'); } nc(); } /** * rexp() -> simpleRE() ('|' simpleRE())* */ function rexp(start: State<T>): State<T>{ var ret = simpleRE(start); while(!eof() && c === '|'){ nc(); var es = ns(); start.epsilonTo(es); simpleRE(es).epsilonTo(ret); } return ret; } /** * simpleRE() -> ( basicRE() )+ */ function simpleRE(start: State<T>): State<T>{ var ret = start; do{ ret = basicRE(ret); }while(!eof() && c !== '|' && c !== ')'); return ret; } /** * basicRE() -> primitive() ['+'|'*'|'?'] */ function basicRE(start: State<T>): State<T>{ var holder = ns(); start.epsilonTo(holder); var ret = primitive(holder); if(c === '*'){ nc(); ret.epsilonTo(holder); var nn = ns(); holder.epsilonTo(nn); return nn; } else if(c === '+'){ nc(); var count = ns(); ret.epsilonTo(count); count.epsilonTo(holder); return count; } else if(c === '?'){ nc(); var nn2 = ns(); holder.epsilonTo(nn2); ret.epsilonTo(nn2); return nn2; } else { return ret; } } /** * primitive() -> * '(' rexpr() ')' * | '.' * | '[' set() ']' * | '{' NAME '}' * | * */ function primitive(start: State<T>): State<T>{ notEof(); if(c === '('){ nc(); let ret = rexp(start); expect(')'); return ret; } else if(c === '.'){ nc(); let ret = ns(); start.to(ret).chars.addAll(); return ret; } else if(c === '['){ nc(); var neg = c as string === '^'; neg && nc(); let ret = ns(); var set = start.to(ret).chars; neg && set.addAll(); while(c as string !== ']' && !eof()){ setItem(set,neg); } expect(']'); return ret; } else if(c === '{'){ nc(); var name = ''; while(c as string !== '}'){ notEof(); name += c; nc(); } nc(); reader.pushTo(name); c = reader.peek(); return simpleRE(start); } else { let ret = ns(); start.to(ret).chars.add(gchar()); return ret; } } function gchar(): number{ notEof(); if(c === '\\'){ nc(); let ret: number | string = c.charCodeAt(0); switch(c as string){ case 't': ret = '\t'; case 'n': ret = '\n'; case 'r': ret = '\r'; case 'x': nc(); var code = ''; while(c !== null && /[0-9a-fA-F]/.test(c)){ code += c; nc(); } return parseInt(code,16); default: ret = c; } nc(); return ret.charCodeAt(0); } else { var ret = c.charCodeAt(0); nc(); return ret; } } /** * setItem() -> * gchar() ['-'gchar()] */ function setItem(set: CharSet<any>, neg: boolean){ var s = gchar(); var from = s,to = s; if(c === '-'){ nc(); to = gchar(); if(to < from){ throw new E('left hand side must be larger than right hand side in wild card character (got "' + from.toString(16) + '" < "' + to.toString(16) + '")'); } } if(neg){ set.remove(from,to); } else { set.add(from,to); } } var head = ns(); head.isStart = true; var tail = rexp(head); return { result: head, tail: tail, }; } /** * compile a string into e-NFA,i.e.,regarde it as a regular * expression with all metacharacters escaped. * * @returns {{result: State,stateCount: number,tail: State}} */ export function compileRaw<T>(input: string){ var sptr = 0; var c = input.charAt(sptr); function ns(){ var s = new State<T>(); return s; } function nc(){ c = input.charAt(++sptr) || null; } function eof(){ return c === null; } var head = ns(); head.isStart = true; var tail = head; while(!eof()){ var s = ns(); tail.to(s).chars.add(c.charCodeAt(0)); tail = s; nc(); } return { result: head, tail: tail, }; }