jaycc
Version:
### the Javascript C Compiler
318 lines (275 loc) • 7.77 kB
text/typescript
/*
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 };