Skip to content

Instantly share code, notes, and snippets.

@bakso
Last active July 24, 2016 15:00
Show Gist options
  • Save bakso/5fe7be46242c34a37e4d44acfa60f139 to your computer and use it in GitHub Desktop.
Save bakso/5fe7be46242c34a37e4d44acfa60f139 to your computer and use it in GitHub Desktop.
Caculator implement by lexer and parser
'use strict'
const EOF = "EOF"
class Lexer {
constructor(input) {
this.input = input
this.index = -1
this.currentChar = null
this.step()
}
nextToken() {
if(this.currentChar === undefined) {
return {
type: EOF
}
}
if(this.currentChar.trim() === '') {
this.step()
return this.nextToken()
}
var token = {}
if(this.matchNumber()) {
token.type = 'number'
token.text = this.number()
} else if(this.matchOperator()) {
token.type = 'operator'
token.text = this.operator()
this.step()
} else if(this.matchPair()) {
token.type = 'pair'
token.text = this.pair()
this.step()
} else {
throw new Error("Unexpect identifier: " + this.currentChar + " at column: " + (this.index + 1))
}
return token
}
step() {
this.currentChar = this.input[++this.index]
}
lastChar() {
return this.index >= this.input.length
}
matchNumber() {
return /[\d\.]/.test(this.currentChar)
}
number() {
var buf = []
buf.push(this.currentChar)
this.step()
while(!this.lastChar() && this.matchNumber()) {
buf.push(this.currentChar)
this.step()
}
return buf.join('')
}
matchOperator() {
var char = this.currentChar
return char === '+' || char === '-' || char === '*' || char === '/'
}
operator() {
return this.currentChar
}
matchPair() {
var char = this.currentChar
return char === '(' || char === ')'
}
pair() {
return this.currentChar
}
}
class Parser {
constructor(input) {
this.lexer = input
}
compute() {
return this.parseExpression()
}
getToken() {
var token
if(this.token) {
token = this.token
this.token = null
} else {
token = this.lexer.nextToken()
}
return token
}
unGetToken(token) {
this.token = token
}
parseExpression() {
var v1, v2
var v1 = this.parseTerm()
while(true) {
var token = this.getToken()
if(token.type !== 'operator' || (token.text !== '+' && token.text !== '-')) {
this.unGetToken(token)
break
}
v2 = this.parseTerm()
if(token.text === '+') {
v1 += v2
} else if(token.text === '-') {
v1 -= v2
}
}
console.log(v1)
return v1
}
parseTerm() {
var v1, v2
var v1 = this.parsePrimaryExpression()
while(true) {
var token = this.getToken()
if(token.type !== 'operator' || (token.text !== '*' && token.text !== '/')) {
this.unGetToken(token)
break
}
v2 = this.parsePrimaryExpression()
if(token.text === '*') {
v1 *= v2
} else if(token.text === '/') {
v1 /= v2
}
}
return v1
}
parsePrimaryExpression() {
var token = this.lexer.nextToken()
var neg = false
var ret = 0
if (token.type === 'operator' && (token.text === '-' || token.text === '+')) {
if (token.text === '-') {
neg = true
}
token = this.lexer.nextToken()
}
if(token.type === 'number') {
ret = parseInt(token.text, 10)
} else if(token.type === 'pair' && token.text === '(') {
ret = this.parseExpression()
var token = this.getToken()
if(token.type !== 'pair' || token.text !== ')') {
throw new Error('Expect ")" end.')
}
}
if (neg) {
ret = -ret
}
return ret
}
}
var lexer = new Lexer("-(11+1)*(-1/(3+1))")
var parser = new Parser(lexer)
console.log(parser.compute())
// while (true) {
// var token = lexer.nextToken()
// if (token.type === EOF) {
// break
// } else {
// console.log(token)
// }
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment