// src/types.ts
function createSelector(value) {
  let selectorKind = "complex";
  if (value.startsWith("#") && !value.includes(" ")) {
    selectorKind = "id";
  } else if (value.startsWith(".") && !value.includes(" ")) {
    selectorKind = "class";
  } else if (value.startsWith("[") && value.endsWith("]")) {
    selectorKind = "attribute";
  } else if (/^[a-z][a-z0-9]*$/i.test(value)) {
    selectorKind = "element";
  }
  return { type: "selector", value, selectorKind };
}
function createLiteral(value, dataType) {
  const result = { type: "literal", value };
  if (dataType !== void 0) {
    return { type: "literal", value, dataType };
  }
  return result;
}
function createReference(value) {
  return { type: "reference", value };
}
function createPropertyPath(object, property) {
  return { type: "property-path", object, property };
}
function createCommandNode(action, roles, metadata) {
  const node = {
    kind: "command",
    action,
    roles: new Map(Object.entries(roles))
  };
  if (metadata !== void 0) {
    return { ...node, metadata };
  }
  return node;
}
function createEventHandler(event, body, modifiers, metadata, parameterNames) {
  const roles = /* @__PURE__ */ new Map();
  roles.set("event", event);
  const node = {
    kind: "event-handler",
    action: "on",
    roles,
    body
  };
  if (modifiers !== void 0) {
    node.eventModifiers = modifiers;
  }
  if (metadata !== void 0) {
    node.metadata = metadata;
  }
  if (parameterNames !== void 0 && parameterNames.length > 0) {
    node.parameterNames = parameterNames;
  }
  return node;
}

// src/parser/utils/type-validation.ts
function isTypeCompatible(actualType, expectedTypes) {
  if (!expectedTypes || expectedTypes.length === 0) {
    return true;
  }
  if (expectedTypes.includes(actualType)) {
    return true;
  }
  if (expectedTypes.includes("expression")) {
    return true;
  }
  if (actualType === "property-path") {
    return expectedTypes.some((t) => ["selector", "reference", "expression"].includes(t));
  }
  return false;
}

// src/parser/utils/possessive-keywords.ts
function getPossessiveReference(profile, keyword) {
  return profile.possessive?.keywords?.[keyword];
}

// src/registry.ts
var tokenizers = /* @__PURE__ */ new Map();
var profiles = /* @__PURE__ */ new Map();
var patternCache = /* @__PURE__ */ new Map();
var patternGenerator = null;
function deepMerge(base, variant) {
  const result = { ...base };
  for (const key of Object.keys(variant)) {
    const variantValue = variant[key];
    const baseValue = base[key];
    if (variantValue === void 0) {
      continue;
    }
    if (typeof variantValue === "object" && variantValue !== null && !Array.isArray(variantValue) && typeof baseValue === "object" && baseValue !== null && !Array.isArray(baseValue)) {
      result[key] = deepMerge(
        baseValue,
        variantValue
      );
    } else {
      result[key] = variantValue;
    }
  }
  return result;
}
function mergeProfiles(base, variant) {
  return deepMerge(base, variant);
}
function resolveProfile(profile) {
  if (!profile.extends) {
    return profile;
  }
  const baseProfile = profiles.get(profile.extends);
  if (!baseProfile) {
    console.warn(
      `[Registry] Profile '${profile.code}' extends '${profile.extends}' but base is not registered. Make sure to import the base language before the variant.`
    );
    return profile;
  }
  const resolvedBase = resolveProfile(baseProfile);
  return mergeProfiles(resolvedBase, profile);
}
function registerLanguage(code, tokenizer, profile) {
  tokenizers.set(code, tokenizer);
  profiles.set(code, profile);
  patternCache.delete(code);
}
function setPatternGenerator(generator) {
  patternGenerator = generator;
}
var registeredPatterns = /* @__PURE__ */ new Map();
function registerPatterns(code, patterns) {
  registeredPatterns.set(code, patterns);
  patternCache.delete(code);
}
function getBaseLanguageCode(code) {
  return code.split("-")[0];
}
function isLanguageVariant(code) {
  return code.includes("-");
}
function getTokenizer(code) {
  let tokenizer = tokenizers.get(code);
  if (!tokenizer && isLanguageVariant(code)) {
    const baseCode = getBaseLanguageCode(code);
    tokenizer = tokenizers.get(baseCode);
  }
  if (!tokenizer) {
    const registered = Array.from(tokenizers.keys()).join(", ");
    throw new Error(
      `Language '${code}' is not registered. Registered languages: ${registered || "none"}. Import the language module first: import '@lokascript/semantic/languages/${code}';`
    );
  }
  return tokenizer;
}
function getProfile(code) {
  let profile = profiles.get(code);
  if (!profile && isLanguageVariant(code)) {
    const baseCode = getBaseLanguageCode(code);
    profile = profiles.get(baseCode);
  }
  if (!profile) {
    const registered = Array.from(profiles.keys()).join(", ");
    throw new Error(
      `Language profile '${code}' is not registered. Registered languages: ${registered || "none"}. Import the language module first: import '@lokascript/semantic/languages/${code}';`
    );
  }
  return resolveProfile(profile);
}
function tryGetTokenizer(code) {
  let tokenizer = tokenizers.get(code);
  if (!tokenizer && isLanguageVariant(code)) {
    tokenizer = tokenizers.get(getBaseLanguageCode(code));
  }
  return tokenizer;
}
function tryGetProfile(code) {
  let profile = profiles.get(code);
  if (!profile && isLanguageVariant(code)) {
    profile = profiles.get(getBaseLanguageCode(code));
  }
  return profile ? resolveProfile(profile) : void 0;
}
function getRegisteredLanguages() {
  return Array.from(tokenizers.keys());
}
function isLanguageRegistered(code) {
  if (tokenizers.has(code) && profiles.has(code)) {
    return true;
  }
  if (isLanguageVariant(code)) {
    const baseCode = getBaseLanguageCode(code);
    return tokenizers.has(baseCode) && profiles.has(baseCode);
  }
  return false;
}
function isLanguageSupported(code) {
  if (tokenizers.has(code)) {
    return true;
  }
  if (isLanguageVariant(code)) {
    return tokenizers.has(getBaseLanguageCode(code));
  }
  return false;
}
function getPatternsForLanguage(code) {
  let cached = patternCache.get(code);
  if (!cached && isLanguageVariant(code)) {
    cached = patternCache.get(getBaseLanguageCode(code));
  }
  if (cached) {
    return cached;
  }
  let registered = registeredPatterns.get(code);
  if (!registered && isLanguageVariant(code)) {
    registered = registeredPatterns.get(getBaseLanguageCode(code));
  }
  if (registered) {
    patternCache.set(code, registered);
    return registered;
  }
  if (!patternGenerator) {
    throw new Error(
      `No patterns registered for language '${code}'. Either import the language module or set a pattern generator.`
    );
  }
  const profile = getProfile(code);
  const patterns = patternGenerator(profile);
  patternCache.set(code, patterns);
  return patterns;
}
function getPatternsForLanguageAndCommand(language, command) {
  return getPatternsForLanguage(language).filter((p) => p.command === command).sort((a, b) => b.priority - a.priority);
}

