JSFiddle - React, Tailwind, and code Playground

HTML

<input id=expr value="1 + 1 = 3 ? no : 2 + 2 = 5 ? no : yes"/>
<button id=evaluate>Evaluate</button>
<h3>Result</h3>
<div id=result>???</div>
<h3>AST</h3>
<div id=ast></div>

CSS

body, input, button {
  font-size: 14pt;
  font-family: monospace;
}
.hover {
  background-color: #eee;
}
.hover .inner {
  background-color: #fff;
}

h3 {
  margin-bottom: 0px;
}

.inner {
  display: inline-block;
  border: 1px solid #888;
  border-radius: 8px;
  padding: 3px;
  margin: 3px;
}
.id {
  color: #a55;
  font-weight: bold;
}
.num {
  color: #008;
  font-weight: bold;
}
.op {
  padding: 3px;
}

JavaScript

var op_re = "([\\(\\)\\[\\]\\{\\}]|[!@#$%^&*|/?:~+=<>-]+)"
var id_re = "([A-Za-z_][A-Za-z_0-9]*)"
var num_re = "([0-9]+)"
var str_re = "(\"(?:[^\"]|\\\\.)*\")"
var re = new RegExp([op_re, id_re, num_re, str_re, "([^ \n])"].join("|"), "g");
var toktypes = ["op", "id", "num", "str", "other"];

function tokenize(text) {
    var m;
    var results = [{token: "$", type: "boundary"}];
    var last = "op";
    while (m = re.exec(text)) {
        var type = toktypes[m.slice(1).indexOf(m[0])];
        var tok = {token: m[0], type: type};
        if (last.type === "op" && type === "op" && !getPrio(last)[2])
            tok.prefix = true;
        results.push(tok);
        last = tok;
    }
    results.push({token: "$", type: "boundary"});
    return results;
}

function oparse(next, order, finalize) {
    var stack = [];
    var between = null;
    var left = next();
    var right = next();
    var current = [null, left];
    while (true) {
        switch (order(left, right)) {
            case "done":
                return between;
            case "<•":
                stack.push(current);
                current = [between, right];
                between = null;
                left = right;
                right = next();
                break;
            case "•>":
                current.push(between);
                between = finalize(current);
                current = stack.pop()
                left = current[current.length - 1];
                break;
            case "=•":
                current.push(between, right);
                between = null;
                left = right;
                right = next();
                break;
        }
    }
}

priorities = {
    "boundary:$": [-1,   -1],
    "type:id":    [2000, 2000],
    "type:num":   [2000, 2000],
    "op:(":       [500,  0],
    "op:P(":      [500,  0],
    "op:)":       [0,    1000, true],
    "op:P)":      [0,    1000, true],
    "op:[":       [500,  0],
    "op:P[":      [500,  0],
   ...