All files / src/shared/lib/rpn tokenizer.ts

90% Statements 27/30
87.5% Branches 14/16
100% Functions 4/4
89.66% Lines 26/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 10943x                   397x   43x 39x               176x 34x         142x 8x         134x 21x       113x 20x       93x 5x       88x 45x         43x 43x                                 176x 5x 1x                     4x 4x       1x                       174x         43x  
import {
  isFunctionSymbol,
  isNumeric,
  toNumber,
  isOperatorSymbol,
  isVariable,
  isConstant,
} from "./helpers";
import { Token } from "./types";
 
const identity = <T>(i: T): T => i;
 
const tokenizer = (input: string): Token[] => {
  return (
    input
      .replace(/\s+/g, "")
      .split(
        /(@[a-f0-9-]{36}@|<<|>>|==|!=|>=|>|<=|<|&&|\|\||[+\-*/^%&|~!@(),])/,
      )
      .filter(identity)
      .map((token): Token => {
        if (isNumeric(token)) {
          return {
            type: "VAL",
            value: toNumber(token),
          };
        }
        if (isFunctionSymbol(token)) {
          return {
            type: "FUN",
            function: token,
          };
        }
        if (token === "(") {
          return {
            type: "LBRACE",
          };
        }
        if (token === ")") {
          return {
            type: "RBRACE",
          };
        }
        if (token === ",") {
          return {
            type: "SEPERATOR",
          };
        }
        if (isOperatorSymbol(token)) {
          return {
            type: "OP",
            operator: token,
          };
        }
        if (isVariable(token)) {
          return {
            type: "VAR",
            symbol: token,
          };
        }
        Iif (isConstant(token)) {
          return {
            type: "CONST",
            symbol: token.replaceAll(/@/g, ""),
          };
        }
        throw new Error(`Unexpected token ${token}`);
      })
      .filter(identity) as Token[]
  )
    .map((token, i, tokens): Token[] => {
      // Handle unary negation
      if (token.type === "OP" && token.operator === "-") {
        if (i === 0) {
          return [
            {
              type: "VAL",
              value: 0,
            },
            {
              type: "OP",
              operator: "-",
            },
          ];
        }
        const previous = tokens[i - 1];
        if (
          previous.type === "LBRACE" ||
          (previous.type === "OP" && isOperatorSymbol(previous.operator))
        ) {
          return [
            {
              type: "VAL",
              value: 0,
            },
            {
              type: "OP",
              operator: "-",
            },
          ];
        }
      }
      return [token];
    })
    .flat();
};
 
export default tokenizer;