m-chrzan.xyz
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--__tests__/parser.test.js35
-rw-r--r--src/parser.js60
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