Skip to content

Instantly share code, notes, and snippets.

@sue71
Last active February 2, 2018 03:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sue71/07bc0d172e1a5b1d8d1d0fca64dcf7b8 to your computer and use it in GitHub Desktop.
Save sue71/07bc0d172e1a5b1d8d1d0fca64dcf7b8 to your computer and use it in GitHub Desktop.
simple LL-parser
declare const process: any;
const stdin = process.openStdin();
enum TokenType {
VarName = 'VarName',
Assign = 'Assign',
Plus = 'Plus',
Minus = 'Minus',
Multi = 'Multi',
Divi = 'Divi',
Number = 'Number',
Print = 'Print',
Lparen = 'Lparen',
Rparen = 'Rparen',
Unknown = 'Unknown'
}
const isToken = (token: Token, type: TokenType) => {
return token.type === type;
};
class Token {
constructor(
public type: TokenType, public value: number, public raw: string) {}
}
class Parser {
stack = [];
vars = [];
currentLine: string = '';
currentPointer: number = 0;
token = undefined;
constructor() {
stdin.addListener('data', d => {
const inputText = d.toString().trim();
this.currentLine = inputText;
this.currentPointer = 0;
try {
this.statement();
} catch (e) {
console.error(e);
process.exit(1);
}
});
}
// 文
statement() {
let token = this.nextToken();
switch (token.type) {
case TokenType.Print:
this.expression();
const value = this.stack.pop();
console.info(value);
break;
case TokenType.VarName:
if (!this.isToken(this.nextToken(), TokenType.Assign)) {
throw new Error(`Invalid token: ${token.value}`);
}
this.expression();
this.setVar(token.value, this.popStack());
break;
}
}
// 式
expression() {
let token = this.term();
while (this.isToken(token, TokenType.Plus) ||
this.isToken(token, TokenType.Minus)) {
let op = token.type;
token = this.term();
this.operate(op);
}
}
// 項
term(): Token {
let token = this.factor();
while (this.isToken(token, TokenType.Multi) ||
this.isToken(token, TokenType.Divi)) {
let op = token.type;
token = this.factor();
this.operate(op);
}
return token;
}
// 因子
factor(): Token {
let token = this.nextToken();
switch (token.type) {
case TokenType.VarName:
this.pushStack(this.vars[token.value]);
return this.nextToken();
case TokenType.Number:
this.pushStack(token.value);
return this.nextToken();
case TokenType.Lparen:
token = this.nextToken();
this.expression();
token = this.nextToken();
if (!this.isToken(token, TokenType.Rparen)) {
throw new Error('');
}
return this.nextToken();
}
return this.nextToken();
}
// 演算実行
operate(op: TokenType) {
let b = this.stack.pop(), a = this.stack.pop();
switch (op) {
case TokenType.Plus:
this.pushStack(a + b);
break;
case TokenType.Minus:
this.pushStack(a - b);
break;
case TokenType.Multi:
this.pushStack(a * b);
break;
case TokenType.Divi:
this.pushStack(a / b);
break;
}
}
character(): string {
let character = this.currentLine[this.currentPointer];
if (/\s/.test(character)) {
return this.nextCharacter();
}
return character;
}
nextCharacter(): string {
let character = this.currentLine[++this.currentPointer];
if (/\s/.test(character)) {
return this.nextCharacter();
}
return character;
}
nextToken(): Token {
let character = this.character();
let token = new Token(TokenType.Unknown, 0, character);
if (character === undefined) {
return token;
}
if (this.isNumber(character)) {
let numberText = '';
while (this.isNumber(character)) {
numberText += character;
character = this.nextCharacter();
}
token.type = TokenType.Number;
token.value = parseInt(numberText, 10);
return token;
}
if (this.isVariable(character)) {
token.type = TokenType.VarName;
token.value = character.charCodeAt(0) - 'a'.charCodeAt(0);
this.nextCharacter();
return token;
}
switch (character) {
case '+':
token.type = TokenType.Plus;
break;
case '-':
token.type = TokenType.Minus;
break;
case '*':
token.type = TokenType.Multi;
break;
case '/':
token.type = TokenType.Divi;
break;
case '=':
token.type = TokenType.Assign;
break;
case '?':
token.type = TokenType.Print;
break;
case '(':
token.type = TokenType.Lparen;
break;
case ')':
token.type = TokenType.Rparen;
break;
default:
token.type = TokenType.Unknown;
}
this.nextCharacter();
return token;
}
isToken(token: Token, type: TokenType) {
return token.type == type;
}
isNumber(text: string): boolean {
return /\d+/.test(text);
}
isVariable(text: string): boolean {
return /[a-z]+/.test(text);
}
setVar(index: number, value: number) {
this.vars[index] = value;
}
pushStack(token) {
this.stack.push(token);
}
popStack(): number {
return this.stack.pop();
}
}
new Parser();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment