punyexpr poc

by Arnaud Buchholz

HTML

<h1>
  punyexpr POC
</h1>

<p>
  This code illustrates an addition to
  <a href="https://www.npmjs.com/package/punybind" target="_blank">punybind</a>
  that would make it
  <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" target="_blank">CSP</a>
  compliant by parsing / building expressions in a safe manner
  (rather than using an <a href="https://github.com/ArnaudBuchholz/punybind/blob/main/punybind.js#L4" target="_blank">eval-like</a> syntax).
</p>

<p>
  Suprisingly, the new syntax appears to be faster than the used one
  <i>(check the console)</i>.
</p>

JavaScript

const bind = (impl, ...params) => impl.bind(null, ...params)

// +
const add = (...args) => {
  const context = args.pop()
  const first = args.shift()
  return args.reduce((sum, arg) => sum + arg(context), first(context))
}

// -
const sub = (...args) => {
  const context = args.pop()
  const first = args.shift()
  return args.reduce((sum, arg) => sum - arg(context), first(context))
}

// [] or .
const get = (member, context) => {
  return context[member]
}

// ? : 
const iif = (condition, trueValue, falseValue, context) => {
  if (condition(context)) {
    return trueValue(context)
  }
  return falseValue(context)
}

// sum ? 1 + test : 1 - test
const punyexpr = bind(
  iif,
    bind(get, 'sum'),
    bind(add, () => 1, bind(get, 'test')),
    bind(sub, () => 1, bind(get, 'test'))
)

const evalexpr = function (context) {
  with (context) {
    return sum ? 1 + test : 1 - test
  }
}

const context = {
  sum: true,
  test: 2
}

function perf (expr, context) {
  const now = performance.now()
  let count = 0
  while (performance.now() - now < 1000) {
    expr(context)
    ++count
  }
  return count
}

console.log(
  'punyexpr : ', punyexpr(context),
  'evalexpr : ', evalexpr(context),
  'equal :', punyexpr(context) === evalexpr(context)
)

console.log('perf(punyexpr)', perf(punyexpr,context))
console.log('perf(evalexpr)', perf(evalexpr,context))