script(src="https://unpkg.com/petite-vue")
  

main.container.my-3(v-scope="js2jsonlogic()" @mounted="formula();math();logic()")
  .row.my-3
    .col
      div
        label.form-label Formula
        input.form-control.font-monospace(@keyup='formula();math();logic()' v-model="form")
        div.form-text Needs spaces around all operators to be turned into JsonLogic
  .row.my-3
    .col-6
      div
        label.form-label JsonLogic
        textarea.form-control.font-monospace(rows="13" v-model="jsonlogic" readonly="true")
    .col-6
      div
        label.form-label Data
        textarea.form-control.font-monospace(rows="13" @keyup='formula();math();logic()' v-model="data")
  .row.my-3
  
    .col-6
      .input-group
        span.input-group-text json-logic
        input.form-control(v-model="jsonlogicresult" readonly="true")
  
    .col-6
      .input-group
        span.input-group-text math.js
        input.form-control(v-model="mathresult" readonly="true")

View Compiled
PetiteVue.createApp().mount()

function js2jsonlogic() {
  return {
    form: this.form || '( label1 + label2 ) * select',
    jsonlogic: '',
    mathresult: '',
    jsonlogicresult: '',
    data: JSON.stringify({
      label1: 1,
      label2: "2",
      select: "3°C"
    },null,'  '),
    formula() {
      this.jsonlogic = JSON.stringify(js2jl(this.form),null,'  ')
    },
    // TODO: calc
    math() {
      try {
        this.mathresult = math.evaluate(this.form, JSON.parse(this.data))
      } catch(e) {
        this.mathresult = e
      }
      
    },
    logic() {
      this.jsonlogicresult = jsonLogic.apply( JSON.parse(this.jsonlogic), JSON.parse(this.data));
    }
  }
    
} 

// @see https://github.com/lucas2595/js2jl
function js2jl(infix) {
  let ops = {
    not: { precedency: 5, single: true },
    "*": { precedency: 4 },
    "/": { precedency: 4 },
    "%": { precedency: 4 },
    "+": { precedency: 3 },
    "-": { precedency: 3 },
    cat: { precedency: 3 },
    in: { precedency: 3 },
    substr: { precedency: 3 },
    "===": { precedency: 2 },
    "!==": { precedency: 2 },
    "!=": { precedency: 2 },
    "==": { precedency: 2 },
    "<": { precedency: 2 },
    ">": { precedency: 2 },
    "<=": { precedency: 2 },
    ">=": { precedency: 2 },
    or: { precedency: 2 },
    and: { precedency: 2 },
    filter: { precedency: 1 },
    map: { precedency: 1 },
    "?": { precedency: 1 },
    reduce: { precedency: 1 },
    method: { precedency: 1 },
    ":": { precedency: 1, helper: "?", key: "if" },
    initial: { precedency: 1, helper: "reduce", key: "reduce" },
    arguments: { precedency: 1, helper: "method", key: "method" }
  };

  let helped = Object.entries(ops)
    .filter(a => a[1].helper)
    .map(a => a[0]);
  let single = Object.entries(ops)
    .filter(a => a[1].single)
    .map(a => a[0]);

  let peek = a => a[a.length - 1];
  let stack = [];
  return infix
    .split(" ")
    .reduce((output, token) => {
      if (token in ops) {
        while (
          peek(stack) in ops &&
          ops[token].precedency <= ops[peek(stack)].precedency
        )
          output.push(stack.pop());
        stack.push(token);
      } else if (token === "(") {
        stack.push(token);
      } else if (token === ")") {
        while (peek(stack) !== "(" && stack.length > 0) {
          output.push(stack.pop());
        }
        stack.pop();
      } else {
        output.push(token);
      }
      return output;
    }, [])
    .concat(stack.reverse())
    .reduce(
      (acc, val) => {
        if (!(val in ops)) {
          if (!isNaN(parseFloat(val))) {
            acc.push(parseFloat(val));
          } else if (
            val !== undefined &&
            (val.split(/"/).length === 3 || val.split(/'/).length === 3)
          ) {
            acc.push(val.replace(/"/g, ""));
          } else if (
            val !== undefined &&
            ((val[0] === "[" && val[val.length - 1] === "]") ||
              (val[0] === "{" && val[val.length - 1] === "}"))
          ) {
            try {
              acc.push(JSON.parse(val));
            } catch (err) {
              acc.push({ var: val });
            }
          } else {
            acc.push({ var: val });
          }
        } else if (helped.includes(val)) {
          // Three arguments
          let a = acc.pop();
          let b = acc.pop();
          if (b && b[ops[val].helper])
            acc = [
              ...acc,
              {
                [ops[val].key]: [
                  b[ops[val].helper][0],
                  b[ops[val].helper][1],
                  a
                ]
              }
            ];
        } else if (single.includes(val)) {
          // Single argument
          let a = acc.pop();
          acc = [...acc, { [val]: a }];
        } else {
          // Two arguments
          let a = acc.pop();
          let b = acc.pop();
          acc = [...acc, { [val]: [b, a] }];
        }
        return acc;
      },
      [{}]
    )
    .pop();
}

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/mathjs/9.4.4/math.js
  2. https://cdn.jsdelivr.net/npm/json-logic-js@2.0.1/logic.min.js