// src/parser/pattern-matcher.ts
var _PatternMatcher = class _PatternMatcher {
  constructor() {
    /**
     * Track stem matches for confidence calculation.
     * This is set during matching and read during confidence calculation.
     */
    this.stemMatchCount = 0;
    this.totalKeywordMatches = 0;
  }
  /**
   * Try to match a single pattern against the token stream.
   * Returns the match result or null if no match.
   */
  matchPattern(tokens, pattern) {
    const mark = tokens.mark();
    const captured = /* @__PURE__ */ new Map();
    this.currentProfile = tryGetProfile(pattern.language);
    this.stemMatchCount = 0;
    this.totalKeywordMatches = 0;
    const success = this.matchTokenSequence(tokens, pattern.template.tokens, captured);
    if (!success) {
      tokens.reset(mark);
      return null;
    }
    const confidence = this.calculateConfidence(pattern, captured);
    this.applyExtractionRules(pattern, captured);
    return {
      pattern,
      captured,
      consumedTokens: tokens.position() - mark.position,
      confidence
    };
  }
  /**
   * Try to match multiple patterns, return the best match.
   */
  matchBest(tokens, patterns) {
    const matches = [];
    for (const pattern of patterns) {
      const mark = tokens.mark();
      const result = this.matchPattern(tokens, pattern);
      if (result) {
        matches.push(result);
      }
      tokens.reset(mark);
    }
    if (matches.length === 0) {
      return null;
    }
    matches.sort((a, b) => {
      const priorityDiff = b.pattern.priority - a.pattern.priority;
      if (priorityDiff !== 0) return priorityDiff;
      return b.confidence - a.confidence;
    });
    const best = matches[0];
    this.matchPattern(tokens, best.pattern);
    return best;
  }
  /**
   * Match a sequence of pattern tokens against the token stream.
   */
  matchTokenSequence(tokens, patternTokens, captured) {
    const firstPatternToken = patternTokens[0];
    const patternExpectsConjunction = firstPatternToken?.type === "literal" && (firstPatternToken.value === "and" || firstPatternToken.value === "then" || firstPatternToken.alternatives?.includes("and") || firstPatternToken.alternatives?.includes("then"));
    if (this.currentProfile?.code === "ar" && !patternExpectsConjunction) {
      while (tokens.peek()?.kind === "conjunction") {
        tokens.advance();
      }
    }
    for (const patternToken of patternTokens) {
      const matched = this.matchPatternToken(tokens, patternToken, captured);
      if (!matched) {
        if (this.isOptional(patternToken)) {
          continue;
        }
        return false;
      }
    }
    return true;
  }
  /**
   * Match a single pattern token against the current position in the stream.
   */
  matchPatternToken(tokens, patternToken, captured) {
    switch (patternToken.type) {
      case "literal":
        return this.matchLiteralToken(tokens, patternToken);
      case "role":
        return this.matchRoleToken(tokens, patternToken, captured);
      case "group":
        return this.matchGroupToken(tokens, patternToken, captured);
      default:
        return false;
    }
  }
  /**
   * Match a literal pattern token (keyword or particle).
   */
  matchLiteralToken(tokens, patternToken) {
    const token = tokens.peek();
    if (!token) return false;
    const matchType = this.getMatchType(token, patternToken.value);
    if (matchType !== "none") {
      this.totalKeywordMatches++;
      if (matchType === "stem") {
        this.stemMatchCount++;
      }
      tokens.advance();
      return true;
    }
    if (patternToken.alternatives) {
      for (const alt of patternToken.alternatives) {
        const altMatchType = this.getMatchType(token, alt);
        if (altMatchType !== "none") {
          this.totalKeywordMatches++;
          if (altMatchType === "stem") {
            this.stemMatchCount++;
          }
          tokens.advance();
          return true;
        }
      }
    }
    return false;
  }
  /**
   * Match a role pattern token (captures a semantic value).
   * Handles multi-token expressions like:
   * - 'my value' (possessive keyword + property)
   * - '#dialog.showModal()' (method call)
   * - "#element's *opacity" (possessive selector + property)
   */
  matchRoleToken(tokens, patternToken, captured) {
    this.skipNoiseWords(tokens);
    const token = tokens.peek();
    if (!token) {
      return patternToken.optional || false;
    }
    const possessiveValue = this.tryMatchPossessiveExpression(tokens);
    if (possessiveValue) {
      if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
        if (!patternToken.expectedTypes.includes(possessiveValue.type) && !patternToken.expectedTypes.includes("expression")) {
          return patternToken.optional || false;
        }
      }
      captured.set(patternToken.role, possessiveValue);
      return true;
    }
    const methodCallValue = this.tryMatchMethodCallExpression(tokens);
    if (methodCallValue) {
      if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
        if (!patternToken.expectedTypes.includes(methodCallValue.type) && !patternToken.expectedTypes.includes("expression")) {
          return patternToken.optional || false;
        }
      }
      captured.set(patternToken.role, methodCallValue);
      return true;
    }
    const possessiveSelectorValue = this.tryMatchPossessiveSelectorExpression(tokens);
    if (possessiveSelectorValue) {
      if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
        if (!isTypeCompatible(possessiveSelectorValue.type, patternToken.expectedTypes)) {
          return patternToken.optional || false;
        }
      }
      captured.set(patternToken.role, possessiveSelectorValue);
      return true;
    }
    const propertyAccessValue = this.tryMatchPropertyAccessExpression(tokens);
    if (propertyAccessValue) {
      if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
        if (!patternToken.expectedTypes.includes(propertyAccessValue.type) && !patternToken.expectedTypes.includes("expression")) {
          return patternToken.optional || false;
        }
      }
      captured.set(patternToken.role, propertyAccessValue);
      return true;
    }
    const selectorPropertyValue = this.tryMatchSelectorPropertyExpression(tokens);
    if (selectorPropertyValue) {
      if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
        if (!isTypeCompatible(selectorPropertyValue.type, patternToken.expectedTypes)) {
          return patternToken.optional || false;
        }
      }
      captured.set(patternToken.role, selectorPropertyValue);
      return true;
    }
    const value = this.tokenToSemanticValue(token);
    if (!value) {
      return patternToken.optional || false;
    }
    if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
      if (!patternToken.expectedTypes.includes(value.type)) {
        return patternToken.optional || false;
      }
    }
    captured.set(patternToken.role, value);
    tokens.advance();
    return true;
  }
  /**
   * Try to match a possessive expression like 'my value' or 'its innerHTML'.
   * Returns the PropertyPathValue if matched, or null if not.
   */
  tryMatchPossessiveExpression(tokens) {
    const token = tokens.peek();
    if (!token) return null;
    if (!this.currentProfile) return null;
    const tokenLower = (token.normalized || token.value).toLowerCase();
    const baseRef = getPossessiveReference(this.currentProfile, tokenLower);
    if (!baseRef) return null;
    const mark = tokens.mark();
    tokens.advance();
    const propertyToken = tokens.peek();
    if (!propertyToken) {
      tokens.reset(mark);
      return null;
    }
    if (propertyToken.kind === "identifier" || propertyToken.kind === "keyword" && !this.isStructuralKeyword(propertyToken.value) || propertyToken.kind === "selector" && propertyToken.value.startsWith("*")) {
      tokens.advance();
      return createPropertyPath(createReference(baseRef), propertyToken.value);
    }
    tokens.reset(mark);
    return null;
  }
  /**
   * Check if a keyword is a structural keyword (preposition, control flow, etc.)
   * that shouldn't be consumed as a property name.
   */
  isStructuralKeyword(value) {
    const structural = /* @__PURE__ */ new Set([
      // Prepositions
      "into",
      "in",
      "to",
      "from",
      "at",
      "by",
      "with",
      "without",
      "before",
      "after",
      "of",
      "as",
      "on",
      // Control flow
      "then",
      "end",
      "else",
      "if",
      "repeat",
      "while",
      "for",
      // Commands (shouldn't be property names)
      "toggle",
      "add",
      "remove",
      "put",
      "set",
      "show",
      "hide",
      "increment",
      "decrement",
      "send",
      "trigger",
      "call"
    ]);
    return structural.has(value.toLowerCase());
  }
  /**
   * Try to match a method call expression like '#dialog.showModal()'.
   * Pattern: selector + '.' + identifier + '(' + [args] + ')'
   * Returns an expression value if matched, or null if not.
   */
  tryMatchMethodCallExpression(tokens) {
    const token = tokens.peek();
    if (!token || token.kind !== "selector") return null;
    const mark = tokens.mark();
    tokens.advance();
    const dotToken = tokens.peek();
    if (!dotToken || dotToken.kind !== "operator" || dotToken.value !== ".") {
      tokens.reset(mark);
      return null;
    }
    tokens.advance();
    const methodToken = tokens.peek();
    if (!methodToken || methodToken.kind !== "identifier") {
      tokens.reset(mark);
      return null;
    }
    tokens.advance();
    const openParen = tokens.peek();
    if (!openParen || openParen.kind !== "punctuation" || openParen.value !== "(") {
      tokens.reset(mark);
      return null;
    }
    tokens.advance();
    const args = [];
    while (!tokens.isAtEnd() && args.length < _PatternMatcher.MAX_METHOD_ARGS) {
      const argToken = tokens.peek();
      if (!argToken) break;
      if (argToken.kind === "punctuation" && argToken.value === ")") {
        tokens.advance();
        break;
      }
      if (argToken.kind === "punctuation" && argToken.value === ",") {
        tokens.advance();
        continue;
      }
      args.push(argToken.value);
      tokens.advance();
    }
    const methodCall = `${token.value}.${methodToken.value}(${args.join(", ")})`;
    return {
      type: "expression",
      raw: methodCall
    };
  }
  /**
   * Try to match a property access expression like 'userData.name' or 'it.data'.
   * Pattern: (identifier | keyword) + '.' + identifier [+ '.' + identifier ...]
   * Returns an expression value if matched, or null if not.
   */
  tryMatchPropertyAccessExpression(tokens) {
    const token = tokens.peek();
    if (!token) return null;
    if (token.kind !== "identifier" && token.kind !== "keyword") return null;
    const mark = tokens.mark();
    tokens.advance();
    const dotToken = tokens.peek();
    if (!dotToken || dotToken.kind !== "operator" || dotToken.value !== ".") {
      tokens.reset(mark);
      return null;
    }
    tokens.advance();
    const propertyToken = tokens.peek();
    if (!propertyToken || propertyToken.kind !== "identifier") {
      tokens.reset(mark);
      return null;
    }
    tokens.advance();
    let chain = `${token.value}.${propertyToken.value}`;
    let depth = 1;
    while (!tokens.isAtEnd() && depth < _PatternMatcher.MAX_PROPERTY_DEPTH) {
      const nextDot = tokens.peek();
      if (!nextDot || nextDot.kind !== "operator" || nextDot.value !== ".") {
        break;
      }
      tokens.advance();
      const nextProp = tokens.peek();
      if (!nextProp || nextProp.kind !== "identifier") {
        break;
      }
      tokens.advance();
      chain += `.${nextProp.value}`;
      depth++;
    }
    const openParen = tokens.peek();
    if (openParen && openParen.kind === "punctuation" && openParen.value === "(") {
      tokens.advance();
      const args = [];
      let argDepth = 0;
      while (!tokens.isAtEnd() && args.length < _PatternMatcher.MAX_METHOD_ARGS) {
        const argToken = tokens.peek();
        if (!argToken) break;
        if (argToken.kind === "punctuation" && argToken.value === ")") {
          if (argDepth === 0) {
            tokens.advance();
            break;
          }
          argDepth--;
        }
        if (argToken.kind === "punctuation" && argToken.value === "(") {
          argDepth++;
        }
        if (argToken.kind === "punctuation" && argToken.value === ",") {
          tokens.advance();
          continue;
        }
        args.push(argToken.value);
        tokens.advance();
      }
      const methodCall = `${chain}(${args.join(", ")})`;
      return {
        type: "expression",
        raw: methodCall
      };
    }
    return {
      type: "expression",
      raw: chain
    };
  }
  /**
   * Try to match a possessive selector expression like "#element's *opacity".
   * Pattern: selector + "'s" + (selector | identifier)
   * Returns a property-path value if matched, or null if not.
   */
  tryMatchPossessiveSelectorExpression(tokens) {
    const token = tokens.peek();
    if (!token || token.kind !== "selector") return null;
    const mark = tokens.mark();
    tokens.advance();
    const possessiveToken = tokens.peek();
    if (!possessiveToken || possessiveToken.kind !== "punctuation" || possessiveToken.value !== "'s") {
      tokens.reset(mark);
      return null;
    }
    tokens.advance();
    const propertyToken = tokens.peek();
    if (!propertyToken) {
      tokens.reset(mark);
      return null;
    }
    if (propertyToken.kind !== "selector" && propertyToken.kind !== "identifier") {
      tokens.reset(mark);
      return null;
    }
    tokens.advance();
    return createPropertyPath(createSelector(token.value), propertyToken.value);
  }
  /**
   * Try to match a selector + property expression like "#output.innerText".
   * This handles cases where the tokenizer produces two selector tokens:
   * - #output (id selector)
   * - .innerText (looks like class selector, but is actually property)
   *
   * Pattern: id-selector + class-selector-that-is-actually-property
   * Returns a property-path value if matched, or null if not.
   */
  tryMatchSelectorPropertyExpression(tokens) {
    const token = tokens.peek();
    if (!token || token.kind !== "selector") return null;
    if (!token.value.startsWith("#")) return null;
    const mark = tokens.mark();
    tokens.advance();
    const propertyToken = tokens.peek();
    if (!propertyToken || propertyToken.kind !== "selector") {
      tokens.reset(mark);
      return null;
    }
    if (!propertyToken.value.startsWith(".")) {
      tokens.reset(mark);
      return null;
    }
    const peek2 = tokens.peek(1);
    if (peek2 && peek2.kind === "selector") {
    }
    tokens.advance();
    const propertyName = propertyToken.value.slice(1);
    return createPropertyPath(createSelector(token.value), propertyName);
  }
  /**
   * Match a group pattern token (optional sequence).
   */
  matchGroupToken(tokens, patternToken, captured) {
    const mark = tokens.mark();
    const capturedBefore = new Set(captured.keys());
    const success = this.matchTokenSequence(tokens, patternToken.tokens, captured);
    if (!success) {
      tokens.reset(mark);
      for (const role of captured.keys()) {
        if (!capturedBefore.has(role)) {
          captured.delete(role);
        }
      }
      return patternToken.optional || false;
    }
    return true;
  }
  /**
   * Get the type of match for a token against a value.
   * Used for confidence calculation.
   */
  getMatchType(token, value) {
    if (token.value === value) return "exact";
    if (token.normalized === value) return "normalized";
    if (token.stem === value && token.stemConfidence !== void 0 && token.stemConfidence >= 0.7) {
      return "stem";
    }
    if (token.kind === "keyword" && token.value.toLowerCase() === value.toLowerCase()) {
      return "case-insensitive";
    }
    return "none";
  }
  /**
   * Convert a language token to a semantic value.
   */
  tokenToSemanticValue(token) {
    switch (token.kind) {
      case "selector":
        return createSelector(token.value);
      case "literal":
        return this.parseLiteralValue(token.value);
      case "keyword":
        const lower = (token.normalized || token.value).toLowerCase();
        if (["me", "you", "it", "result", "event", "target", "body"].includes(lower)) {
          return createReference(lower);
        }
        return createLiteral(token.normalized || token.value);
      case "identifier":
        if (token.value.startsWith(":")) {
          return createReference(token.value);
        }
        const identLower = token.value.toLowerCase();
        if (["me", "you", "it", "result", "event", "target", "body"].includes(identLower)) {
          return createReference(identLower);
        }
        return { type: "expression", raw: token.value };
      case "url":
        return createLiteral(token.value, "string");
      default:
        return null;
    }
  }
  /**
   * Parse a literal value (string, number, boolean).
   */
  parseLiteralValue(value) {
    if (value.startsWith('"') || value.startsWith("'") || value.startsWith("`") || value.startsWith("\u300C")) {
      const inner = value.slice(1, -1);
      return createLiteral(inner, "string");
    }
    if (value === "true") return createLiteral(true, "boolean");
    if (value === "false") return createLiteral(false, "boolean");
    const durationMatch = value.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)?$/);
    if (durationMatch) {
      const num2 = parseFloat(durationMatch[1]);
      const unit = durationMatch[2];
      if (unit) {
        return createLiteral(value, "duration");
      }
      return createLiteral(num2, "number");
    }
    const num = parseFloat(value);
    if (!isNaN(num)) {
      return createLiteral(num, "number");
    }
    return createLiteral(value, "string");
  }
  /**
   * Apply extraction rules to fill in default values for missing roles.
   */
  applyExtractionRules(pattern, captured) {
    for (const [role, rule] of Object.entries(pattern.extraction)) {
      if (!captured.has(role) && rule.default) {
        captured.set(role, rule.default);
      }
    }
  }
  /**
   * Check if a pattern token is optional.
   */
  isOptional(patternToken) {
    return patternToken.optional === true;
  }
  /**
   * Calculate confidence score for a match (0-1).
   *
   * Confidence is reduced for:
   * - Stem matches (morphological normalization has inherent uncertainty)
   * - Missing optional roles (but less penalty if role has a default value)
   *
   * Confidence is increased for:
   * - VSO languages (Arabic) when pattern starts with a verb
   */
  calculateConfidence(pattern, captured) {
    let score = 0;
    let maxScore = 0;
    const hasDefault = (role) => {
      return pattern.extraction?.[role]?.default !== void 0;
    };
    for (const token of pattern.template.tokens) {
      if (token.type === "role") {
        maxScore += 1;
        if (captured.has(token.role)) {
          score += 1;
        }
      } else if (token.type === "group") {
        for (const subToken of token.tokens) {
          if (subToken.type === "role") {
            const roleHasDefault = hasDefault(subToken.role);
            const weight = 0.8;
            maxScore += weight;
            if (captured.has(subToken.role)) {
              score += weight;
            } else if (roleHasDefault) {
              score += weight * 0.6;
            }
          }
        }
      }
    }
    let baseConfidence = maxScore > 0 ? score / maxScore : 1;
    if (this.stemMatchCount > 0 && this.totalKeywordMatches > 0) {
      const stemPenalty = this.stemMatchCount / this.totalKeywordMatches * 0.15;
      baseConfidence = Math.max(0.5, baseConfidence - stemPenalty);
    }
    const vsoBoost = this.calculateVSOConfidenceBoost(pattern);
    baseConfidence = Math.min(1, baseConfidence + vsoBoost);
    const prepositionAdjustment = this.arabicPrepositionDisambiguation(pattern, captured);
    baseConfidence = Math.max(0, Math.min(1, baseConfidence + prepositionAdjustment));
    return baseConfidence;
  }
  /**
   * Calculate confidence boost for VSO (Verb-Subject-Object) language patterns.
   * Arabic naturally uses VSO word order, so patterns that start with a verb
   * should receive a confidence boost.
   *
   * Returns +0.15 confidence boost if:
   * - Language is Arabic ('ar')
   * - Pattern's first token is a verb keyword
   *
   * @param pattern The language pattern being matched
   * @returns Confidence boost (0 or 0.15)
   */
  calculateVSOConfidenceBoost(pattern) {
    if (pattern.language !== "ar") {
      return 0;
    }
    const firstToken = pattern.template.tokens[0];
    if (!firstToken || firstToken.type !== "literal") {
      return 0;
    }
    const ARABIC_VERBS = /* @__PURE__ */ new Set([
      "\u0628\u062F\u0644",
      "\u063A\u064A\u0631",
      "\u0623\u0636\u0641",
      "\u0623\u0632\u0644",
      "\u0636\u0639",
      "\u0627\u062C\u0639\u0644",
      "\u0639\u064A\u0646",
      "\u0632\u062F",
      "\u0627\u0646\u0642\u0635",
      "\u0633\u062C\u0644",
      "\u0623\u0638\u0647\u0631",
      "\u0623\u062E\u0641",
      "\u0634\u063A\u0644",
      "\u0623\u0631\u0633\u0644",
      "\u0631\u0643\u0632",
      "\u0634\u0648\u0634",
      "\u062A\u0648\u0642\u0641",
      "\u0627\u0646\u0633\u062E",
      "\u0627\u062D\u0630\u0641",
      "\u0627\u0635\u0646\u0639",
      "\u0627\u0646\u062A\u0638\u0631",
      "\u0627\u0646\u062A\u0642\u0627\u0644",
      "\u0623\u0648"
    ]);
    if (ARABIC_VERBS.has(firstToken.value)) {
      return 0.15;
    }
    if (firstToken.alternatives) {
      for (const alt of firstToken.alternatives) {
        if (ARABIC_VERBS.has(alt)) {
          return 0.15;
        }
      }
    }
    return 0;
  }
  /**
   * Arabic preposition disambiguation for confidence adjustment.
   *
   * Different Arabic prepositions are more or less natural for different semantic roles:
   * - على (on/upon) is preferred for patient/target roles (element selectors)
   * - إلى (to) is preferred for destination roles
   * - من (from) is preferred for source roles
   * - في (in) is preferred for location roles
   *
   * This method analyzes the prepositions used with captured semantic roles and
   * adjusts confidence based on idiomaticity:
   * - +0.10 for highly idiomatic preposition choices
   * - -0.10 for less natural preposition choices
   *
   * @param pattern The language pattern being matched
   * @param captured The captured semantic values
   * @returns Confidence adjustment (-0.10 to +0.10)
   */
  arabicPrepositionDisambiguation(pattern, captured) {
    if (pattern.language !== "ar") {
      return 0;
    }
    let adjustment = 0;
    const PREFERRED_PREPOSITIONS = {
      patient: ["\u0639\u0644\u0649"],
      // element selectors prefer على (on/upon)
      destination: ["\u0625\u0644\u0649", "\u0627\u0644\u0649"],
      // destination prefers إلى (to)
      source: ["\u0645\u0646"],
      // source prefers من (from)
      agent: ["\u0645\u0646"],
      // agent/by prefers من (from/by)
      manner: ["\u0628"],
      // manner prefers ب (with/by)
      style: ["\u0628"],
      // style prefers ب (with)
      goal: ["\u0625\u0644\u0649", "\u0627\u0644\u0649"],
      // target state prefers إلى (to)
      method: ["\u0628"]
      // method prefers ب (with/by)
    };
    for (const [role, value] of captured.entries()) {
      const preferred = PREFERRED_PREPOSITIONS[role];
      if (!preferred || preferred.length === 0) {
        continue;
      }
      const metadata = value.metadata;
      if (metadata && typeof metadata.prepositionValue === "string") {
        const usedPreposition = metadata.prepositionValue;
        if (preferred.includes(usedPreposition)) {
          adjustment += 0.1;
        } else {
          adjustment -= 0.1;
        }
      }
    }
    return Math.max(-0.1, Math.min(0.1, adjustment));
  }
  /**
   * Skip noise words like "the" before selectors.
   * This enables more natural English syntax like "toggle the .active".
   */
  skipNoiseWords(tokens) {
    const token = tokens.peek();
    if (!token) return;
    const tokenLower = token.value.toLowerCase();
    if (_PatternMatcher.ENGLISH_NOISE_WORDS.has(tokenLower)) {
      const mark = tokens.mark();
      tokens.advance();
      const nextToken = tokens.peek();
      if (nextToken && nextToken.kind === "selector") {
        return;
      }
      tokens.reset(mark);
    }
    if (tokenLower === "class") {
      tokens.advance();
    }
  }
  /**
   * Extract event modifiers from the token stream.
   * Event modifiers are .once, .debounce(N), .throttle(N), .queue(strategy)
   * that can appear after event names.
   *
   * Returns EventModifiers object or undefined if no modifiers found.
   */
  extractEventModifiers(tokens) {
    const modifiers = {};
    let foundModifier = false;
    while (!tokens.isAtEnd()) {
      const token = tokens.peek();
      if (!token || token.kind !== "event-modifier") {
        break;
      }
      const metadata = token.metadata;
      if (!metadata) {
        break;
      }
      foundModifier = true;
      switch (metadata.modifierName) {
        case "once":
          modifiers.once = true;
          break;
        case "debounce":
          if (typeof metadata.value === "number") {
            modifiers.debounce = metadata.value;
          }
          break;
        case "throttle":
          if (typeof metadata.value === "number") {
            modifiers.throttle = metadata.value;
          }
          break;
        case "queue":
          if (metadata.value === "first" || metadata.value === "last" || metadata.value === "all" || metadata.value === "none") {
            modifiers.queue = metadata.value;
          }
          break;
      }
      tokens.advance();
    }
    return foundModifier ? modifiers : void 0;
  }
};
// ==========================================================================
// Depth Limits for Expression Parsing (security hardening)
// ==========================================================================
/** Maximum depth for nested property access (e.g., a.b.c.d...) */
_PatternMatcher.MAX_PROPERTY_DEPTH = 10;
/** Maximum number of arguments in method calls */
_PatternMatcher.MAX_METHOD_ARGS = 20;
// ===========================================================================
// English Idiom Support - Noise Word Handling
// ===========================================================================
/**
 * Noise words that can be skipped in English for more natural syntax.
 * - "the" before selectors: "toggle the .active" → "toggle .active"
 * - "class" after class selectors: "add the .visible class" → "add .visible"
 */
