"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
require("../chevrotain/lib-src-parse-parser-parser.cjs");
require("../chevrotain/lib-src-scan-lexer_public.cjs");
const tokens_public = require("../chevrotain/lib-src-scan-tokens_public.cjs");
const lookahead = require("../chevrotain/lib-src-parse-grammar-lookahead.cjs");
const llk_lookahead = require("../chevrotain/lib-src-parse-grammar-llk_lookahead.cjs");
require("../chevrotain/lib-src-parse-errors_public.cjs");
require("../chevrotain/lib-src-parse-exceptions_public.cjs");
const model = require("../@chevrotain/gast/lib-src-model.cjs");
const atn = require("./lib-atn.cjs");
const dfa = require("./lib-dfa.cjs");
const min = require("../lodash-es/min.cjs");
const flatMap = require("../lodash-es/flatMap.cjs");
const uniqBy = require("../lodash-es/uniqBy.cjs");
const map = require("../lodash-es/map.cjs");
const flatten = require("../lodash-es/flatten.cjs");
const forEach = require("../lodash-es/forEach.cjs");
const isEmpty = require("../lodash-es/isEmpty.cjs");
const reduce = require("../lodash-es/reduce.cjs");
function createDFACache(startState, decision) {
  const map2 = {};
  return (predicateSet) => {
    const key = predicateSet.toString();
    let existing = map2[key];
    if (existing !== void 0) {
      return existing;
    } else {
      existing = {
        atnStartState: startState,
        decision,
        states: {}
      };
      map2[key] = existing;
      return existing;
    }
  };
}
class PredicateSet {
  constructor() {
    this.predicates = [];
  }
  is(index) {
    return index >= this.predicates.length || this.predicates[index];
  }
  set(index, value) {
    this.predicates[index] = value;
  }
  toString() {
    let value = "";
    const size = this.predicates.length;
    for (let i = 0; i < size; i++) {
      value += this.predicates[i] === true ? "1" : "0";
    }
    return value;
  }
}
const EMPTY_PREDICATES = new PredicateSet();
class LLStarLookaheadStrategy extends llk_lookahead.LLkLookaheadStrategy {
  constructor(options) {
    var _a;
    super();
    this.logging = (_a = options === null || options === void 0 ? void 0 : options.logging) !== null && _a !== void 0 ? _a : ((message) => console.log(message));
  }
  initialize(options) {
    this.atn = atn.createATN(options.rules);
    this.dfas = initATNSimulator(this.atn);
  }
  validateAmbiguousAlternationAlternatives() {
    return [];
  }
  validateEmptyOrAlternatives() {
    return [];
  }
  buildLookaheadForAlternation(options) {
    const { prodOccurrence, rule, hasPredicates, dynamicTokensEnabled } = options;
    const dfas = this.dfas;
    const logging = this.logging;
    const key = atn.buildATNKey(rule, "Alternation", prodOccurrence);
    const decisionState = this.atn.decisionMap[key];
    const decisionIndex = decisionState.decision;
    const partialAlts = map.default(lookahead.getLookaheadPaths({
      maxLookahead: 1,
      occurrence: prodOccurrence,
      prodType: "Alternation",
      rule
    }), (currAlt) => map.default(currAlt, (path) => path[0]));
    if (isLL1Sequence(partialAlts, false) && !dynamicTokensEnabled) {
      const choiceToAlt = reduce.default(partialAlts, (result, currAlt, idx) => {
        forEach.default(currAlt, (currTokType) => {
          if (currTokType) {
            result[currTokType.tokenTypeIdx] = idx;
            forEach.default(currTokType.categoryMatches, (currExtendingType) => {
              result[currExtendingType] = idx;
            });
          }
        });
        return result;
      }, {});
      if (hasPredicates) {
        return function(orAlts) {
          var _a;
          const nextToken = this.LA(1);
          const prediction = choiceToAlt[nextToken.tokenTypeIdx];
          if (orAlts !== void 0 && prediction !== void 0) {
            const gate = (_a = orAlts[prediction]) === null || _a === void 0 ? void 0 : _a.GATE;
            if (gate !== void 0 && gate.call(this) === false) {
              return void 0;
            }
          }
          return prediction;
        };
      } else {
        return function() {
          const nextToken = this.LA(1);
          return choiceToAlt[nextToken.tokenTypeIdx];
        };
      }
    } else if (hasPredicates) {
      return function(orAlts) {
        const predicates = new PredicateSet();
        const length = orAlts === void 0 ? 0 : orAlts.length;
        for (let i = 0; i < length; i++) {
          const gate = orAlts === null || orAlts === void 0 ? void 0 : orAlts[i].GATE;
          predicates.set(i, gate === void 0 || gate.call(this));
        }
        const result = adaptivePredict.call(this, dfas, decisionIndex, predicates, logging);
        return typeof result === "number" ? result : void 0;
      };
    } else {
      return function() {
        const result = adaptivePredict.call(this, dfas, decisionIndex, EMPTY_PREDICATES, logging);
        return typeof result === "number" ? result : void 0;
      };
    }
  }
  buildLookaheadForOptional(options) {
    const { prodOccurrence, rule, prodType, dynamicTokensEnabled } = options;
    const dfas = this.dfas;
    const logging = this.logging;
    const key = atn.buildATNKey(rule, prodType, prodOccurrence);
    const decisionState = this.atn.decisionMap[key];
    const decisionIndex = decisionState.decision;
    const alts = map.default(lookahead.getLookaheadPaths({
      maxLookahead: 1,
      occurrence: prodOccurrence,
      prodType,
      rule
    }), (e) => {
      return map.default(e, (g) => g[0]);
    });
    if (isLL1Sequence(alts) && alts[0][0] && !dynamicTokensEnabled) {
      const alt = alts[0];
      const singleTokensTypes = flatten.default(alt);
      if (singleTokensTypes.length === 1 && isEmpty.default(singleTokensTypes[0].categoryMatches)) {
        const expectedTokenType = singleTokensTypes[0];
        const expectedTokenUniqueKey = expectedTokenType.tokenTypeIdx;
        return function() {
          return this.LA(1).tokenTypeIdx === expectedTokenUniqueKey;
        };
      } else {
        const choiceToAlt = reduce.default(singleTokensTypes, (result, currTokType) => {
          if (currTokType !== void 0) {
            result[currTokType.tokenTypeIdx] = true;
            forEach.default(currTokType.categoryMatches, (currExtendingType) => {
              result[currExtendingType] = true;
            });
          }
          return result;
        }, {});
        return function() {
          const nextToken = this.LA(1);
          return choiceToAlt[nextToken.tokenTypeIdx] === true;
        };
      }
    }
    return function() {
      const result = adaptivePredict.call(this, dfas, decisionIndex, EMPTY_PREDICATES, logging);
      return typeof result === "object" ? false : result === 0;
    };
  }
}
function isLL1Sequence(sequences, allowEmpty = true) {
  const fullSet = /* @__PURE__ */ new Set();
  for (const alt of sequences) {
    const altSet = /* @__PURE__ */ new Set();
    for (const tokType of alt) {
      if (tokType === void 0) {
        if (allowEmpty) {
          break;
        } else {
          return false;
        }
      }
      const indices = [tokType.tokenTypeIdx].concat(tokType.categoryMatches);
      for (const index of indices) {
        if (fullSet.has(index)) {
          if (!altSet.has(index)) {
            return false;
          }
        } else {
          fullSet.add(index);
          altSet.add(index);
        }
      }
    }
  }
  return true;
}
function initATNSimulator(atn2) {
  const decisionLength = atn2.decisionStates.length;
  const decisionToDFA = Array(decisionLength);
  for (let i = 0; i < decisionLength; i++) {
    decisionToDFA[i] = createDFACache(atn2.decisionStates[i], i);
  }
  return decisionToDFA;
}
function adaptivePredict(dfaCaches, decision, predicateSet, logging) {
  const dfa2 = dfaCaches[decision](predicateSet);
  let start = dfa2.start;
  if (start === void 0) {
    const closure2 = computeStartState(dfa2.atnStartState);
    start = addDFAState(dfa2, newDFAState(closure2));
    dfa2.start = start;
  }
  const alt = performLookahead.apply(this, [dfa2, start, predicateSet, logging]);
  return alt;
}
function performLookahead(dfa$1, s0, predicateSet, logging) {
  let previousD = s0;
  let i = 1;
  const path = [];
  let t = this.LA(i++);
  while (true) {
    let d = getExistingTargetState(previousD, t);
    if (d === void 0) {
      d = computeLookaheadTarget.apply(this, [dfa$1, previousD, t, i, predicateSet, logging]);
    }
    if (d === dfa.DFA_ERROR) {
      return buildAdaptivePredictError(path, previousD, t);
    }
    if (d.isAcceptState === true) {
      return d.prediction;
    }
    previousD = d;
    path.push(t);
    t = this.LA(i++);
  }
}
function computeLookaheadTarget(dfa$1, previousD, token, lookahead2, predicateSet, logging) {
  const reach = computeReachSet(previousD.configs, token, predicateSet);
  if (reach.size === 0) {
    addDFAEdge(dfa$1, previousD, token, dfa.DFA_ERROR);
    return dfa.DFA_ERROR;
  }
  let newState = newDFAState(reach);
  const predictedAlt = getUniqueAlt(reach, predicateSet);
  if (predictedAlt !== void 0) {
    newState.isAcceptState = true;
    newState.prediction = predictedAlt;
    newState.configs.uniqueAlt = predictedAlt;
  } else if (hasConflictTerminatingPrediction(reach)) {
    const prediction = min.default(reach.alts);
    newState.isAcceptState = true;
    newState.prediction = prediction;
    newState.configs.uniqueAlt = prediction;
    reportLookaheadAmbiguity.apply(this, [dfa$1, lookahead2, reach.alts, logging]);
  }
  newState = addDFAEdge(dfa$1, previousD, token, newState);
  return newState;
}
function reportLookaheadAmbiguity(dfa2, lookahead2, ambiguityIndices, logging) {
  const prefixPath = [];
  for (let i = 1; i <= lookahead2; i++) {
    prefixPath.push(this.LA(i).tokenType);
  }
  const atnState = dfa2.atnStartState;
  const topLevelRule = atnState.rule;
  const production = atnState.production;
  const message = buildAmbiguityError({
    topLevelRule,
    ambiguityIndices,
    production,
    prefixPath
  });
  logging(message);
}
function buildAmbiguityError(options) {
  const pathMsg = map.default(options.prefixPath, (currtok) => tokens_public.tokenLabel(currtok)).join(", ");
  const occurrence = options.production.idx === 0 ? "" : options.production.idx;
  let currMessage = `Ambiguous Alternatives Detected: <${options.ambiguityIndices.join(", ")}> in <${getProductionDslName(options.production)}${occurrence}> inside <${options.topLevelRule.name}> Rule,
<${pathMsg}> may appears as a prefix path in all these alternatives.
`;
  currMessage = currMessage + `See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES
For Further details.`;
  return currMessage;
}
function getProductionDslName(prod) {
  if (prod instanceof model.NonTerminal) {
    return "SUBRULE";
  } else if (prod instanceof model.Option) {
    return "OPTION";
  } else if (prod instanceof model.Alternation) {
    return "OR";
  } else if (prod instanceof model.RepetitionMandatory) {
    return "AT_LEAST_ONE";
  } else if (prod instanceof model.RepetitionMandatoryWithSeparator) {
    return "AT_LEAST_ONE_SEP";
  } else if (prod instanceof model.RepetitionWithSeparator) {
    return "MANY_SEP";
  } else if (prod instanceof model.Repetition) {
    return "MANY";
  } else if (prod instanceof model.Terminal) {
    return "CONSUME";
  } else {
    throw Error("non exhaustive match");
  }
}
function buildAdaptivePredictError(path, previous, current) {
  const nextTransitions = flatMap.default(previous.configs.elements, (e) => e.state.transitions);
  const nextTokenTypes = uniqBy.default(nextTransitions.filter((e) => e instanceof atn.AtomTransition).map((e) => e.tokenType), (e) => e.tokenTypeIdx);
  return {
    actualToken: current,
    possibleTokenTypes: nextTokenTypes,
    tokenPath: path
  };
}
function getExistingTargetState(state, token) {
  return state.edges[token.tokenTypeIdx];
}
function computeReachSet(configs, token, predicateSet) {
  const intermediate = new dfa.ATNConfigSet();
  const skippedStopStates = [];
  for (const c of configs.elements) {
    if (predicateSet.is(c.alt) === false) {
      continue;
    }
    if (c.state.type === atn.ATN_RULE_STOP) {
      skippedStopStates.push(c);
      continue;
    }
    const transitionLength = c.state.transitions.length;
    for (let i = 0; i < transitionLength; i++) {
      const transition = c.state.transitions[i];
      const target = getReachableTarget(transition, token);
      if (target !== void 0) {
        intermediate.add({
          state: target,
          alt: c.alt,
          stack: c.stack
        });
      }
    }
  }
  let reach;
  if (skippedStopStates.length === 0 && intermediate.size === 1) {
    reach = intermediate;
  }
  if (reach === void 0) {
    reach = new dfa.ATNConfigSet();
    for (const c of intermediate.elements) {
      closure(c, reach);
    }
  }
  if (skippedStopStates.length > 0 && !hasConfigInRuleStopState(reach)) {
    for (const c of skippedStopStates) {
      reach.add(c);
    }
  }
  return reach;
}
function getReachableTarget(transition, token) {
  if (transition instanceof atn.AtomTransition && tokens_public.tokenMatcher(token, transition.tokenType)) {
    return transition.target;
  }
  return void 0;
}
function getUniqueAlt(configs, predicateSet) {
  let alt;
  for (const c of configs.elements) {
    if (predicateSet.is(c.alt) === true) {
      if (alt === void 0) {
        alt = c.alt;
      } else if (alt !== c.alt) {
        return void 0;
      }
    }
  }
  return alt;
}
function newDFAState(closure2) {
  return {
    configs: closure2,
    edges: {},
    isAcceptState: false,
    prediction: -1
  };
}
function addDFAEdge(dfa2, from, token, to) {
  to = addDFAState(dfa2, to);
  from.edges[token.tokenTypeIdx] = to;
  return to;
}
function addDFAState(dfa$1, state) {
  if (state === dfa.DFA_ERROR) {
    return state;
  }
  const mapKey = state.configs.key;
  const existing = dfa$1.states[mapKey];
  if (existing !== void 0) {
    return existing;
  }
  state.configs.finalize();
  dfa$1.states[mapKey] = state;
  return state;
}
function computeStartState(atnState) {
  const configs = new dfa.ATNConfigSet();
  const numberOfTransitions = atnState.transitions.length;
  for (let i = 0; i < numberOfTransitions; i++) {
    const target = atnState.transitions[i].target;
    const config = {
      state: target,
      alt: i,
      stack: []
    };
    closure(config, configs);
  }
  return configs;
}
function closure(config, configs) {
  const p = config.state;
  if (p.type === atn.ATN_RULE_STOP) {
    if (config.stack.length > 0) {
      const atnStack = [...config.stack];
      const followState = atnStack.pop();
      const followConfig = {
        state: followState,
        alt: config.alt,
        stack: atnStack
      };
      closure(followConfig, configs);
    } else {
      configs.add(config);
    }
    return;
  }
  if (!p.epsilonOnlyTransitions) {
    configs.add(config);
  }
  const transitionLength = p.transitions.length;
  for (let i = 0; i < transitionLength; i++) {
    const transition = p.transitions[i];
    const c = getEpsilonTarget(config, transition);
    if (c !== void 0) {
      closure(c, configs);
    }
  }
}
function getEpsilonTarget(config, transition) {
  if (transition instanceof atn.EpsilonTransition) {
    return {
      state: transition.target,
      alt: config.alt,
      stack: config.stack
    };
  } else if (transition instanceof atn.RuleTransition) {
    const stack = [...config.stack, transition.followState];
    return {
      state: transition.target,
      alt: config.alt,
      stack
    };
  }
  return void 0;
}
function hasConfigInRuleStopState(configs) {
  for (const c of configs.elements) {
    if (c.state.type === atn.ATN_RULE_STOP) {
      return true;
    }
  }
  return false;
}
function allConfigsInRuleStopStates(configs) {
  for (const c of configs.elements) {
    if (c.state.type !== atn.ATN_RULE_STOP) {
      return false;
    }
  }
  return true;
}
function hasConflictTerminatingPrediction(configs) {
  if (allConfigsInRuleStopStates(configs)) {
    return true;
  }
  const altSets = getConflictingAltSets(configs.elements);
  const heuristic = hasConflictingAltSet(altSets) && !hasStateAssociatedWithOneAlt(altSets);
  return heuristic;
}
function getConflictingAltSets(configs) {
  const configToAlts = /* @__PURE__ */ new Map();
  for (const c of configs) {
    const key = dfa.getATNConfigKey(c, false);
    let alts = configToAlts.get(key);
    if (alts === void 0) {
      alts = {};
      configToAlts.set(key, alts);
    }
    alts[c.alt] = true;
  }
  return configToAlts;
}
function hasConflictingAltSet(altSets) {
  for (const value of Array.from(altSets.values())) {
    if (Object.keys(value).length > 1) {
      return true;
    }
  }
  return false;
}
function hasStateAssociatedWithOneAlt(altSets) {
  for (const value of Array.from(altSets.values())) {
    if (Object.keys(value).length === 1) {
      return true;
    }
  }
  return false;
}
exports.LLStarLookaheadStrategy = LLStarLookaheadStrategy;
//# sourceMappingURL=lib-all-star-lookahead.cjs.map
