import { __export } from "../../../../_virtual/rolldown_runtime.js";
import { isCompositeCstNode } from "../syntax-tree.js";
import { getContainerOfType, streamAllContents } from "./ast-utils.js";
import { isAbstractElement, isAbstractParserRule, isAction, isArrayType, isAssignment, isCharacterRange, isCrossReference, isGroup, isInferredType, isInfixRule, isInterface, isKeyword, isNegatedToken, isParserRule, isReferenceType, isRegexToken, isReturnType, isRuleCall, isSimpleType, isTerminalAlternatives, isTerminalGroup, isTerminalRule, isTerminalRuleCall, isType, isUnionType, isUntilToken, isWildcard } from "../languages/generated/ast.js";
import { streamCst } from "./cst-utils.js";
import { assertUnreachable } from "./errors.js";
import { escapeRegExp, isWhitespace } from "./regexp-utils.js";
var grammar_utils_exports = /* @__PURE__ */ __export({
	findAssignment: () => findAssignment,
	findNameAssignment: () => findNameAssignment,
	findNodeForKeyword: () => findNodeForKeyword,
	findNodeForProperty: () => findNodeForProperty,
	findNodesForKeyword: () => findNodesForKeyword,
	findNodesForKeywordInternal: () => findNodesForKeywordInternal,
	findNodesForProperty: () => findNodesForProperty,
	getActionAtElement: () => getActionAtElement,
	getActionType: () => getActionType,
	getAllReachableRules: () => getAllReachableRules,
	getAllRulesUsedForCrossReferences: () => getAllRulesUsedForCrossReferences,
	getCrossReferenceTerminal: () => getCrossReferenceTerminal,
	getEntryRule: () => getEntryRule,
	getExplicitRuleType: () => getExplicitRuleType,
	getHiddenRules: () => getHiddenRules,
	getRuleType: () => getRuleType,
	getRuleTypeName: () => getRuleTypeName,
	getTypeName: () => getTypeName,
	isArrayCardinality: () => isArrayCardinality,
	isArrayOperator: () => isArrayOperator,
	isCommentTerminal: () => isCommentTerminal,
	isDataType: () => isDataType,
	isDataTypeRule: () => isDataTypeRule,
	isOptionalCardinality: () => isOptionalCardinality,
	terminalRegex: () => terminalRegex
}, 1);
function getEntryRule(b) {
	return b.rules.find((b) => isParserRule(b) && b.entry);
}
function getHiddenRules(b) {
	return b.rules.filter((b) => isTerminalRule(b) && b.hidden);
}
function getAllReachableRules(b, Y) {
	let X = /* @__PURE__ */ new Set(), Z = getEntryRule(b);
	if (!Z) return new Set(b.rules);
	let Q = [Z].concat(getHiddenRules(b));
	for (let b of Q) ruleDfs(b, X, Y);
	let $ = /* @__PURE__ */ new Set();
	for (let Y of b.rules) (X.has(Y.name) || isTerminalRule(Y) && Y.hidden) && $.add(Y);
	return $;
}
function ruleDfs(b, Y, X) {
	Y.add(b.name), streamAllContents(b).forEach((b) => {
		if (isRuleCall(b) || X && isTerminalRuleCall(b)) {
			let Z = b.rule.ref;
			Z && !Y.has(Z.name) && ruleDfs(Z, Y, X);
		}
	});
}
function getAllRulesUsedForCrossReferences(b) {
	let Y = /* @__PURE__ */ new Set();
	return streamAllContents(b).forEach((b) => {
		isCrossReference(b) && (isParserRule(b.type.ref) && Y.add(b.type.ref), isInferredType(b.type.ref) && isParserRule(b.type.ref.$container) && Y.add(b.type.ref.$container));
	}), Y;
}
function getCrossReferenceTerminal(b) {
	if (b.terminal) return b.terminal;
	if (b.type.ref) return findNameAssignment(b.type.ref)?.terminal;
}
function isCommentTerminal(b) {
	return b.hidden && !isWhitespace(terminalRegex(b));
}
function findNodesForProperty(b, Y) {
	return !b || !Y ? [] : findNodesForPropertyInternal(b, Y, b.astNode, !0);
}
function findNodeForProperty(b, Y, X) {
	if (!b || !Y) return;
	let Z = findNodesForPropertyInternal(b, Y, b.astNode, !0);
	if (Z.length !== 0) return X = X === void 0 ? 0 : Math.max(0, Math.min(X, Z.length - 1)), Z[X];
}
function findNodesForPropertyInternal(b, Z, Q, $) {
	if (!$) {
		let Y = getContainerOfType(b.grammarSource, isAssignment);
		if (Y && Y.feature === Z) return [b];
	}
	return isCompositeCstNode(b) && b.astNode === Q ? b.content.flatMap((b) => findNodesForPropertyInternal(b, Z, Q, !1)) : [];
}
function findNodesForKeyword(b, Y) {
	return b ? findNodesForKeywordInternal(b, Y, b?.astNode) : [];
}
function findNodeForKeyword(b, Y, X) {
	if (!b) return;
	let Z = findNodesForKeywordInternal(b, Y, b?.astNode);
	if (Z.length !== 0) return X = X === void 0 ? 0 : Math.max(0, Math.min(X, Z.length - 1)), Z[X];
}
function findNodesForKeywordInternal(b, Y, X) {
	if (b.astNode !== X) return [];
	if (isKeyword(b.grammarSource) && b.grammarSource.value === Y) return [b];
	let Z = streamCst(b).iterator(), Q, $ = [];
	do
		if (Q = Z.next(), !Q.done) {
			let b = Q.value;
			b.astNode === X ? isKeyword(b.grammarSource) && b.grammarSource.value === Y && $.push(b) : Z.prune();
		}
	while (!Q.done);
	return $;
}
function findAssignment(b) {
	let Y = b.astNode;
	for (; Y === b.container?.astNode;) {
		let Y = getContainerOfType(b.grammarSource, isAssignment);
		if (Y) return Y;
		b = b.container;
	}
}
function findNameAssignment(b) {
	let Y = b;
	return isInferredType(Y) && (isAction(Y.$container) ? Y = Y.$container.$container : isAbstractParserRule(Y.$container) ? Y = Y.$container : assertUnreachable(Y.$container)), findNameAssignmentInternal(b, Y, /* @__PURE__ */ new Map());
}
function findNameAssignmentInternal(b, Y, Q) {
	function $(Y, Z) {
		let $;
		return getContainerOfType(Y, isAssignment) || ($ = findNameAssignmentInternal(Z, Z, Q)), Q.set(b, $), $;
	}
	if (Q.has(b)) return Q.get(b);
	Q.set(b, void 0);
	for (let X of streamAllContents(Y)) if (isAssignment(X) && X.feature.toLowerCase() === "name") return Q.set(b, X), X;
	else if (isRuleCall(X) && isParserRule(X.rule.ref)) return $(X, X.rule.ref);
	else if (isSimpleType(X) && X.typeRef?.ref) return $(X, X.typeRef.ref);
}
function getActionAtElement(b) {
	let Y = b.$container;
	if (isGroup(Y)) {
		let X = Y.elements, Q = X.indexOf(b);
		for (let b = Q - 1; b >= 0; b--) {
			let Y = X[b];
			if (isAction(Y)) return Y;
			{
				let Y = streamAllContents(X[b]).find(isAction);
				if (Y) return Y;
			}
		}
	}
	if (isAbstractElement(Y)) return getActionAtElement(Y);
}
function isOptionalCardinality(b, Y) {
	return b === "?" || b === "*" || isGroup(Y) && !!Y.guardCondition;
}
function isArrayCardinality(b) {
	return b === "*" || b === "+";
}
function isArrayOperator(b) {
	return b === "+=";
}
function isDataTypeRule(b) {
	return isDataTypeRuleInternal(b, /* @__PURE__ */ new Set());
}
function isDataTypeRuleInternal(b, Y) {
	if (Y.has(b)) return !0;
	Y.add(b);
	for (let X of streamAllContents(b)) if (isRuleCall(X)) {
		if (!X.rule.ref || isParserRule(X.rule.ref) && !isDataTypeRuleInternal(X.rule.ref, Y) || isInfixRule(X.rule.ref)) return !1;
	} else if (isAssignment(X)) return !1;
	else if (isAction(X)) return !1;
	return !!b.definition;
}
function isDataType(b) {
	return isDataTypeInternal(b.type, /* @__PURE__ */ new Set());
}
function isDataTypeInternal(b, Y) {
	if (Y.has(b)) return !0;
	if (Y.add(b), isArrayType(b) || isReferenceType(b)) return !1;
	if (isUnionType(b)) return b.types.every((b) => isDataTypeInternal(b, Y));
	if (isSimpleType(b)) {
		if (b.primitiveType !== void 0 || b.stringType !== void 0) return !0;
		if (b.typeRef !== void 0) {
			let X = b.typeRef.ref;
			return isType(X) ? isDataTypeInternal(X.type, Y) : !1;
		} else return !1;
	} else return !1;
}
function getExplicitRuleType(b) {
	if (!isTerminalRule(b)) {
		if (b.inferredType) return b.inferredType.name;
		if (b.dataType) return b.dataType;
		if (b.returnType) {
			let Y = b.returnType.ref;
			if (Y) return Y.name;
		}
	}
}
function getTypeName(b) {
	if (isAbstractParserRule(b)) return isParserRule(b) && isDataTypeRule(b) ? b.name : getExplicitRuleType(b) ?? b.name;
	if (isInterface(b) || isType(b) || isReturnType(b)) return b.name;
	if (isAction(b)) {
		let Y = getActionType(b);
		if (Y) return Y;
	} else if (isInferredType(b)) return b.name;
	throw Error("Cannot get name of Unknown Type");
}
function getActionType(b) {
	if (b.inferredType) return b.inferredType.name;
	if (b.type?.ref) return getTypeName(b.type.ref);
}
function getRuleTypeName(b) {
	return isTerminalRule(b) ? b.type?.name ?? "string" : isParserRule(b) && isDataTypeRule(b) ? b.name : getExplicitRuleType(b) ?? b.name;
}
function getRuleType(b) {
	return isTerminalRule(b) ? b.type?.name ?? "string" : getExplicitRuleType(b) ?? b.name;
}
function terminalRegex(b) {
	let Y = {
		s: !1,
		i: !1,
		u: !1
	}, X = abstractElementToRegex(b.definition, Y), Z = Object.entries(Y).filter(([, b]) => b).map(([b]) => b).join("");
	return new RegExp(X, Z);
}
var WILDCARD = "[\\s\\S]";
function abstractElementToRegex(b, Y) {
	if (isTerminalAlternatives(b)) return terminalAlternativesToRegex(b);
	if (isTerminalGroup(b)) return terminalGroupToRegex(b);
	if (isCharacterRange(b)) return characterRangeToRegex(b);
	if (isTerminalRuleCall(b)) {
		let Y = b.rule.ref;
		if (!Y) throw Error("Missing rule reference.");
		return withCardinality(abstractElementToRegex(Y.definition), {
			cardinality: b.cardinality,
			lookahead: b.lookahead,
			parenthesized: b.parenthesized
		});
	} else if (isNegatedToken(b)) return negateTokenToRegex(b);
	else if (isUntilToken(b)) return untilTokenToRegex(b);
	else if (isRegexToken(b)) {
		let X = b.regex.lastIndexOf("/"), Z = b.regex.substring(1, X), Q = b.regex.substring(X + 1);
		return Y && (Y.i = Q.includes("i"), Y.s = Q.includes("s"), Y.u = Q.includes("u")), withCardinality(Z, {
			cardinality: b.cardinality,
			lookahead: b.lookahead,
			parenthesized: b.parenthesized,
			wrap: !1
		});
	} else if (isWildcard(b)) return withCardinality(WILDCARD, {
		cardinality: b.cardinality,
		lookahead: b.lookahead,
		parenthesized: b.parenthesized
	});
	else throw Error(`Invalid terminal element: ${b?.$type}, ${b?.$cstNode?.text}`);
}
function terminalAlternativesToRegex(b) {
	return withCardinality(b.elements.map((b) => abstractElementToRegex(b)).join("|"), {
		cardinality: b.cardinality,
		lookahead: b.lookahead,
		parenthesized: b.parenthesized,
		wrap: !1
	});
}
function terminalGroupToRegex(b) {
	return withCardinality(b.elements.map((b) => abstractElementToRegex(b)).join(""), {
		cardinality: b.cardinality,
		lookahead: b.lookahead,
		parenthesized: b.parenthesized,
		wrap: !1
	});
}
function untilTokenToRegex(b) {
	return withCardinality(`${WILDCARD}*?${abstractElementToRegex(b.terminal)}`, {
		cardinality: b.cardinality,
		lookahead: b.lookahead,
		parenthesized: b.parenthesized
	});
}
function negateTokenToRegex(b) {
	return withCardinality(`(?!${abstractElementToRegex(b.terminal)})${WILDCARD}*?`, {
		cardinality: b.cardinality,
		lookahead: b.lookahead,
		parenthesized: b.parenthesized
	});
}
function characterRangeToRegex(b) {
	return b.right ? withCardinality(`[${keywordToRegex(b.left)}-${keywordToRegex(b.right)}]`, {
		cardinality: b.cardinality,
		lookahead: b.lookahead,
		parenthesized: b.parenthesized,
		wrap: !1
	}) : withCardinality(keywordToRegex(b.left), {
		cardinality: b.cardinality,
		lookahead: b.lookahead,
		parenthesized: b.parenthesized,
		wrap: !1
	});
}
function keywordToRegex(b) {
	return escapeRegExp(b.value);
}
function withCardinality(b, Y) {
	return (Y.parenthesized || Y.lookahead || Y.wrap !== !1) && (b = `(${Y.lookahead ?? (Y.parenthesized ? "" : "?:")}${b})`), Y.cardinality ? `${b}${Y.cardinality}` : b;
}
export { findAssignment, findNameAssignment, findNodeForKeyword, findNodeForProperty, findNodesForProperty, getAllReachableRules, getCrossReferenceTerminal, getRuleType, getTypeName, grammar_utils_exports, isCommentTerminal, isDataTypeRule, terminalRegex };
