punyexpr grammar
by Arnaud Buchholz
HTML
Simplified <a href="https://tc39.es/ecma262/#sec-expressions" target="_blank">grammar</a>
<pre>
Literal :
IdentifierLiteral
NullLiteral
BooleanLiteral
NumbericLiteral
StringLiteral
PrimaryExpression :
Literal
<s><i>[</i>ArrayLiteral</s>
<s><i>{</i>ObjectLiteral</s>
<i>(</i> CoverParenthesizedExpressionAndArrowParameterList
CoverParenthesizedExpressionAndArrowParameterList :
<b>(</b> Expression (<b>,</b> Expression)* <b>)</b>
ConditionalExpression :
ShortCircuitExpression <b>?</b> AssignmentExpression <b>:</b> AssignmentExpression
ShortCircuitExpression :
EqualityExpression :
RelationalExpression
EqualityExpression <b>==</b> RelationalExpression
EqualityExpression <b>!=</b> RelationalExpression
EqualityExpression <b>===</b> RelationalExpression
EqualityExpression <b>!==</b> RelationalExpression
RelationalExpression :
AdditiveExpression
RelationalExpression <b><</b> AdditiveExpression
RelationalExpression <b>></b> AdditiveExpression
RelationalExpression <b><=</b> AdditiveExpression
RelationalExpression <b>>=</b> AdditiveExpression
AdditiveExpression :
MultiplicativeExpression
AdditiveExpression <b>+</b> MultiplicativeExpression
AdditiveExpression <b>-</b> MultiplicativeExpression
MultiplicativeExpression :
ExponentiationExpression
MultiplicativeExpression <b>*</b> MemberExpression
MultiplicativeExpression <b>/</b> MemberExpression
MultiplicativeExpression <b>%</b> MemberExpression
MemberExpression :
PrimaryExpression
MemberExpression <b>[</b> Expression <b>]</b>
MemberExpression <b>.</b> IdentifierLiteral
</pre>
JavaScript
'use strict'
const log = text => {
const div = document.createElement('div')
if (typeof text !== 'string') {
text = JSON.stringify(text)
}
div.appendChild(document.createTextNode(text))
document.body.appendChild(div)
}
const parse = tokens => {
const bind = (impl, ...params) => impl.bind(null, ...params)
const addImpl = (...args) => {
const context = args.pop()
const first = args.shift()
log({ addImpl: args, first: first(), context })
return args.reduce((sum, arg) => sum + arg(context), first(context))
}
const subImpl = (...args) => {
const context = args.pop()
const first = args.shift()
return args.reduce((sum, arg) => sum - arg(context), first(context))
}
const getImpl = (member, context) => {
return context[member]
}
const iifImpl = (condition, trueValue, falseValue, context) => {
if (condition(context)) {
return trueValue(context)
}
return falseValue(context)
}
const current = () => tokens[0]
const preview = (steps = 1) => tokens[steps]
const move = (steps = 1) => { tokens = tokens.splice(steps) }
const constantExpr = () => {
const token = current()
log({ constantExpr: token || 'EOT' })
if (['number', 'string'].includes(token[0])) {
move()
return () => token[1]
}
throw new Error('Unexpected')
}
const addSubExpr = () => {
const nextToken = preview()
log({ addSubExpr: nextToken || 'EOT' })
if (nextToken && nextToken[0] === 'symbol' && ['-', '+'].includes(nextToken[1])) {
const first = constantExpr()
move()
const second = addSubExpr()
log({ first: first, second: second })
if (nextToken[1] === '+') {
return bind(addImpl, first, second)
}
return bind(subImpl, first, second)
}
return constantExpr()
}
const parsed = addSubExpr()
return context => parsed(context)
}
const expr = parse([
['number', 1],
['symbol', '+'],
['number', 2]
])
log(expr())