@rsconcept/domain 1.8.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/diagnostics-Clo3mkba.d.ts +279 -0
- package/dist/diagnostics-D7SiHAwW.js +518 -0
- package/dist/diagnostics-D7SiHAwW.js.map +1 -0
- package/dist/index.d.ts +6 -7
- package/dist/index.js +7 -6
- package/dist/library/diagnostics.d.ts +2 -0
- package/dist/library/diagnostics.js +2 -0
- package/dist/library/index.d.ts +3 -4
- package/dist/library/index.js +5 -5
- package/dist/library/oss-api.d.ts +1 -1
- package/dist/library/oss-api.js +1 -1
- package/dist/library/rsengine.d.ts +1 -2
- package/dist/library/rsengine.js +1 -1
- package/dist/library/rsform-api.d.ts +2 -2
- package/dist/library/rsform-api.js +1 -404
- package/dist/library/rsform.d.ts +2 -198
- package/dist/library/rsform.js.map +1 -1
- package/dist/library/rsmodel-api.d.ts +1 -2
- package/dist/library/rsmodel-api.js +2 -4
- package/dist/library/rsmodel-api.js.map +1 -1
- package/dist/library/rsmodel.d.ts +2 -46
- package/dist/library/structure-planner.d.ts +1 -1
- package/dist/library/structure-planner.js +1 -1
- package/dist/rslang/eval/evaluator.js +2 -2
- package/dist/rslang/index.js +2 -2
- package/dist/rslang/parser/syntax-errors/index.js +1 -1
- package/dist/rslang/semantic/analyzer.js +3 -3
- package/dist/rslang/semantic/arguments-extractor.js +1 -1
- package/dist/rslang/semantic/type-auditor.js +2 -2
- package/dist/rslang/semantic/value-auditor.js +2 -2
- package/dist/{syntax-errors-B6c2vsan.js → syntax-errors-D_VV5hAM.js} +3 -3
- package/dist/{syntax-errors-B6c2vsan.js.map → syntax-errors-D_VV5hAM.js.map} +1 -1
- package/package.json +5 -1
- package/src/index.ts +8 -0
- package/src/library/diagnostics.test.ts +85 -0
- package/src/library/diagnostics.ts +182 -0
- package/src/library/index.ts +8 -0
- package/src/library/oss-api.test.ts +1 -2
- package/src/library/rsform-api.ts +12 -17
- package/src/library/rsform.ts +4 -2
- package/src/library/rsmodel-api.ts +2 -8
- package/src/library/template-instantiation.test.ts +1 -2
- package/dist/library/rsform-api.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syntax-errors-B6c2vsan.js","names":["TypeClassEnum"],"sources":["../src/rslang/parser/syntax-errors/context.ts","../src/rslang/parser/syntax-errors/ast-utils.ts","../src/rslang/parser/syntax-errors/function-calls.ts","../src/rslang/parser/syntax-errors/error-builders.ts","../src/rslang/parser/syntax-errors/generators.ts","../src/rslang/parser/syntax-errors/trailing-slot.ts","../src/rslang/parser/syntax-errors/incomplete-formal.ts","../src/rslang/parser/syntax-errors/incomplete-operands.ts","../src/rslang/parser/syntax-errors/classify.ts","../src/rslang/parser/syntax-errors/expression-errors.ts","../src/rslang/parser/syntax-errors/index.ts"],"sourcesContent":["/**\n * Types for the parse-error classifier pipeline.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport type { RSErrorDescription } from '../../error';\nimport type { TypeClass } from '../../semantic/typification';\n\n/** Shared state passed through parse-error classifiers. */\nexport interface ClassifyContext {\n hasBracketErrors: boolean;\n expected?: TypeClass;\n}\n\n/** Classifies a single ERROR node into a specific syntax error, if possible. */\nexport type ErrorClassifier = (\n node: AstNode,\n expression: string,\n context: ClassifyContext\n) => RSErrorDescription | null;\n\n/** Skips classifiers that conflict with bracket-level diagnostics. */\nexport function whenNoBracketErrors(classifier: ErrorClassifier): ErrorClassifier {\n return (node, expression, context) => (context.hasBracketErrors ? null : classifier(node, expression, context));\n}\n","/**\n * AST navigation helpers and node-type predicates for classifiers.\n *\n * Predicates accept grammar terms **or** {@link TokenID} because `normalizeAST` runs before\n * error extraction and leaves `hasError` subtrees unnormalized — see `parser/README.md`.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { Filter, Function, Function_decl, Logic_quantor, Predicate } from '../parser.terms';\nimport { TokenID } from '../token';\n\nexport function findAncestor(node: AstNode, predicate: (node: AstNode) => boolean): AstNode | null {\n let current: AstNode | null = node;\n while (current !== null) {\n if (predicate(current)) {\n return current;\n }\n current = current.parent;\n }\n return null;\n}\n\nexport function findRelatedNode(node: AstNode, predicate: (target: AstNode) => boolean): AstNode | null {\n let current: AstNode | null = node.parent;\n while (current !== null) {\n for (const child of current.children) {\n if (predicate(child)) {\n return child;\n }\n }\n current = current.parent;\n }\n return null;\n}\n\nexport function isDescendantOf(ancestor: AstNode, node: AstNode): boolean {\n let current: AstNode | null = node.parent;\n while (current !== null) {\n if (current === ancestor) {\n return true;\n }\n current = current.parent;\n }\n return false;\n}\n\nexport function subtreeHasError(node: AstNode): boolean {\n if (node.typeID === TokenID.ERROR) {\n return true;\n }\n return node.children.some(subtreeHasError);\n}\n\nexport function isFunctionDeclNode(node: AstNode): boolean {\n return node.typeID === Function_decl || node.typeID === TokenID.NT_FUNC_DEFINITION;\n}\n\nexport function isQuantifierNode(node: AstNode): boolean {\n return node.typeID === Logic_quantor;\n}\n\nexport function isFunctionNode(node: AstNode): boolean {\n return node.typeID === Function || node.typeID === TokenID.ID_FUNCTION;\n}\n\nexport function isPredicateNode(node: AstNode): boolean {\n return node.typeID === Predicate || node.typeID === TokenID.ID_PREDICATE;\n}\n\nexport function isFilterNode(node: AstNode): boolean {\n return node.typeID === Filter || node.typeID === TokenID.FILTER;\n}\n\nexport function findMatchingCloseBracket(expression: string, openPos: number): number {\n if (expression[openPos] !== '[') {\n return -1;\n }\n\n let depth = 0;\n for (let index = openPos; index < expression.length; index++) {\n const symbol = expression[index];\n if (symbol === '[') {\n depth++;\n } else if (symbol === ']') {\n depth--;\n if (depth === 0) {\n return index;\n }\n }\n }\n return -1;\n}\n\nexport function getErrorOperandNode(node: AstNode): AstNode | null {\n if (node.typeID === TokenID.ERROR) {\n return node;\n }\n if (node.children.length === 1) {\n return getErrorOperandNode(node.children[0]);\n }\n return null;\n}\n\nexport function isErrorPlaceholder(node: AstNode): boolean {\n if (node.typeID === TokenID.ERROR) {\n return true;\n }\n if (node.children.length === 0) {\n return false;\n }\n return node.children.every(child => isErrorPlaceholder(child));\n}\n","/**\n * Malformed calls: `Fi…(…)`, `F1` / `P1` without or with wrong argument brackets.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\n\nimport {\n findAncestor,\n findMatchingCloseBracket,\n findRelatedNode,\n isFilterNode,\n isFunctionNode,\n isPredicateNode\n} from './ast-utils';\nimport type { ErrorClassifier } from './context';\n\nexport const classifyFilterParenMismatch: ErrorClassifier = (node, expression) => {\n const filterNode = findAncestor(node, isFilterNode) ?? findRelatedNode(node, isFilterNode);\n if (filterNode === null || expression[filterNode.to] !== '(') {\n return null;\n }\n\n return {\n code: RSErrorCode.invalidFilterSyntax,\n from: filterNode.from,\n to: filterNode.to + 1\n };\n};\n\nexport const classifyGlobalFuncCall: ErrorClassifier = (node, expression) => {\n const funcNode =\n findAncestor(node, n => isFunctionNode(n) || isPredicateNode(n)) ??\n findRelatedNode(node, n => isFunctionNode(n) || isPredicateNode(n));\n if (funcNode === null) {\n return null;\n }\n return classifyFuncWithoutArgs(funcNode, expression);\n};\n\nfunction classifyFuncWithoutArgs(funcNode: AstNode, expression: string): RSErrorDescription | null {\n const name = expression.slice(funcNode.from, funcNode.to);\n const rest = expression.slice(funcNode.to);\n\n const withoutArgs: RSErrorDescription = {\n code: RSErrorCode.globalFuncWithoutArgs,\n from: funcNode.from,\n to: funcNode.to,\n params: [name]\n };\n\n if (/^\\s*$/.test(rest)) {\n return withoutArgs;\n }\n\n if (/^\\s*\\(/.test(rest)) {\n return {\n code: RSErrorCode.globalFuncParenCall,\n from: funcNode.from,\n to: funcNode.to,\n params: [name]\n };\n }\n\n const openBracket = /^\\s*\\[/.exec(rest);\n if (openBracket === null) {\n return null;\n }\n\n const closeIdx = findMatchingCloseBracket(rest, openBracket[0].length - 1);\n if (closeIdx >= 0 && rest.slice(openBracket[0].length, closeIdx).trim().length === 0) {\n return withoutArgs;\n }\n\n return null;\n}\n","/**\n * Small helpers for building `RSErrorDescription` values from AST nodes.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { type RSErrorCode, type RSErrorDescription } from '../../error';\n\nexport function errorAt(node: AstNode, code: RSErrorCode, params?: readonly string[]): RSErrorDescription {\n return {\n code,\n from: node.from,\n to: node.to,\n params\n };\n}\n\nexport function hasOnlyClosingSuffix(expression: string, from: number): boolean {\n const suffix = expression.slice(from).trim();\n return suffix.length === 0 || /^[\\]\\)\\}]*$/.test(suffix);\n}\n\nexport function sliceText(node: AstNode, expression: string): string {\n return expression.slice(node.from, node.to);\n}\n\nexport function isTextToken(node: AstNode, expression: string, text: string): boolean {\n return sliceText(node, expression) === text;\n}\n","/**\n * Incomplete generator bodies: declarative `D{…}`, imperative `I{…}`, recursive `R{…}`.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\nimport { Declarative, Imp_blocks, Imperative, Recursion, Setexpr } from '../parser.terms';\nimport { TokenID } from '../token';\n\nimport { findAncestor, isDescendantOf, isErrorPlaceholder, subtreeHasError } from './ast-utils';\nimport type { ErrorClassifier } from './context';\nimport { errorAt, isTextToken } from './error-builders';\n\nexport const classifyIncompleteGenerator: ErrorClassifier = (node, expression) =>\n detectIncompleteDeclarative(node, expression) ??\n detectIncompleteImperative(node, expression) ??\n detectIncompleteRecursion(node, expression);\n\nexport function isIncompleteGeneratorBodyCode(code: RSErrorCode): boolean {\n return (\n code === RSErrorCode.expectedDeclarativeBody ||\n code === RSErrorCode.expectedImperativeBody ||\n code === RSErrorCode.expectedRecursiveBody\n );\n}\n\ninterface GeneratorBodySlot {\n nodes: AstNode[];\n headerEndIndex: number;\n}\n\nfunction detectIncompleteDeclarative(node: AstNode, expression: string): RSErrorDescription | null {\n const generatorNode = findAncestor(\n node,\n target => target.typeID === Declarative || target.typeID === TokenID.NT_DECLARATIVE_EXPR\n );\n return detectIncompleteGeneratorBody(node, expression, generatorNode, RSErrorCode.expectedDeclarativeBody, slot =>\n isIncompleteDeclarativeBody(slot.nodes)\n );\n}\n\nfunction detectIncompleteImperative(node: AstNode, expression: string): RSErrorDescription | null {\n const generatorNode = findAncestor(\n node,\n target => target.typeID === Imperative || target.typeID === TokenID.NT_IMPERATIVE_EXPR\n );\n return detectIncompleteGeneratorBody(node, expression, generatorNode, RSErrorCode.expectedImperativeBody, slot =>\n isIncompleteImperativeBody(slot, expression)\n );\n}\n\nfunction detectIncompleteRecursion(node: AstNode, expression: string): RSErrorDescription | null {\n const generatorNode = findAncestor(node, target => target.typeID === Recursion);\n return detectIncompleteGeneratorBody(node, expression, generatorNode, RSErrorCode.expectedRecursiveBody, slot =>\n isIncompleteRecursionBody(slot.nodes)\n );\n}\n\nfunction detectIncompleteGeneratorBody(\n node: AstNode,\n expression: string,\n generatorNode: AstNode | null,\n code: RSErrorCode,\n isIncomplete: (slot: GeneratorBodySlot) => boolean\n): RSErrorDescription | null {\n if (generatorNode === null || expression.slice(generatorNode.to).trim().length > 0) {\n return null;\n }\n\n const slot = getGeneratorBodySlot(generatorNode, expression);\n if (slot === null || !isIncomplete(slot) || !generatorHeaderIsValid(generatorNode, expression, slot.headerEndIndex)) {\n return null;\n }\n\n if (!isErrorInBodySlot(node, slot, generatorNode)) {\n return null;\n }\n\n return errorAt(node, code);\n}\n\nfunction getGeneratorBodySlot(generatorNode: AstNode, expression: string): GeneratorBodySlot | null {\n if (generatorNode.typeID === Declarative || generatorNode.typeID === TokenID.NT_DECLARATIVE_EXPR) {\n return getDeclarativeBodySlot(generatorNode, expression);\n }\n if (generatorNode.typeID === Imperative || generatorNode.typeID === TokenID.NT_IMPERATIVE_EXPR) {\n return getImperativeBodySlot(generatorNode, expression);\n }\n if (generatorNode.typeID === Recursion) {\n return getRecursionBodySlot(generatorNode, expression);\n }\n return null;\n}\n\nfunction getDeclarativeBodySlot(declNode: AstNode, expression: string): GeneratorBodySlot | null {\n const pipeIndex = declNode.children.findIndex(child => isTextToken(child, expression, '|'));\n if (pipeIndex >= 0) {\n return {\n nodes: contentChildren(declNode, expression, pipeIndex + 1),\n headerEndIndex: pipeIndex + 1\n };\n }\n\n const domainNode = findDeclarativeDomain(declNode, expression);\n if (domainNode === null) {\n return null;\n }\n\n const domainIndex = declNode.children.indexOf(domainNode);\n return {\n nodes: contentChildren(declNode, expression, domainIndex + 1),\n headerEndIndex: domainIndex + 1\n };\n}\n\nfunction getImperativeBodySlot(impNode: AstNode, expression: string): GeneratorBodySlot | null {\n const pipeIndex = impNode.children.findIndex(child => isTextToken(child, expression, '|'));\n if (pipeIndex >= 0) {\n return {\n nodes: contentChildren(impNode, expression, pipeIndex + 1),\n headerEndIndex: pipeIndex + 1\n };\n }\n\n const tupleNode = findImperativeTuple(impNode);\n if (tupleNode === null) {\n return null;\n }\n\n const tupleIndex = impNode.children.indexOf(tupleNode);\n return {\n nodes: contentChildren(impNode, expression, tupleIndex + 1),\n headerEndIndex: tupleIndex + 1\n };\n}\n\nfunction getRecursionBodySlot(recNode: AstNode, expression: string): GeneratorBodySlot | null {\n const initNode = findRecursionInit(recNode, expression);\n if (initNode === null) {\n return null;\n }\n\n const initIndex = recNode.children.indexOf(initNode);\n const afterInit = contentChildren(recNode, expression, initIndex + 1);\n const lastPipeIndex = findLastPipeIndex(afterInit, expression);\n if (lastPipeIndex < 0) {\n return {\n nodes: afterInit,\n headerEndIndex: initIndex + 1\n };\n }\n\n return {\n nodes: afterInit.slice(lastPipeIndex + 1),\n headerEndIndex: recNode.children.indexOf(afterInit[lastPipeIndex]) + 1\n };\n}\n\nfunction contentChildren(generatorNode: AstNode, expression: string, fromIndex: number): AstNode[] {\n return generatorNode.children.slice(fromIndex).filter(child => !isClosingBrace(child, expression));\n}\n\nfunction findDeclarativeDomain(declNode: AstNode, expression: string): AstNode | null {\n const inIndex = declNode.children.findIndex(child => isTextToken(child, expression, '∈'));\n if (inIndex < 0) {\n return null;\n }\n\n for (let index = inIndex + 1; index < declNode.children.length; index++) {\n const child = declNode.children[index];\n if (isTextToken(child, expression, '|') || isClosingBrace(child, expression)) {\n break;\n }\n if (child.typeID === Setexpr) {\n return child;\n }\n }\n\n return null;\n}\n\nfunction findImperativeTuple(impNode: AstNode): AstNode | null {\n for (const child of impNode.children) {\n if (child.typeID === Setexpr) {\n return child;\n }\n }\n return null;\n}\n\nfunction findLastPipeIndex(nodes: AstNode[], expression: string): number {\n for (let index = nodes.length - 1; index >= 0; index--) {\n if (isTextToken(nodes[index], expression, '|')) {\n return index;\n }\n }\n return -1;\n}\n\nfunction generatorHeaderIsValid(generatorNode: AstNode, expression: string, headerEndIndex: number): boolean {\n for (let index = 0; index < headerEndIndex; index++) {\n const child = generatorNode.children[index];\n if (child.typeID === TokenID.ERROR) {\n return false;\n }\n if (isStructuralGeneratorToken(child, expression)) {\n continue;\n }\n if (subtreeHasError(child)) {\n return false;\n }\n }\n return true;\n}\n\nfunction isIncompleteDeclarativeBody(nodes: AstNode[]): boolean {\n return isEmptyOrErrorOnlyBody(nodes);\n}\n\nfunction isIncompleteImperativeBody(slot: GeneratorBodySlot, expression: string): boolean {\n if (slot.nodes.length === 0) {\n return true;\n }\n if (slot.nodes.length === 1 && slot.nodes[0].typeID === Imp_blocks) {\n return isImpBlocksIncomplete(slot.nodes[0], expression);\n }\n return isEmptyOrErrorOnlyBody(slot.nodes);\n}\n\nfunction isIncompleteRecursionBody(nodes: AstNode[]): boolean {\n return isEmptyOrErrorOnlyBody(nodes);\n}\n\nfunction isEmptyOrErrorOnlyBody(nodes: AstNode[]): boolean {\n if (nodes.length === 0) {\n return true;\n }\n if (nodes.length === 1) {\n return isErrorPlaceholder(nodes[0]);\n }\n return false;\n}\n\nfunction isImpBlocksIncomplete(blocksNode: AstNode, expression: string): boolean {\n if (blocksNode.children.length === 0) {\n return true;\n }\n\n const lastChild = blocksNode.children[blocksNode.children.length - 1];\n if (lastChild.typeID === TokenID.ERROR) {\n return true;\n }\n if (isTextToken(lastChild, expression, ';')) {\n return true;\n }\n if (lastChild.typeID === Setexpr && isErrorPlaceholder(lastChild)) {\n return true;\n }\n\n return false;\n}\n\nfunction isErrorInBodySlot(errorNode: AstNode, slot: GeneratorBodySlot, generatorNode: AstNode): boolean {\n if (slot.nodes.length === 0) {\n const bodyStart = generatorNode.children[slot.headerEndIndex]?.from ?? generatorNode.from;\n return errorNode.from >= bodyStart;\n }\n\n for (const bodyNode of slot.nodes) {\n if (errorNode === bodyNode || isDescendantOf(bodyNode, errorNode)) {\n if (isErrorPlaceholder(bodyNode)) {\n return true;\n }\n if (bodyNode.typeID === Imp_blocks && isImpBlocksTailError(bodyNode, errorNode)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nfunction isImpBlocksTailError(blocksNode: AstNode, errorNode: AstNode): boolean {\n const lastChild = blocksNode.children[blocksNode.children.length - 1];\n return lastChild.typeID === TokenID.ERROR && (errorNode === lastChild || isDescendantOf(lastChild, errorNode));\n}\n\nfunction findRecursionInit(recursionNode: AstNode, expression: string): AstNode | null {\n const assignIndex = recursionNode.children.findIndex(child => isTextToken(child, expression, ':='));\n if (assignIndex < 0) {\n return null;\n }\n\n for (let index = assignIndex + 1; index < recursionNode.children.length; index++) {\n const child = recursionNode.children[index];\n if (child.typeID === TokenID.ERROR) {\n break;\n }\n if (isTextToken(child, expression, '|') || isClosingBrace(child, expression)) {\n continue;\n }\n return child;\n }\n\n return null;\n}\n\nfunction isClosingBrace(node: AstNode, expression: string): boolean {\n return node.typeID !== TokenID.ERROR && isTextToken(node, expression, '}');\n}\n\nfunction isStructuralGeneratorToken(node: AstNode, expression: string): boolean {\n const text = expression.slice(node.from, node.to);\n return text === '{' || text === '}' || text === '|' || text === '∈' || text === ':=' || text === ';';\n}\n","/**\n * Reusable pattern: container header is valid, last child is `ERROR`, input ends here.\n *\n * @see incomplete-formal.ts — function declarations, quantifiers\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { type RSErrorCode, type RSErrorDescription } from '../../error';\nimport { TokenID } from '../token';\n\nimport { errorAt } from './error-builders';\n\nexport function detectTrailingErrorSlot(\n node: AstNode,\n expression: string,\n container: AstNode | null,\n options: {\n headerValid?: (container: AstNode) => boolean;\n code: RSErrorCode | ((container: AstNode, bodyChild: AstNode) => RSErrorCode | null);\n }\n): RSErrorDescription | null {\n if (container === null) {\n return null;\n }\n\n const bodyChild = container.children[container.children.length - 1];\n if (bodyChild?.typeID !== TokenID.ERROR || node !== bodyChild) {\n return null;\n }\n\n if (expression.slice(container.to).trim().length > 0) {\n return null;\n }\n\n if (options.headerValid !== undefined && !options.headerValid(container)) {\n return null;\n }\n\n const code = typeof options.code === 'function' ? options.code(container, bodyChild) : options.code;\n if (code === null) {\n return null;\n }\n\n return errorAt(bodyChild, code);\n}\n","/**\n * Incomplete formal headers: `[α∈…]` declarations and quantifiers (`∀α∈…`).\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\nimport { TypeClass as TypeClassEnum } from '../../semantic/typification';\nimport { Arguments, Variable_pack } from '../parser.terms';\nimport { TokenID } from '../token';\n\nimport { findAncestor, isFunctionDeclNode, isQuantifierNode, subtreeHasError } from './ast-utils';\nimport type { ClassifyContext, ErrorClassifier } from './context';\nimport { detectTrailingErrorSlot } from './trailing-slot';\n\nexport const classifyIncompleteFormal: ErrorClassifier = (node, expression, context) =>\n detectIncompleteFunctionDecl(node, expression, context.expected) ?? detectIncompleteQuantifier(node, expression);\n\nfunction detectIncompleteFunctionDecl(\n node: AstNode,\n expression: string,\n expected?: ClassifyContext['expected']\n): RSErrorDescription | null {\n return detectTrailingErrorSlot(node, expression, findAncestor(node, isFunctionDeclNode), {\n headerValid: container => {\n const argsNode = container.children.find(\n child => child.typeID === Arguments || child.typeID === TokenID.NT_ARGUMENTS\n );\n return argsNode !== undefined && !subtreeHasError(argsNode);\n },\n code: () => incompleteFormalExpressionCode(expected)\n });\n}\n\nfunction incompleteFormalExpressionCode(expected?: ClassifyContext['expected']): RSErrorCode {\n switch (expected) {\n case TypeClassEnum.function:\n return RSErrorCode.expectedExpressionBody;\n case TypeClassEnum.predicate:\n case TypeClassEnum.logic:\n return RSErrorCode.expectedLogicBody;\n default:\n return RSErrorCode.expectedFunctionBody;\n }\n}\n\nfunction detectIncompleteQuantifier(node: AstNode, expression: string): RSErrorDescription | null {\n return detectTrailingErrorSlot(node, expression, findAncestor(node, isQuantifierNode), {\n headerValid: container => {\n const varPack = container.children.find(\n child => child.typeID === Variable_pack || child.typeID === TokenID.NT_ENUM_DECL\n );\n return varPack !== undefined && !subtreeHasError(varPack);\n },\n code: (container, bodyChild) => {\n const inIndex = container.children.findIndex(child => expression.slice(child.from, child.to) === '∈');\n if (inIndex < 0) {\n return RSErrorCode.expectedQuantifierDomain;\n }\n\n const domainChild = container.children[inIndex + 1];\n if (domainChild === undefined || domainChild === bodyChild || subtreeHasError(domainChild)) {\n return RSErrorCode.expectedQuantifierDomain;\n }\n\n return RSErrorCode.expectedQuantifierBody;\n }\n });\n}\n","/**\n * Incomplete operators and bracket argument lists (`P1[S1, ]`).\n *\n * Tuple enums `(α,β,)` intentionally stay `unknownSyntax` — not `expectedArgument`.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\nimport { Boolean, Expr_enum_min2, Logic_binary, Logic_predicates, Logic_unary, Setexpr_binary } from '../parser.terms';\nimport { TokenID } from '../token';\n\nimport {\n findAncestor,\n findMatchingCloseBracket,\n getErrorOperandNode,\n isFilterNode,\n isFunctionNode,\n isPredicateNode,\n subtreeHasError\n} from './ast-utils';\nimport type { ErrorClassifier } from './context';\nimport { errorAt, hasOnlyClosingSuffix, isTextToken, sliceText } from './error-builders';\n\nexport const classifyIncompleteOperands: ErrorClassifier = (node, expression) =>\n detectIncompleteUnaryOperand(node, expression) ??\n detectIncompleteBinaryOperand(node, expression) ??\n detectIncompleteBooleanOperand(node, expression);\n\nexport const classifyIncompleteArgument: ErrorClassifier = (node, expression) =>\n detectIncompleteArgument(node, expression);\n\nfunction detectIncompleteUnaryOperand(node: AstNode, expression: string): RSErrorDescription | null {\n const unaryNode = findAncestor(node, target => target.typeID === Logic_unary);\n if (unaryNode === null) {\n return null;\n }\n\n const operandChild = unaryNode.children[unaryNode.children.length - 1];\n if (operandChild?.typeID !== TokenID.ERROR || node !== operandChild) {\n return null;\n }\n\n if (expression.slice(unaryNode.to).trim().length > 0) {\n return null;\n }\n\n const operatorChild = unaryNode.children[0];\n if (operatorChild === undefined || operatorChild === operandChild) {\n return null;\n }\n\n if (!isTextToken(operatorChild, expression, '¬')) {\n return null;\n }\n\n return errorAt(operandChild, RSErrorCode.expectedUnaryOperand);\n}\n\nfunction detectIncompleteBinaryOperand(node: AstNode, expression: string): RSErrorDescription | null {\n const binaryNode = findAncestor(\n node,\n target => target.typeID === Logic_binary || target.typeID === Logic_predicates || target.typeID === Setexpr_binary\n );\n if (binaryNode === null) {\n return null;\n }\n\n const operandChild = binaryNode.children[binaryNode.children.length - 1];\n if (operandChild?.typeID !== TokenID.ERROR || node !== operandChild) {\n return null;\n }\n\n if (expression.slice(binaryNode.to).trim().length > 0) {\n return null;\n }\n\n if (binaryNode.children.length < 2) {\n return null;\n }\n\n const leftChild = binaryNode.children[0];\n if (leftChild.typeID === TokenID.ERROR || subtreeHasError(leftChild)) {\n return null;\n }\n\n return errorAt(operandChild, RSErrorCode.expectedRightOperand);\n}\n\nfunction detectIncompleteBooleanOperand(node: AstNode, expression: string): RSErrorDescription | null {\n const booleanNode = findAncestor(node, target => target.typeID === Boolean);\n if (booleanNode === null) {\n return null;\n }\n\n const operandChild = findTrailingErrorOperand(booleanNode, expression);\n if (operandChild === null || node !== operandChild) {\n return null;\n }\n\n if (!hasOnlyClosingSuffix(expression, booleanNode.to)) {\n return null;\n }\n\n return errorAt(operandChild, RSErrorCode.expectedRightOperand);\n}\n\nfunction detectIncompleteArgument(node: AstNode, expression: string): RSErrorDescription | null {\n const enumNode = findAncestor(node, target => target.typeID === Expr_enum_min2);\n if (enumNode === null || !isLegitimateArgumentEnum(enumNode, expression)) {\n return null;\n }\n\n const errorChild = findEnumArgumentError(enumNode, expression);\n if (errorChild === null || node !== errorChild || !hasOnlyClosingSuffix(expression, enumNode.to)) {\n return null;\n }\n\n return errorAt(errorChild, RSErrorCode.expectedArgument);\n}\n\nfunction findTrailingErrorOperand(container: AstNode, expression: string): AstNode | null {\n for (let index = container.children.length - 1; index >= 0; index--) {\n const child = container.children[index];\n if (isOperandStructuralToken(child, expression)) {\n continue;\n }\n return getErrorOperandNode(child);\n }\n return null;\n}\n\nfunction findEnumArgumentError(enumNode: AstNode, expression: string): AstNode | null {\n for (const child of enumNode.children) {\n if (isTextToken(child, expression, ',')) {\n continue;\n }\n const errorNode = getErrorOperandNode(child);\n if (errorNode !== null) {\n return errorNode;\n }\n }\n return null;\n}\n\nfunction isOperandStructuralToken(node: AstNode, expression: string): boolean {\n const text = sliceText(node, expression);\n return text === '(' || text === ')' || text === 'ℬ';\n}\n\nfunction isLegitimateArgumentEnum(enumNode: AstNode, expression: string): boolean {\n return findCallHostForBracketEnum(enumNode, expression) !== null;\n}\n\nfunction findCallHostForBracketEnum(enumNode: AstNode, expression: string): AstNode | null {\n let current: AstNode | null = enumNode.parent;\n while (current !== null) {\n for (const child of current.children) {\n if (!isFunctionNode(child) && !isPredicateNode(child) && !isFilterNode(child)) {\n continue;\n }\n if (isEnumInCallBrackets(enumNode, child, expression)) {\n return child;\n }\n }\n current = current.parent;\n }\n return null;\n}\n\nfunction isEnumInCallBrackets(enumNode: AstNode, nameNode: AstNode, expression: string): boolean {\n const rest = expression.slice(nameNode.to);\n const match = /^\\s*\\[/.exec(rest);\n if (match === null) {\n return false;\n }\n\n const openPos = nameNode.to + match.index + match[0].indexOf('[');\n const closePos = findMatchingCloseBracket(expression, openPos);\n if (closePos < 0) {\n return false;\n }\n\n return enumNode.from > openPos && enumNode.from < closePos;\n}\n","/**\n * Parse-error classification: ordered classifier chain and P8400 fallback.\n *\n * Register new detectors in `PARSE_ERROR_CLASSIFIERS` (first match wins).\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\nimport { Variable } from '../parser.terms';\n\nimport type { ClassifyContext, ErrorClassifier } from './context';\nimport { whenNoBracketErrors } from './context';\nimport { classifyFilterParenMismatch, classifyGlobalFuncCall } from './function-calls';\nimport { classifyIncompleteGenerator } from './generators';\nimport { classifyIncompleteFormal } from './incomplete-formal';\nimport { classifyIncompleteArgument, classifyIncompleteOperands } from './incomplete-operands';\n\nconst PARSE_ERROR_CLASSIFIERS: ErrorClassifier[] = [\n classifyIncompleteFormal,\n classifyIncompleteGenerator,\n whenNoBracketErrors(classifyIncompleteOperands),\n whenNoBracketErrors(classifyIncompleteArgument),\n classifyFilterParenMismatch,\n classifyGlobalFuncCall\n];\n\n/** Runs classifiers in priority order for one `ERROR` AST node. */\nexport function classifyParseError(\n node: AstNode,\n expression: string,\n context: ClassifyContext\n): RSErrorDescription | null {\n for (const classifier of PARSE_ERROR_CLASSIFIERS) {\n const error = classifier(node, expression, context);\n if (error !== null) {\n return error;\n }\n }\n return null;\n}\n\n/** Default rules when no classifier matched: `expectedLocal` or `unknownSyntax`. */\nexport function classifyFallbackError(\n node: AstNode,\n ignoreUnknownErrors: boolean\n): { error: RSErrorDescription; target: AstNode } | null {\n const parent = node.parent;\n if (parent === null) {\n return ignoreUnknownErrors\n ? null\n : { error: { code: RSErrorCode.unknownSyntax, from: node.from, to: node.to }, target: node };\n }\n\n if (parent.typeID === Variable) {\n return {\n error: { code: RSErrorCode.expectedLocal, from: parent.from, to: parent.to },\n target: parent\n };\n }\n\n if (ignoreUnknownErrors) {\n return null;\n }\n\n const target = node.from === node.to ? parent : node;\n return {\n error: { code: RSErrorCode.unknownSyntax, from: target.from, to: target.to },\n target\n };\n}\n","/**\n * Expression-level diagnostics (no AST required) and error deduplication.\n */\n\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\n\ntype OpenBracket = '(' | '[' | '{';\ntype CloseBracket = ')' | ']' | '}';\n\ninterface BracketFrame {\n bracket: OpenBracket;\n index: number;\n}\n\nconst ALLOWED_EXPRESSION_CHARS = buildAllowedExpressionChars();\n\nfunction buildAllowedExpressionChars(): Set<string> {\n const chars = new Set<string>();\n for (const chunk of [\n '0123456789',\n 'abcdefghijklmnopqrstuvwxyz',\n 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n '_',\n '∅¬∀∃⇔⇒∨&ℬ+-*∪\\\\∆∩×∈∉⊆⊄⊂<>≥≤≠=,:;|[]{}()'\n ]) {\n for (const symbol of chunk) {\n chars.add(symbol);\n }\n }\n for (let codePoint = 0x03b1; codePoint <= 0x03c9; codePoint++) {\n chars.add(String.fromCodePoint(codePoint));\n }\n return chars;\n}\n\nfunction isAllowedExpressionChar(symbol: string): boolean {\n return /\\s/u.test(symbol) || ALLOWED_EXPRESSION_CHARS.has(symbol);\n}\n\nexport function extractForbiddenCharacterErrors(expression: string): RSErrorDescription[] {\n const errors: RSErrorDescription[] = [];\n let pos = 0;\n\n while (pos < expression.length) {\n const symbol = expression[pos];\n if (isAllowedExpressionChar(symbol)) {\n pos++;\n continue;\n }\n\n const start = pos;\n while (pos < expression.length && !isAllowedExpressionChar(expression[pos])) {\n pos++;\n }\n\n errors.push({\n code: RSErrorCode.forbiddenCharacter,\n from: start,\n to: pos,\n params: [expression.slice(start, pos)]\n });\n }\n\n return errors;\n}\n\nexport function extractBracketErrors(expression: string): RSErrorDescription | null {\n const stack: BracketFrame[] = [];\n\n for (let pos = 0; pos < expression.length; pos++) {\n const symbol = expression[pos];\n if (isOpenBracket(symbol)) {\n if (isDoubleParenthesis(expression, pos)) {\n return {\n code: RSErrorCode.doubleParenthesis,\n from: pos,\n to: pos + 2\n };\n }\n stack.push({ bracket: symbol, index: pos });\n continue;\n }\n if (!isCloseBracket(symbol)) {\n continue;\n }\n\n const expectedOpen = closeToOpen(symbol);\n const top = stack[stack.length - 1];\n if (top === undefined) {\n return {\n code: RSErrorCode.missingOpenBracket,\n from: pos,\n to: pos + 1,\n params: [symbol]\n };\n }\n\n if (top.bracket !== expectedOpen) {\n return {\n code: RSErrorCode.bracketMismatch,\n from: pos,\n to: pos + 1,\n params: [openToClose(top.bracket), symbol]\n };\n }\n\n stack.pop();\n }\n\n if (stack.length > 0) {\n const unclosed = stack[0];\n return {\n code: RSErrorCode.missingCloseBracket,\n from: unclosed.index,\n to: unclosed.index + 1,\n params: [unclosed.bracket]\n };\n }\n\n return null;\n}\n\nexport function deduplicateErrors(errors: RSErrorDescription[]): RSErrorDescription[] {\n const result: RSErrorDescription[] = [];\n\n for (const error of errors) {\n const duplicate = result.some(\n existing => existing.code === error.code && existing.from === error.from && existing.to === error.to\n );\n if (duplicate) {\n continue;\n }\n\n if (error.code !== RSErrorCode.unknownSyntax) {\n for (let index = result.length - 1; index >= 0; index--) {\n if (result[index].code === RSErrorCode.unknownSyntax && rangesOverlap(error, result[index])) {\n result.splice(index, 1);\n }\n }\n }\n\n result.push(error);\n }\n\n const specific = result.filter(error => error.code !== RSErrorCode.unknownSyntax);\n const filtered = result.filter(\n error => error.code !== RSErrorCode.unknownSyntax || !specific.some(item => rangesOverlap(error, item))\n );\n return collapseContiguousUnknownSyntax(filtered);\n}\n\nfunction collapseContiguousUnknownSyntax(errors: RSErrorDescription[]): RSErrorDescription[] {\n const unknowns = errors.filter(error => error.code === RSErrorCode.unknownSyntax);\n if (unknowns.length <= 1) {\n return errors;\n }\n\n const others = errors.filter(error => error.code !== RSErrorCode.unknownSyntax);\n const sorted = [...unknowns].sort((left, right) => left.from - right.from || left.to - right.to);\n const merged: RSErrorDescription[] = [];\n\n let clusterFrom = sorted[0].from;\n let clusterTo = sorted[0].to;\n\n for (let index = 1; index < sorted.length; index++) {\n const error = sorted[index];\n if (error.from <= clusterTo) {\n clusterTo = Math.max(clusterTo, error.to);\n continue;\n }\n\n merged.push({ code: RSErrorCode.unknownSyntax, from: clusterFrom, to: clusterTo });\n clusterFrom = error.from;\n clusterTo = error.to;\n }\n\n merged.push({ code: RSErrorCode.unknownSyntax, from: clusterFrom, to: clusterTo });\n return [...others, ...merged];\n}\n\nfunction rangesOverlap(a: RSErrorDescription, b: RSErrorDescription): boolean {\n return a.from < b.to && b.from < a.to;\n}\n\nfunction isOpenBracket(symbol: string): symbol is OpenBracket {\n return symbol === '(' || symbol === '[' || symbol === '{';\n}\n\nfunction isCloseBracket(symbol: string): symbol is CloseBracket {\n return symbol === ')' || symbol === ']' || symbol === '}';\n}\n\nfunction openToClose(symbol: OpenBracket): CloseBracket {\n switch (symbol) {\n case '(':\n return ')';\n case '[':\n return ']';\n case '{':\n return '}';\n }\n}\n\nfunction closeToOpen(symbol: CloseBracket): OpenBracket {\n switch (symbol) {\n case ')':\n return '(';\n case ']':\n return '[';\n case '}':\n return '{';\n }\n}\n\nfunction isDoubleParenthesis(expression: string, pos: number): boolean {\n const isOpenDoubleParenthesis = expression[pos] === '(' && expression[pos + 1] === '(';\n if (!isOpenDoubleParenthesis) {\n return false;\n }\n\n const outerClose = findMatchingCloseParen(expression, pos);\n const innerClose = findMatchingCloseParen(expression, pos + 1);\n if (outerClose < 0 || innerClose < 0) {\n return false;\n }\n\n return outerClose === innerClose + 1;\n}\n\nfunction findMatchingCloseParen(expression: string, openPos: number): number {\n if (expression[openPos] !== '(') {\n return -1;\n }\n\n const stack: OpenBracket[] = [];\n for (let index = openPos; index < expression.length; index++) {\n const symbol = expression[index];\n if (isOpenBracket(symbol)) {\n stack.push(symbol);\n continue;\n }\n if (!isCloseBracket(symbol)) {\n continue;\n }\n const top = stack.pop();\n if (top === undefined || top !== closeToOpen(symbol)) {\n return -1;\n }\n if (stack.length === 0) {\n return index;\n }\n }\n\n return -1;\n}\n","/**\n * Syntax error extraction — public entry point.\n *\n * Expects the **hybrid** AST produced by `normalizeAST` (see `parser/README.md`).\n * See {@link ./README.md} for pipeline, classifier order, and how to add codes.\n *\n * @module rslang/parser/syntax-errors\n */\n\nimport { type AstNode, visitAstDFS } from '../../../parsing';\nimport { annotateError } from '../../ast-annotations';\nimport { type ErrorReporter, type RSErrorDescription } from '../../error';\nimport { type TypeClass } from '../../semantic/typification';\nimport { TokenID } from '../token';\n\nimport { classifyFallbackError, classifyParseError } from './classify';\nimport { deduplicateErrors, extractBracketErrors, extractForbiddenCharacterErrors } from './expression-errors';\nimport { isIncompleteGeneratorBodyCode } from './generators';\n\n/** When set, refines incomplete formal headers (`[α∈…]`, quantifiers). */\nexport interface SyntaxErrorOptions {\n expected?: TypeClass;\n}\n\n/**\n * Walks a parsed expression AST and reports syntax errors.\n *\n * @param ast - Root AST from `buildTree(parser.parse(expression))`\n * @param expression - Original source text (positions must match the AST)\n * @param reporter - Called once per deduplicated error\n * @param annotateErrors - When true, also calls `annotateError` on the triggering node\n * @param options - Optional typification hint for incomplete formal expressions\n */\nexport function extractSyntaxErrors(\n ast: AstNode,\n expression: string,\n reporter: ErrorReporter,\n annotateErrors: boolean = false,\n options?: SyntaxErrorOptions\n) {\n const forbiddenErrors = extractForbiddenCharacterErrors(expression);\n if (forbiddenErrors.length > 0) {\n for (const error of forbiddenErrors) {\n reporter(error);\n if (annotateErrors) {\n annotateError(ast, error.code, error.params);\n }\n }\n return;\n }\n\n const collected: RSErrorDescription[] = [];\n const annotationTargets = new Map<RSErrorDescription, AstNode>();\n const collect = (error: RSErrorDescription, target?: AstNode) => {\n collected.push(error);\n if (target !== undefined) {\n annotationTargets.set(error, target);\n }\n };\n\n const bracketError = extractBracketErrors(expression);\n const hasBracketErrors = bracketError !== null;\n visitAstDFS(ast, node => extractInternal(node, expression, collect, hasBracketErrors, options));\n\n const hasIncompleteGenerator = collected.some(error => isIncompleteGeneratorBodyCode(error.code));\n if (bracketError !== null && !hasIncompleteGenerator) {\n collect(bracketError, ast);\n }\n\n for (const error of deduplicateErrors(collected)) {\n reporter(error);\n if (annotateErrors) {\n const target = annotationTargets.get(error);\n if (target !== undefined) {\n annotateError(target, error.code, error.params);\n }\n }\n }\n}\n\nfunction extractInternal(\n node: AstNode,\n expression: string,\n collect: (error: RSErrorDescription, target: AstNode) => void,\n hasBracketErrors: boolean,\n options?: SyntaxErrorOptions\n) {\n if (node.typeID !== TokenID.ERROR) {\n return;\n }\n\n const classified = classifyParseError(node, expression, { hasBracketErrors, expected: options?.expected });\n if (classified !== null) {\n collect(classified, node);\n return;\n }\n\n const fallback = classifyFallbackError(node, hasBracketErrors);\n if (fallback !== null) {\n collect(fallback.error, fallback.target);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"syntax-errors-D_VV5hAM.js","names":["TypeClassEnum"],"sources":["../src/rslang/parser/syntax-errors/context.ts","../src/rslang/parser/syntax-errors/ast-utils.ts","../src/rslang/parser/syntax-errors/function-calls.ts","../src/rslang/parser/syntax-errors/error-builders.ts","../src/rslang/parser/syntax-errors/generators.ts","../src/rslang/parser/syntax-errors/trailing-slot.ts","../src/rslang/parser/syntax-errors/incomplete-formal.ts","../src/rslang/parser/syntax-errors/incomplete-operands.ts","../src/rslang/parser/syntax-errors/classify.ts","../src/rslang/parser/syntax-errors/expression-errors.ts","../src/rslang/parser/syntax-errors/index.ts"],"sourcesContent":["/**\n * Types for the parse-error classifier pipeline.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport type { RSErrorDescription } from '../../error';\nimport type { TypeClass } from '../../semantic/typification';\n\n/** Shared state passed through parse-error classifiers. */\nexport interface ClassifyContext {\n hasBracketErrors: boolean;\n expected?: TypeClass;\n}\n\n/** Classifies a single ERROR node into a specific syntax error, if possible. */\nexport type ErrorClassifier = (\n node: AstNode,\n expression: string,\n context: ClassifyContext\n) => RSErrorDescription | null;\n\n/** Skips classifiers that conflict with bracket-level diagnostics. */\nexport function whenNoBracketErrors(classifier: ErrorClassifier): ErrorClassifier {\n return (node, expression, context) => (context.hasBracketErrors ? null : classifier(node, expression, context));\n}\n","/**\n * AST navigation helpers and node-type predicates for classifiers.\n *\n * Predicates accept grammar terms **or** {@link TokenID} because `normalizeAST` runs before\n * error extraction and leaves `hasError` subtrees unnormalized — see `parser/README.md`.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { Filter, Function, Function_decl, Logic_quantor, Predicate } from '../parser.terms';\nimport { TokenID } from '../token';\n\nexport function findAncestor(node: AstNode, predicate: (node: AstNode) => boolean): AstNode | null {\n let current: AstNode | null = node;\n while (current !== null) {\n if (predicate(current)) {\n return current;\n }\n current = current.parent;\n }\n return null;\n}\n\nexport function findRelatedNode(node: AstNode, predicate: (target: AstNode) => boolean): AstNode | null {\n let current: AstNode | null = node.parent;\n while (current !== null) {\n for (const child of current.children) {\n if (predicate(child)) {\n return child;\n }\n }\n current = current.parent;\n }\n return null;\n}\n\nexport function isDescendantOf(ancestor: AstNode, node: AstNode): boolean {\n let current: AstNode | null = node.parent;\n while (current !== null) {\n if (current === ancestor) {\n return true;\n }\n current = current.parent;\n }\n return false;\n}\n\nexport function subtreeHasError(node: AstNode): boolean {\n if (node.typeID === TokenID.ERROR) {\n return true;\n }\n return node.children.some(subtreeHasError);\n}\n\nexport function isFunctionDeclNode(node: AstNode): boolean {\n return node.typeID === Function_decl || node.typeID === TokenID.NT_FUNC_DEFINITION;\n}\n\nexport function isQuantifierNode(node: AstNode): boolean {\n return node.typeID === Logic_quantor;\n}\n\nexport function isFunctionNode(node: AstNode): boolean {\n return node.typeID === Function || node.typeID === TokenID.ID_FUNCTION;\n}\n\nexport function isPredicateNode(node: AstNode): boolean {\n return node.typeID === Predicate || node.typeID === TokenID.ID_PREDICATE;\n}\n\nexport function isFilterNode(node: AstNode): boolean {\n return node.typeID === Filter || node.typeID === TokenID.FILTER;\n}\n\nexport function findMatchingCloseBracket(expression: string, openPos: number): number {\n if (expression[openPos] !== '[') {\n return -1;\n }\n\n let depth = 0;\n for (let index = openPos; index < expression.length; index++) {\n const symbol = expression[index];\n if (symbol === '[') {\n depth++;\n } else if (symbol === ']') {\n depth--;\n if (depth === 0) {\n return index;\n }\n }\n }\n return -1;\n}\n\nexport function getErrorOperandNode(node: AstNode): AstNode | null {\n if (node.typeID === TokenID.ERROR) {\n return node;\n }\n if (node.children.length === 1) {\n return getErrorOperandNode(node.children[0]);\n }\n return null;\n}\n\nexport function isErrorPlaceholder(node: AstNode): boolean {\n if (node.typeID === TokenID.ERROR) {\n return true;\n }\n if (node.children.length === 0) {\n return false;\n }\n return node.children.every(child => isErrorPlaceholder(child));\n}\n","/**\n * Malformed calls: `Fi…(…)`, `F1` / `P1` without or with wrong argument brackets.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\n\nimport {\n findAncestor,\n findMatchingCloseBracket,\n findRelatedNode,\n isFilterNode,\n isFunctionNode,\n isPredicateNode\n} from './ast-utils';\nimport type { ErrorClassifier } from './context';\n\nexport const classifyFilterParenMismatch: ErrorClassifier = (node, expression) => {\n const filterNode = findAncestor(node, isFilterNode) ?? findRelatedNode(node, isFilterNode);\n if (filterNode === null || expression[filterNode.to] !== '(') {\n return null;\n }\n\n return {\n code: RSErrorCode.invalidFilterSyntax,\n from: filterNode.from,\n to: filterNode.to + 1\n };\n};\n\nexport const classifyGlobalFuncCall: ErrorClassifier = (node, expression) => {\n const funcNode =\n findAncestor(node, n => isFunctionNode(n) || isPredicateNode(n)) ??\n findRelatedNode(node, n => isFunctionNode(n) || isPredicateNode(n));\n if (funcNode === null) {\n return null;\n }\n return classifyFuncWithoutArgs(funcNode, expression);\n};\n\nfunction classifyFuncWithoutArgs(funcNode: AstNode, expression: string): RSErrorDescription | null {\n const name = expression.slice(funcNode.from, funcNode.to);\n const rest = expression.slice(funcNode.to);\n\n const withoutArgs: RSErrorDescription = {\n code: RSErrorCode.globalFuncWithoutArgs,\n from: funcNode.from,\n to: funcNode.to,\n params: [name]\n };\n\n if (/^\\s*$/.test(rest)) {\n return withoutArgs;\n }\n\n if (/^\\s*\\(/.test(rest)) {\n return {\n code: RSErrorCode.globalFuncParenCall,\n from: funcNode.from,\n to: funcNode.to,\n params: [name]\n };\n }\n\n const openBracket = /^\\s*\\[/.exec(rest);\n if (openBracket === null) {\n return null;\n }\n\n const closeIdx = findMatchingCloseBracket(rest, openBracket[0].length - 1);\n if (closeIdx >= 0 && rest.slice(openBracket[0].length, closeIdx).trim().length === 0) {\n return withoutArgs;\n }\n\n return null;\n}\n","/**\n * Small helpers for building `RSErrorDescription` values from AST nodes.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { type RSErrorCode, type RSErrorDescription } from '../../error';\n\nexport function errorAt(node: AstNode, code: RSErrorCode, params?: readonly string[]): RSErrorDescription {\n return {\n code,\n from: node.from,\n to: node.to,\n params\n };\n}\n\nexport function hasOnlyClosingSuffix(expression: string, from: number): boolean {\n const suffix = expression.slice(from).trim();\n return suffix.length === 0 || /^[\\]\\)\\}]*$/.test(suffix);\n}\n\nexport function sliceText(node: AstNode, expression: string): string {\n return expression.slice(node.from, node.to);\n}\n\nexport function isTextToken(node: AstNode, expression: string, text: string): boolean {\n return sliceText(node, expression) === text;\n}\n","/**\n * Incomplete generator bodies: declarative `D{…}`, imperative `I{…}`, recursive `R{…}`.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\nimport { Declarative, Imp_blocks, Imperative, Recursion, Setexpr } from '../parser.terms';\nimport { TokenID } from '../token';\n\nimport { findAncestor, isDescendantOf, isErrorPlaceholder, subtreeHasError } from './ast-utils';\nimport type { ErrorClassifier } from './context';\nimport { errorAt, isTextToken } from './error-builders';\n\nexport const classifyIncompleteGenerator: ErrorClassifier = (node, expression) =>\n detectIncompleteDeclarative(node, expression) ??\n detectIncompleteImperative(node, expression) ??\n detectIncompleteRecursion(node, expression);\n\nexport function isIncompleteGeneratorBodyCode(code: RSErrorCode): boolean {\n return (\n code === RSErrorCode.expectedDeclarativeBody ||\n code === RSErrorCode.expectedImperativeBody ||\n code === RSErrorCode.expectedRecursiveBody\n );\n}\n\ninterface GeneratorBodySlot {\n nodes: AstNode[];\n headerEndIndex: number;\n}\n\nfunction detectIncompleteDeclarative(node: AstNode, expression: string): RSErrorDescription | null {\n const generatorNode = findAncestor(\n node,\n target => target.typeID === Declarative || target.typeID === TokenID.NT_DECLARATIVE_EXPR\n );\n return detectIncompleteGeneratorBody(node, expression, generatorNode, RSErrorCode.expectedDeclarativeBody, slot =>\n isIncompleteDeclarativeBody(slot.nodes)\n );\n}\n\nfunction detectIncompleteImperative(node: AstNode, expression: string): RSErrorDescription | null {\n const generatorNode = findAncestor(\n node,\n target => target.typeID === Imperative || target.typeID === TokenID.NT_IMPERATIVE_EXPR\n );\n return detectIncompleteGeneratorBody(node, expression, generatorNode, RSErrorCode.expectedImperativeBody, slot =>\n isIncompleteImperativeBody(slot, expression)\n );\n}\n\nfunction detectIncompleteRecursion(node: AstNode, expression: string): RSErrorDescription | null {\n const generatorNode = findAncestor(node, target => target.typeID === Recursion);\n return detectIncompleteGeneratorBody(node, expression, generatorNode, RSErrorCode.expectedRecursiveBody, slot =>\n isIncompleteRecursionBody(slot.nodes)\n );\n}\n\nfunction detectIncompleteGeneratorBody(\n node: AstNode,\n expression: string,\n generatorNode: AstNode | null,\n code: RSErrorCode,\n isIncomplete: (slot: GeneratorBodySlot) => boolean\n): RSErrorDescription | null {\n if (generatorNode === null || expression.slice(generatorNode.to).trim().length > 0) {\n return null;\n }\n\n const slot = getGeneratorBodySlot(generatorNode, expression);\n if (slot === null || !isIncomplete(slot) || !generatorHeaderIsValid(generatorNode, expression, slot.headerEndIndex)) {\n return null;\n }\n\n if (!isErrorInBodySlot(node, slot, generatorNode)) {\n return null;\n }\n\n return errorAt(node, code);\n}\n\nfunction getGeneratorBodySlot(generatorNode: AstNode, expression: string): GeneratorBodySlot | null {\n if (generatorNode.typeID === Declarative || generatorNode.typeID === TokenID.NT_DECLARATIVE_EXPR) {\n return getDeclarativeBodySlot(generatorNode, expression);\n }\n if (generatorNode.typeID === Imperative || generatorNode.typeID === TokenID.NT_IMPERATIVE_EXPR) {\n return getImperativeBodySlot(generatorNode, expression);\n }\n if (generatorNode.typeID === Recursion) {\n return getRecursionBodySlot(generatorNode, expression);\n }\n return null;\n}\n\nfunction getDeclarativeBodySlot(declNode: AstNode, expression: string): GeneratorBodySlot | null {\n const pipeIndex = declNode.children.findIndex(child => isTextToken(child, expression, '|'));\n if (pipeIndex >= 0) {\n return {\n nodes: contentChildren(declNode, expression, pipeIndex + 1),\n headerEndIndex: pipeIndex + 1\n };\n }\n\n const domainNode = findDeclarativeDomain(declNode, expression);\n if (domainNode === null) {\n return null;\n }\n\n const domainIndex = declNode.children.indexOf(domainNode);\n return {\n nodes: contentChildren(declNode, expression, domainIndex + 1),\n headerEndIndex: domainIndex + 1\n };\n}\n\nfunction getImperativeBodySlot(impNode: AstNode, expression: string): GeneratorBodySlot | null {\n const pipeIndex = impNode.children.findIndex(child => isTextToken(child, expression, '|'));\n if (pipeIndex >= 0) {\n return {\n nodes: contentChildren(impNode, expression, pipeIndex + 1),\n headerEndIndex: pipeIndex + 1\n };\n }\n\n const tupleNode = findImperativeTuple(impNode);\n if (tupleNode === null) {\n return null;\n }\n\n const tupleIndex = impNode.children.indexOf(tupleNode);\n return {\n nodes: contentChildren(impNode, expression, tupleIndex + 1),\n headerEndIndex: tupleIndex + 1\n };\n}\n\nfunction getRecursionBodySlot(recNode: AstNode, expression: string): GeneratorBodySlot | null {\n const initNode = findRecursionInit(recNode, expression);\n if (initNode === null) {\n return null;\n }\n\n const initIndex = recNode.children.indexOf(initNode);\n const afterInit = contentChildren(recNode, expression, initIndex + 1);\n const lastPipeIndex = findLastPipeIndex(afterInit, expression);\n if (lastPipeIndex < 0) {\n return {\n nodes: afterInit,\n headerEndIndex: initIndex + 1\n };\n }\n\n return {\n nodes: afterInit.slice(lastPipeIndex + 1),\n headerEndIndex: recNode.children.indexOf(afterInit[lastPipeIndex]) + 1\n };\n}\n\nfunction contentChildren(generatorNode: AstNode, expression: string, fromIndex: number): AstNode[] {\n return generatorNode.children.slice(fromIndex).filter(child => !isClosingBrace(child, expression));\n}\n\nfunction findDeclarativeDomain(declNode: AstNode, expression: string): AstNode | null {\n const inIndex = declNode.children.findIndex(child => isTextToken(child, expression, '∈'));\n if (inIndex < 0) {\n return null;\n }\n\n for (let index = inIndex + 1; index < declNode.children.length; index++) {\n const child = declNode.children[index];\n if (isTextToken(child, expression, '|') || isClosingBrace(child, expression)) {\n break;\n }\n if (child.typeID === Setexpr) {\n return child;\n }\n }\n\n return null;\n}\n\nfunction findImperativeTuple(impNode: AstNode): AstNode | null {\n for (const child of impNode.children) {\n if (child.typeID === Setexpr) {\n return child;\n }\n }\n return null;\n}\n\nfunction findLastPipeIndex(nodes: AstNode[], expression: string): number {\n for (let index = nodes.length - 1; index >= 0; index--) {\n if (isTextToken(nodes[index], expression, '|')) {\n return index;\n }\n }\n return -1;\n}\n\nfunction generatorHeaderIsValid(generatorNode: AstNode, expression: string, headerEndIndex: number): boolean {\n for (let index = 0; index < headerEndIndex; index++) {\n const child = generatorNode.children[index];\n if (child.typeID === TokenID.ERROR) {\n return false;\n }\n if (isStructuralGeneratorToken(child, expression)) {\n continue;\n }\n if (subtreeHasError(child)) {\n return false;\n }\n }\n return true;\n}\n\nfunction isIncompleteDeclarativeBody(nodes: AstNode[]): boolean {\n return isEmptyOrErrorOnlyBody(nodes);\n}\n\nfunction isIncompleteImperativeBody(slot: GeneratorBodySlot, expression: string): boolean {\n if (slot.nodes.length === 0) {\n return true;\n }\n if (slot.nodes.length === 1 && slot.nodes[0].typeID === Imp_blocks) {\n return isImpBlocksIncomplete(slot.nodes[0], expression);\n }\n return isEmptyOrErrorOnlyBody(slot.nodes);\n}\n\nfunction isIncompleteRecursionBody(nodes: AstNode[]): boolean {\n return isEmptyOrErrorOnlyBody(nodes);\n}\n\nfunction isEmptyOrErrorOnlyBody(nodes: AstNode[]): boolean {\n if (nodes.length === 0) {\n return true;\n }\n if (nodes.length === 1) {\n return isErrorPlaceholder(nodes[0]);\n }\n return false;\n}\n\nfunction isImpBlocksIncomplete(blocksNode: AstNode, expression: string): boolean {\n if (blocksNode.children.length === 0) {\n return true;\n }\n\n const lastChild = blocksNode.children[blocksNode.children.length - 1];\n if (lastChild.typeID === TokenID.ERROR) {\n return true;\n }\n if (isTextToken(lastChild, expression, ';')) {\n return true;\n }\n if (lastChild.typeID === Setexpr && isErrorPlaceholder(lastChild)) {\n return true;\n }\n\n return false;\n}\n\nfunction isErrorInBodySlot(errorNode: AstNode, slot: GeneratorBodySlot, generatorNode: AstNode): boolean {\n if (slot.nodes.length === 0) {\n const bodyStart = generatorNode.children[slot.headerEndIndex]?.from ?? generatorNode.from;\n return errorNode.from >= bodyStart;\n }\n\n for (const bodyNode of slot.nodes) {\n if (errorNode === bodyNode || isDescendantOf(bodyNode, errorNode)) {\n if (isErrorPlaceholder(bodyNode)) {\n return true;\n }\n if (bodyNode.typeID === Imp_blocks && isImpBlocksTailError(bodyNode, errorNode)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nfunction isImpBlocksTailError(blocksNode: AstNode, errorNode: AstNode): boolean {\n const lastChild = blocksNode.children[blocksNode.children.length - 1];\n return lastChild.typeID === TokenID.ERROR && (errorNode === lastChild || isDescendantOf(lastChild, errorNode));\n}\n\nfunction findRecursionInit(recursionNode: AstNode, expression: string): AstNode | null {\n const assignIndex = recursionNode.children.findIndex(child => isTextToken(child, expression, ':='));\n if (assignIndex < 0) {\n return null;\n }\n\n for (let index = assignIndex + 1; index < recursionNode.children.length; index++) {\n const child = recursionNode.children[index];\n if (child.typeID === TokenID.ERROR) {\n break;\n }\n if (isTextToken(child, expression, '|') || isClosingBrace(child, expression)) {\n continue;\n }\n return child;\n }\n\n return null;\n}\n\nfunction isClosingBrace(node: AstNode, expression: string): boolean {\n return node.typeID !== TokenID.ERROR && isTextToken(node, expression, '}');\n}\n\nfunction isStructuralGeneratorToken(node: AstNode, expression: string): boolean {\n const text = expression.slice(node.from, node.to);\n return text === '{' || text === '}' || text === '|' || text === '∈' || text === ':=' || text === ';';\n}\n","/**\n * Reusable pattern: container header is valid, last child is `ERROR`, input ends here.\n *\n * @see incomplete-formal.ts — function declarations, quantifiers\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { type RSErrorCode, type RSErrorDescription } from '../../error';\nimport { TokenID } from '../token';\n\nimport { errorAt } from './error-builders';\n\nexport function detectTrailingErrorSlot(\n node: AstNode,\n expression: string,\n container: AstNode | null,\n options: {\n headerValid?: (container: AstNode) => boolean;\n code: RSErrorCode | ((container: AstNode, bodyChild: AstNode) => RSErrorCode | null);\n }\n): RSErrorDescription | null {\n if (container === null) {\n return null;\n }\n\n const bodyChild = container.children[container.children.length - 1];\n if (bodyChild?.typeID !== TokenID.ERROR || node !== bodyChild) {\n return null;\n }\n\n if (expression.slice(container.to).trim().length > 0) {\n return null;\n }\n\n if (options.headerValid !== undefined && !options.headerValid(container)) {\n return null;\n }\n\n const code = typeof options.code === 'function' ? options.code(container, bodyChild) : options.code;\n if (code === null) {\n return null;\n }\n\n return errorAt(bodyChild, code);\n}\n","/**\n * Incomplete formal headers: `[α∈…]` declarations and quantifiers (`∀α∈…`).\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\nimport { TypeClass as TypeClassEnum } from '../../semantic/typification';\nimport { Arguments, Variable_pack } from '../parser.terms';\nimport { TokenID } from '../token';\n\nimport { findAncestor, isFunctionDeclNode, isQuantifierNode, subtreeHasError } from './ast-utils';\nimport type { ClassifyContext, ErrorClassifier } from './context';\nimport { detectTrailingErrorSlot } from './trailing-slot';\n\nexport const classifyIncompleteFormal: ErrorClassifier = (node, expression, context) =>\n detectIncompleteFunctionDecl(node, expression, context.expected) ?? detectIncompleteQuantifier(node, expression);\n\nfunction detectIncompleteFunctionDecl(\n node: AstNode,\n expression: string,\n expected?: ClassifyContext['expected']\n): RSErrorDescription | null {\n return detectTrailingErrorSlot(node, expression, findAncestor(node, isFunctionDeclNode), {\n headerValid: container => {\n const argsNode = container.children.find(\n child => child.typeID === Arguments || child.typeID === TokenID.NT_ARGUMENTS\n );\n return argsNode !== undefined && !subtreeHasError(argsNode);\n },\n code: () => incompleteFormalExpressionCode(expected)\n });\n}\n\nfunction incompleteFormalExpressionCode(expected?: ClassifyContext['expected']): RSErrorCode {\n switch (expected) {\n case TypeClassEnum.function:\n return RSErrorCode.expectedExpressionBody;\n case TypeClassEnum.predicate:\n case TypeClassEnum.logic:\n return RSErrorCode.expectedLogicBody;\n default:\n return RSErrorCode.expectedFunctionBody;\n }\n}\n\nfunction detectIncompleteQuantifier(node: AstNode, expression: string): RSErrorDescription | null {\n return detectTrailingErrorSlot(node, expression, findAncestor(node, isQuantifierNode), {\n headerValid: container => {\n const varPack = container.children.find(\n child => child.typeID === Variable_pack || child.typeID === TokenID.NT_ENUM_DECL\n );\n return varPack !== undefined && !subtreeHasError(varPack);\n },\n code: (container, bodyChild) => {\n const inIndex = container.children.findIndex(child => expression.slice(child.from, child.to) === '∈');\n if (inIndex < 0) {\n return RSErrorCode.expectedQuantifierDomain;\n }\n\n const domainChild = container.children[inIndex + 1];\n if (domainChild === undefined || domainChild === bodyChild || subtreeHasError(domainChild)) {\n return RSErrorCode.expectedQuantifierDomain;\n }\n\n return RSErrorCode.expectedQuantifierBody;\n }\n });\n}\n","/**\n * Incomplete operators and bracket argument lists (`P1[S1, ]`).\n *\n * Tuple enums `(α,β,)` intentionally stay `unknownSyntax` — not `expectedArgument`.\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\nimport { Boolean, Expr_enum_min2, Logic_binary, Logic_predicates, Logic_unary, Setexpr_binary } from '../parser.terms';\nimport { TokenID } from '../token';\n\nimport {\n findAncestor,\n findMatchingCloseBracket,\n getErrorOperandNode,\n isFilterNode,\n isFunctionNode,\n isPredicateNode,\n subtreeHasError\n} from './ast-utils';\nimport type { ErrorClassifier } from './context';\nimport { errorAt, hasOnlyClosingSuffix, isTextToken, sliceText } from './error-builders';\n\nexport const classifyIncompleteOperands: ErrorClassifier = (node, expression) =>\n detectIncompleteUnaryOperand(node, expression) ??\n detectIncompleteBinaryOperand(node, expression) ??\n detectIncompleteBooleanOperand(node, expression);\n\nexport const classifyIncompleteArgument: ErrorClassifier = (node, expression) =>\n detectIncompleteArgument(node, expression);\n\nfunction detectIncompleteUnaryOperand(node: AstNode, expression: string): RSErrorDescription | null {\n const unaryNode = findAncestor(node, target => target.typeID === Logic_unary);\n if (unaryNode === null) {\n return null;\n }\n\n const operandChild = unaryNode.children[unaryNode.children.length - 1];\n if (operandChild?.typeID !== TokenID.ERROR || node !== operandChild) {\n return null;\n }\n\n if (expression.slice(unaryNode.to).trim().length > 0) {\n return null;\n }\n\n const operatorChild = unaryNode.children[0];\n if (operatorChild === undefined || operatorChild === operandChild) {\n return null;\n }\n\n if (!isTextToken(operatorChild, expression, '¬')) {\n return null;\n }\n\n return errorAt(operandChild, RSErrorCode.expectedUnaryOperand);\n}\n\nfunction detectIncompleteBinaryOperand(node: AstNode, expression: string): RSErrorDescription | null {\n const binaryNode = findAncestor(\n node,\n target => target.typeID === Logic_binary || target.typeID === Logic_predicates || target.typeID === Setexpr_binary\n );\n if (binaryNode === null) {\n return null;\n }\n\n const operandChild = binaryNode.children[binaryNode.children.length - 1];\n if (operandChild?.typeID !== TokenID.ERROR || node !== operandChild) {\n return null;\n }\n\n if (expression.slice(binaryNode.to).trim().length > 0) {\n return null;\n }\n\n if (binaryNode.children.length < 2) {\n return null;\n }\n\n const leftChild = binaryNode.children[0];\n if (leftChild.typeID === TokenID.ERROR || subtreeHasError(leftChild)) {\n return null;\n }\n\n return errorAt(operandChild, RSErrorCode.expectedRightOperand);\n}\n\nfunction detectIncompleteBooleanOperand(node: AstNode, expression: string): RSErrorDescription | null {\n const booleanNode = findAncestor(node, target => target.typeID === Boolean);\n if (booleanNode === null) {\n return null;\n }\n\n const operandChild = findTrailingErrorOperand(booleanNode, expression);\n if (operandChild === null || node !== operandChild) {\n return null;\n }\n\n if (!hasOnlyClosingSuffix(expression, booleanNode.to)) {\n return null;\n }\n\n return errorAt(operandChild, RSErrorCode.expectedRightOperand);\n}\n\nfunction detectIncompleteArgument(node: AstNode, expression: string): RSErrorDescription | null {\n const enumNode = findAncestor(node, target => target.typeID === Expr_enum_min2);\n if (enumNode === null || !isLegitimateArgumentEnum(enumNode, expression)) {\n return null;\n }\n\n const errorChild = findEnumArgumentError(enumNode, expression);\n if (errorChild === null || node !== errorChild || !hasOnlyClosingSuffix(expression, enumNode.to)) {\n return null;\n }\n\n return errorAt(errorChild, RSErrorCode.expectedArgument);\n}\n\nfunction findTrailingErrorOperand(container: AstNode, expression: string): AstNode | null {\n for (let index = container.children.length - 1; index >= 0; index--) {\n const child = container.children[index];\n if (isOperandStructuralToken(child, expression)) {\n continue;\n }\n return getErrorOperandNode(child);\n }\n return null;\n}\n\nfunction findEnumArgumentError(enumNode: AstNode, expression: string): AstNode | null {\n for (const child of enumNode.children) {\n if (isTextToken(child, expression, ',')) {\n continue;\n }\n const errorNode = getErrorOperandNode(child);\n if (errorNode !== null) {\n return errorNode;\n }\n }\n return null;\n}\n\nfunction isOperandStructuralToken(node: AstNode, expression: string): boolean {\n const text = sliceText(node, expression);\n return text === '(' || text === ')' || text === 'ℬ';\n}\n\nfunction isLegitimateArgumentEnum(enumNode: AstNode, expression: string): boolean {\n return findCallHostForBracketEnum(enumNode, expression) !== null;\n}\n\nfunction findCallHostForBracketEnum(enumNode: AstNode, expression: string): AstNode | null {\n let current: AstNode | null = enumNode.parent;\n while (current !== null) {\n for (const child of current.children) {\n if (!isFunctionNode(child) && !isPredicateNode(child) && !isFilterNode(child)) {\n continue;\n }\n if (isEnumInCallBrackets(enumNode, child, expression)) {\n return child;\n }\n }\n current = current.parent;\n }\n return null;\n}\n\nfunction isEnumInCallBrackets(enumNode: AstNode, nameNode: AstNode, expression: string): boolean {\n const rest = expression.slice(nameNode.to);\n const match = /^\\s*\\[/.exec(rest);\n if (match === null) {\n return false;\n }\n\n const openPos = nameNode.to + match.index + match[0].indexOf('[');\n const closePos = findMatchingCloseBracket(expression, openPos);\n if (closePos < 0) {\n return false;\n }\n\n return enumNode.from > openPos && enumNode.from < closePos;\n}\n","/**\n * Parse-error classification: ordered classifier chain and P8400 fallback.\n *\n * Register new detectors in `PARSE_ERROR_CLASSIFIERS` (first match wins).\n */\n\nimport type { AstNode } from '../../../parsing';\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\nimport { Variable } from '../parser.terms';\n\nimport type { ClassifyContext, ErrorClassifier } from './context';\nimport { whenNoBracketErrors } from './context';\nimport { classifyFilterParenMismatch, classifyGlobalFuncCall } from './function-calls';\nimport { classifyIncompleteGenerator } from './generators';\nimport { classifyIncompleteFormal } from './incomplete-formal';\nimport { classifyIncompleteArgument, classifyIncompleteOperands } from './incomplete-operands';\n\nconst PARSE_ERROR_CLASSIFIERS: ErrorClassifier[] = [\n classifyIncompleteFormal,\n classifyIncompleteGenerator,\n whenNoBracketErrors(classifyIncompleteOperands),\n whenNoBracketErrors(classifyIncompleteArgument),\n classifyFilterParenMismatch,\n classifyGlobalFuncCall\n];\n\n/** Runs classifiers in priority order for one `ERROR` AST node. */\nexport function classifyParseError(\n node: AstNode,\n expression: string,\n context: ClassifyContext\n): RSErrorDescription | null {\n for (const classifier of PARSE_ERROR_CLASSIFIERS) {\n const error = classifier(node, expression, context);\n if (error !== null) {\n return error;\n }\n }\n return null;\n}\n\n/** Default rules when no classifier matched: `expectedLocal` or `unknownSyntax`. */\nexport function classifyFallbackError(\n node: AstNode,\n ignoreUnknownErrors: boolean\n): { error: RSErrorDescription; target: AstNode } | null {\n const parent = node.parent;\n if (parent === null) {\n return ignoreUnknownErrors\n ? null\n : { error: { code: RSErrorCode.unknownSyntax, from: node.from, to: node.to }, target: node };\n }\n\n if (parent.typeID === Variable) {\n return {\n error: { code: RSErrorCode.expectedLocal, from: parent.from, to: parent.to },\n target: parent\n };\n }\n\n if (ignoreUnknownErrors) {\n return null;\n }\n\n const target = node.from === node.to ? parent : node;\n return {\n error: { code: RSErrorCode.unknownSyntax, from: target.from, to: target.to },\n target\n };\n}\n","/**\n * Expression-level diagnostics (no AST required) and error deduplication.\n */\n\nimport { RSErrorCode, type RSErrorDescription } from '../../error';\n\ntype OpenBracket = '(' | '[' | '{';\ntype CloseBracket = ')' | ']' | '}';\n\ninterface BracketFrame {\n bracket: OpenBracket;\n index: number;\n}\n\nconst ALLOWED_EXPRESSION_CHARS = buildAllowedExpressionChars();\n\nfunction buildAllowedExpressionChars(): Set<string> {\n const chars = new Set<string>();\n for (const chunk of [\n '0123456789',\n 'abcdefghijklmnopqrstuvwxyz',\n 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',\n '_',\n '∅¬∀∃⇔⇒∨&ℬ+-*∪\\\\∆∩×∈∉⊆⊄⊂<>≥≤≠=,:;|[]{}()'\n ]) {\n for (const symbol of chunk) {\n chars.add(symbol);\n }\n }\n for (let codePoint = 0x03b1; codePoint <= 0x03c9; codePoint++) {\n chars.add(String.fromCodePoint(codePoint));\n }\n return chars;\n}\n\nfunction isAllowedExpressionChar(symbol: string): boolean {\n return /\\s/u.test(symbol) || ALLOWED_EXPRESSION_CHARS.has(symbol);\n}\n\nexport function extractForbiddenCharacterErrors(expression: string): RSErrorDescription[] {\n const errors: RSErrorDescription[] = [];\n let pos = 0;\n\n while (pos < expression.length) {\n const symbol = expression[pos];\n if (isAllowedExpressionChar(symbol)) {\n pos++;\n continue;\n }\n\n const start = pos;\n while (pos < expression.length && !isAllowedExpressionChar(expression[pos])) {\n pos++;\n }\n\n errors.push({\n code: RSErrorCode.forbiddenCharacter,\n from: start,\n to: pos,\n params: [expression.slice(start, pos)]\n });\n }\n\n return errors;\n}\n\nexport function extractBracketErrors(expression: string): RSErrorDescription | null {\n const stack: BracketFrame[] = [];\n\n for (let pos = 0; pos < expression.length; pos++) {\n const symbol = expression[pos];\n if (isOpenBracket(symbol)) {\n if (isDoubleParenthesis(expression, pos)) {\n return {\n code: RSErrorCode.doubleParenthesis,\n from: pos,\n to: pos + 2\n };\n }\n stack.push({ bracket: symbol, index: pos });\n continue;\n }\n if (!isCloseBracket(symbol)) {\n continue;\n }\n\n const expectedOpen = closeToOpen(symbol);\n const top = stack[stack.length - 1];\n if (top === undefined) {\n return {\n code: RSErrorCode.missingOpenBracket,\n from: pos,\n to: pos + 1,\n params: [symbol]\n };\n }\n\n if (top.bracket !== expectedOpen) {\n return {\n code: RSErrorCode.bracketMismatch,\n from: pos,\n to: pos + 1,\n params: [openToClose(top.bracket), symbol]\n };\n }\n\n stack.pop();\n }\n\n if (stack.length > 0) {\n const unclosed = stack[0];\n return {\n code: RSErrorCode.missingCloseBracket,\n from: unclosed.index,\n to: unclosed.index + 1,\n params: [unclosed.bracket]\n };\n }\n\n return null;\n}\n\nexport function deduplicateErrors(errors: RSErrorDescription[]): RSErrorDescription[] {\n const result: RSErrorDescription[] = [];\n\n for (const error of errors) {\n const duplicate = result.some(\n existing => existing.code === error.code && existing.from === error.from && existing.to === error.to\n );\n if (duplicate) {\n continue;\n }\n\n if (error.code !== RSErrorCode.unknownSyntax) {\n for (let index = result.length - 1; index >= 0; index--) {\n if (result[index].code === RSErrorCode.unknownSyntax && rangesOverlap(error, result[index])) {\n result.splice(index, 1);\n }\n }\n }\n\n result.push(error);\n }\n\n const specific = result.filter(error => error.code !== RSErrorCode.unknownSyntax);\n const filtered = result.filter(\n error => error.code !== RSErrorCode.unknownSyntax || !specific.some(item => rangesOverlap(error, item))\n );\n return collapseContiguousUnknownSyntax(filtered);\n}\n\nfunction collapseContiguousUnknownSyntax(errors: RSErrorDescription[]): RSErrorDescription[] {\n const unknowns = errors.filter(error => error.code === RSErrorCode.unknownSyntax);\n if (unknowns.length <= 1) {\n return errors;\n }\n\n const others = errors.filter(error => error.code !== RSErrorCode.unknownSyntax);\n const sorted = [...unknowns].sort((left, right) => left.from - right.from || left.to - right.to);\n const merged: RSErrorDescription[] = [];\n\n let clusterFrom = sorted[0].from;\n let clusterTo = sorted[0].to;\n\n for (let index = 1; index < sorted.length; index++) {\n const error = sorted[index];\n if (error.from <= clusterTo) {\n clusterTo = Math.max(clusterTo, error.to);\n continue;\n }\n\n merged.push({ code: RSErrorCode.unknownSyntax, from: clusterFrom, to: clusterTo });\n clusterFrom = error.from;\n clusterTo = error.to;\n }\n\n merged.push({ code: RSErrorCode.unknownSyntax, from: clusterFrom, to: clusterTo });\n return [...others, ...merged];\n}\n\nfunction rangesOverlap(a: RSErrorDescription, b: RSErrorDescription): boolean {\n return a.from < b.to && b.from < a.to;\n}\n\nfunction isOpenBracket(symbol: string): symbol is OpenBracket {\n return symbol === '(' || symbol === '[' || symbol === '{';\n}\n\nfunction isCloseBracket(symbol: string): symbol is CloseBracket {\n return symbol === ')' || symbol === ']' || symbol === '}';\n}\n\nfunction openToClose(symbol: OpenBracket): CloseBracket {\n switch (symbol) {\n case '(':\n return ')';\n case '[':\n return ']';\n case '{':\n return '}';\n }\n}\n\nfunction closeToOpen(symbol: CloseBracket): OpenBracket {\n switch (symbol) {\n case ')':\n return '(';\n case ']':\n return '[';\n case '}':\n return '{';\n }\n}\n\nfunction isDoubleParenthesis(expression: string, pos: number): boolean {\n const isOpenDoubleParenthesis = expression[pos] === '(' && expression[pos + 1] === '(';\n if (!isOpenDoubleParenthesis) {\n return false;\n }\n\n const outerClose = findMatchingCloseParen(expression, pos);\n const innerClose = findMatchingCloseParen(expression, pos + 1);\n if (outerClose < 0 || innerClose < 0) {\n return false;\n }\n\n return outerClose === innerClose + 1;\n}\n\nfunction findMatchingCloseParen(expression: string, openPos: number): number {\n if (expression[openPos] !== '(') {\n return -1;\n }\n\n const stack: OpenBracket[] = [];\n for (let index = openPos; index < expression.length; index++) {\n const symbol = expression[index];\n if (isOpenBracket(symbol)) {\n stack.push(symbol);\n continue;\n }\n if (!isCloseBracket(symbol)) {\n continue;\n }\n const top = stack.pop();\n if (top === undefined || top !== closeToOpen(symbol)) {\n return -1;\n }\n if (stack.length === 0) {\n return index;\n }\n }\n\n return -1;\n}\n","/**\n * Syntax error extraction — public entry point.\n *\n * Expects the **hybrid** AST produced by `normalizeAST` (see `parser/README.md`).\n * See {@link ./README.md} for pipeline, classifier order, and how to add codes.\n *\n * @module rslang/parser/syntax-errors\n */\n\nimport { type AstNode, visitAstDFS } from '../../../parsing';\nimport { annotateError } from '../../ast-annotations';\nimport { type ErrorReporter, type RSErrorDescription } from '../../error';\nimport { type TypeClass } from '../../semantic/typification';\nimport { TokenID } from '../token';\n\nimport { classifyFallbackError, classifyParseError } from './classify';\nimport { deduplicateErrors, extractBracketErrors, extractForbiddenCharacterErrors } from './expression-errors';\nimport { isIncompleteGeneratorBodyCode } from './generators';\n\n/** When set, refines incomplete formal headers (`[α∈…]`, quantifiers). */\nexport interface SyntaxErrorOptions {\n expected?: TypeClass;\n}\n\n/**\n * Walks a parsed expression AST and reports syntax errors.\n *\n * @param ast - Root AST from `buildTree(parser.parse(expression))`\n * @param expression - Original source text (positions must match the AST)\n * @param reporter - Called once per deduplicated error\n * @param annotateErrors - When true, also calls `annotateError` on the triggering node\n * @param options - Optional typification hint for incomplete formal expressions\n */\nexport function extractSyntaxErrors(\n ast: AstNode,\n expression: string,\n reporter: ErrorReporter,\n annotateErrors: boolean = false,\n options?: SyntaxErrorOptions\n) {\n const forbiddenErrors = extractForbiddenCharacterErrors(expression);\n if (forbiddenErrors.length > 0) {\n for (const error of forbiddenErrors) {\n reporter(error);\n if (annotateErrors) {\n annotateError(ast, error.code, error.params);\n }\n }\n return;\n }\n\n const collected: RSErrorDescription[] = [];\n const annotationTargets = new Map<RSErrorDescription, AstNode>();\n const collect = (error: RSErrorDescription, target?: AstNode) => {\n collected.push(error);\n if (target !== undefined) {\n annotationTargets.set(error, target);\n }\n };\n\n const bracketError = extractBracketErrors(expression);\n const hasBracketErrors = bracketError !== null;\n visitAstDFS(ast, node => extractInternal(node, expression, collect, hasBracketErrors, options));\n\n const hasIncompleteGenerator = collected.some(error => isIncompleteGeneratorBodyCode(error.code));\n if (bracketError !== null && !hasIncompleteGenerator) {\n collect(bracketError, ast);\n }\n\n for (const error of deduplicateErrors(collected)) {\n reporter(error);\n if (annotateErrors) {\n const target = annotationTargets.get(error);\n if (target !== undefined) {\n annotateError(target, error.code, error.params);\n }\n }\n }\n}\n\nfunction extractInternal(\n node: AstNode,\n expression: string,\n collect: (error: RSErrorDescription, target: AstNode) => void,\n hasBracketErrors: boolean,\n options?: SyntaxErrorOptions\n) {\n if (node.typeID !== TokenID.ERROR) {\n return;\n }\n\n const classified = classifyParseError(node, expression, { hasBracketErrors, expected: options?.expected });\n if (classified !== null) {\n collect(classified, node);\n return;\n }\n\n const fallback = classifyFallbackError(node, hasBracketErrors);\n if (fallback !== null) {\n collect(fallback.error, fallback.target);\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsconcept/domain",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Shared TypeScript domain for RS language, RSForm, RSModel, RSEngine, OSS, and concept-text helpers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "IRBorisov",
|
|
@@ -63,6 +63,10 @@
|
|
|
63
63
|
"types": "./dist/library/index.d.ts",
|
|
64
64
|
"import": "./dist/library/index.js"
|
|
65
65
|
},
|
|
66
|
+
"./library/diagnostics": {
|
|
67
|
+
"types": "./dist/library/diagnostics.d.ts",
|
|
68
|
+
"import": "./dist/library/diagnostics.js"
|
|
69
|
+
},
|
|
66
70
|
"./library/library": {
|
|
67
71
|
"types": "./dist/library/library.d.ts",
|
|
68
72
|
"import": "./dist/library/library.js"
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,14 @@ export {
|
|
|
8
8
|
type WordForm
|
|
9
9
|
} from './cctext/language';
|
|
10
10
|
export { Graph } from './graph/graph';
|
|
11
|
+
export {
|
|
12
|
+
assignSchemaDiagnostics,
|
|
13
|
+
type CstDiagnostic,
|
|
14
|
+
DiagnosticKind,
|
|
15
|
+
hasCstDiagnostic,
|
|
16
|
+
modelStatusCstDiagnostic,
|
|
17
|
+
RSDiagnosticCode
|
|
18
|
+
} from './library/diagnostics';
|
|
11
19
|
export { FolderNode, FolderTree } from './library/folder-tree';
|
|
12
20
|
export {
|
|
13
21
|
AccessPolicy,
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
assignSchemaDiagnostics,
|
|
5
|
+
DiagnosticKind,
|
|
6
|
+
hasCstDiagnostic,
|
|
7
|
+
modelStatusCstDiagnostic,
|
|
8
|
+
RSDiagnosticCode
|
|
9
|
+
} from './diagnostics';
|
|
10
|
+
import { type Constituenta, CstType } from './rsform';
|
|
11
|
+
import { EvalStatus } from './rsmodel';
|
|
12
|
+
|
|
13
|
+
function stubCst(id: number, alias: string, overrides: Partial<Constituenta> = {}): Constituenta {
|
|
14
|
+
return {
|
|
15
|
+
id,
|
|
16
|
+
alias,
|
|
17
|
+
term_resolved: '',
|
|
18
|
+
definition_formal: '',
|
|
19
|
+
convention: '',
|
|
20
|
+
cst_type: CstType.BASE,
|
|
21
|
+
is_type_mismatch: false,
|
|
22
|
+
typification_manual: '',
|
|
23
|
+
analysis: { success: true, type: null },
|
|
24
|
+
diagnostics: [],
|
|
25
|
+
...overrides
|
|
26
|
+
} as Constituenta;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('hasCstDiagnostic', () => {
|
|
30
|
+
it('returns true when constituent has the requested code', () => {
|
|
31
|
+
const cst = stubCst(1, 'X1', {
|
|
32
|
+
diagnostics: [{ kind: DiagnosticKind.SCHEMA, code: RSDiagnosticCode.schemaHomonym, params: ['X2'] }]
|
|
33
|
+
});
|
|
34
|
+
expect(hasCstDiagnostic(cst, RSDiagnosticCode.schemaHomonym)).toBe(true);
|
|
35
|
+
expect(hasCstDiagnostic(cst, RSDiagnosticCode.schemaFormalDuplicate)).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('returns false when diagnostics are absent', () => {
|
|
39
|
+
expect(hasCstDiagnostic(stubCst(1, 'X1'), RSDiagnosticCode.schemaHomonym)).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('modelStatusCstDiagnostic', () => {
|
|
44
|
+
it('maps model evaluation statuses to compact diagnostics', () => {
|
|
45
|
+
expect(modelStatusCstDiagnostic(EvalStatus.EMPTY, CstType.BASE)).toEqual({
|
|
46
|
+
kind: DiagnosticKind.MODEL,
|
|
47
|
+
code: RSDiagnosticCode.modelEmpty,
|
|
48
|
+
params: [String(EvalStatus.EMPTY)]
|
|
49
|
+
});
|
|
50
|
+
expect(modelStatusCstDiagnostic(EvalStatus.AXIOM_FALSE, CstType.AXIOM)).toEqual({
|
|
51
|
+
kind: DiagnosticKind.MODEL,
|
|
52
|
+
code: RSDiagnosticCode.modelAxiomFalse,
|
|
53
|
+
params: [String(EvalStatus.AXIOM_FALSE)]
|
|
54
|
+
});
|
|
55
|
+
expect(modelStatusCstDiagnostic(EvalStatus.HAS_DATA, CstType.BASE)).toBeNull();
|
|
56
|
+
expect(modelStatusCstDiagnostic(EvalStatus.EMPTY, CstType.TERM)).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('assignSchemaDiagnostics', () => {
|
|
61
|
+
it('detects homonyms and formal duplicates', () => {
|
|
62
|
+
const items = [
|
|
63
|
+
stubCst(1, 'X1', { term_resolved: 'person' }),
|
|
64
|
+
stubCst(2, 'X2', { term_resolved: 'Person' }),
|
|
65
|
+
stubCst(3, 'D1', { cst_type: CstType.TERM, definition_formal: 'Pr1(X1)' }),
|
|
66
|
+
stubCst(4, 'D2', { cst_type: CstType.TERM, definition_formal: 'Pr1(X1)' })
|
|
67
|
+
];
|
|
68
|
+
const resolveAlias = (id: number) => items.find(item => item.id === id)!.alias;
|
|
69
|
+
const normalizedDefinitions = new Map<number, string>([
|
|
70
|
+
[3, 'pr1(x1)'],
|
|
71
|
+
[4, 'pr1(x1)']
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
assignSchemaDiagnostics(items, resolveAlias, normalizedDefinitions);
|
|
75
|
+
|
|
76
|
+
expect(hasCstDiagnostic(items[0], RSDiagnosticCode.schemaHomonym)).toBe(true);
|
|
77
|
+
expect(items[0].diagnostics.find(item => item.code === RSDiagnosticCode.schemaHomonym)?.params?.[0]).toBe('X2');
|
|
78
|
+
expect(hasCstDiagnostic(items[1], RSDiagnosticCode.schemaHomonym)).toBe(true);
|
|
79
|
+
expect(items[1].diagnostics.find(item => item.code === RSDiagnosticCode.schemaHomonym)?.params?.[0]).toBe('X1');
|
|
80
|
+
expect(hasCstDiagnostic(items[2], RSDiagnosticCode.schemaFormalDuplicate)).toBe(true);
|
|
81
|
+
expect(items[2].diagnostics.find(item => item.code === RSDiagnosticCode.schemaFormalDuplicate)?.params?.[0]).toBe(
|
|
82
|
+
'D2'
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module: Compact diagnostics for constituents (schema and model issue codes).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { labelType } from '../rslang/labels';
|
|
6
|
+
|
|
7
|
+
import { type Constituenta, type CstType } from './rsform';
|
|
8
|
+
import { isBasicConcept, isLogical } from './rsform-api';
|
|
9
|
+
import { EvalStatus } from './rsmodel';
|
|
10
|
+
|
|
11
|
+
/** Origin of a diagnostic. */
|
|
12
|
+
export const DiagnosticKind = {
|
|
13
|
+
EXPRESSION: 'expression',
|
|
14
|
+
SCHEMA: 'schema',
|
|
15
|
+
MODEL: 'model'
|
|
16
|
+
} as const;
|
|
17
|
+
export type DiagnosticKind = (typeof DiagnosticKind)[keyof typeof DiagnosticKind];
|
|
18
|
+
|
|
19
|
+
/** Schema and model diagnostic codes (same numeric format as {@link RSErrorCode}). */
|
|
20
|
+
export const RSDiagnosticCode = {
|
|
21
|
+
schemaHomonym: 0x8901,
|
|
22
|
+
schemaFormalDuplicate: 0x8902,
|
|
23
|
+
schemaMissingConvention: 0x8903,
|
|
24
|
+
schemaMissingTerm: 0x8904,
|
|
25
|
+
schemaTypeMismatch: 0x8905,
|
|
26
|
+
|
|
27
|
+
modelEmpty: 0x8910,
|
|
28
|
+
modelAxiomFalse: 0x8911,
|
|
29
|
+
modelInvalidData: 0x8912,
|
|
30
|
+
modelEvalFail: 0x8913
|
|
31
|
+
} as const;
|
|
32
|
+
export type RSDiagnosticCode = (typeof RSDiagnosticCode)[keyof typeof RSDiagnosticCode];
|
|
33
|
+
|
|
34
|
+
/** Compact diagnostic stored on {@link Constituenta} after schema loading. */
|
|
35
|
+
export interface CstDiagnostic {
|
|
36
|
+
kind: DiagnosticKind;
|
|
37
|
+
code: number;
|
|
38
|
+
/** Positional message arguments (per-code; homonyms/duplicates use `[aliases]`). */
|
|
39
|
+
params?: readonly string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Whether a constituent has a schema/model diagnostic with the given code. */
|
|
43
|
+
export function hasCstDiagnostic(cst: Constituenta, code: RSDiagnosticCode): boolean {
|
|
44
|
+
return cst.diagnostics?.some(item => item.code === code) ?? false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Populate `diagnostics` on each constituent (homonyms, duplicates, metadata, type mismatch).
|
|
49
|
+
*
|
|
50
|
+
* Requires normalized formal definitions for duplicate detection (same as Portal loader).
|
|
51
|
+
*/
|
|
52
|
+
export function assignSchemaDiagnostics(
|
|
53
|
+
items: Constituenta[],
|
|
54
|
+
resolveAlias: (id: number) => string,
|
|
55
|
+
normalizedDefinitions: ReadonlyMap<number, string>
|
|
56
|
+
): void {
|
|
57
|
+
const homonymById = detectHomonymDiagnostics(items, resolveAlias);
|
|
58
|
+
const duplicateById = detectFormalDuplicateDiagnostics(items, resolveAlias, normalizedDefinitions);
|
|
59
|
+
|
|
60
|
+
for (const cst of items) {
|
|
61
|
+
const diagnostics: CstDiagnostic[] = [];
|
|
62
|
+
const homonym = homonymById.get(cst.id);
|
|
63
|
+
if (homonym) {
|
|
64
|
+
diagnostics.push(homonym);
|
|
65
|
+
}
|
|
66
|
+
const duplicate = duplicateById.get(cst.id);
|
|
67
|
+
if (duplicate) {
|
|
68
|
+
diagnostics.push(duplicate);
|
|
69
|
+
}
|
|
70
|
+
diagnostics.push(...collectLocalSchemaDiagnostics(cst));
|
|
71
|
+
cst.diagnostics = diagnostics;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Map a model evaluation status to a compact diagnostic, or `null` when not an issue. */
|
|
76
|
+
export function modelStatusCstDiagnostic(status: EvalStatus, cstType: CstType): CstDiagnostic | null {
|
|
77
|
+
const code = modelStatusToCode(status, cstType);
|
|
78
|
+
if (code === null) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
return { kind: DiagnosticKind.MODEL, code, params: [String(status)] };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function collectLocalSchemaDiagnostics(cst: Constituenta): CstDiagnostic[] {
|
|
85
|
+
const result: CstDiagnostic[] = [];
|
|
86
|
+
if (cst.is_type_mismatch) {
|
|
87
|
+
result.push({
|
|
88
|
+
kind: DiagnosticKind.SCHEMA,
|
|
89
|
+
code: RSDiagnosticCode.schemaTypeMismatch,
|
|
90
|
+
params: [cst.typification_manual, labelType(cst.analysis.type)]
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (isBasicConcept(cst.cst_type) && !isLogical(cst.cst_type)) {
|
|
94
|
+
if (!cst.convention.trim()) {
|
|
95
|
+
result.push({ kind: DiagnosticKind.SCHEMA, code: RSDiagnosticCode.schemaMissingConvention });
|
|
96
|
+
}
|
|
97
|
+
if (!cst.term_resolved.trim()) {
|
|
98
|
+
result.push({ kind: DiagnosticKind.SCHEMA, code: RSDiagnosticCode.schemaMissingTerm });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function detectGroupedAliasDiagnostics(
|
|
105
|
+
items: Constituenta[],
|
|
106
|
+
resolveAlias: (id: number) => string,
|
|
107
|
+
groupKey: (cst: Constituenta) => string | undefined,
|
|
108
|
+
code: RSDiagnosticCode
|
|
109
|
+
): Map<number, CstDiagnostic> {
|
|
110
|
+
const groups = new Map<string, Constituenta[]>();
|
|
111
|
+
for (const cst of items) {
|
|
112
|
+
const key = groupKey(cst);
|
|
113
|
+
if (!key) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const group = groups.get(key) ?? [];
|
|
117
|
+
group.push(cst);
|
|
118
|
+
groups.set(key, group);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const result = new Map<number, CstDiagnostic>();
|
|
122
|
+
for (const group of groups.values()) {
|
|
123
|
+
if (group.length <= 1) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
for (const cst of group) {
|
|
127
|
+
const aliases = group
|
|
128
|
+
.filter(other => other.id !== cst.id)
|
|
129
|
+
.map(other => resolveAlias(other.id))
|
|
130
|
+
.join(', ');
|
|
131
|
+
result.set(cst.id, {
|
|
132
|
+
kind: DiagnosticKind.SCHEMA,
|
|
133
|
+
code,
|
|
134
|
+
params: [aliases]
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function detectHomonymDiagnostics(
|
|
142
|
+
items: Constituenta[],
|
|
143
|
+
resolveAlias: (id: number) => string
|
|
144
|
+
): Map<number, CstDiagnostic> {
|
|
145
|
+
return detectGroupedAliasDiagnostics(
|
|
146
|
+
items,
|
|
147
|
+
resolveAlias,
|
|
148
|
+
cst => {
|
|
149
|
+
const key = cst.term_resolved.trim().toLocaleLowerCase();
|
|
150
|
+
return key === '' ? undefined : key;
|
|
151
|
+
},
|
|
152
|
+
RSDiagnosticCode.schemaHomonym
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function detectFormalDuplicateDiagnostics(
|
|
157
|
+
items: Constituenta[],
|
|
158
|
+
resolveAlias: (id: number) => string,
|
|
159
|
+
normalizedDefinitions: ReadonlyMap<number, string>
|
|
160
|
+
): Map<number, CstDiagnostic> {
|
|
161
|
+
return detectGroupedAliasDiagnostics(
|
|
162
|
+
items,
|
|
163
|
+
resolveAlias,
|
|
164
|
+
cst => normalizedDefinitions.get(cst.id),
|
|
165
|
+
RSDiagnosticCode.schemaFormalDuplicate
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function modelStatusToCode(status: EvalStatus, cstType: CstType): RSDiagnosticCode | null {
|
|
170
|
+
switch (status) {
|
|
171
|
+
case EvalStatus.EMPTY:
|
|
172
|
+
return isBasicConcept(cstType) ? RSDiagnosticCode.modelEmpty : null;
|
|
173
|
+
case EvalStatus.AXIOM_FALSE:
|
|
174
|
+
return RSDiagnosticCode.modelAxiomFalse;
|
|
175
|
+
case EvalStatus.INVALID_DATA:
|
|
176
|
+
return RSDiagnosticCode.modelInvalidData;
|
|
177
|
+
case EvalStatus.EVAL_FAIL:
|
|
178
|
+
return RSDiagnosticCode.modelEvalFail;
|
|
179
|
+
default:
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
package/src/library/index.ts
CHANGED
|
@@ -19,8 +19,7 @@ function mockConstituenta(
|
|
|
19
19
|
term_forms: [],
|
|
20
20
|
typification_manual: '',
|
|
21
21
|
value_is_property: false,
|
|
22
|
-
|
|
23
|
-
formalDuplicates: [],
|
|
22
|
+
diagnostics: [],
|
|
24
23
|
analysis: { success: true } as Constituenta['analysis'],
|
|
25
24
|
effectiveType: null,
|
|
26
25
|
is_type_mismatch: false,
|