diff options
-rw-r--r-- | __tests__/parser.test.js | 35 | ||||
-rw-r--r-- | src/parser.js | 60 |
2 files changed, 95 insertions, 0 deletions
diff --git a/__tests__/parser.test.js b/__tests__/parser.test.js new file mode 100644 index 0000000..004e18b --- /dev/null +++ b/__tests__/parser.test.js @@ -0,0 +1,35 @@ +const { parse } = require('../src/parser.js') + +describe('parse', () => { + it('parses a constant', () => { + expect(parse('5')).toEqual({ type: 'constant', value: 5 }) + }) + + it('parses a simple die (1d6)', () => { + expect(parse('1d6')).toEqual({ + type: 'd', + left: { type: 'constant', value: 1 }, + right: { type: 'constant', value: 6 } + }) + }) + + it('parses a simple die (10d42)', () => { + expect(parse('10d42')).toEqual({ + type: 'd', + left: { type: 'constant', value: 10 }, + right: { type: 'constant', value: 42 } + }) + }) + + it('parses a compound die (1d2d3)', () => { + expect(parse('1d2d3')).toEqual({ + type: 'd', + left: { type: 'constant', value: 1 }, + right: { + type: 'd', + left: { type: 'constant', value: 2 }, + right: { type: 'constant', value: 3 } + } + }) + }) +}) diff --git a/src/parser.js b/src/parser.js new file mode 100644 index 0000000..01b5494 --- /dev/null +++ b/src/parser.js @@ -0,0 +1,60 @@ +const { lex } = require('./lexer.js') + +let symbols = {} + +let newSymbol = (type, nud, lbp, led) => { + symbols[type] = { type, nud, lbp, led } +} + +let lexemeToToken = lexeme => { + let token = Object.create(symbols[lexeme.type]) + token.value = lexeme.value + return token +} + +newSymbol('constant', function() { + return { type: 'constant', value: this.value } +}) + +newSymbol('d', null, 30, (left, parser) => { + return { + type: 'd', + left: left, + right: parser.expression(29) + } +}) + +newSymbol('end', null, -1) + +const newParser = (tokens) => { + return { + tokens, + currentToken: 0, + token: function() { return this.tokens[this.currentToken] }, + advanceToken: function() { this.currentToken++ }, + expression: function(rbp) { + let symbol = this.token() + let left = symbol.nud() + this.advanceToken() + + while (rbp < this.token().lbp) { + symbol = this.token() + this.advanceToken() + left = symbol.led(left, this) + } + + return left + } + } +} + +const parse = expressionString => { + const tokens = lex(expressionString).map(lexemeToToken) + tokens.push(symbols.end) + + const parser = newParser(tokens) + return parser.expression(0) +} + + +exports.parse = parse |