jaycc
Version:
### the Javascript C Compiler
201 lines (200 loc) • 5.02 kB
JavaScript
/*
int main() {
return 5;
}
*/
let regexplist;
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' }
];
let keyword;
let whitespace;
let identifier;
let openround;
let closeround;
let opencurly;
let closecurly;
let semicolon;
let numberconst;
let eof;
let eq;
keyword = (arg = '') => ({
tag: 'keyword',
contents: arg
});
whitespace = () => ({
tag: 'whitespace',
contents: undefined
});
identifier = (arg = '') => ({
tag: 'identifier',
contents: arg
});
openround = () => ({
tag: 'openround',
contents: undefined
});
closeround = () => ({
tag: 'closeround',
contents: undefined
});
opencurly = () => ({
tag: 'opencurly',
contents: undefined
});
closecurly = () => ({
tag: 'closecurly',
contents: undefined
});
semicolon = () => ({
tag: 'semicolon',
contents: undefined
});
numberconst = (arg = 0) => ({
tag: 'numberconst',
contents: arg
});
eof = () => ({
tag: '<eof>',
contents: undefined
});
eq = (t1, t2) => (t1 && t2 && (t1.tag == t2.tag));
class Lexer {
input;
token;
constructor(str) {
this.input = str;
this.lex();
return this;
}
lex() {
let regexp;
let tokenname;
let entry;
let input;
let inputarr;
let n;
let matcharr;
let token;
let tmp;
let numconst;
input = this.input;
if (!input.length) {
this.token = eof();
return void 0;
}
entry = regexplist.find((x) => (input.match(x.regexp)) ? true : false);
if (!entry)
throw "Parse error (lexer)";
regexp = entry.regexp;
tokenname = entry.tokenname;
matcharr = input.match(regexp);
// 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 {
input;
tokens;
constructor(str) {
let ret;
if (!str)
throw "Empty input string";
this.input = str;
this.tokens = [];
ret = this.lexfile();
if (!ret)
throw "Parse error (lexing)";
}
lexfile() {
let ret;
ret = this.lexfile_([], this.input);
this.tokens = this.tokens.filter((x) => (!eq(x, whitespace())));
return ret;
}
lexfile_(ts, str) {
let tok;
let toks;
let str_;
let lxr;
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) {
if (err instanceof String)
console.error(err);
return false;
}
}
}
export { Lexer, FileLexer, eq };
export { keyword, whitespace, identifier, openround, closeround, opencurly, closecurly, semicolon, numberconst, eof };