_PatternMatcher.ENGLISH_NOISE_WORDS = /* @__PURE__ */ new Set(["the", "a", "an"]);
var PatternMatcher = _PatternMatcher;
var patternMatcher = new PatternMatcher();

// src/tokenizers/index.ts
function getTokenizer2(language) {
  return tryGetTokenizer(language);
}

// src/cache/semantic-cache.ts
var SemanticCache = class {
  constructor(config = {}) {
    this.cache = /* @__PURE__ */ new Map();
    this.config = {
      maxSize: config.maxSize ?? 1e3,
      ttlMs: config.ttlMs ?? 0,
      enabled: config.enabled ?? true
    };
    this.stats = {
      hits: 0,
      misses: 0,
      evictions: 0,
      expirations: 0
    };
  }
  /**
   * Generate cache key from input and language.
   */
  makeKey(input, language) {
    return `${language}:${input}`;
  }
  /**
   * Check if an entry has expired.
   */
  isExpired(entry) {
    if (this.config.ttlMs === 0) return false;
    return Date.now() - entry.createdAt > this.config.ttlMs;
  }
  /**
   * Evict the least recently used entry.
   */
  evictLRU() {
    const firstKey = this.cache.keys().next().value;
    if (firstKey !== void 0) {
      this.cache.delete(firstKey);
      this.stats.evictions++;
    }
  }
  /**
   * Get a cached result.
   *
   * @param input - The input string
   * @param language - The language code
   * @returns The cached result, or undefined if not found/expired
   */
  get(input, language) {
    if (!this.config.enabled) {
      this.stats.misses++;
      return void 0;
    }
    const key = this.makeKey(input, language);
    const entry = this.cache.get(key);
    if (!entry) {
      this.stats.misses++;
      return void 0;
    }
    if (this.isExpired(entry)) {
      this.cache.delete(key);
      this.stats.expirations++;
      this.stats.misses++;
      return void 0;
    }
    this.cache.delete(key);
    entry.lastAccessed = Date.now();
    this.cache.set(key, entry);
    this.stats.hits++;
    return entry.result;
  }
  /**
   * Store a result in the cache.
   *
   * @param input - The input string
   * @param language - The language code
   * @param result - The analysis result to cache
   */
  set(input, language, result) {
    if (!this.config.enabled) return;
    if (result.confidence === 0) return;
    const key = this.makeKey(input, language);
    const now = Date.now();
    while (this.cache.size >= this.config.maxSize) {
      this.evictLRU();
    }
    this.cache.set(key, {
      result,
      createdAt: now,
      lastAccessed: now
    });
  }
  /**
   * Check if a result is cached (without updating LRU).
   */
  has(input, language) {
    if (!this.config.enabled) return false;
    const key = this.makeKey(input, language);
    const entry = this.cache.get(key);
    if (!entry) return false;
    if (this.isExpired(entry)) {
      this.cache.delete(key);
      this.stats.expirations++;
      return false;
    }
    return true;
  }
  /**
   * Remove a specific entry from the cache.
   */
  delete(input, language) {
    const key = this.makeKey(input, language);
    return this.cache.delete(key);
  }
  /**
   * Clear all cached entries.
   */
  clear() {
    this.cache.clear();
  }
  /**
   * Reset statistics.
   */
  resetStats() {
    this.stats = {
      hits: 0,
      misses: 0,
      evictions: 0,
      expirations: 0
    };
  }
  /**
   * Get cache statistics.
   */
  getStats() {
    const total = this.stats.hits + this.stats.misses;
    return {
      hits: this.stats.hits,
      misses: this.stats.misses,
      size: this.cache.size,
      maxSize: this.config.maxSize,
      hitRate: total > 0 ? this.stats.hits / total : 0,
      evictions: this.stats.evictions,
      expirations: this.stats.expirations,
      enabled: this.config.enabled
    };
  }
  /**
   * Update cache configuration.
   */
  configure(config) {
    if (config.maxSize !== void 0) {
      this.config.maxSize = config.maxSize;
      while (this.cache.size > this.config.maxSize) {
        this.evictLRU();
      }
    }
    if (config.ttlMs !== void 0) {
      this.config.ttlMs = config.ttlMs;
    }
    if (config.enabled !== void 0) {
      this.config.enabled = config.enabled;
    }
  }
  /**
   * Enable caching.
   */
  enable() {
    this.config.enabled = true;
  }
  /**
   * Disable caching.
   */
  disable() {
    this.config.enabled = false;
  }
  /**
   * Get current configuration.
   */
  getConfig() {
    return { ...this.config };
  }
};
var semanticCache = new SemanticCache();
function createSemanticCache(config) {
  return new SemanticCache(config);
}
function withCache(analyzeFn, cache = semanticCache) {
  return ((input, language) => {
    const cached = cache.get(input, language);
    if (cached) {
      return cached;
    }
    const result = analyzeFn(input, language);
    cache.set(input, language, result);
    return result;
  });
}

