Mateja Petrovic
Mateja Petrovic

Reputation: 4357

Writing Interpreter Using Javascript Tagged Template Literals

Have you ever thought it'd be cool if you could use your favorite features from other languages in plain old Javascript. For example, I'd love to be able to use the F# pipeline operator '|>', but the proposal for the pipeline operator in JS is only stage 1 ATM.

For starters I've attempted to enable the use of post-fix notation i.e. '1 2 +' = 3. I figured that using ES6 Tagged Template Literals I might get somewhere, but I've barely scratched the surface here. This is just a proof of concept.

const Interpret = (strings, ...expressions) => {
  
  const hasPlus = strings.map(s => s.trim()).join('').match(/[+]$/)
  
  return hasPlus ? expressions.reduce((t, c) => t + c, 0) : null
  
}

const a = 1
const b = 2
const c = 3
const d = 4

const result = Interpret`${a} ${b} ${c} ${d} +`

console.log(result) // 10

Moreover, I've stumbled upon a big hurdle. The signature of tagged template literal function is as follows - myTag(strings: string[], ...expressions). Expressions are anything ranging from numbers, string, booleans functions and so on. The problem is that the original order of strings and expressions is apparently lost.

It'd be fabulous if you could have access to an array of strings and expression, with their original order preserved ( the order in which they appeared between the backticks ).

For that would enable me to evaluate the elements of the array like so: Did I get an expression, great, push it onto the stack ( just an array for starters )... Next did I get an expression, cool, push it too onto the stack... Next did I get an expression, no - it's the plus operator. Now I can take value 'a' and value 'b' which the first two expressions evaluated to and feed them to the plus operator ( could very well be a function ). Next push the return value onto the stack and so on.

Does anyone have an idea on how to take the next step or perhaps pivot in another direction?

This seems like a step forward, thanks mpm.

const Interpret = (strings, ...expressions) => {
  
  const raw = strings
  .map( (s, i) => s + (expressions[i] || '') )
  .map(s => s.trim())
  
  const plusPosition = raw.findIndex(s => s.match(/[+]/))
  
  return raw.slice(0, plusPosition).reduce((t, c) => t + parseInt(c), 0)
    
}

const a = 1
const b = 2
const c = 3
const d = 4

const result = Interpret`${a} ${b} ${c} ${d} +`

console.log(result)

Upvotes: 3

Views: 133

Answers (1)

mpm
mpm

Reputation: 20155

I'm pretty sure the order is not lost otherwise string templates would be useless, strings should return all the string parts and the expressions rest parameter has all the expression values. Just rebuild the order by putting each expression between 2 strings element

const i = (strings, ...expressions) => {
  const list = [];
  for (let i = 0; i < strings.length; i++) {
    if (strings[i].trim() !== '') {
      list.push(strings[i].trim())
    }
    if (i < expressions.length) {
      list.push(expressions[i])
    }
  }
  let stack = [];
  let result = 0;
  do {
    const token = list.shift()
    if (token === '+') {
      result = stack.reduce(function(result, value) {
        return result + value
      }, result)
      stack.length = 0;

    } else if (token === '-') {
      result = stack.reduce(function(result, value) {
        return result - value
      }, result)
      stack.length = 0;
    } else {
      stack.push(token)
    }
  } while (list.length > 0);
  return result;
}
const a = 2,
  b = 1,
  c = 4,
  d = 3,
  e = 4;
console.log("result", i `${a} ${b} ${c} ${d} + ${e} - `);

Upvotes: 1

Related Questions