"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var LogicParser_exports = {};
__export(LogicParser_exports, {
  Operator: () => Operator,
  SyntaxKind: () => SyntaxKind,
  TokenKind: () => TokenKind,
  parse: () => parse,
  toRulesLogic: () => toRulesLogic,
  tokenize: () => tokenize
});
module.exports = __toCommonJS(LogicParser_exports);
var import_shared = require("@zwave-js/shared");
var import_helpers = require("alcalzone-shared/helpers");
var SyntaxKind;
(function(SyntaxKind2) {
  SyntaxKind2[SyntaxKind2["Group"] = 0] = "Group";
  SyntaxKind2[SyntaxKind2["Or"] = 1] = "Or";
  SyntaxKind2[SyntaxKind2["And"] = 2] = "And";
  SyntaxKind2[SyntaxKind2["Comparison"] = 3] = "Comparison";
  SyntaxKind2[SyntaxKind2["Identifier"] = 4] = "Identifier";
  SyntaxKind2[SyntaxKind2["NumberLiteral"] = 5] = "NumberLiteral";
  SyntaxKind2[SyntaxKind2["Version"] = 6] = "Version";
})(SyntaxKind || (SyntaxKind = {}));
var Operator;
(function(Operator2) {
  Operator2[Operator2["Equal"] = 0] = "Equal";
  Operator2[Operator2["NotEqual"] = 1] = "NotEqual";
  Operator2[Operator2["LessThan"] = 2] = "LessThan";
  Operator2[Operator2["LessThanOrEqual"] = 3] = "LessThanOrEqual";
  Operator2[Operator2["GreaterThan"] = 4] = "GreaterThan";
  Operator2[Operator2["GreaterThanOrEqual"] = 5] = "GreaterThanOrEqual";
})(Operator || (Operator = {}));
var TokenKind;
(function(TokenKind2) {
  TokenKind2[TokenKind2["Identifier"] = 0] = "Identifier";
  TokenKind2[TokenKind2["NumberLiteral"] = 1] = "NumberLiteral";
  TokenKind2[TokenKind2["Dot"] = 2] = "Dot";
  TokenKind2[TokenKind2["LeftParen"] = 3] = "LeftParen";
  TokenKind2[TokenKind2["RightParen"] = 4] = "RightParen";
  TokenKind2[TokenKind2["BarBar"] = 5] = "BarBar";
  TokenKind2[TokenKind2["AmpAmp"] = 6] = "AmpAmp";
  TokenKind2[TokenKind2["EqualsEquals"] = 7] = "EqualsEquals";
  TokenKind2[TokenKind2["EqualsEqualsEquals"] = 8] = "EqualsEqualsEquals";
  TokenKind2[TokenKind2["ExclamationEquals"] = 9] = "ExclamationEquals";
  TokenKind2[TokenKind2["ExclamationEqualsEquals"] = 10] = "ExclamationEqualsEquals";
  TokenKind2[TokenKind2["LessThan"] = 11] = "LessThan";
  TokenKind2[TokenKind2["LessThanEquals"] = 12] = "LessThanEquals";
  TokenKind2[TokenKind2["GreaterThan"] = 13] = "GreaterThan";
  TokenKind2[TokenKind2["GreaterThanEquals"] = 14] = "GreaterThanEquals";
})(TokenKind || (TokenKind = {}));
function* tokenize(input) {
  for (let i = 0; i < input.length; i++) {
    switch (input[i]) {
      case ".": {
        yield { kind: TokenKind.Dot, start: i };
        break;
      }
      case "(": {
        yield { kind: TokenKind.LeftParen, start: i };
        break;
      }
      case ")": {
        yield { kind: TokenKind.RightParen, start: i };
        break;
      }
      case "|": {
        if (input[i + 1] === "|") {
          yield { kind: TokenKind.BarBar, start: i };
          i++;
        }
        break;
      }
      case "&": {
        if (input[i + 1] === "&") {
          yield { kind: TokenKind.AmpAmp, start: i };
          i++;
        }
        break;
      }
      case "=": {
        if (input[i + 1] === "=") {
          if (input[i + 2] === "=") {
            yield { kind: TokenKind.EqualsEqualsEquals, start: i };
            i += 2;
          } else {
            yield { kind: TokenKind.EqualsEquals, start: i };
            i++;
          }
        }
        break;
      }
      case "!": {
        if (input[i + 1] === "=") {
          if (input[i + 2] === "=") {
            yield {
              kind: TokenKind.ExclamationEqualsEquals,
              start: i
            };
            i += 2;
          } else {
            yield { kind: TokenKind.ExclamationEquals, start: i };
            i++;
          }
        }
        break;
      }
      case "<": {
        if (input[i + 1] === "=") {
          yield { kind: TokenKind.LessThanEquals, start: i };
          i++;
        } else {
          yield { kind: TokenKind.LessThan, start: i };
        }
        break;
      }
      case ">": {
        if (input[i + 1] === "=") {
          yield { kind: TokenKind.GreaterThanEquals, start: i };
          i++;
        } else {
          yield { kind: TokenKind.GreaterThan, start: i };
        }
        break;
      }
      default: {
        if (/\s/.test(input[i])) {
          continue;
        }
        if (input[i] === "0" && input[i + 1] === "x") {
          const start = i;
          let hex = "0x";
          i += 2;
          while (i < input.length && /[0-9a-fA-F]/.test(input[i])) {
            hex += input[i];
            i++;
          }
          yield {
            kind: TokenKind.NumberLiteral,
            value: hex,
            start
          };
          i--;
          continue;
        }
        if (/\d/.test(input[i])) {
          const start = i;
          let num = "";
          while (i < input.length && /\d/.test(input[i])) {
            num += input[i];
            i++;
          }
          yield {
            kind: TokenKind.NumberLiteral,
            value: num,
            start
          };
          i--;
          continue;
        }
        if (/[a-zA-Z_$]/.test(input[i])) {
          const start = i;
          let id = "";
          while (i < input.length && /[a-zA-Z0-9_$]/.test(input[i])) {
            id += input[i];
            i++;
          }
          yield { kind: TokenKind.Identifier, value: id, start };
          i--;
          continue;
        }
        throw new Error(`Unexpected character '${input[i]}' at index ${i}`);
      }
    }
  }
}
__name(tokenize, "tokenize");
function parse(input) {
  const tokens = Array.from(tokenize(input));
  const state = { input, tokens, pos: 0 };
  const ret = parseExpression(state);
  if (state.pos < tokens.length) {
    const token = tokens[state.pos];
    throw new Error(`Unexpected token ${(0, import_shared.getEnumMemberName)(TokenKind, token.kind)} at position ` + token.start);
  }
  return ret;
}
__name(parse, "parse");
function parseExpression(s) {
  return parseOr(s) || parseAnd(s) || parseComparison(s) || parseGroup(s);
}
__name(parseExpression, "parseExpression");
function parseAnd(s) {
  let startPos = s.pos;
  const first = parseComparison(s) || parseGroup(s);
  if (!first) {
    s.pos = startPos;
    return;
  }
  if (s.tokens[s.pos]?.kind !== TokenKind.AmpAmp) {
    s.pos = startPos;
    return;
  }
  s.pos++;
  const second = parseComparison(s) || parseGroup(s);
  if (!second) {
    s.pos = startPos;
    return;
  }
  startPos = s.pos;
  const operands = [first, second];
  while (s.tokens[s.pos]?.kind === TokenKind.AmpAmp) {
    s.pos++;
    const next = parseComparison(s) || parseGroup(s);
    if (!next) {
      s.pos = startPos;
      break;
    }
    operands.push(next);
    startPos = s.pos;
  }
  return {
    kind: SyntaxKind.And,
    operands
  };
}
__name(parseAnd, "parseAnd");
function parseOr(s) {
  let startPos = s.pos;
  const first = parseAnd(s) || parseComparison(s) || parseGroup(s);
  if (!first) {
    s.pos = startPos;
    return;
  }
  if (s.tokens[s.pos]?.kind !== TokenKind.BarBar) {
    s.pos = startPos;
    return;
  }
  s.pos++;
  const second = parseAnd(s) || parseComparison(s) || parseGroup(s);
  if (!second) {
    s.pos = startPos;
    return;
  }
  startPos = s.pos;
  const operands = [first, second];
  while (s.tokens[s.pos]?.kind === TokenKind.BarBar) {
    s.pos++;
    const next = parseAnd(s) || parseComparison(s) || parseGroup(s);
    if (!next) {
      s.pos = startPos;
      break;
    }
    operands.push(next);
    startPos = s.pos;
  }
  return {
    kind: SyntaxKind.Or,
    operands
  };
}
__name(parseOr, "parseOr");
function parseGroup(s) {
  const startPos = s.pos;
  if (s.tokens[s.pos]?.kind !== TokenKind.LeftParen) {
    return;
  }
  s.pos++;
  const expression = parseExpression(s);
  if (!expression) {
    s.pos = startPos;
    return;
  }
  if (s.tokens[s.pos]?.kind !== TokenKind.RightParen) {
    s.pos = startPos;
    return;
  }
  s.pos++;
  return expression;
}
__name(parseGroup, "parseGroup");
function parseComparison(s) {
  const startPos = s.pos;
  const left = parseIdentifier(s);
  if (!left) {
    s.pos = startPos;
    return;
  }
  const operator = parseOperator(s);
  if (operator == void 0) {
    s.pos = startPos;
    return;
  }
  const right = parseVersion(s) || parseNumberLiteral(s) || parseIdentifier(s);
  if (!right) {
    s.pos = startPos;
    return;
  } else if (right.kind === SyntaxKind.Identifier) {
    throw new Error(`Right-hand side of comparisons must be a version or number literal`);
  }
  return {
    kind: SyntaxKind.Comparison,
    left,
    operator,
    right
  };
}
__name(parseComparison, "parseComparison");
function parseIdentifier(s) {
  const token = s.tokens[s.pos];
  if (token?.kind === TokenKind.Identifier) {
    s.pos++;
    return { kind: SyntaxKind.Identifier, name: token.value };
  }
}
__name(parseIdentifier, "parseIdentifier");
function parseOperator(s) {
  const token = s.tokens[s.pos];
  if (!token)
    return null;
  switch (token.kind) {
    case TokenKind.EqualsEquals:
    case TokenKind.EqualsEqualsEquals:
      s.pos++;
      return Operator.Equal;
    case TokenKind.ExclamationEquals:
    case TokenKind.ExclamationEqualsEquals:
      s.pos++;
      return Operator.NotEqual;
    case TokenKind.LessThan:
      s.pos++;
      return Operator.LessThan;
    case TokenKind.LessThanEquals:
      s.pos++;
      return Operator.LessThanOrEqual;
    case TokenKind.GreaterThan:
      s.pos++;
      return Operator.GreaterThan;
    case TokenKind.GreaterThanEquals:
      s.pos++;
      return Operator.GreaterThanOrEqual;
    default:
      return null;
  }
}
__name(parseOperator, "parseOperator");
function parseVersion(s) {
  let pos = s.pos;
  if (s.tokens[pos]?.kind !== TokenKind.NumberLiteral) {
    return;
  }
  let version = s.tokens[pos].value;
  pos++;
  if (s.tokens[pos]?.kind !== TokenKind.Dot) {
    return;
  }
  version += ".";
  pos++;
  if (s.tokens[pos]?.kind !== TokenKind.NumberLiteral) {
    return;
  }
  version += s.tokens[pos].value;
  pos++;
  if (s.tokens[pos]?.kind === TokenKind.Dot) {
    version += ".";
    pos++;
    if (s.tokens[pos]?.kind !== TokenKind.NumberLiteral) {
      return;
    }
    version += s.tokens[pos].value;
    pos++;
  }
  s.pos = pos;
  return { kind: SyntaxKind.Version, value: version };
}
__name(parseVersion, "parseVersion");
function parseNumberLiteral(s) {
  if (s.tokens[s.pos]?.kind === TokenKind.NumberLiteral) {
    const token = s.tokens[s.pos];
    s.pos++;
    return {
      kind: SyntaxKind.NumberLiteral,
      value: parseInt(token.value)
    };
  }
}
__name(parseNumberLiteral, "parseNumberLiteral");
function toRulesLogic(expr) {
  if (expr.kind === SyntaxKind.Or) {
    return {
      or: expr.operands.map(toRulesLogic)
    };
  } else if (expr.kind === SyntaxKind.And) {
    return {
      and: expr.operands.map(toRulesLogic)
    };
  } else if (expr.kind === SyntaxKind.Comparison) {
    if (expr.right.kind === SyntaxKind.Version) {
      const opMap = {
        [Operator.Equal]: "ver ===",
        [Operator.NotEqual]: "ver !==",
        [Operator.LessThan]: "ver <",
        [Operator.LessThanOrEqual]: "ver <=",
        [Operator.GreaterThan]: "ver >",
        [Operator.GreaterThanOrEqual]: "ver >="
      };
      return {
        [opMap[expr.operator]]: [
          { var: expr.left.name },
          expr.right.value
        ]
      };
    } else {
      const opMap = {
        [Operator.Equal]: "===",
        [Operator.NotEqual]: "!==",
        [Operator.LessThan]: "<",
        [Operator.LessThanOrEqual]: "<=",
        [Operator.GreaterThan]: ">",
        [Operator.GreaterThanOrEqual]: ">="
      };
      return {
        [opMap[expr.operator]]: [
          { var: expr.left.name },
          expr.right.value
        ]
      };
    }
  } else {
    (0, import_helpers.assertNever)(expr);
  }
}
__name(toRulesLogic, "toRulesLogic");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  Operator,
  SyntaxKind,
  TokenKind,
  parse,
  toRulesLogic,
  tokenize
});
//# sourceMappingURL=LogicParser.js.map