// src/core-bridge.ts
var SemanticAnalyzerImpl = class {
  constructor(options = {}) {
    this.patternMatcher = new PatternMatcher();
    this.languages = new Set(getRegisteredLanguages());
    if (options.cache === false) {
      this.cache = new SemanticCache({ enabled: false });
    } else {
      this.cache = options.cache ? new SemanticCache(options.cache) : semanticCache;
    }
  }
  analyze(input, language) {
    if (!this.supportsLanguage(language)) {
      return {
        confidence: 0,
        errors: [`Language '${language}' is not supported for semantic parsing`]
      };
    }
    const cached = this.cache.get(input, language);
    if (cached) {
      return cached;
    }
    const result = this.analyzeUncached(input, language);
    this.cache.set(input, language, result);
    return result;
  }
  /**
   * Perform analysis without cache lookup.
   */
  analyzeUncached(input, language) {
    try {
      const tokenizer = getTokenizer2(language);
      if (!tokenizer) {
        return {
          confidence: 0,
          errors: [`No tokenizer available for language '${language}'`]
        };
      }
      const tokenStream = tokenizer.tokenize(input);
      const patterns = getPatternsForLanguage(language);
      if (patterns.length === 0) {
        return {
          confidence: 0,
          errors: [`No patterns available for language '${language}'`]
        };
      }
      const match = this.patternMatcher.matchBest(tokenStream, patterns);
      if (!match) {
        return {
          confidence: 0,
          errors: ["No pattern matched the input"]
        };
      }
      const node = this.buildSemanticNode(match);
      return {
        confidence: match.confidence,
        command: {
          name: match.pattern.command,
          roles: match.captured
        },
        node,
        tokensConsumed: match.consumedTokens
      };
    } catch (error) {
      return {
        confidence: 0,
        errors: [error instanceof Error ? error.message : String(error)]
      };
    }
  }
  supportsLanguage(language) {
    return this.languages.has(language);
  }
  supportedLanguages() {
    return Array.from(this.languages);
  }
  getCacheStats() {
    return this.cache.getStats();
  }
  clearCache() {
    this.cache.clear();
  }
  configureCache(config) {
    this.cache.configure(config);
  }
  buildSemanticNode(match) {
    return {
      kind: "command",
      action: match.pattern.command,
      roles: match.captured,
      metadata: {
        patternId: match.pattern.id
      }
    };
  }
};
function createSemanticAnalyzer(options) {
  return new SemanticAnalyzerImpl(options);
}
var DEFAULT_CONFIDENCE_THRESHOLD = 0.5;
var HIGH_CONFIDENCE_THRESHOLD = 0.8;
function shouldUseSemanticResult(result, threshold = DEFAULT_CONFIDENCE_THRESHOLD) {
  return result.confidence >= threshold && result.command !== void 0;
}
function rolesToCommandArgs(roles, command) {
  const args = [];
  const modifiers = {};
  for (const [role, value] of roles) {
    switch (role) {
      // Primary arguments (positional)
      case "patient":
      case "event":
        args.push(value);
        break;
      // Destination: context-dependent preposition
      case "destination":
        if (command === "put") {
          modifiers["into"] = value;
        } else {
          modifiers["on"] = value;
        }
        break;
      // Source: always 'from'
      case "source":
        modifiers["from"] = value;
        break;
      // Quantitative roles
      case "quantity":
        modifiers["by"] = value;
        break;
      case "duration":
        modifiers["over"] = value;
        break;
      // Adverbial roles
      case "method":
        modifiers["as"] = value;
        break;
      case "style":
        modifiers["with"] = value;
        break;
      // Conditional
      case "condition":
        modifiers["if"] = value;
        break;
      // Agent (for future multi-actor systems)
      case "agent":
        modifiers["agent"] = value;
        break;
      default:
        modifiers[role] = value;
    }
  }
  return { args, modifiers };
}

