Skip to content

Instantly share code, notes, and snippets.

@raisch
Created January 26, 2017 21:56
Show Gist options
  • Save raisch/da4a4121f183beff2af8b966c9d7e9a6 to your computer and use it in GitHub Desktop.
Save raisch/da4a4121f183beff2af8b966c9d7e9a6 to your computer and use it in GitHub Desktop.
{
/*
Filename: grammar.pegjs
Author: raisch@gmail.com
*/
const util = require('util')
const _ = require('lodash')
/**
* Storage of Declared Variables
* @type {Object}
*/
const vars = {}
/**
* If true, debugging messages will be emitted via debug
* @type {Boolean}
*/
const DEBUGGING = false
/**
* Emit debugging message via debug if DEBUGGING is true.
* @param {Array} args - arguments supplied to util.format
*/
const debug = (...args) => {
if (DEBUGGING) {
console.log('DEBUG: ' + util.format.apply(null, args))
}
}
}
start = result:statement {
debug(`found statement "${text()}" = ${JSON.stringify(result)}`)
return result
}
// ==== STATEMENTS ====
statement = expr:expression {
debug(`found expression_statement:"${text()}" = ${JSON.stringify(expr)}`)
return expr
}
/ primary:primary {
debug(`found primary_statement:"${text()}"`)
return primary
}
// ==== EXPRESSION ====
expression = head:term tail:( SP* addsub_op SP* term )* {
debug(`found expression:"${text()}"`)
return tail.reduce((res, elt) => {
const op = elt[1]
debug(`found ${op} operator`)
if(op === '+') {
return res + elt[3]
}
else if(op === '-') {
return res - elt[3]
}
}, head)
}
term = SP* head:factor tail:( SP* multdiv_op SP* factor)* {
debug(`found term:"${text()}"`)
return tail.reduce((res, elt) => {
const op = elt[1]
debug(`found ${op} operator`)
if(op === '*') {
return res * elt[3]
}
else if (op === '/') {
return res / elt[3]
}
}, head)
}
factor = OPENPAREN expr:expression CLOSEPAREN {
debug(`found paren factor:"${text()}"`)
return expr
}
/ primary:primary {
debug(`found primary factor:"${text()}"`)
return primary
}
primary
= float {
debug(`found float primary:"${text()}"`)
return parseFloat(text())
}
/ integer {
debug(`found integer primary:"${text()}"`)
return parseInt(text(), 10)
}
/ cond:path QUESTION result:path {
debug(`found condition primary:"${text()}"`)
return _.get(options, cond, null) ? _.get(options, result) : 0
}
/ path:path {
debug(`found path primary:"${text()}"`)
return _.get(options, path)
}
path = $( symbol ( POINT ( integer / symbol ) )* )
integer = SIGN? intdigits {
debug(`found integer:"${text()}"`)
return text()
}
float = SIGN? intdigits float_frac? {
debug(`found float:"${text()}"`)
return text()
}
float_frac = POINT digits {
return text()
}
addsub_op = op:( ADD / SUB ) {
debug(`found addsub op:"${op}"`)
return op
}
multdiv_op = op:( MULT / DIV ) {
debug(`found multdiv op:"${op}"`)
return op
}
symbol = LETTER ( LETTER / DIGIT )* {
debug(`found symbol:"${text()}"`)
return text()
}
digits = DIGIT DIGIT* {
debug(`found digits:"${text()}"`)
return text()
}
intdigits = INTDIGIT DIGIT* {
debug(`found intdigits:"${text()}"`)
return text()
}
// ==== LEXICALS ====
OPENCURLY = '{'
CLOSECURLY = '}'
OPENPAREN = '('
CLOSEPAREN = ')'
LETTER = [a-z]
DIGIT = [0-9]
INTDIGIT = [1-9]
ASSIGN = '='
SEMI = ';'
SIGN = '+' / '-'
POINT = '.'
ADD = '+'
SUB = '-'
MULT = '*'
DIV = '/'
QUESTION = '?'
SP = [ \t]
/*
Filename: testParser.js
Author: raisch@gmail.com
Tester for parser created from grammar.pegjs
*/
const assert = require('assert')
const parser = require('./parser')
const target = '500 + depth+material.include?edge.face+material.thickness'
const expected = 624.5
const context = {
depth: 100,
material: {
thickness: 20,
include: true
},
edge: {
face: 4.5
}
}
let result
try {
result = parser.parse(target, context)
} catch (err) {
console.log(JSON.stringify(err, null, 2))
}
assert(expected === result, `found unexpected result: ${result}`)
console.log('The result is %s', result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment