UNPKG

jaycc

Version:

### the Javascript C Compiler

318 lines (275 loc) 7.77 kB
/* int main() { return 5; } */ interface Iregexp { regexp:RegExp; tokenname:string; } let regexplist:Iregexp[]; regexplist = [ { regexp: /^\s/, tokenname: 'whitespace' }, { regexp: /^\(/, tokenname:'openround' }, { regexp: /^\)/, tokenname:'closeround' }, { regexp: /^{/, tokenname:'opencurly' }, { regexp: /^}/, tokenname:'closecurly' }, { regexp: /^;/, tokenname:'semicolon' }, { regexp: /^int/, tokenname:'keyword' }, { regexp: /^return/, tokenname:'keyword' }, { regexp: /^[0-9]+/, tokenname:'numberconst' }, { regexp: /^[a-zA-Z_'][a-zA-Z_0-9']+/, tokenname:'identifier' } ]; type Identifier = string; type EOF = '<eof>'; type Whitespace = ' '; type Openround = '('; type Closeround = ')'; type Opencurly = '{'; type Closecurly = '}'; type Semicolon = ';'; type Numberconst = number; type Keyword = 'int' | 'return'; type Token = Tkeyword | Twhitespace | Tidentifier | Topenround | Tcloseround | Topencurly | Tclosecurly | Tsemicolon | Tnumberconst | Teof; interface Twhitespace { tag: string; contents?: Whitespace; } interface Tidentifier { tag: string; contents?: Identifier; } interface Tkeyword { tag: string; contents?: Keyword; } interface Topenround { tag: string; contents?: Openround; } interface Tcloseround { tag: string; contents?: Closeround; } interface Topencurly { tag: string; contents?: Opencurly; } interface Tclosecurly { tag: string; contents?: Closecurly; } interface Tsemicolon { tag: string; contents?: Semicolon; } interface Tnumberconst { tag: string; contents?: Numberconst; } interface Teof { tag: string; contents?: EOF; } let keyword:(arg?:string)=>Tkeyword; let whitespace:()=>Twhitespace; let identifier:(arg?:string)=>Tidentifier; let openround:()=>Topenround; let closeround:()=>Tcloseround; let opencurly:()=>Topencurly; let closecurly:()=>Tclosecurly; let semicolon:()=>Tsemicolon; let numberconst:(arg?:number)=>Tnumberconst; let eof:()=>Teof; let eq:(t1:Token, t2:Token)=>boolean; keyword = (arg:string=''): Tkeyword => ({ tag: 'keyword', contents: arg as Keyword }); whitespace = (): Twhitespace => ({ tag: 'whitespace', contents: undefined as unknown as Whitespace }); identifier = (arg:string=''): Tidentifier => ({ tag: 'identifier', contents: arg as Identifier }); openround = (): Topenround => ({ tag: 'openround', contents: undefined as unknown as Openround }); closeround = (): Tcloseround => ({ tag: 'closeround', contents: undefined as unknown as Closeround }); opencurly = (): Topencurly => ({ tag: 'opencurly', contents: undefined as unknown as Opencurly }); closecurly = (): Tclosecurly => ({ tag: 'closecurly', contents: undefined as unknown as Closecurly }); semicolon = (): Tsemicolon => ({ tag: 'semicolon', contents: undefined as unknown as Semicolon }); numberconst = (arg:number=0): Tnumberconst => ({ tag: 'numberconst', contents: arg as Numberconst }); eof = (): Teof => ({ tag: '<eof>', contents: undefined as unknown as EOF }); eq = (t1:Token, t2:Token): boolean => (t1 && t2 && (t1.tag==t2.tag)); interface Ilexer { constructor: Function; lex:()=>void; input: string; token?: Token; } interface Ifilelexer { constructor: Function; lexfile:()=>boolean; lexfile_:(ts:Token[], str:string)=>boolean; input: string; tokens: Token[]; } class Lexer implements Ilexer { public input:string; public token?:Token; constructor(str:string) { this.input = str; this.lex(); return this; } public lex() { let regexp:RegExp; let tokenname:string; let entry:Iregexp|undefined; let input:string; let inputarr:string[]; let n:number; let matcharr:RegExpMatchArray; let token:Token|null; let tmp:string; let numconst:number; input = this.input; if (!input.length) { this.token = eof(); return void 0; } entry = regexplist.find((x:Iregexp): boolean => (input.match(x.regexp)) ? true : false); if (!entry) throw "Parse error (lexer)"; regexp = entry.regexp; tokenname = entry.tokenname; matcharr = input.match(regexp) as RegExpMatchArray; // console.log('matcharr: ', matcharr); n = matcharr[0].length; inputarr = input.split(''); while (n--) inputarr.shift(); input = inputarr.join(''); token = null; switch(tokenname) { case 'whitespace': token = whitespace(); break; case 'openround': token = openround(); break; case 'closeround': token = closeround(); break; case 'opencurly': token = opencurly(); break; case 'closecurly': token = closecurly(); break; case 'semicolon': token = semicolon(); break; case 'identifier': tmp = matcharr[0]; token = identifier(tmp); break; case 'keyword': tmp = matcharr[0]; token = keyword(tmp); break; case 'numberconst': tmp = matcharr[0]; numconst = Number.parseInt(tmp); token = numberconst(numconst); break; default: throw 'Parse error (lexer)'; } if (!token) throw 'Parse error (lexer)'; this.input = input; this.token = token; return void 0; } } class FileLexer implements Ifilelexer { public input:string; public tokens:Token[]; constructor(str:string) { let ret:boolean; if (!str) throw "Empty input string"; this.input = str; this.tokens = []; ret = this.lexfile(); if (!ret) throw "Parse error (lexing)"; } public lexfile(): boolean { let ret:boolean; ret = this.lexfile_([], this.input); this.tokens = this.tokens.filter((x:Token): boolean => (!eq(x,whitespace()))); return ret; } public lexfile_(ts:Token[], str:string): boolean { let tok:Token; let toks:Token[]; let str_:string; let lxr:Lexer; try { lxr = new Lexer(str); if (lxr.token) { tok = lxr.token; str_ = lxr.input; } else throw "Parse error (lexer)" toks = ts; toks.push(tok); if (eq(tok, eof())) { this.tokens = toks; this.input = ''; return true; } else return this.lexfile_(toks, str_); } catch(err:unknown) { if (err instanceof String) console.error(err as string); return false; } } } export { Lexer, FileLexer, eq }; export type { Token }; export { keyword, whitespace, identifier, openround, closeround, opencurly, closecurly, semicolon, numberconst, eof }; export type { Identifier, EOF, Whitespace, Openround, Closeround, Opencurly, Closecurly, Semicolon, Numberconst, Keyword };