/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { FieldValue } from "../common/index.js";
function isNameChar(c) {
  return c >= "A" && c <= "Z" || c >= "a" && c <= "z" || c >= "0" && c <= "9" || c === "_";
}
class Lexer {
  #keywords;
  constructor(keywords) {
    if (keywords instanceof Set) {
      this.#keywords = keywords;
    } else {
      this.#keywords = new Set(keywords);
    }
  }
  lex(text, error) {
    return lex(text, error, this.#keywords);
  }
}
((Lexer2) => {
  Lexer2.Basic = new Lexer2();
})(Lexer || (Lexer = {}));
function* lex(text, error, keywords) {
  const i = text[Symbol.iterator]();
  let current = i.next();
  if (current.done) {
    return;
  }
  let peeked = i.next();
  let line = 1;
  let char = 1;
  let startLine = -1;
  let startChar = -1;
  function markStart() {
    startLine = line;
    startChar = char;
  }
  function next() {
    current = peeked;
    if (!current.done) {
      char++;
      peeked = i.next();
    }
  }
  function tokenizeName() {
    const chars = [current.value];
    while (peeked.value !== void 0 && isNameChar(peeked.value)) {
      next();
      chars.push(current.value);
    }
    const name = chars.join("");
    if (keywords.has(name)) {
      return { type: name, startLine, startChar };
    }
    return { type: "word", value: name, startLine, startChar };
  }
  function binaryValueOf(digit) {
    if (digit === "0") {
      return 0;
    }
    if (digit === "1") {
      return 1;
    }
  }
  function decimalValueOf(digit) {
    if (digit === void 0) {
      return;
    }
    if (digit >= "0" && digit <= "9") {
      return digit.charCodeAt(0) - "0".charCodeAt(0);
    }
  }
  function hexadecimalValueOf(digit) {
    if (digit === void 0) {
      return;
    }
    if (digit >= "0" && digit <= "9") {
      return digit.charCodeAt(0) - "0".charCodeAt(0);
    }
    if (digit >= "a" && digit <= "f") {
      return 10 + digit.charCodeAt(0) - "a".charCodeAt(0);
    }
    if (digit >= "A" && digit <= "F") {
      return 10 + digit.charCodeAt(0) - "A".charCodeAt(0);
    }
    return;
  }
  function tokenizeDigits(base, valueOf) {
    let num = valueOf(current.value);
    if (num === void 0) {
      error("INVALID_NUMBER", `Expected digit following numeric prefix`);
      return { type: "value", value: 0, startLine, startChar };
    }
    while (true) {
      const digitValue = valueOf(peeked.value);
      if (digitValue === void 0) {
        break;
      }
      next();
      num = num * base + digitValue;
    }
    if (base === 10 && peeked.value === ".") {
      next();
      let fraction = "";
      while (true) {
        const digitValue = valueOf(peeked.value);
        if (digitValue === void 0) {
          break;
        }
        fraction += peeked.value;
        next();
      }
      num = Number.parseFloat(`${num}.${fraction}`);
    }
    if (peeked.value === "%") {
      next();
      return { type: "value", value: FieldValue.Percent(num), startLine, startChar };
    } else if (peeked.value === "\xB0") {
      next();
      if (peeked.value?.toLowerCase() === "c") {
        next();
      }
      return { type: "value", value: FieldValue.Celsius(num), startLine, startChar };
    }
    return { type: "value", value: num, startLine, startChar };
  }
  while (!current.done) {
    switch (current.value) {
      case "|":
      case "^":
      case "&":
      case ".":
      case ",":
      case "[":
      case "]":
      case "(":
      case ")":
      case "{":
      case "}":
      case "-":
      case "+":
      case "/":
      case "*":
        yield { type: current.value, startLine: line, startChar: char };
        break;
      case "0":
        markStart();
        if (current.value === "0") {
          if (peeked.value === "x") {
            next();
            next();
            yield tokenizeDigits(16, hexadecimalValueOf);
            break;
          }
          if (peeked.value === "b") {
            next();
            next();
            yield tokenizeDigits(2, binaryValueOf);
            break;
          }
        }
        yield tokenizeDigits(10, decimalValueOf);
        break;
      case "1":
      case "2":
      case "3":
      case "4":
      case "5":
      case "6":
      case "7":
      case "8":
      case "9":
        yield tokenizeDigits(10, decimalValueOf);
        break;
      case "!":
      case ">":
      case "<":
        {
          const base = current.value;
          if (peeked.value === "=") {
            const type = `${base}${peeked.value}`;
            next();
            yield {
              type,
              startLine: line,
              startChar: char
            };
          } else {
            yield { type: base, startLine: line, startChar: char };
          }
        }
        break;
      case "=":
        markStart();
        if (peeked.value === "=") {
          next();
        } else {
          error("BAD_EQUAL", `"=" must be followed by another "="`);
        }
        yield { type: "==", startLine, startChar };
        break;
      case "\n":
        line++;
        char = 0;
        break;
      case " ":
      case "	":
      case "\r":
      case "\v":
      case "\f":
        break;
      default:
        if (isNameChar(current.value)) {
          markStart();
          yield tokenizeName();
        } else {
          error("GARBAGE_CHARACTER", `Unexpected character "${current.value}"`);
        }
        break;
    }
    next();
  }
}
export {
  Lexer
};
//# sourceMappingURL=Lexer.js.map
