tscc-compiler
Version:
An LALR(1) compiler compiler written in Typescript
279 lines (269 loc) • 7.12 kB
text/typescript
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,
};
}