import { assignMandatoryProperties, getContainerOfType, linkContentToContainer } from "../utils/ast-utils.js";
import { isAssignment, isCrossReference, isInfixRule, isKeyword, isParserRule } from "../languages/generated/ast.js";
import { getTypeName, isDataTypeRule } from "../utils/grammar-utils.js";
import { defaultParserErrorProvider } from "../../../chevrotain/lib/src/parse/errors_public.js";
import { LLkLookaheadStrategy } from "../../../chevrotain/lib/src/parse/grammar/llk_lookahead.js";
import { EmbeddedActionsParser } from "../../../chevrotain/lib/src/parse/parser/parser.js";
import "../../../chevrotain/lib/src/api.js";
import { LLStarLookaheadStrategy } from "../../../chevrotain-allstar/lib/all-star-lookahead.js";
import "../../../chevrotain-allstar/lib/index.js";
import { CstNodeBuilder } from "./cst-node-builder.js";
const DatatypeSymbol = Symbol("Datatype");
function isDataTypeNode(e) {
	return e.$type === DatatypeSymbol;
}
var ruleSuffix = "​", withRuleSuffix = (e) => e.endsWith(ruleSuffix) ? e : e + ruleSuffix, AbstractLangiumParser = class {
	constructor(e) {
		this._unorderedGroups = /* @__PURE__ */ new Map(), this.allRules = /* @__PURE__ */ new Map(), this.lexer = e.parser.Lexer;
		let m = this.lexer.definition, h = e.LanguageMetaData.mode === "production";
		e.shared.profilers.LangiumProfiler?.isActive("parsing") ? this.wrapper = new ProfilerWrapper(m, {
			...e.parser.ParserConfig,
			skipValidations: h,
			errorMessageProvider: e.parser.ParserErrorMessageProvider
		}, e.shared.profilers.LangiumProfiler.createTask("parsing", e.LanguageMetaData.languageId)) : this.wrapper = new ChevrotainWrapper(m, {
			...e.parser.ParserConfig,
			skipValidations: h,
			errorMessageProvider: e.parser.ParserErrorMessageProvider
		});
	}
	alternatives(e, m) {
		this.wrapper.wrapOr(e, m);
	}
	optional(e, m) {
		this.wrapper.wrapOption(e, m);
	}
	many(e, m) {
		this.wrapper.wrapMany(e, m);
	}
	atLeastOne(e, m) {
		this.wrapper.wrapAtLeastOne(e, m);
	}
	getRule(e) {
		return this.allRules.get(e);
	}
	isRecording() {
		return this.wrapper.IS_RECORDING;
	}
	get unorderedGroups() {
		return this._unorderedGroups;
	}
	getRuleStack() {
		return this.wrapper.RULE_STACK;
	}
	finalize() {
		this.wrapper.wrapSelfAnalysis();
	}
}, LangiumParser = class extends AbstractLangiumParser {
	get current() {
		return this.stack[this.stack.length - 1];
	}
	constructor(e) {
		super(e), this.nodeBuilder = new CstNodeBuilder(), this.stack = [], this.assignmentMap = /* @__PURE__ */ new Map(), this.operatorPrecedence = /* @__PURE__ */ new Map(), this.linker = e.references.Linker, this.converter = e.parser.ValueConverter, this.astReflection = e.shared.AstReflection;
	}
	rule(e, m) {
		let h = this.computeRuleType(e), g;
		isInfixRule(e) && (g = e.name, this.registerPrecedenceMap(e));
		let _ = this.wrapper.DEFINE_RULE(withRuleSuffix(e.name), this.startImplementation(h, g, m).bind(this));
		return this.allRules.set(e.name, _), isParserRule(e) && e.entry && (this.mainRule = _), _;
	}
	registerPrecedenceMap(e) {
		let m = e.name, h = /* @__PURE__ */ new Map();
		for (let m = 0; m < e.operators.precedences.length; m++) {
			let g = e.operators.precedences[m];
			for (let e of g.operators) h.set(e.value, {
				precedence: m,
				rightAssoc: g.associativity === "right"
			});
		}
		this.operatorPrecedence.set(m, h);
	}
	computeRuleType(e) {
		return isInfixRule(e) ? getTypeName(e) : e.fragment ? void 0 : isDataTypeRule(e) ? DatatypeSymbol : getTypeName(e);
	}
	parse(e, m = {}) {
		this.nodeBuilder.buildRootNode(e);
		let g = this.lexerResult = this.lexer.tokenize(e);
		this.wrapper.input = g.tokens;
		let _ = m.rule ? this.allRules.get(m.rule) : this.mainRule;
		if (!_) throw Error(m.rule ? `No rule found with name '${m.rule}'` : "No main rule available.");
		let v = this.doParse(_);
		return this.nodeBuilder.addHiddenNodes(g.hidden), this.unorderedGroups.clear(), this.lexerResult = void 0, linkContentToContainer(v, { deep: !0 }), {
			value: v,
			lexerErrors: g.errors,
			lexerReport: g.report,
			parserErrors: this.wrapper.errors
		};
	}
	doParse(e) {
		let m = this.wrapper.rule(e);
		if (this.stack.length > 0 && (m = this.construct()), m === void 0) throw Error("No result from parser");
		if (this.stack.length > 0) throw Error("Parser stack is not empty after parsing");
		return m;
	}
	startImplementation(e, m, h) {
		return (g) => {
			let _ = !this.isRecording() && e !== void 0;
			if (_) {
				let h = { $type: e };
				this.stack.push(h), e === DatatypeSymbol ? h.value = "" : m !== void 0 && (h.$infixName = m);
			}
			return h(g), _ ? this.construct() : void 0;
		};
	}
	extractHiddenTokens(e) {
		let m = this.lexerResult.hidden;
		if (!m.length) return [];
		let h = e.startOffset;
		for (let e = 0; e < m.length; e++) if (m[e].startOffset > h) return m.splice(0, e);
		return m.splice(0, m.length);
	}
	consume(e, m, h) {
		let g = this.wrapper.wrapConsume(e, m);
		if (!this.isRecording() && this.isValidToken(g)) {
			let e = this.extractHiddenTokens(g);
			this.nodeBuilder.addHiddenNodes(e);
			let m = this.nodeBuilder.buildLeafNode(g, h), { assignment: _, crossRef: v } = this.getAssignment(h), b = this.current;
			if (_) {
				let e = isKeyword(h) ? g.image : this.converter.convert(g.image, m);
				this.assign(_.operator, _.feature, e, m, v);
			} else if (isDataTypeNode(b)) {
				let e = g.image;
				isKeyword(h) || (e = this.converter.convert(e, m).toString()), b.value += e;
			}
		}
	}
	isValidToken(e) {
		return !e.isInsertedInRecovery && !isNaN(e.startOffset) && typeof e.endOffset == "number" && !isNaN(e.endOffset);
	}
	subrule(e, m, h, g, _) {
		let v;
		!this.isRecording() && !h && (v = this.nodeBuilder.buildCompositeNode(g));
		let y;
		try {
			y = this.wrapper.wrapSubrule(e, m, _);
		} finally {
			this.isRecording() || (y === void 0 && !h && (y = this.construct()), y !== void 0 && v && v.length > 0 && this.performSubruleAssignment(y, g, v));
		}
	}
	performSubruleAssignment(e, m, h) {
		let { assignment: g, crossRef: _ } = this.getAssignment(m);
		if (g) this.assign(g.operator, g.feature, e, h, _);
		else if (!g) {
			let m = this.current;
			if (isDataTypeNode(m)) m.value += e.toString();
			else if (typeof e == "object" && e) {
				let h = this.assignWithoutOverride(e, m);
				this.stack.pop(), this.stack.push(h);
			}
		}
	}
	action(e, m) {
		if (!this.isRecording()) {
			let h = this.current;
			if (m.feature && m.operator) {
				h = this.construct(), this.nodeBuilder.removeNode(h.$cstNode), this.nodeBuilder.buildCompositeNode(m).content.push(h.$cstNode);
				let g = { $type: e };
				this.stack.push(g), this.assign(m.operator, m.feature, h, h.$cstNode);
			} else h.$type = e;
		}
	}
	construct() {
		if (this.isRecording()) return;
		let m = this.stack.pop();
		return this.nodeBuilder.construct(m), "$infixName" in m ? this.constructInfix(m, this.operatorPrecedence.get(m.$infixName)) : isDataTypeNode(m) ? this.converter.convert(m.value, m.$cstNode) : (assignMandatoryProperties(this.astReflection, m), m);
	}
	constructInfix(e, m) {
		let h = e.parts;
		if (!Array.isArray(h) || h.length === 0) return;
		let g = e.operators;
		if (!Array.isArray(g) || h.length < 2) return h[0];
		let _ = 0, v = -1;
		for (let e = 0; e < g.length; e++) {
			let h = g[e], y = m.get(h) ?? {
				precedence: Infinity,
				rightAssoc: !1
			};
			y.precedence > v ? (v = y.precedence, _ = e) : y.precedence === v && (y.rightAssoc || (_ = e));
		}
		let y = g.slice(0, _), b = g.slice(_ + 1), x = h.slice(0, _ + 1), S = h.slice(_ + 1), C = {
			$infixName: e.$infixName,
			$type: e.$type,
			$cstNode: e.$cstNode,
			parts: x,
			operators: y
		}, w = {
			$infixName: e.$infixName,
			$type: e.$type,
			$cstNode: e.$cstNode,
			parts: S,
			operators: b
		}, T = this.constructInfix(C, m), E = this.constructInfix(w, m);
		return {
			$type: e.$type,
			$cstNode: e.$cstNode,
			left: T,
			operator: g[_],
			right: E
		};
	}
	getAssignment(e) {
		if (!this.assignmentMap.has(e)) {
			let h = getContainerOfType(e, isAssignment);
			this.assignmentMap.set(e, {
				assignment: h,
				crossRef: h && isCrossReference(h.terminal) ? h.terminal.isMulti ? "multi" : "single" : void 0
			});
		}
		return this.assignmentMap.get(e);
	}
	assign(e, m, h, g, _) {
		let v = this.current, y;
		switch (y = _ === "single" && typeof h == "string" ? this.linker.buildReference(v, m, g, h) : _ === "multi" && typeof h == "string" ? this.linker.buildMultiReference(v, m, g, h) : h, e) {
			case "=":
				v[m] = y;
				break;
			case "?=":
				v[m] = !0;
				break;
			case "+=": Array.isArray(v[m]) || (v[m] = []), v[m].push(y);
		}
	}
	assignWithoutOverride(e, m) {
		for (let [h, g] of Object.entries(m)) {
			let m = e[h];
			m === void 0 ? e[h] = g : Array.isArray(m) && Array.isArray(g) && (g.push(...m), e[h] = g);
		}
		let h = e.$cstNode;
		return h && (h.astNode = void 0, e.$cstNode = void 0), e;
	}
	get definitionErrors() {
		return this.wrapper.definitionErrors;
	}
}, AbstractParserErrorMessageProvider = class {
	buildMismatchTokenMessage(e) {
		return defaultParserErrorProvider.buildMismatchTokenMessage(e);
	}
	buildNotAllInputParsedMessage(e) {
		return defaultParserErrorProvider.buildNotAllInputParsedMessage(e);
	}
	buildNoViableAltMessage(e) {
		return defaultParserErrorProvider.buildNoViableAltMessage(e);
	}
	buildEarlyExitMessage(e) {
		return defaultParserErrorProvider.buildEarlyExitMessage(e);
	}
}, LangiumParserErrorMessageProvider = class extends AbstractParserErrorMessageProvider {
	buildMismatchTokenMessage({ expected: e, actual: m }) {
		return `Expecting ${e.LABEL ? "`" + e.LABEL + "`" : e.name.endsWith(":KW") ? `keyword '${e.name.substring(0, e.name.length - 3)}'` : `token of type '${e.name}'`} but found \`${m.image}\`.`;
	}
	buildNotAllInputParsedMessage({ firstRedundant: e }) {
		return `Expecting end of file but found \`${e.image}\`.`;
	}
}, LangiumCompletionParser = class extends AbstractLangiumParser {
	constructor() {
		super(...arguments), this.tokens = [], this.elementStack = [], this.lastElementStack = [], this.nextTokenIndex = 0, this.stackSize = 0;
	}
	action() {}
	construct() {}
	parse(e) {
		return this.resetState(), this.tokens = this.lexer.tokenize(e, { mode: "partial" }).tokens, this.wrapper.input = [...this.tokens], this.mainRule.call(this.wrapper, {}), this.unorderedGroups.clear(), {
			tokens: this.tokens,
			elementStack: [...this.lastElementStack],
			tokenIndex: this.nextTokenIndex
		};
	}
	rule(e, m) {
		let h = this.wrapper.DEFINE_RULE(withRuleSuffix(e.name), this.startImplementation(m).bind(this));
		return this.allRules.set(e.name, h), e.entry && (this.mainRule = h), h;
	}
	resetState() {
		this.elementStack = [], this.lastElementStack = [], this.nextTokenIndex = 0, this.stackSize = 0;
	}
	startImplementation(e) {
		return (m) => {
			let h = this.keepStackSize();
			try {
				e(m);
			} finally {
				this.resetStackSize(h);
			}
		};
	}
	removeUnexpectedElements() {
		this.elementStack.splice(this.stackSize);
	}
	keepStackSize() {
		let e = this.elementStack.length;
		return this.stackSize = e, e;
	}
	resetStackSize(e) {
		this.removeUnexpectedElements(), this.stackSize = e;
	}
	consume(e, m, h) {
		this.wrapper.wrapConsume(e, m), this.isRecording() || (this.lastElementStack = [...this.elementStack, h], this.nextTokenIndex = this.currIdx + 1);
	}
	subrule(e, m, h, g, _) {
		this.before(g), this.wrapper.wrapSubrule(e, m, _), this.after(g);
	}
	before(e) {
		this.isRecording() || this.elementStack.push(e);
	}
	after(e) {
		if (!this.isRecording()) {
			let m = this.elementStack.lastIndexOf(e);
			m >= 0 && this.elementStack.splice(m);
		}
	}
	get currIdx() {
		return this.wrapper.currIdx;
	}
}, defaultConfig = {
	recoveryEnabled: !0,
	nodeLocationTracking: "full",
	skipValidations: !0,
	errorMessageProvider: new LangiumParserErrorMessageProvider()
}, ChevrotainWrapper = class extends EmbeddedActionsParser {
	constructor(e, m) {
		let h = m && "maxLookahead" in m;
		super(e, {
			...defaultConfig,
			lookaheadStrategy: h ? new LLkLookaheadStrategy({ maxLookahead: m.maxLookahead }) : new LLStarLookaheadStrategy({ logging: m.skipValidations ? () => {} : void 0 }),
			...m
		});
	}
	get IS_RECORDING() {
		return this.RECORDING_PHASE;
	}
	DEFINE_RULE(e, m, h) {
		return this.RULE(e, m, h);
	}
	wrapSelfAnalysis() {
		this.performSelfAnalysis();
	}
	wrapConsume(e, m) {
		return this.consume(e, m, void 0);
	}
	wrapSubrule(e, m, h) {
		return this.subrule(e, m, { ARGS: [h] });
	}
	wrapOr(e, m) {
		this.or(e, m);
	}
	wrapOption(e, m) {
		this.option(e, m);
	}
	wrapMany(e, m) {
		this.many(e, m);
	}
	wrapAtLeastOne(e, m) {
		this.atLeastOne(e, m);
	}
	rule(e) {
		return e.call(this, {});
	}
}, ProfilerWrapper = class extends ChevrotainWrapper {
	constructor(e, m, h) {
		super(e, m), this.task = h;
	}
	rule(e) {
		this.task.start(), this.task.startSubTask(this.ruleName(e));
		try {
			return super.rule(e);
		} finally {
			this.task.stopSubTask(this.ruleName(e)), this.task.stop();
		}
	}
	ruleName(e) {
		return e.ruleName;
	}
	subrule(e, m, h) {
		this.task.startSubTask(this.ruleName(m));
		try {
			return super.subrule(e, m, h);
		} finally {
			this.task.stopSubTask(this.ruleName(m));
		}
	}
};
export { AbstractLangiumParser, AbstractParserErrorMessageProvider, DatatypeSymbol, LangiumCompletionParser, LangiumParser, LangiumParserErrorMessageProvider };
