import { AsText, TextPiece } from "../words";
import { Expr } from "./expr";

describe("expr", () => {
  describe("does math", () => {
    test.each([
      ["1", "1"],
      ["-1", "-1"],
      ["1 + 2", "3"],
      ["1 - 2", "-1"],
      ["1 * 2", "2"],
      ["1 / 2", "0.5"],
      // floored division
      ["1 // 5", "0"],
      ["6 // 5", "1"],
      ["-1 // 5", "-1"],
      ["-6 // 5", "-2"],
      ["1 // -5", "-1"],
      ["6 // -5", "-2"],
      ["-1 // -5", "0"],
      ["-6 // -5", "1"],
      // floored modulo
      ["1 % 5", "1"],
      ["6 % 5", "1"],
      ["-6 % 5", "4"],
      ["-1 % 5", "4"],
      ["1 % -5", "-4"],
      ["6 % -5", "-4"],
      ["-6 % -5", "-1"],
      ["-1 % -5", "-1"],
    ])("%s", (expression, result) => {
      const actualResult = Expr({}, [{ text: expression }]);
      expect("error" in actualResult).toBeFalsy();
      expect(AsText(actualResult as TextPiece)).toEqual(result);
    });
  });

  describe("handles operator precedence", () => {
    test.each([
      ["1 - 2 + 1", "0"],
      ["1 + 2 * 3", "7"],
      ["1 / 2 + 3", "3.5"],
    ])("%s", (expression, result) => {
      const actualResult = Expr({}, [{ text: expression }]);
      expect("error" in actualResult).toBeFalsy();
      expect(AsText(actualResult as TextPiece)).toEqual(result);
    });
  });

  // TODO: parentheses

  // TODO; error reporting
  describe("rejects invalid expressions", () => {
    test.each([[""], ["1 $ 2"], ["1 1 + 2"], ["1 + + 2"], ["$ 1"]])(
      "%s",
      (expression) => {
        const actualResult = Expr({}, [{ text: expression }]);
        expect("error" in actualResult).toBeTruthy();
      }
    );
  });

  // TODO: operators should only be accepted as bare words

  describe("ignores an expr prefix", () => {
    test.each([
      [["1", "+", "2"]],
      [["expr", "1", "+", "2"]],
      [["1 + 2"]],
      [["expr", "1 + 2"]],
    ])("%s", (argv) => {
      const actualResult = Expr(
        {},
        argv.map((text) => ({ bare: text }))
      );
      expect("error" in actualResult).toBeFalsy();
      expect(AsText(actualResult as TextPiece)).toEqual("3");
    });
  });
});