@rsconcept/domain 1.0.0
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/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/cctext/index.d.ts +1 -0
- package/dist/cctext/index.js +42 -0
- package/dist/cctext/index.js.map +1 -0
- package/dist/cctext/language-api.d.ts +43 -0
- package/dist/cctext/language-api.js +252 -0
- package/dist/cctext/language-api.js.map +1 -0
- package/dist/cctext/language.d.ts +58 -0
- package/dist/cctext/language.js +44 -0
- package/dist/cctext/language.js.map +1 -0
- package/dist/graph/graph.d.ts +62 -0
- package/dist/graph/graph.js +385 -0
- package/dist/graph/graph.js.map +1 -0
- package/dist/graph/index.d.ts +1 -0
- package/dist/graph/index.js +384 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +5851 -0
- package/dist/index.js.map +1 -0
- package/dist/library/folder-tree.d.ts +32 -0
- package/dist/library/folder-tree.js +136 -0
- package/dist/library/folder-tree.js.map +1 -0
- package/dist/library/index.d.ts +17 -0
- package/dist/library/index.js +2800 -0
- package/dist/library/index.js.map +1 -0
- package/dist/library/library-api.d.ts +6 -0
- package/dist/library/library-api.js +13 -0
- package/dist/library/library-api.js.map +1 -0
- package/dist/library/library.d.ts +56 -0
- package/dist/library/library.js +23 -0
- package/dist/library/library.js.map +1 -0
- package/dist/library/oss-api.d.ts +47 -0
- package/dist/library/oss-api.js +1105 -0
- package/dist/library/oss-api.js.map +1 -0
- package/dist/library/oss-layout-api.d.ts +36 -0
- package/dist/library/oss-layout-api.js +330 -0
- package/dist/library/oss-layout-api.js.map +1 -0
- package/dist/library/oss-layout.d.ts +25 -0
- package/dist/library/oss-layout.js +1 -0
- package/dist/library/oss-layout.js.map +1 -0
- package/dist/library/oss.d.ts +136 -0
- package/dist/library/oss.js +30 -0
- package/dist/library/oss.js.map +1 -0
- package/dist/library/rsengine.d.ts +116 -0
- package/dist/library/rsengine.js +2604 -0
- package/dist/library/rsengine.js.map +1 -0
- package/dist/library/rsform-api.d.ts +74 -0
- package/dist/library/rsform-api.js +879 -0
- package/dist/library/rsform-api.js.map +1 -0
- package/dist/library/rsform.d.ts +206 -0
- package/dist/library/rsform.js +32 -0
- package/dist/library/rsform.js.map +1 -0
- package/dist/library/rsmodel-api.d.ts +43 -0
- package/dist/library/rsmodel-api.js +836 -0
- package/dist/library/rsmodel-api.js.map +1 -0
- package/dist/library/rsmodel.d.ts +52 -0
- package/dist/library/rsmodel.js +25 -0
- package/dist/library/rsmodel.js.map +1 -0
- package/dist/library/structure-planner.d.ts +33 -0
- package/dist/library/structure-planner.js +481 -0
- package/dist/library/structure-planner.js.map +1 -0
- package/dist/parsing/ast.d.ts +49 -0
- package/dist/parsing/ast.js +93 -0
- package/dist/parsing/ast.js.map +1 -0
- package/dist/parsing/index.d.ts +3 -0
- package/dist/parsing/index.js +141 -0
- package/dist/parsing/index.js.map +1 -0
- package/dist/parsing/lezer-tree.d.ts +13 -0
- package/dist/parsing/lezer-tree.js +50 -0
- package/dist/parsing/lezer-tree.js.map +1 -0
- package/dist/rslang/api.d.ts +53 -0
- package/dist/rslang/api.js +846 -0
- package/dist/rslang/api.js.map +1 -0
- package/dist/rslang/ast-annotations.d.ts +18 -0
- package/dist/rslang/ast-annotations.js +56 -0
- package/dist/rslang/ast-annotations.js.map +1 -0
- package/dist/rslang/error.d.ts +85 -0
- package/dist/rslang/error.js +159 -0
- package/dist/rslang/error.js.map +1 -0
- package/dist/rslang/eval/calculator.d.ts +43 -0
- package/dist/rslang/eval/calculator.js +1639 -0
- package/dist/rslang/eval/calculator.js.map +1 -0
- package/dist/rslang/eval/evaluation-cache.d.ts +36 -0
- package/dist/rslang/eval/evaluation-cache.js +310 -0
- package/dist/rslang/eval/evaluation-cache.js.map +1 -0
- package/dist/rslang/eval/evaluator.d.ts +70 -0
- package/dist/rslang/eval/evaluator.js +1514 -0
- package/dist/rslang/eval/evaluator.js.map +1 -0
- package/dist/rslang/eval/value-api.d.ts +48 -0
- package/dist/rslang/eval/value-api.js +490 -0
- package/dist/rslang/eval/value-api.js.map +1 -0
- package/dist/rslang/eval/value.d.ts +36 -0
- package/dist/rslang/eval/value.js +118 -0
- package/dist/rslang/eval/value.js.map +1 -0
- package/dist/rslang/index.d.ts +17 -0
- package/dist/rslang/index.js +4314 -0
- package/dist/rslang/index.js.map +1 -0
- package/dist/rslang/labels.d.ts +16 -0
- package/dist/rslang/labels.js +315 -0
- package/dist/rslang/labels.js.map +1 -0
- package/dist/rslang/parser/expression-generator.d.ts +10 -0
- package/dist/rslang/parser/expression-generator.js +451 -0
- package/dist/rslang/parser/expression-generator.js.map +1 -0
- package/dist/rslang/parser/normalize.d.ts +11 -0
- package/dist/rslang/parser/normalize.js +507 -0
- package/dist/rslang/parser/normalize.js.map +1 -0
- package/dist/rslang/parser/parser.d.ts +5 -0
- package/dist/rslang/parser/parser.js +24 -0
- package/dist/rslang/parser/parser.js.map +1 -0
- package/dist/rslang/parser/parser.terms.d.ts +42 -0
- package/dist/rslang/parser/parser.terms.js +84 -0
- package/dist/rslang/parser/parser.terms.js.map +1 -0
- package/dist/rslang/parser/syntax-errors.d.ts +11 -0
- package/dist/rslang/parser/syntax-errors.js +403 -0
- package/dist/rslang/parser/syntax-errors.js.map +1 -0
- package/dist/rslang/parser/token.d.ts +79 -0
- package/dist/rslang/parser/token.js +95 -0
- package/dist/rslang/parser/token.js.map +1 -0
- package/dist/rslang/semantic/analyzer.d.ts +39 -0
- package/dist/rslang/semantic/analyzer.js +2604 -0
- package/dist/rslang/semantic/analyzer.js.map +1 -0
- package/dist/rslang/semantic/arguments-extractor.d.ts +42 -0
- package/dist/rslang/semantic/arguments-extractor.js +366 -0
- package/dist/rslang/semantic/arguments-extractor.js.map +1 -0
- package/dist/rslang/semantic/type-auditor.d.ts +73 -0
- package/dist/rslang/semantic/type-auditor.js +1570 -0
- package/dist/rslang/semantic/type-auditor.js.map +1 -0
- package/dist/rslang/semantic/typification-api.d.ts +27 -0
- package/dist/rslang/semantic/typification-api.js +320 -0
- package/dist/rslang/semantic/typification-api.js.map +1 -0
- package/dist/rslang/semantic/typification-parser.d.ts +12 -0
- package/dist/rslang/semantic/typification-parser.js +226 -0
- package/dist/rslang/semantic/typification-parser.js.map +1 -0
- package/dist/rslang/semantic/typification.d.ts +119 -0
- package/dist/rslang/semantic/typification.js +74 -0
- package/dist/rslang/semantic/typification.js.map +1 -0
- package/dist/rslang/semantic/value-auditor.d.ts +43 -0
- package/dist/rslang/semantic/value-auditor.js +523 -0
- package/dist/rslang/semantic/value-auditor.js.map +1 -0
- package/dist/rslang/semantic/value-class.d.ts +10 -0
- package/dist/rslang/semantic/value-class.js +9 -0
- package/dist/rslang/semantic/value-class.js.map +1 -0
- package/dist/rslang/typification-graph.d.ts +33 -0
- package/dist/rslang/typification-graph.js +311 -0
- package/dist/rslang/typification-graph.js.map +1 -0
- package/dist/shared/branded.d.ts +7 -0
- package/dist/shared/branded.js +1 -0
- package/dist/shared/branded.js.map +1 -0
- package/dist/shared/hash.d.ts +6 -0
- package/dist/shared/hash.js +18 -0
- package/dist/shared/hash.js.map +1 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +18 -0
- package/dist/shared/index.js.map +1 -0
- package/package.json +184 -0
- package/src/cctext/index.ts +9 -0
- package/src/cctext/language-api.test.ts +149 -0
- package/src/cctext/language-api.ts +285 -0
- package/src/cctext/language.ts +80 -0
- package/src/graph/graph.test.ts +392 -0
- package/src/graph/graph.ts +433 -0
- package/src/graph/index.ts +1 -0
- package/src/index.ts +96 -0
- package/src/library/folder-tree.test.ts +47 -0
- package/src/library/folder-tree.ts +156 -0
- package/src/library/index.ts +46 -0
- package/src/library/library-api.test.ts +32 -0
- package/src/library/library-api.ts +11 -0
- package/src/library/library.ts +61 -0
- package/src/library/oss-api.ts +449 -0
- package/src/library/oss-layout-api.ts +377 -0
- package/src/library/oss-layout.ts +27 -0
- package/src/library/oss.ts +150 -0
- package/src/library/rsengine.ts +593 -0
- package/src/library/rsform-api.ts +533 -0
- package/src/library/rsform.ts +228 -0
- package/src/library/rsmodel-api.ts +340 -0
- package/src/library/rsmodel.ts +50 -0
- package/src/library/structure-planner.ts +143 -0
- package/src/parsing/ast.ts +136 -0
- package/src/parsing/index.ts +15 -0
- package/src/parsing/lezer-tree.ts +69 -0
- package/src/rslang/api.test.ts +116 -0
- package/src/rslang/api.ts +183 -0
- package/src/rslang/ast-annotations.ts +70 -0
- package/src/rslang/error.ts +129 -0
- package/src/rslang/eval/calculator.test.ts +124 -0
- package/src/rslang/eval/calculator.ts +121 -0
- package/src/rslang/eval/evaluation-cache.ts +257 -0
- package/src/rslang/eval/evaluator.test.ts +352 -0
- package/src/rslang/eval/evaluator.ts +935 -0
- package/src/rslang/eval/value-api.test.ts +105 -0
- package/src/rslang/eval/value-api.ts +444 -0
- package/src/rslang/eval/value.ts +102 -0
- package/src/rslang/index.ts +23 -0
- package/src/rslang/labels.ts +191 -0
- package/src/rslang/parser/expression-generator.test.ts +100 -0
- package/src/rslang/parser/expression-generator.ts +466 -0
- package/src/rslang/parser/normalize.test.ts +99 -0
- package/src/rslang/parser/normalize.ts +462 -0
- package/src/rslang/parser/parser.terms.ts +42 -0
- package/src/rslang/parser/parser.test.ts +153 -0
- package/src/rslang/parser/parser.ts +20 -0
- package/src/rslang/parser/rslang.grammar +251 -0
- package/src/rslang/parser/syntax-errors.ts +209 -0
- package/src/rslang/parser/token.ts +106 -0
- package/src/rslang/semantic/analyzer.test.ts +59 -0
- package/src/rslang/semantic/analyzer.ts +179 -0
- package/src/rslang/semantic/arguments-extractor.ts +327 -0
- package/src/rslang/semantic/type-auditor.test.ts +326 -0
- package/src/rslang/semantic/type-auditor.ts +1049 -0
- package/src/rslang/semantic/typification-api.test.ts +46 -0
- package/src/rslang/semantic/typification-api.ts +321 -0
- package/src/rslang/semantic/typification-parser.test.ts +50 -0
- package/src/rslang/semantic/typification-parser.ts +220 -0
- package/src/rslang/semantic/typification.ts +180 -0
- package/src/rslang/semantic/value-auditor.test.ts +206 -0
- package/src/rslang/semantic/value-auditor.ts +332 -0
- package/src/rslang/semantic/value-class.ts +11 -0
- package/src/rslang/typification-graph.ts +155 -0
- package/src/shared/branded.ts +6 -0
- package/src/shared/hash.ts +17 -0
- package/src/shared/index.ts +2 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { makeTypePath, TypeID, type TypePath, type Typification } from '../rslang';
|
|
2
|
+
import { splitTemplateDefinition } from '../rslang/api';
|
|
3
|
+
import { labelType } from '../rslang/labels';
|
|
4
|
+
import { bool, type EchelonFunctional, isTypification } from '../rslang/semantic/typification';
|
|
5
|
+
|
|
6
|
+
import { type Constituenta, CstType, type RSForm } from './rsform';
|
|
7
|
+
import { findCstByStructure } from './rsform-api';
|
|
8
|
+
|
|
9
|
+
/** Represents a node in the structure planner. */
|
|
10
|
+
export interface SPNode {
|
|
11
|
+
key: string;
|
|
12
|
+
path: TypePath;
|
|
13
|
+
type: Typification;
|
|
14
|
+
parent: number | null;
|
|
15
|
+
definition: string;
|
|
16
|
+
existing: Constituenta | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Structure planner for {@link Constituenta}. */
|
|
20
|
+
export class StructurePlanner {
|
|
21
|
+
private items: SPNode[] = [];
|
|
22
|
+
private schema: RSForm;
|
|
23
|
+
private target: Constituenta;
|
|
24
|
+
private rootType: Typification;
|
|
25
|
+
|
|
26
|
+
constructor(schema: RSForm, target: Constituenta) {
|
|
27
|
+
const eff = target.effectiveType;
|
|
28
|
+
if (eff == null) {
|
|
29
|
+
throw new Error('Invalid typification for target');
|
|
30
|
+
}
|
|
31
|
+
this.schema = schema;
|
|
32
|
+
this.target = target;
|
|
33
|
+
if (target.cst_type === CstType.FUNCTION && !isTypification(eff)) {
|
|
34
|
+
this.rootType = (eff as EchelonFunctional).result;
|
|
35
|
+
} else if (isTypification(eff)) {
|
|
36
|
+
this.rootType = eff as Typification;
|
|
37
|
+
} else {
|
|
38
|
+
throw new Error('Invalid typification for target');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public build(): SPNode[] {
|
|
43
|
+
this.items.push({
|
|
44
|
+
key: 'root',
|
|
45
|
+
path: makeTypePath([]),
|
|
46
|
+
type: this.rootType,
|
|
47
|
+
parent: null,
|
|
48
|
+
definition: labelType(this.target.effectiveType),
|
|
49
|
+
existing: this.target
|
|
50
|
+
});
|
|
51
|
+
this.visit(this.rootType, this.rootType.typeID === TypeID.collection ? [0] : [], 0);
|
|
52
|
+
return this.items;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private visit(type: Typification, path: number[], parent: number) {
|
|
56
|
+
switch (type.typeID) {
|
|
57
|
+
case TypeID.collection:
|
|
58
|
+
if (type.base.typeID === TypeID.tuple) {
|
|
59
|
+
type.base.factors.forEach((factor, index) => {
|
|
60
|
+
const nextPath = [...path, index + 1];
|
|
61
|
+
const nextType = bool(factor);
|
|
62
|
+
const nextNode = this.pushNode(nextPath, nextType, parent);
|
|
63
|
+
this.visit(nextType, nextPath, nextNode);
|
|
64
|
+
});
|
|
65
|
+
} else if (type.base.typeID === TypeID.collection) {
|
|
66
|
+
const nextPath = [...path, 0];
|
|
67
|
+
const nextNode = this.pushNode(nextPath, type.base, parent);
|
|
68
|
+
this.visit(type.base, nextPath, nextNode);
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
case TypeID.tuple:
|
|
72
|
+
type.factors.forEach((factor, index) => {
|
|
73
|
+
const nextPath = [...path, index + 1];
|
|
74
|
+
const nextNode = this.pushNode(nextPath, factor, parent);
|
|
75
|
+
this.visit(factor, nextPath, nextNode);
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
default:
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private pushNode(path: number[], type: Typification, parent: number) {
|
|
84
|
+
const typePath = makeTypePath(path);
|
|
85
|
+
this.items.push({
|
|
86
|
+
key: formatPathText(typePath),
|
|
87
|
+
path: typePath,
|
|
88
|
+
type: type,
|
|
89
|
+
parent: parent,
|
|
90
|
+
definition: produceDefinition(this.target, this.rootType, typePath),
|
|
91
|
+
existing: findCstByStructure(this.schema, this.target, typePath)
|
|
92
|
+
});
|
|
93
|
+
return this.items.length - 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ======= Internals =======
|
|
98
|
+
function produceDefinition(target: Constituenta, rootType: Typification, path: TypePath): string {
|
|
99
|
+
let current = rootType;
|
|
100
|
+
let expression = target.alias;
|
|
101
|
+
let index = rootType.typeID === TypeID.collection ? 1 : 0;
|
|
102
|
+
let prefix = '';
|
|
103
|
+
if (target.cst_type === CstType.FUNCTION) {
|
|
104
|
+
prefix = `[${splitTemplateDefinition(target.definition_formal).head}] `;
|
|
105
|
+
const funcType =
|
|
106
|
+
target.effectiveType && !isTypification(target.effectiveType)
|
|
107
|
+
? (target.effectiveType as EchelonFunctional)
|
|
108
|
+
: null;
|
|
109
|
+
if (funcType) {
|
|
110
|
+
expression = `${expression}[${funcType.args.map(arg => arg.alias).join(', ')}]`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
while (index < path.length) {
|
|
115
|
+
const step = path[index];
|
|
116
|
+
switch (current.typeID) {
|
|
117
|
+
case TypeID.collection:
|
|
118
|
+
if (current.base.typeID === TypeID.tuple) {
|
|
119
|
+
expression = `Pr${step}(${expression})`;
|
|
120
|
+
current = bool(current.base.factors[step - 1]);
|
|
121
|
+
index += 1;
|
|
122
|
+
} else {
|
|
123
|
+
expression = `red(${expression})`;
|
|
124
|
+
current = current.base;
|
|
125
|
+
index += 1;
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
case TypeID.tuple:
|
|
129
|
+
expression = `pr${step}(${expression})`;
|
|
130
|
+
current = current.factors[step - 1];
|
|
131
|
+
index += 1;
|
|
132
|
+
break;
|
|
133
|
+
default:
|
|
134
|
+
return prefix + expression;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return prefix + expression;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function formatPathText(path: TypePath): string {
|
|
142
|
+
return path.map(step => String(step)).join('.');
|
|
143
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { type TreeCursor } from './lezer-tree';
|
|
2
|
+
|
|
3
|
+
export const TOKEN_ERROR = 0;
|
|
4
|
+
|
|
5
|
+
/** Represents AST node data. */
|
|
6
|
+
interface AstNodeData extends Record<string, unknown> {
|
|
7
|
+
dataType: string;
|
|
8
|
+
value: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Represents AST structured node base. */
|
|
12
|
+
export interface AstNodeBase {
|
|
13
|
+
typeID: number;
|
|
14
|
+
data: AstNodeData;
|
|
15
|
+
annotation?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Represents AST structured node. */
|
|
19
|
+
export interface AstNode extends Record<string, unknown>, AstNodeBase {
|
|
20
|
+
uid: number;
|
|
21
|
+
from: number;
|
|
22
|
+
to: number;
|
|
23
|
+
hasError: boolean;
|
|
24
|
+
parenthesis?: boolean;
|
|
25
|
+
parent: AstNode | null;
|
|
26
|
+
children: AstNode[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Represents AST node. */
|
|
30
|
+
export interface FlatAstNode extends Record<string, unknown>, AstNodeBase {
|
|
31
|
+
uid: number;
|
|
32
|
+
parent: number | null;
|
|
33
|
+
from: number;
|
|
34
|
+
to: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Represents Syntax tree flat representation. */
|
|
38
|
+
export type FlatAST = FlatAstNode[];
|
|
39
|
+
|
|
40
|
+
/** Builds AST tree from a given tree cursor, generating unique uids for each node. */
|
|
41
|
+
export function buildTree(cursor: TreeCursor): AstNode {
|
|
42
|
+
let nextUid = 1;
|
|
43
|
+
function genUid() {
|
|
44
|
+
return nextUid++;
|
|
45
|
+
}
|
|
46
|
+
return buildTreeInternal(cursor, null, genUid);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Flattens AST tree to a array form. */
|
|
50
|
+
export function flattenAst(node: AstNode, parent: number | null = null, out: FlatAST = []): FlatAST {
|
|
51
|
+
out.push({
|
|
52
|
+
uid: node.uid,
|
|
53
|
+
parent: parent,
|
|
54
|
+
typeID: node.typeID,
|
|
55
|
+
from: node.from,
|
|
56
|
+
to: node.to,
|
|
57
|
+
data: node.data,
|
|
58
|
+
annotation: node.annotation
|
|
59
|
+
});
|
|
60
|
+
for (const child of node.children) {
|
|
61
|
+
flattenAst(child, node.uid, out);
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Visits AST tree in depth-first order. */
|
|
67
|
+
export function visitAstDFS(node: AstNode, callback: (node: AstNode) => void) {
|
|
68
|
+
for (const child of node.children) {
|
|
69
|
+
visitAstDFS(child, callback);
|
|
70
|
+
}
|
|
71
|
+
callback(node);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Finds and returns the AstNode with the given ui. */
|
|
75
|
+
export function findByUid(root: AstNode, uid: number): AstNode | null {
|
|
76
|
+
let found: AstNode | null = null;
|
|
77
|
+
visitAstDFS(root, node => {
|
|
78
|
+
if (node.uid === uid && !found) {
|
|
79
|
+
found = node;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
return found;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Prints AST tree. */
|
|
86
|
+
export function printAst(node: AstNode, printNode: (node: AstNode) => string): string {
|
|
87
|
+
let children: string = '';
|
|
88
|
+
for (const child of node.children) {
|
|
89
|
+
children += `${printAst(child, printNode)}`;
|
|
90
|
+
}
|
|
91
|
+
return `[${printNode(node)}${children}]`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Extracts node text. */
|
|
95
|
+
export function getNodeText(node: AstNode): string {
|
|
96
|
+
if (node.data.dataType === 'string' && typeof node.data.value === 'string') {
|
|
97
|
+
return node.data.value;
|
|
98
|
+
}
|
|
99
|
+
return `NO DATA NODE: ${node.typeID}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Extracts node indices. */
|
|
103
|
+
export function getNodeIndices(node: AstNode): number[] {
|
|
104
|
+
if (node.data.dataType === 'string[]' && Array.isArray(node.data.value)) {
|
|
105
|
+
return (node.data.value as string[]).map(s => parseInt(s, 10)).filter(n => !isNaN(n));
|
|
106
|
+
}
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ======== Internals ========
|
|
111
|
+
function buildTreeInternal(cursor: TreeCursor, parent: AstNode | null = null, genUid: () => number): AstNode {
|
|
112
|
+
const node = cursor.node;
|
|
113
|
+
|
|
114
|
+
const result: AstNode = {
|
|
115
|
+
uid: genUid(),
|
|
116
|
+
typeID: node.type.isError ? 0 : node.type.id,
|
|
117
|
+
from: node.from,
|
|
118
|
+
to: node.to,
|
|
119
|
+
hasError: node.type.isError,
|
|
120
|
+
data: node.type.isError ? { dataType: 'null', value: null } : { dataType: 'string', value: node.type.name },
|
|
121
|
+
parent,
|
|
122
|
+
children: []
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
if (cursor.firstChild()) {
|
|
126
|
+
do {
|
|
127
|
+
const child = buildTreeInternal(cursor, result, genUid);
|
|
128
|
+
if (child.hasError) {
|
|
129
|
+
result.hasError = true;
|
|
130
|
+
}
|
|
131
|
+
result.children.push(child);
|
|
132
|
+
} while (cursor.nextSibling());
|
|
133
|
+
cursor.parent();
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type AstNode,
|
|
3
|
+
type AstNodeBase,
|
|
4
|
+
buildTree,
|
|
5
|
+
findByUid,
|
|
6
|
+
type FlatAST,
|
|
7
|
+
type FlatAstNode,
|
|
8
|
+
flattenAst,
|
|
9
|
+
getNodeIndices,
|
|
10
|
+
getNodeText,
|
|
11
|
+
printAst,
|
|
12
|
+
TOKEN_ERROR,
|
|
13
|
+
visitAstDFS
|
|
14
|
+
} from './ast';
|
|
15
|
+
export { type CMSyntaxNode, printTree } from './lezer-tree';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type NodeType, type Tree, type TreeCursor } from '@lezer/common';
|
|
2
|
+
export { type TreeCursor } from '@lezer/common';
|
|
3
|
+
|
|
4
|
+
/** Represents syntax tree node data. */
|
|
5
|
+
export interface CMSyntaxNode {
|
|
6
|
+
type: NodeType;
|
|
7
|
+
from: number;
|
|
8
|
+
to: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Prints tree to compact string. */
|
|
12
|
+
export function printTree(tree: Tree): string {
|
|
13
|
+
const state = {
|
|
14
|
+
output: '',
|
|
15
|
+
prefixes: [] as string[]
|
|
16
|
+
};
|
|
17
|
+
traverseTree(tree, {
|
|
18
|
+
onEnter: node => {
|
|
19
|
+
state.output += '[';
|
|
20
|
+
state.output += node.type.name;
|
|
21
|
+
},
|
|
22
|
+
onLeave: () => {
|
|
23
|
+
state.output += ']';
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return state.output;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ======== Internals ========
|
|
30
|
+
|
|
31
|
+
interface CursorNode extends CMSyntaxNode {
|
|
32
|
+
isLeaf: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function cursorNode({ type, from, to }: TreeCursor, isLeaf = false): CursorNode {
|
|
36
|
+
return { type, from, to, isLeaf };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface TreeTraversalOptions {
|
|
40
|
+
beforeEnter?: (cursor: TreeCursor) => void;
|
|
41
|
+
onEnter: (node: CursorNode) => false | void;
|
|
42
|
+
onLeave?: (node: CursorNode) => false | void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Implements depth-first traversal. */
|
|
46
|
+
function traverseTree(tree: Tree, { beforeEnter, onEnter, onLeave }: TreeTraversalOptions) {
|
|
47
|
+
const cursor = tree.cursor();
|
|
48
|
+
for (;;) {
|
|
49
|
+
let node = cursorNode(cursor);
|
|
50
|
+
let leave = false;
|
|
51
|
+
const enter = !node.type.isAnonymous;
|
|
52
|
+
if (enter && beforeEnter) beforeEnter(cursor);
|
|
53
|
+
node.isLeaf = !cursor.firstChild();
|
|
54
|
+
if (enter) {
|
|
55
|
+
leave = true;
|
|
56
|
+
if (onEnter(node) === false) return;
|
|
57
|
+
}
|
|
58
|
+
if (!node.isLeaf) continue;
|
|
59
|
+
for (;;) {
|
|
60
|
+
node = cursorNode(cursor, node.isLeaf);
|
|
61
|
+
if (leave && onLeave) if (onLeave(node) === false) return;
|
|
62
|
+
leave = cursor.type.isAnonymous;
|
|
63
|
+
node.isLeaf = false;
|
|
64
|
+
if (cursor.nextSibling()) break;
|
|
65
|
+
if (!cursor.parent()) return;
|
|
66
|
+
leave = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
applyTypificationMapping,
|
|
5
|
+
extractGlobals,
|
|
6
|
+
isSetTypification,
|
|
7
|
+
isSimpleExpression,
|
|
8
|
+
splitTemplateDefinition
|
|
9
|
+
} from './api';
|
|
10
|
+
|
|
11
|
+
const globalsData: [string, string][] = [
|
|
12
|
+
['', ''],
|
|
13
|
+
['X1', 'X1'],
|
|
14
|
+
['X11 X1 X11', 'X11 X1'],
|
|
15
|
+
['∀α∈S1 ∀β∈F1[α] Pr1,1(β)∩β=∅', 'S1 F1']
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
describe('Testing extract globals', () => {
|
|
19
|
+
globalsData.forEach(([input, expected]) => {
|
|
20
|
+
it(`Extract globals "${input}"`, () => {
|
|
21
|
+
const result = extractGlobals(input);
|
|
22
|
+
expect([...result].join(' ')).toBe(expected);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const simpleExpressionData: [string, string][] = [
|
|
28
|
+
['', 'true'],
|
|
29
|
+
['Pr1(S1)', 'true'],
|
|
30
|
+
['pr1(S1)', 'true'],
|
|
31
|
+
['red(S1)', 'true'],
|
|
32
|
+
['red(Pr1(F1[α,σ]))', 'true'],
|
|
33
|
+
['ℬℬ(X1)', 'false'],
|
|
34
|
+
['ℬ(X1)', 'false'],
|
|
35
|
+
['D{(α,β)∈D6×D6 | α≠β & α∩β≠∅}', 'false'],
|
|
36
|
+
['D{ξ ∈ X1 | (ξ,ξ)∈S1 }', 'false'],
|
|
37
|
+
['I{(β,α) | α:∈D2; σ:=F5[α]; β:∈σ}', 'false'],
|
|
38
|
+
['∀σ∈S1 (F1[σ]×F1[σ])∩D11=∅', 'false']
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
describe('Testing simple expression', () => {
|
|
42
|
+
simpleExpressionData.forEach(([input, expected]) => {
|
|
43
|
+
it(`isSimpleExpression "${input}"`, () => {
|
|
44
|
+
const result = isSimpleExpression(input);
|
|
45
|
+
expect(String(result)).toBe(expected);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const splitData: [string, string][] = [
|
|
51
|
+
['', '||'],
|
|
52
|
+
['[α∈ℬ(R1)] α⊆red(σ)', 'α∈ℬ(R1)||α⊆red(σ)'],
|
|
53
|
+
['[α∈ℬ(R1)] α⊆red(σ) ', 'α∈ℬ(R1)||α⊆red(σ)'],
|
|
54
|
+
[' [α∈ℬ(R1)] α⊆red(σ)', 'α∈ℬ(R1)||α⊆red(σ)'],
|
|
55
|
+
['[α∈ℬ(R1)]α⊆red(σ)', 'α∈ℬ(R1)||α⊆red(σ)'],
|
|
56
|
+
['[α∈ℬ(R1), σ∈ℬℬ(R1)] α⊆red(σ)', 'α∈ℬ(R1), σ∈ℬℬ(R1)||α⊆red(σ)']
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
describe('Testing split template', () => {
|
|
60
|
+
splitData.forEach(([input, expected]) => {
|
|
61
|
+
it(`Split "${input}"`, () => {
|
|
62
|
+
const result = splitTemplateDefinition(input);
|
|
63
|
+
expect(`${result.head}||${result.body}`).toBe(expected);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const typificationMappingData: [string, string, string, string][] = [
|
|
69
|
+
['', '', '', ''],
|
|
70
|
+
['X1', 'X2', 'X1', 'X2'],
|
|
71
|
+
['X1', 'X2', 'ℬ(X1)', 'ℬ(X2)'],
|
|
72
|
+
['X1', 'X2', 'X1×X3', 'X2×X3'],
|
|
73
|
+
['X1', '(X1×X1)', 'X1', 'X1×X1'],
|
|
74
|
+
['X1', '(X1×X1)', 'ℬ(X1)', 'ℬ(X1×X1)'],
|
|
75
|
+
['X1', '(X1×X1)', 'ℬ(X1×X2)', 'ℬ((X1×X1)×X2)'],
|
|
76
|
+
['X1', 'ℬ(X3)', 'ℬ(X1)', 'ℬℬ(X3)'],
|
|
77
|
+
['X1', 'ℬ(X3)', 'ℬ(X1×X1)', 'ℬ(ℬ(X3)×ℬ(X3))']
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
describe('Testing typification mapping', () => {
|
|
81
|
+
typificationMappingData.forEach(([original, replacement, input, expected]) => {
|
|
82
|
+
it(`Apply typification mapping: ${original} -> ${replacement} in "${input}"`, () => {
|
|
83
|
+
const result = applyTypificationMapping(input, { [original]: replacement });
|
|
84
|
+
expect(result).toBe(expected);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('Testing isSetTypification', () => {
|
|
89
|
+
const isSetTypificationData: [string, boolean][] = [
|
|
90
|
+
['ℬ(X1)', true],
|
|
91
|
+
['ℬ(C1)', true],
|
|
92
|
+
['ℬ(Z)', true],
|
|
93
|
+
['ℬ(ℬ(X1))', true],
|
|
94
|
+
['ℬ(X1×X2)', true],
|
|
95
|
+
['ℬ(X1×Z)', true],
|
|
96
|
+
['ℬ(X1×C1)', true],
|
|
97
|
+
['ℬℬ(X1×X2)', true],
|
|
98
|
+
['ℬℬ(ℬ(X1×X2)×ℬ(X2))', true],
|
|
99
|
+
['ℬ(ℬ(X1×X2)×ℬ(X2))', true],
|
|
100
|
+
['ℬ(ℬℬ(X1×X2)×ℬ(X2))', true],
|
|
101
|
+
['Z', false],
|
|
102
|
+
['С1', false],
|
|
103
|
+
['X1', false],
|
|
104
|
+
['X1×X2', false],
|
|
105
|
+
['ℬ(', false],
|
|
106
|
+
['ℬX1', false],
|
|
107
|
+
['', false],
|
|
108
|
+
['ℬ(ℬ(X1×X2)×ℬ(X2)×X42)', true]
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
isSetTypificationData.forEach(([input, expected]) => {
|
|
112
|
+
it(`isSetTypification("${input}") should be ${expected}`, () => {
|
|
113
|
+
expect(isSetTypification(input)).toBe(expected);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module: API for RSLanguage.
|
|
3
|
+
*/
|
|
4
|
+
import { type AstNode } from '../parsing';
|
|
5
|
+
|
|
6
|
+
import { ArgumentsExtractor, type ArgumentsType } from './semantic/arguments-extractor';
|
|
7
|
+
export { generateExpressionFromAst } from './parser/expression-generator';
|
|
8
|
+
|
|
9
|
+
/** Represents alias mapping. */
|
|
10
|
+
export type AliasMapping = Record<string, string>;
|
|
11
|
+
|
|
12
|
+
// cspell:disable
|
|
13
|
+
const LOCALS_REGEXP = /[_a-zα-ω][a-zα-ω]*\d*/g;
|
|
14
|
+
const GLOBALS_REGEXP = /[XCSADFPTN]\d+/g;
|
|
15
|
+
const COMPLEX_SYMBOLS_REGEXP = /[∀∃×ℬ;|:]/g;
|
|
16
|
+
const TYPIFICATION_SET = /^ℬ+\([ℬ\(((X|C)\d+|Z)\)×]*\)$/g;
|
|
17
|
+
// cspell:enable
|
|
18
|
+
|
|
19
|
+
/** Extract arguments from AST. */
|
|
20
|
+
export function extractArguments(ast: AstNode): ArgumentsType[] {
|
|
21
|
+
return new ArgumentsExtractor().run(ast);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Extracts global variable names from a given expression. */
|
|
25
|
+
export function extractGlobals(expression: string): Set<string> {
|
|
26
|
+
return new Set(expression.match(GLOBALS_REGEXP) ?? []);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Check if expression is simple derivation. */
|
|
30
|
+
export function isSimpleExpression(text: string): boolean {
|
|
31
|
+
return !text.match(COMPLEX_SYMBOLS_REGEXP);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Check if expression is set typification. */
|
|
35
|
+
export function isSetTypification(text: string): boolean {
|
|
36
|
+
return !!text.match(TYPIFICATION_SET);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Splits a string containing a template definition into its head and body parts.
|
|
41
|
+
*
|
|
42
|
+
* A template definition is expected to have the following format: `[head] body`.
|
|
43
|
+
* If the input string does not contain the opening square bracket '[', the entire
|
|
44
|
+
* string is treated as the body, and an empty string is assigned to the head.
|
|
45
|
+
* If the opening bracket is present, the function attempts to find the matching
|
|
46
|
+
* closing bracket ']' to determine the head and body parts.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const template = '[header] body content';
|
|
50
|
+
* const result = splitTemplateDefinition(template);
|
|
51
|
+
* // result: `{ head: 'header', body: 'body content' }`
|
|
52
|
+
*/
|
|
53
|
+
export function splitTemplateDefinition(target: string) {
|
|
54
|
+
let start = 0;
|
|
55
|
+
for (; start < target.length && target[start] !== '['; ++start);
|
|
56
|
+
if (start < target.length) {
|
|
57
|
+
for (let counter = 0, end = start + 1; end < target.length; ++end) {
|
|
58
|
+
if (target[end] === '[') {
|
|
59
|
+
++counter;
|
|
60
|
+
} else if (target[end] === ']') {
|
|
61
|
+
if (counter !== 0) {
|
|
62
|
+
--counter;
|
|
63
|
+
} else {
|
|
64
|
+
return {
|
|
65
|
+
head: target.substring(start + 1, end).trim(),
|
|
66
|
+
body: target.substring(end + 1).trim()
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
head: '',
|
|
74
|
+
body: target
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Substitutes values for template arguments in a given expression.
|
|
80
|
+
*
|
|
81
|
+
* This function takes an input mathematical expression and a list of argument values.
|
|
82
|
+
* It replaces template argument placeholders in the expression with their corresponding values
|
|
83
|
+
* from the provided arguments.
|
|
84
|
+
*/
|
|
85
|
+
export function substituteTemplateArgs(expression: string, mapping: AliasMapping): string {
|
|
86
|
+
if (Object.keys(mapping).length === 0) {
|
|
87
|
+
return expression;
|
|
88
|
+
}
|
|
89
|
+
let { head, body } = splitTemplateDefinition(expression);
|
|
90
|
+
body = applyPattern(body, mapping, LOCALS_REGEXP);
|
|
91
|
+
const argTexts = head.split(',').map(text => text.trim());
|
|
92
|
+
head = argTexts
|
|
93
|
+
.filter(arg => [...arg.matchAll(LOCALS_REGEXP)].every(local => local.every(match => !(match in mapping))))
|
|
94
|
+
.join(', ');
|
|
95
|
+
|
|
96
|
+
if (!head) {
|
|
97
|
+
return body;
|
|
98
|
+
} else {
|
|
99
|
+
return `[${head}] ${body}`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Apply alias mapping. */
|
|
104
|
+
export function applyAliasMapping(target: string, mapping: AliasMapping): string {
|
|
105
|
+
return applyPattern(target, mapping, GLOBALS_REGEXP);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Apply alias typification mapping. */
|
|
109
|
+
export function applyTypificationMapping(target: string, mapping: AliasMapping): string {
|
|
110
|
+
const modified = applyAliasMapping(target, mapping);
|
|
111
|
+
if (modified === target) {
|
|
112
|
+
return target;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const deleteBrackets: number[] = [];
|
|
116
|
+
const positions: number[] = [];
|
|
117
|
+
const booleans: number[] = [];
|
|
118
|
+
let boolCount: number = 0;
|
|
119
|
+
let stackSize: number = 0;
|
|
120
|
+
|
|
121
|
+
for (let i = 0; i < modified.length; i++) {
|
|
122
|
+
const char = modified[i];
|
|
123
|
+
if (char === 'ℬ') {
|
|
124
|
+
boolCount++;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (char === '(') {
|
|
128
|
+
stackSize++;
|
|
129
|
+
positions.push(i);
|
|
130
|
+
booleans.push(boolCount);
|
|
131
|
+
}
|
|
132
|
+
boolCount = 0;
|
|
133
|
+
if (char === ')') {
|
|
134
|
+
if (
|
|
135
|
+
i < modified.length - 1 &&
|
|
136
|
+
modified[i + 1] === ')' &&
|
|
137
|
+
stackSize > 1 &&
|
|
138
|
+
positions[stackSize - 2] + booleans[stackSize - 1] + 1 === positions[stackSize - 1]
|
|
139
|
+
) {
|
|
140
|
+
deleteBrackets.push(i);
|
|
141
|
+
deleteBrackets.push(positions[stackSize - 2]);
|
|
142
|
+
}
|
|
143
|
+
if (i === modified.length - 1 && stackSize === 1 && positions[0] === 0) {
|
|
144
|
+
deleteBrackets.push(i);
|
|
145
|
+
deleteBrackets.push(positions[0]);
|
|
146
|
+
}
|
|
147
|
+
stackSize--;
|
|
148
|
+
positions.pop();
|
|
149
|
+
booleans.pop();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let result = '';
|
|
154
|
+
for (let i = 0; i < modified.length; i++) {
|
|
155
|
+
if (!deleteBrackets.includes(i)) {
|
|
156
|
+
result += modified[i];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ====== Internals =========
|
|
164
|
+
/** Text substitution guided by mapping and regular expression. */
|
|
165
|
+
function applyPattern(text: string, mapping: AliasMapping, pattern: RegExp): string {
|
|
166
|
+
if (text === '' || pattern === null) {
|
|
167
|
+
return text;
|
|
168
|
+
}
|
|
169
|
+
let posInput = 0;
|
|
170
|
+
let output = '';
|
|
171
|
+
const patternMatches = text.matchAll(pattern);
|
|
172
|
+
for (const segment of patternMatches) {
|
|
173
|
+
const entity = segment[0];
|
|
174
|
+
const start = segment.index ?? 0;
|
|
175
|
+
if (entity in mapping) {
|
|
176
|
+
output += text.substring(posInput, start);
|
|
177
|
+
output += mapping[entity];
|
|
178
|
+
posInput = start + segment[0].length;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
output += text.substring(posInput);
|
|
182
|
+
return output;
|
|
183
|
+
}
|