// src/ast-builder/expression-parser/tokenizer.ts
var CONTEXT_VARS = /* @__PURE__ */ new Set([
  "me",
  "my",
  "myself",
  "you",
  "your",
  "yourself",
  "it",
  "its",
  "result",
  "event",
  "target",
  "body",
  "detail",
  "window",
  "document"
]);
var LOGICAL_OPERATORS = /* @__PURE__ */ new Set(["and", "or", "not", "no"]);
var BOOLEAN_LITERALS = /* @__PURE__ */ new Set(["true", "false", "null", "undefined"]);
var TIME_UNITS = /* @__PURE__ */ new Set([
  "ms",
  "s",
  "seconds",
  "second",
  "milliseconds",
  "millisecond",
  "minutes",
  "minute",
  "hours",
  "hour"
]);
function tokenize2(input) {
  const tokens = [];
  let pos = 0;
  let line = 1;
  let column = 1;
  function previousTokenAllowsSelector() {
    if (tokens.length === 0) return true;
    const prev = tokens[tokens.length - 1];
    return [
      "OPERATOR" /* OPERATOR */,
      "COMPARISON" /* COMPARISON */,
      "LOGICAL" /* LOGICAL */,
      "LPAREN" /* LPAREN */,
      "LBRACKET" /* LBRACKET */,
      "LBRACE" /* LBRACE */,
      "COMMA" /* COMMA */,
      "COLON" /* COLON */
    ].includes(prev.type);
  }
  function peek(offset = 0) {
    return input[pos + offset] ?? "";
  }
  function advance() {
    const char = input[pos];
    pos++;
    if (char === "\n") {
      line++;
      column = 1;
    } else {
      column++;
    }
    return char;
  }
  function skipWhitespace() {
    while (pos < input.length && /\s/.test(input[pos])) {
      advance();
    }
  }
  function readWhile(predicate) {
    let result = "";
    while (pos < input.length && predicate(input[pos])) {
      result += advance();
    }
    return result;
  }
  function readString(quote) {
    let result = quote;
    advance();
    while (pos < input.length && input[pos] !== quote) {
      if (input[pos] === "\\" && pos + 1 < input.length) {
        result += advance();
        result += advance();
      } else {
        result += advance();
      }
    }
    if (pos < input.length) {
      result += advance();
    }
    return result;
  }
  function readTemplateLiteral() {
    let result = "`";
    advance();
    while (pos < input.length && input[pos] !== "`") {
      if (input[pos] === "\\" && pos + 1 < input.length) {
        result += advance();
        result += advance();
      } else {
        result += advance();
      }
    }
    if (pos < input.length) {
      result += advance();
    }
    return result;
  }
  function readQuerySelector() {
    let result = "<";
    advance();
    while (pos < input.length) {
      result += advance();
      if (result.endsWith("/>")) {
        break;
      }
    }
    return result;
  }
  function makeToken(type, value, start) {
    return {
      type,
      value,
      start,
      end: pos,
      line,
      column: column - value.length
    };
  }
  while (pos < input.length) {
    skipWhitespace();
    if (pos >= input.length) break;
    const start = pos;
    const char = peek();
    if (char === "'" && peek(1) === "s" && !/\w/.test(peek(2))) {
      advance();
      advance();
      tokens.push(makeToken("POSSESSIVE" /* POSSESSIVE */, "'s", start));
      continue;
    }
    if (char === '"' || char === "'") {
      const value = readString(char);
      tokens.push(makeToken("STRING" /* STRING */, value, start));
      continue;
    }
    if (char === "`") {
      const value = readTemplateLiteral();
      tokens.push(makeToken("TEMPLATE_LITERAL" /* TEMPLATE_LITERAL */, value, start));
      continue;
    }
    if (char === "<" && /[a-zA-Z.#\[]/.test(peek(1))) {
      const value = readQuerySelector();
      tokens.push(makeToken("QUERY_SELECTOR" /* QUERY_SELECTOR */, value, start));
      continue;
    }
    if (char === "#" && previousTokenAllowsSelector()) {
      advance();
      const name = readWhile((c) => /[\w-]/.test(c));
      tokens.push(makeToken("ID_SELECTOR" /* ID_SELECTOR */, "#" + name, start));
      continue;
    }
    if (char === "." && /[a-zA-Z_-]/.test(peek(1)) && previousTokenAllowsSelector()) {
      advance();
      const name = readWhile((c) => /[\w-]/.test(c));
      tokens.push(makeToken("CLASS_SELECTOR" /* CLASS_SELECTOR */, "." + name, start));
      continue;
    }
    if (char === "[" && previousTokenAllowsSelector()) {
      const nextChar = peek(1);
      if (nextChar === "@" || /[a-zA-Z]/.test(nextChar)) {
        let value = "";
        value += advance();
        while (pos < input.length && input[pos] !== "]") {
          if (input[pos] === '"' || input[pos] === "'") {
            value += readString(input[pos]);
          } else {
            value += advance();
          }
        }
        if (pos < input.length) {
          value += advance();
        }
        tokens.push(makeToken("ATTRIBUTE_SELECTOR" /* ATTRIBUTE_SELECTOR */, value, start));
        continue;
      }
    }
    if (char === "[") {
      advance();
      tokens.push(makeToken("LBRACKET" /* LBRACKET */, "[", start));
      continue;
    }
    if (char === "]") {
      advance();
      tokens.push(makeToken("RBRACKET" /* RBRACKET */, "]", start));
      continue;
    }
    if (/\d/.test(char)) {
      const num = readWhile((c) => /[\d.]/.test(c));
      const unitStart = pos;
      const unit = readWhile((c) => /[a-zA-Z]/.test(c));
      if (TIME_UNITS.has(unit)) {
        tokens.push(makeToken("TIME_EXPRESSION" /* TIME_EXPRESSION */, num + unit, start));
      } else {
        pos = unitStart;
        tokens.push(makeToken("NUMBER" /* NUMBER */, num, start));
      }
      continue;
    }
    if (char === "(") {
      advance();
      tokens.push(makeToken("LPAREN" /* LPAREN */, "(", start));
      continue;
    }
    if (char === ")") {
      advance();
      tokens.push(makeToken("RPAREN" /* RPAREN */, ")", start));
      continue;
    }
    if (char === "{") {
      advance();
      tokens.push(makeToken("LBRACE" /* LBRACE */, "{", start));
      continue;
    }
    if (char === "}") {
      advance();
      tokens.push(makeToken("RBRACE" /* RBRACE */, "}", start));
      continue;
    }
    if (char === ",") {
      advance();
      tokens.push(makeToken("COMMA" /* COMMA */, ",", start));
      continue;
    }
    if (char === ":") {
      advance();
      tokens.push(makeToken("COLON" /* COLON */, ":", start));
      continue;
    }
    if (char === ".") {
      advance();
      tokens.push(makeToken("DOT" /* DOT */, ".", start));
      continue;
    }
    if (char === "+" || char === "-" || char === "*" || char === "/" || char === "%") {
      advance();
      tokens.push(makeToken("OPERATOR" /* OPERATOR */, char, start));
      continue;
    }
    if (char === "=" || char === "!" || char === "<" || char === ">") {
      let op = advance();
      if (peek() === "=") {
        op += advance();
      }
      tokens.push(makeToken("COMPARISON" /* COMPARISON */, op, start));
      continue;
    }
    if (/[a-zA-Z_$]/.test(char)) {
      const word = readWhile((c) => /[\w$]/.test(c));
      const lower = word.toLowerCase();
      if (CONTEXT_VARS.has(lower)) {
        tokens.push(makeToken("CONTEXT_VAR" /* CONTEXT_VAR */, word, start));
      } else if (LOGICAL_OPERATORS.has(lower)) {
        tokens.push(makeToken("LOGICAL" /* LOGICAL */, word, start));
      } else if (BOOLEAN_LITERALS.has(lower)) {
        tokens.push(makeToken("BOOLEAN" /* BOOLEAN */, word, start));
      } else {
        tokens.push(makeToken("IDENTIFIER" /* IDENTIFIER */, word, start));
      }
      continue;
    }
    advance();
  }
  tokens.push(makeToken("EOF" /* EOF */, "", pos));
  return tokens;
}

// src/ast-builder/expression-parser/parser.ts
var ExpressionParser = class {
  constructor() {
    this.tokens = [];
    this.current = 0;
  }
  parse(input) {
    try {
      this.tokens = tokenize2(input);
      this.current = 0;
      if (this.isAtEnd()) {
        return { success: false, error: "Empty expression" };
      }
      const node = this.parseExpression();
      return { success: true, node, consumed: this.current };
    } catch (e) {
      return {
        success: false,
        error: e instanceof Error ? e.message : "Parse error"
      };
    }
  }
  // =============================================================================
  // Token Navigation
  // =============================================================================
  peek() {
    return this.tokens[this.current] ?? { type: "EOF" /* EOF */, value: "", start: 0, end: 0 };
  }
  previous() {
    return this.tokens[this.current - 1] ?? { type: "EOF" /* EOF */, value: "", start: 0, end: 0 };
  }
  isAtEnd() {
    return this.peek().type === "EOF" /* EOF */;
  }
  advance() {
    if (!this.isAtEnd()) {
      this.current++;
    }
    return this.previous();
  }
  check(type) {
    return this.peek().type === type;
  }
  checkValue(value) {
    return this.peek().value.toLowerCase() === value.toLowerCase();
  }
  match(...types) {
    for (const type of types) {
      if (this.check(type)) {
        this.advance();
        return true;
      }
    }
    return false;
  }
  // =============================================================================
  // Expression Parsing (Precedence Climbing)
  // =============================================================================
  parseExpression() {
    return this.parseOr();
  }
  parseOr() {
    let left = this.parseAnd();
    while (this.checkValue("or")) {
      const operator = this.advance().value;
      const right = this.parseAnd();
      left = this.createBinaryExpression(operator, left, right);
    }
    return left;
  }
  parseAnd() {
    let left = this.parseEquality();
    while (this.checkValue("and")) {
      const operator = this.advance().value;
      const right = this.parseEquality();
      left = this.createBinaryExpression(operator, left, right);
    }
    return left;
  }
  parseEquality() {
    let left = this.parseComparison();
    while (this.match("COMPARISON" /* COMPARISON */) || this.checkValue("is") || this.checkValue("matches") || this.checkValue("contains") || this.checkValue("in")) {
      const operator = this.previous().value;
      const right = this.parseComparison();
      left = this.createBinaryExpression(operator, left, right);
    }
    return left;
  }
  parseComparison() {
    let left = this.parseAddition();
    while (this.check("COMPARISON" /* COMPARISON */)) {
      const operator = this.advance().value;
      const right = this.parseAddition();
      left = this.createBinaryExpression(operator, left, right);
    }
    return left;
  }
  parseAddition() {
    let left = this.parseMultiplication();
    while (this.peek().value === "+" || this.peek().value === "-") {
      const operator = this.advance().value;
      const right = this.parseMultiplication();
      left = this.createBinaryExpression(operator, left, right);
    }
    return left;
  }
  parseMultiplication() {
    let left = this.parseUnary();
    while (this.peek().value === "*" || this.peek().value === "/" || this.peek().value === "%") {
      const operator = this.advance().value;
      const right = this.parseUnary();
      left = this.createBinaryExpression(operator, left, right);
    }
    return left;
  }
  parseUnary() {
    if (this.checkValue("not") || this.checkValue("no") || this.peek().value === "-") {
      const operator = this.advance().value;
      const operand = this.parseUnary();
      return this.createUnaryExpression(operator, operand);
    }
    return this.parsePostfix();
  }
  parsePostfix() {
    let expr = this.parsePrimary();
    while (true) {
      if (this.match("DOT" /* DOT */)) {
        if (this.check("IDENTIFIER" /* IDENTIFIER */) || this.check("CONTEXT_VAR" /* CONTEXT_VAR */)) {
          const property = this.advance().value;
          expr = this.createPropertyAccess(expr, property);
        } else {
          break;
        }
      } else if (this.match("POSSESSIVE" /* POSSESSIVE */)) {
        if (this.check("IDENTIFIER" /* IDENTIFIER */) || this.check("CONTEXT_VAR" /* CONTEXT_VAR */)) {
          const property = this.advance().value;
          expr = this.createPossessiveExpression(expr, property);
        } else {
          break;
        }
      } else if (this.match("LPAREN" /* LPAREN */)) {
        const args = this.parseArguments();
        expr = this.createCallExpression(expr, args);
      } else if (this.match("LBRACKET" /* LBRACKET */)) {
        const index = this.parseExpression();
        if (!this.match("RBRACKET" /* RBRACKET */)) {
          throw new Error("Expected ] after index");
        }
        expr = this.createPropertyAccess(expr, index);
      } else {
        break;
      }
    }
    return expr;
  }
  parsePrimary() {
    const token = this.peek();
    if (this.match("NUMBER" /* NUMBER */)) {
      return this.createLiteral(parseFloat(token.value), "number", token);
    }
    if (this.match("STRING" /* STRING */)) {
      const value = token.value.slice(1, -1);
      return this.createLiteral(value, "string", token);
    }
    if (this.match("BOOLEAN" /* BOOLEAN */)) {
      const value = token.value === "true" ? true : token.value === "false" ? false : token.value === "null" ? null : void 0;
      return this.createLiteral(value, token.value, token);
    }
    if (this.match("TEMPLATE_LITERAL" /* TEMPLATE_LITERAL */)) {
      const templateNode = {
        type: "templateLiteral",
        value: token.value,
        start: token.start,
        end: token.end,
        line: token.line,
        column: token.column
      };
      return templateNode;
    }
    if (this.match("TIME_EXPRESSION" /* TIME_EXPRESSION */)) {
      return this.parseTimeExpression(token);
    }
    if (this.match("ID_SELECTOR" /* ID_SELECTOR */)) {
      return this.createSelector(token.value, "id", token);
    }
    if (this.match("CLASS_SELECTOR" /* CLASS_SELECTOR */)) {
      return this.createSelector(token.value, "class", token);
    }
    if (this.match("ATTRIBUTE_SELECTOR" /* ATTRIBUTE_SELECTOR */)) {
      return this.createSelector(token.value, "attribute", token);
    }
    if (this.match("QUERY_SELECTOR" /* QUERY_SELECTOR */)) {
      const selector = token.value.slice(1, -2);
      return this.createSelector(selector, "query", token);
    }
    if (this.match("CONTEXT_VAR" /* CONTEXT_VAR */)) {
      return this.createContextReference(token.value, token);
    }
    if (this.match("IDENTIFIER" /* IDENTIFIER */)) {
      return this.createIdentifier(token.value, token);
    }
    if (this.match("LPAREN" /* LPAREN */)) {
      const expr = this.parseExpression();
      if (!this.match("RPAREN" /* RPAREN */)) {
        throw new Error("Expected ) after expression");
      }
      return expr;
    }
    if (this.match("LBRACKET" /* LBRACKET */)) {
      return this.parseArrayLiteral();
    }
    if (this.match("LBRACE" /* LBRACE */)) {
      return this.parseObjectLiteral();
    }
    throw new Error(`Unexpected token: ${token.value}`);
  }
  parseArguments() {
    const args = [];
    if (!this.check("RPAREN" /* RPAREN */)) {
      do {
        args.push(this.parseExpression());
      } while (this.match("COMMA" /* COMMA */));
    }
    if (!this.match("RPAREN" /* RPAREN */)) {
      throw new Error("Expected ) after arguments");
    }
    return args;
  }
  parseArrayLiteral() {
    const elements = [];
    const start = this.previous().start;
    if (!this.check("RBRACKET" /* RBRACKET */)) {
      do {
        elements.push(this.parseExpression());
      } while (this.match("COMMA" /* COMMA */));
    }
    if (!this.match("RBRACKET" /* RBRACKET */)) {
      throw new Error("Expected ] after array elements");
    }
    return {
      type: "arrayLiteral",
      elements,
      start,
      end: this.previous().end
    };
  }
  parseObjectLiteral() {
    const properties = [];
    const start = this.previous().start;
    if (!this.check("RBRACE" /* RBRACE */)) {
      do {
        let key;
        if (this.check("STRING" /* STRING */)) {
          key = this.advance().value.slice(1, -1);
        } else if (this.check("IDENTIFIER" /* IDENTIFIER */)) {
          key = this.advance().value;
        } else {
          throw new Error("Expected property name");
        }
        if (!this.match("COLON" /* COLON */)) {
          throw new Error("Expected : after property name");
        }
        const value = this.parseExpression();
        properties.push({ key, value });
      } while (this.match("COMMA" /* COMMA */));
    }
    if (!this.match("RBRACE" /* RBRACE */)) {
      throw new Error("Expected } after object properties");
    }
    return {
      type: "objectLiteral",
      properties: properties.map((p) => ({
        type: "objectProperty",
        key: p.key,
        value: p.value
      })),
      start,
      end: this.previous().end
    };
  }
  parseTimeExpression(token) {
    const match = token.value.match(
      /^(\d+(?:\.\d+)?)(ms|s|seconds?|milliseconds?|minutes?|hours?)$/i
    );
    if (!match) {
      throw new Error(`Invalid time expression: ${token.value}`);
    }
    const value = parseFloat(match[1]);
    const unit = match[2].toLowerCase();
    return {
      type: "timeExpression",
      value,
      unit,
      raw: token.value,
      start: token.start,
      end: token.end,
      line: token.line,
      column: token.column
    };
  }
  // =============================================================================
  // Node Factories
  // =============================================================================
  createLiteral(value, dataType, token) {
    return {
      type: "literal",
      value,
      dataType,
      raw: token.value,
      start: token.start,
      end: token.end,
      line: token.line,
      column: token.column
    };
  }
  createSelector(value, kind, token) {
    return {
      type: "selector",
      value,
      selector: value,
      selectorType: kind,
      start: token.start,
      end: token.end,
      line: token.line,
      column: token.column
    };
  }
  createContextReference(contextType, token) {
    return {
      type: "contextReference",
      contextType,
      name: token.value,
      start: token.start,
      end: token.end,
      line: token.line,
      column: token.column
    };
  }
  createIdentifier(name, token) {
    return {
      type: "identifier",
      name,
      start: token.start,
      end: token.end,
      line: token.line,
      column: token.column
    };
  }
  createPropertyAccess(object, property) {
    return {
      type: "propertyAccess",
      object,
      property: typeof property === "string" ? property : property.name ?? "",
      start: object.start,
      end: this.previous().end
    };
  }
  createPossessiveExpression(object, property) {
    return {
      type: "possessiveExpression",
      object,
      property,
      start: object.start,
      end: this.previous().end
    };
  }
  createBinaryExpression(operator, left, right) {
    return {
      type: "binaryExpression",
      operator,
      left,
      right,
      start: left.start,
      end: right.end
    };
  }
  createUnaryExpression(operator, operand) {
    return {
      type: "unaryExpression",
      operator,
      operand,
      prefix: true,
      start: this.previous().start,
      end: operand.end
    };
  }
  createCallExpression(callee, args) {
    return {
      type: "callExpression",
      callee,
      arguments: args,
      start: callee.start,
      end: this.previous().end
    };
  }
};
function parseExpression(input) {
  const parser = new ExpressionParser();
  return parser.parse(input);
}

// src/ast-builder/value-converters.ts
function convertValue(value, warnings) {
  switch (value.type) {
    case "literal":
      return convertLiteral(value);
    case "selector":
      return convertSelector(value, warnings);
    case "reference":
      return convertReference(value);
    case "property-path":
      return convertPropertyPath(value, warnings);
    case "expression":
      return convertExpression(value);
    default:
      const _exhaustive = value;
      throw new Error(`Unknown semantic value type: ${_exhaustive.type}`);
  }
}
function convertLiteral(value) {
  const result = {
    type: "literal",
    value: value.value
  };
  if (value.dataType) {
    return { ...result, dataType: value.dataType };
  }
  return result;
}
function convertSelector(value, warnings) {
  if (warnings && value.value.startsWith("*") && /^[a-zA-Z-]/.test(value.value.slice(1))) {
    warnings.push(
      `Converted '${value.value}' to a CSS selector, but it looks like a CSS property name. CSS properties in commands like 'transition' should be literal strings, not selectors. Consider using expectedTypes: ['literal'] instead of ['literal', 'selector'] in the command schema.`
    );
  }
  return {
    type: "selector",
    value: value.value,
    selector: value.value,
    selectorType: value.selectorKind
  };
}
function convertReference(value) {
  return {
    type: "contextReference",
    contextType: value.value,
    name: value.value
  };
}
function convertPropertyPath(value, warnings) {
  return {
    type: "propertyAccess",
    object: convertValue(value.object, warnings),
    property: value.property
  };
}
function convertExpression(value) {
  const result = parseExpression(value.raw);
  if (!result.success || !result.node) {
    const identifier = {
      type: "identifier",
      name: value.raw
    };
    return identifier;
  }
  return result.node;
}

// src/ast-builder/command-mappers.ts
function getRole(node, role) {
  return node.roles.get(role);
}
function convertRoleValue(node, role, warnings) {
  const value = getRole(node, role);
  return value ? convertValue(value, warnings) : void 0;
}
function createCommandNode2(name, args = [], modifiers, options = {}) {
  const result = {
    type: "command",
    name,
    args
  };
  if (modifiers && Object.keys(modifiers).length > 0) {
    result.modifiers = modifiers;
  }
  if (options.isBlocking) {
    result.isBlocking = options.isBlocking;
  }
  if (options.implicitTarget) {
    result.implicitTarget = options.implicitTarget;
  }
  return result;
}
var toggleMapper = {
  action: "toggle",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const destination = convertRoleValue(node, "destination");
    const duration = convertRoleValue(node, "duration");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (destination) modifiers["on"] = destination;
    if (duration) modifiers["for"] = duration;
    return createCommandNode2("toggle", args, modifiers);
  }
};
var addMapper = {
  action: "add",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const destination = convertRoleValue(node, "destination");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (destination) modifiers["to"] = destination;
    return createCommandNode2("add", args, modifiers);
  }
};
var removeMapper = {
  action: "remove",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const source = convertRoleValue(node, "source");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (source) modifiers["from"] = source;
    return createCommandNode2("remove", args, modifiers);
  }
};
var setMapper = {
  action: "set",
  toAST(node, _builder) {
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const args = [];
    const modifiers = {};
    if (destination) {
      args.push(destination);
    }
    if (patient) modifiers["to"] = patient;
    return createCommandNode2("set", args, modifiers);
  }
};
var showMapper = {
  action: "show",
  toAST(node, _builder) {
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const duration = convertRoleValue(node, "duration");
    const args = [];
    const modifiers = {};
    const target = destination ?? patient;
    if (target) args.push(target);
    if (duration) modifiers["with"] = duration;
    return createCommandNode2("show", args, modifiers);
  }
};
var hideMapper = {
  action: "hide",
  toAST(node, _builder) {
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const duration = convertRoleValue(node, "duration");
    const args = [];
    const modifiers = {};
    const target = destination ?? patient;
    if (target) args.push(target);
    if (duration) modifiers["with"] = duration;
    return createCommandNode2("hide", args, modifiers);
  }
};
var incrementMapper = {
  action: "increment",
  toAST(node, _builder) {
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const quantity = convertRoleValue(node, "quantity");
    const args = [];
    const modifiers = {};
    const target = destination ?? patient;
    if (target) args.push(target);
    if (quantity) modifiers["by"] = quantity;
    return createCommandNode2("increment", args, modifiers);
  }
};
var decrementMapper = {
  action: "decrement",
  toAST(node, _builder) {
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const quantity = convertRoleValue(node, "quantity");
    const args = [];
    const modifiers = {};
    const target = destination ?? patient;
    if (target) args.push(target);
    if (quantity) modifiers["by"] = quantity;
    return createCommandNode2("decrement", args, modifiers);
  }
};
var waitMapper = {
  action: "wait",
  toAST(node, _builder) {
    const duration = convertRoleValue(node, "duration");
    const args = duration ? [duration] : [];
    return createCommandNode2("wait", args, void 0, { isBlocking: true });
  }
};
var logMapper = {
  action: "log",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const args = [];
    if (patient) args.push(patient);
    return createCommandNode2("log", args);
  }
};
var putMapper = {
  action: "put",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const destination = convertRoleValue(node, "destination");
    const method = getRole(node, "method");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (destination) {
      const prep = method?.type === "literal" ? String(method.value) : "into";
      modifiers[prep] = destination;
    }
    return createCommandNode2("put", args, modifiers);
  }
};
var fetchMapper = {
  action: "fetch",
  toAST(node, _builder) {
    const source = convertRoleValue(node, "source");
    const method = convertRoleValue(node, "method");
    const responseType = convertRoleValue(node, "responseType");
    const patient = convertRoleValue(node, "patient");
    const args = source ? [source] : [];
    const modifiers = {};
    if (method) modifiers["with"] = method;
    if (responseType) modifiers["as"] = responseType;
    if (patient) modifiers["body"] = patient;
    return createCommandNode2("fetch", args, modifiers, { isBlocking: true });
  }
};
var appendMapper = {
  action: "append",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const destination = convertRoleValue(node, "destination");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (destination) modifiers["to"] = destination;
    return createCommandNode2("append", args, modifiers);
  }
};
var prependMapper = {
  action: "prepend",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const destination = convertRoleValue(node, "destination");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (destination) modifiers["to"] = destination;
    return createCommandNode2("prepend", args, modifiers);
  }
};
var triggerMapper = {
  action: "trigger",
  toAST(node, _builder) {
    const event = convertRoleValue(node, "event");
    const destination = convertRoleValue(node, "destination");
    const args = event ? [event] : [];
    const modifiers = {};
    if (destination) modifiers["on"] = destination;
    return createCommandNode2("trigger", args, modifiers);
  }
};
var sendMapper = {
  action: "send",
  toAST(node, _builder) {
    const event = convertRoleValue(node, "event");
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const args = event ? [event] : [];
    const modifiers = {};
    if (destination) modifiers["to"] = destination;
    if (patient) modifiers["detail"] = patient;
    return createCommandNode2("send", args, modifiers);
  }
};
var goMapper = {
  action: "go",
  toAST(node, _builder) {
    const source = convertRoleValue(node, "source");
    const destination = convertRoleValue(node, "destination");
    const args = [];
    const modifiers = {};
    if (source) args.push(source);
    if (destination) modifiers["to"] = destination;
    return createCommandNode2("go", args, modifiers);
  }
};
var transitionMapper = {
  action: "transition",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const goal = convertRoleValue(node, "goal");
    const duration = convertRoleValue(node, "duration");
    const destination = convertRoleValue(node, "destination");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (goal) modifiers["to"] = goal;
    if (duration) modifiers["over"] = duration;
    if (destination) modifiers["on"] = destination;
    return createCommandNode2("transition", args, modifiers);
  }
};
var focusMapper = {
  action: "focus",
  toAST(node, _builder) {
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const args = [];
    const modifiers = {};
    const target = destination ?? patient;
    if (target) args.push(target);
    return createCommandNode2("focus", args, modifiers);
  }
};
var blurMapper = {
  action: "blur",
  toAST(node, _builder) {
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const args = [];
    const target = destination ?? patient;
    if (target) args.push(target);
    return createCommandNode2("blur", args);
  }
};
var getMapper = {
  action: "get",
  toAST(node, _builder) {
    const source = convertRoleValue(node, "source");
    const patient = convertRoleValue(node, "patient");
    const args = [];
    const value = source ?? patient;
    if (value) args.push(value);
    return createCommandNode2("get", args);
  }
};
var takeMapper = {
  action: "take",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const source = convertRoleValue(node, "source");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (source) modifiers["from"] = source;
    return createCommandNode2("take", args, modifiers);
  }
};
var callMapper = {
  action: "call",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const args = patient ? [patient] : [];
    return createCommandNode2("call", args);
  }
};
var returnMapper = {
  action: "return",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const args = patient ? [patient] : [];
    return createCommandNode2("return", args);
  }
};
var haltMapper = {
  action: "halt",
  toAST(_node, _builder) {
    return createCommandNode2("halt", []);
  }
};
var throwMapper = {
  action: "throw",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const args = patient ? [patient] : [];
    return createCommandNode2("throw", args);
  }
};
var settleMapper = {
  action: "settle",
  toAST(node, _builder) {
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const args = [];
    const target = destination ?? patient;
    if (target) args.push(target);
    return createCommandNode2("settle", args, void 0, { isBlocking: true });
  }
};
var swapMapper = {
  action: "swap",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const source = convertRoleValue(node, "source");
    const destination = convertRoleValue(node, "destination");
    const style = convertRoleValue(node, "style");
    const args = [];
    const modifiers = {};
    if (patient) args.push(patient);
    if (source) args.push(source);
    if (destination) modifiers["on"] = destination;
    if (style) modifiers["with"] = style;
    return createCommandNode2("swap", args, modifiers);
  }
};
var morphMapper = {
  action: "morph",
  toAST(node, _builder) {
    const source = convertRoleValue(node, "source");
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const args = [];
    const modifiers = {};
    const content = source ?? patient;
    if (content) args.push(content);
    if (destination) modifiers["on"] = destination;
    return createCommandNode2("morph", args, modifiers);
  }
};
var cloneMapper = {
  action: "clone",
  toAST(node, _builder) {
    const source = convertRoleValue(node, "source");
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const args = [];
    const modifiers = {};
    const target = source ?? patient;
    if (target) args.push(target);
    if (destination) modifiers["into"] = destination;
    return createCommandNode2("clone", args, modifiers);
  }
};
var makeMapper = {
  action: "make",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const args = patient ? [patient] : [];
    return createCommandNode2("make", args);
  }
};
var measureMapper = {
  action: "measure",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const destination = convertRoleValue(node, "destination");
    const source = convertRoleValue(node, "source");
    const args = [];
    const modifiers = {};
    if (patient) args.push(patient);
    const element = destination ?? source;
    if (element) modifiers["of"] = element;
    return createCommandNode2("measure", args, modifiers);
  }
};
var tellMapper = {
  action: "tell",
  toAST(node, _builder) {
    const destination = convertRoleValue(node, "destination");
    const patient = convertRoleValue(node, "patient");
    const args = [];
    const target = destination ?? patient;
    if (target) args.push(target);
    return createCommandNode2("tell", args);
  }
};
var jsMapper = {
  action: "js",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const args = patient ? [patient] : [];
    return createCommandNode2("js", args);
  }
};
var asyncMapper = {
  action: "async",
  toAST(_node, _builder) {
    return createCommandNode2("async", []);
  }
};
var ifMapper = {
  action: "if",
  toAST(node, _builder) {
    const condition = convertRoleValue(node, "condition");
    const args = condition ? [condition] : [];
    return createCommandNode2("if", args);
  }
};
var unlessMapper = {
  action: "unless",
  toAST(node, _builder) {
    const condition = convertRoleValue(node, "condition");
    const args = condition ? [condition] : [];
    return createCommandNode2("unless", args);
  }
};
var repeatMapper = {
  action: "repeat",
  toAST(node, _builder) {
    const quantity = convertRoleValue(node, "quantity");
    const patient = convertRoleValue(node, "patient");
    const args = [];
    const count = quantity ?? patient;
    if (count) args.push(count);
    return createCommandNode2("repeat", args);
  }
};
var forMapper = {
  action: "for",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const source = convertRoleValue(node, "source");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (source) modifiers["in"] = source;
    return createCommandNode2("for", args, modifiers);
  }
};
var whileMapper = {
  action: "while",
  toAST(node, _builder) {
    const condition = convertRoleValue(node, "condition");
    const args = condition ? [condition] : [];
    return createCommandNode2("while", args);
  }
};
var continueMapper = {
  action: "continue",
  toAST(_node, _builder) {
    return createCommandNode2("continue", []);
  }
};
var defaultMapper = {
  action: "default",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const source = convertRoleValue(node, "source");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (source) modifiers["to"] = source;
    return createCommandNode2("default", args, modifiers);
  }
};
var initMapper = {
  action: "init",
  toAST(_node, _builder) {
    return createCommandNode2("init", []);
  }
};
var behaviorMapper = {
  action: "behavior",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const args = patient ? [patient] : [];
    return createCommandNode2("behavior", args);
  }
};
var installMapper = {
  action: "install",
  toAST(node, _builder) {
    const patient = convertRoleValue(node, "patient");
    const destination = convertRoleValue(node, "destination");
    const args = patient ? [patient] : [];
    const modifiers = {};
    if (destination) modifiers["on"] = destination;
    return createCommandNode2("install", args, modifiers);
  }
};
var onMapper = {
  action: "on",
  toAST(node, _builder) {
    const event = convertRoleValue(node, "event");
    const source = convertRoleValue(node, "source");
    const args = event ? [event] : [];
    const modifiers = {};
    if (source) modifiers["from"] = source;
    return createCommandNode2("on", args, modifiers);
  }
};
var mappers = /* @__PURE__ */ new Map([
  // Tier 1: Core commands
  ["toggle", toggleMapper],
  ["add", addMapper],
  ["remove", removeMapper],
  ["set", setMapper],
  ["show", showMapper],
  ["hide", hideMapper],
  ["increment", incrementMapper],
  ["decrement", decrementMapper],
  ["wait", waitMapper],
  ["log", logMapper],
  ["put", putMapper],
  ["fetch", fetchMapper],
  // Tier 2: Content manipulation
  ["append", appendMapper],
  ["prepend", prependMapper],
  ["get", getMapper],
  ["take", takeMapper],
  // Tier 2: Events
  ["trigger", triggerMapper],
  ["send", sendMapper],
  ["on", onMapper],
  // Tier 2: Navigation & DOM
  ["go", goMapper],
  ["transition", transitionMapper],
  ["focus", focusMapper],
  ["blur", blurMapper],
  // Tier 2: Control flow
  ["call", callMapper],
  ["return", returnMapper],
  ["halt", haltMapper],
  ["throw", throwMapper],
  ["settle", settleMapper],
  // Tier 3: Advanced DOM
  ["swap", swapMapper],
  ["morph", morphMapper],
  ["clone", cloneMapper],
  ["measure", measureMapper],
  // Tier 3: Object/Types
  ["make", makeMapper],
  ["tell", tellMapper],
  ["default", defaultMapper],
  // Tier 3: JavaScript integration
  ["js", jsMapper],
  ["async", asyncMapper],
  // Tier 3: Conditionals
  ["if", ifMapper],
  ["unless", unlessMapper],
  // Tier 3: Loops
  ["repeat", repeatMapper],
  ["for", forMapper],
  ["while", whileMapper],
  ["continue", continueMapper],
  // Tier 3: Behaviors
  ["init", initMapper],
  ["behavior", behaviorMapper],
  ["install", installMapper]
]);
function getCommandMapper(action) {
  return mappers.get(action);
}
function registerCommandMapper(mapper) {
  mappers.set(mapper.action, mapper);
}
function getRegisteredMappers() {
  return new Map(mappers);
}

