awkrs 0.4.13

Awk implementation in Rust with broad CLI compatibility, parallel records, and experimental Cranelift JIT
Documentation
# Recursive-descent arithmetic evaluator.
# Supports + - * / % ^ unary-minus parentheses and integer/decimal literals.
# ^ is right-associative; * / % share precedence and are left-associative;
# + - share precedence and are left-associative.
#
# Input: one expression per line. Output: "expr = value" (one per line).
# Globals between calls:
#   tk[1..ntk]  token array
#   tp          current token index (1-based)

function tokenize(s,   i, n, c, num) {
  ntk = 0
  delete tk
  n = length(s)
  i = 1
  while (i <= n) {
    c = substr(s, i, 1)
    if (c == " " || c == "\t") { i++; continue }
    if (c ~ /[0-9.]/) {
      num = ""
      while (i <= n && substr(s, i, 1) ~ /[0-9.]/) { num = num substr(s, i, 1); i++ }
      tk[++ntk] = num
      continue
    }
    if (c ~ /[+\-*\/%^()]/) { tk[++ntk] = c; i++; continue }
    printf "tokenize: unexpected '%s' at %d\n", c, i
    return 0
  }
  tk[++ntk] = "$"
  return 1
}

function peek() { return tk[tp] }
function eat(t,   x) { x = tk[tp]; if (x != t) { printf "parse: want %s got %s\n", t, x; exit 1 } tp++; return x }

function p_atom(   v) {
  if (peek() == "(") { eat("("); v = p_expr(); eat(")"); return v }
  v = tk[tp] + 0; tp++; return v
}

function p_power(   a, b) {
  a = p_atom()
  if (peek() == "^") { eat("^"); b = p_unary(); return a ^ b }
  return a
}

function p_unary(   v) {
  if (peek() == "-") { eat("-"); return -p_unary() }
  if (peek() == "+") { eat("+"); return  p_unary() }
  return p_power()
}

function p_muldiv(   a, op, b) {
  a = p_unary()
  while (peek() == "*" || peek() == "/" || peek() == "%") {
    op = peek(); eat(op); b = p_unary()
    if      (op == "*") a = a * b
    else if (op == "/") a = a / b
    else                a = a % b
  }
  return a
}

function p_expr(   a, op, b) {
  a = p_muldiv()
  while (peek() == "+" || peek() == "-") {
    op = peek(); eat(op); b = p_muldiv()
    if (op == "+") a = a + b; else a = a - b
  }
  return a
}

NF == 0 { next }
{
  if (!tokenize($0)) next
  tp = 1
  v = p_expr()
  if (peek() != "$") { printf "%s = parse error (trailing token %s)\n", $0, peek(); next }
  if (v == int(v)) printf "%s = %d\n", $0, v
  else             printf "%s = %g\n", $0, v
}