// src/ast-builder/index.ts
var ASTBuilder = class {
  constructor(_options = {}) {
    /**
     * Warnings collected during AST building (e.g., type inference issues).
     */
    this.warnings = [];
  }
  /**
   * Build an AST from a SemanticNode.
   *
   * @param node - The semantic node to convert
   * @returns The corresponding AST node
   */
  build(node) {
    switch (node.kind) {
      case "command":
        return this.buildCommand(node);
      case "event-handler":
        return this.buildEventHandler(node);
      case "conditional":
        return this.buildConditional(node);
      case "compound":
        return this.buildCompound(node);
      case "loop":
        return this.buildLoop(node);
      default:
        throw new Error(`Unknown semantic node kind: ${node.kind}`);
    }
  }
  /**
   * Build a CommandNode from a CommandSemanticNode.
   */
  buildCommand(node) {
    const mapper = getCommandMapper(node.action);
    if (mapper) {
      const result = mapper.toAST(node, this);
      if ("ast" in result && "warnings" in result) {
        const mapperResult = result;
        this.warnings.push(...mapperResult.warnings);
        return mapperResult.ast;
      } else {
        return result;
      }
    }
    return this.buildGenericCommand(node);
  }
  /**
   * Generic command builder when no specific mapper is available.
   * Maps roles to args in a predictable order.
   */
  buildGenericCommand(node) {
    const args = [];
    const modifiers = {};
    const argRoles = ["patient", "source", "quantity"];
    const modifierRoles = ["destination", "duration", "method", "style"];
    for (const role of argRoles) {
      const value = node.roles.get(role);
      if (value) {
        args.push(convertValue(value));
      }
    }
    for (const role of modifierRoles) {
      const value = node.roles.get(role);
      if (value) {
        const modifierKey = this.roleToModifierKey(role);
        modifiers[modifierKey] = convertValue(value);
      }
    }
    const result = {
      type: "command",
      name: node.action,
      args
    };
    if (Object.keys(modifiers).length > 0) {
      return { ...result, modifiers };
    }
    return result;
  }
  /**
   * Map semantic roles to hyperscript modifier keywords.
   */
  roleToModifierKey(role) {
    const mapping = {
      destination: "on",
      duration: "for",
      source: "from",
      condition: "if",
      method: "via",
      style: "with"
    };
    return mapping[role] ?? role;
  }
  /**
   * Build an EventHandlerNode from an EventHandlerSemanticNode.
   */
  buildEventHandler(node) {
    const eventValue = node.roles.get("event");
    let event;
    let events;
    if (eventValue?.type === "literal") {
      const eventStr = String(eventValue.value);
      if (eventStr.includes("|") || eventStr.includes(" or ")) {
        events = eventStr.split(/\s+or\s+|\|/).map((e) => e.trim());
        event = events[0];
      } else {
        event = eventStr;
      }
    } else if (eventValue?.type === "reference") {
      event = eventValue.value;
    } else {
      event = "click";
    }
    const commands = node.body.map((child) => this.build(child));
    const fromValue = node.roles.get("source");
    let selector;
    let target;
    if (fromValue?.type === "selector") {
      selector = fromValue.value;
      target = fromValue.value;
    } else if (fromValue?.type === "reference") {
      target = fromValue.value;
    } else if (fromValue?.type === "literal") {
      target = String(fromValue.value);
    }
    const conditionValue = node.roles.get("condition");
    const condition = conditionValue ? convertValue(conditionValue) : void 0;
    const destinationValue = node.roles.get("destination");
    const watchTarget = destinationValue ? convertValue(destinationValue) : void 0;
    const modifiers = node.eventModifiers;
    let finalSelector = selector;
    if (modifiers?.from) {
      const fromMod = modifiers.from;
      if (fromMod.type === "selector" && !selector) {
        finalSelector = fromMod.value;
      }
    }
    const args = node.parameterNames ? [...node.parameterNames] : void 0;
    return {
      type: "eventHandler",
      event,
      commands,
      ...events && events.length > 1 ? { events } : {},
      ...finalSelector ? { selector: finalSelector } : {},
      ...target ? { target } : {},
      ...condition ? { condition } : {},
      ...watchTarget ? { watchTarget } : {},
      ...args && args.length > 0 ? { args, params: args } : {}
    };
  }
  /**
   * Build a CommandNode from a ConditionalSemanticNode.
   *
   * Produces a command node with:
   * - args[0]: condition expression
   * - args[1]: then block (wrapped in { type: 'block', commands: [...] })
   * - args[2]: else block (optional, same format)
   *
   * This format matches what IfCommand.parseInput() expects.
   */
  buildConditional(node) {
    const conditionValue = node.roles.get("condition");
    if (!conditionValue) {
      throw new Error("Conditional node missing condition");
    }
    const condition = convertValue(conditionValue);
    const thenBranch = node.thenBranch.map((child) => this.build(child));
    const elseBranch = node.elseBranch?.map((child) => this.build(child));
    const args = [
      condition,
      // args[1]: then block wrapped as block node
      {
        type: "block",
        commands: thenBranch
      }
    ];
    if (elseBranch && elseBranch.length > 0) {
      args.push({
        type: "block",
        commands: elseBranch
      });
    }
    return {
      type: "command",
      name: "if",
      args
    };
  }
  /**
   * Build AST nodes from a CompoundSemanticNode.
   *
   * Converts to CommandSequence for runtime compatibility.
   * The runtime recognizes 'CommandSequence' type and executes commands in order.
   */
  buildCompound(node) {
    const statements = node.statements.map((child) => this.build(child));
    if (statements.length === 1) {
      return statements[0];
    }
    if (statements.length === 0) {
      return {
        type: "block",
        commands: []
      };
    }
    const result = {
      type: "CommandSequence",
      commands: statements
    };
    return result;
  }
  /**
   * Build a CommandNode from a LoopSemanticNode.
   *
   * Produces a 'repeat' command with:
   * - args[0]: loop type identifier (forever, times, for, while, until)
   * - args[1]: count/condition/variable depending on loop type
   * - args[2]: collection (for 'for' loops)
   * - args[last]: body block
   *
   * This format matches what the repeat command parser produces.
   */
  buildLoop(node) {
    const bodyCommands = node.body.map((child) => this.build(child));
    const args = [
      // args[0]: loop type identifier
      {
        type: "identifier",
        name: node.loopVariant
      }
    ];
    switch (node.loopVariant) {
      case "times": {
        const quantity = node.roles.get("quantity");
        if (quantity) {
          args.push(convertValue(quantity));
        }
        break;
      }
      case "for": {
        if (node.loopVariable) {
          args.push({
            type: "string",
            value: node.loopVariable
          });
        }
        const source = node.roles.get("source");
        if (source) {
          args.push(convertValue(source));
        }
        break;
      }
      case "while":
      case "until": {
        const condition = node.roles.get("condition");
        if (condition) {
          args.push(convertValue(condition));
        }
        break;
      }
      case "forever":
        break;
    }
    args.push({
      type: "block",
      commands: bodyCommands
    });
    return {
      type: "command",
      name: "repeat",
      args
    };
  }
  /**
   * Build a BlockNode from an array of semantic nodes.
   * Useful for grouping commands in if/else branches.
   */
  buildBlock(nodes) {
    const commands = nodes.map((child) => this.build(child));
    return {
      type: "block",
      commands
    };
  }
};
function buildAST(node) {
  const builder = new ASTBuilder();
  const ast = builder.build(node);
  return {
    ast,
    warnings: builder.warnings
  };
}
export {
  ASTBuilder,
  DEFAULT_CONFIDENCE_THRESHOLD,
  HIGH_CONFIDENCE_THRESHOLD,
  SemanticAnalyzerImpl,
  SemanticCache,
  buildAST,
  convertExpression,
  convertLiteral,
  convertPropertyPath,
  convertReference,
  convertSelector,
  convertValue,
  createCommandNode,
  createEventHandler,
  createLiteral,
  createPropertyPath,
  createReference,
  createSelector,
  createSemanticAnalyzer,
  createSemanticCache,
  getCommandMapper,
  getPatternsForLanguage,
  getPatternsForLanguageAndCommand,
  getProfile,
  getRegisteredLanguages,
  getRegisteredMappers,
  getTokenizer,
  isLanguageRegistered,
  isLanguageSupported,
  mergeProfiles,
  registerCommandMapper,
  registerLanguage,
  registerPatterns,
  rolesToCommandArgs,
  semanticCache,
  setPatternGenerator,
  shouldUseSemanticResult,
  tryGetProfile,
  withCache
};
//# sourceMappingURL=core.js.map