@malloydata/motly-ts-parser 0.0.1

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/build/ast.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ /** A scalar or reference value. */
2
+ export type ScalarValue = {
3
+ kind: "string";
4
+ value: string;
5
+ } | {
6
+ kind: "number";
7
+ value: number;
8
+ } | {
9
+ kind: "boolean";
10
+ value: boolean;
11
+ } | {
12
+ kind: "date";
13
+ value: string;
14
+ } | {
15
+ kind: "reference";
16
+ ups: number;
17
+ path: RefPathSegment[];
18
+ };
19
+ /** A segment in a reference path: either a named property or an array index. */
20
+ export type RefPathSegment = {
21
+ kind: "name";
22
+ name: string;
23
+ } | {
24
+ kind: "index";
25
+ index: number;
26
+ };
27
+ /** A value that can be assigned with `=`. */
28
+ export type TagValue = {
29
+ kind: "scalar";
30
+ value: ScalarValue;
31
+ } | {
32
+ kind: "array";
33
+ elements: ArrayElement[];
34
+ };
35
+ /** An element in an array literal. */
36
+ export interface ArrayElement {
37
+ value: TagValue | null;
38
+ properties: Statement[] | null;
39
+ }
40
+ /** A parsed statement (the IR between the parser and interpreter). */
41
+ export type Statement = {
42
+ kind: "setEq";
43
+ path: string[];
44
+ value: TagValue;
45
+ properties: Statement[] | null;
46
+ preserveProperties: boolean;
47
+ } | {
48
+ kind: "replaceProperties";
49
+ path: string[];
50
+ properties: Statement[];
51
+ preserveValue: boolean;
52
+ } | {
53
+ kind: "updateProperties";
54
+ path: string[];
55
+ properties: Statement[];
56
+ } | {
57
+ kind: "define";
58
+ path: string[];
59
+ deleted: boolean;
60
+ } | {
61
+ kind: "clearAll";
62
+ };
package/build/ast.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export type { MOTLYScalar, MOTLYRef, MOTLYValue, MOTLYNode, MOTLYError, MOTLYSchemaError, MOTLYValidationError, } from "motly-ts-interface";
2
+ export { MOTLYSession } from "./session";
package/build/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MOTLYSession = void 0;
4
+ var session_1 = require("./session");
5
+ Object.defineProperty(exports, "MOTLYSession", { enumerable: true, get: function () { return session_1.MOTLYSession; } });
@@ -0,0 +1,4 @@
1
+ import { Statement } from "./ast";
2
+ import { MOTLYValue } from "motly-ts-interface";
3
+ /** Execute a list of parsed statements against an existing MOTLYValue. */
4
+ export declare function execute(statements: Statement[], root: MOTLYValue): MOTLYValue;
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.execute = execute;
4
+ /** Execute a list of parsed statements against an existing MOTLYValue. */
5
+ function execute(statements, root) {
6
+ for (const stmt of statements) {
7
+ executeStatement(stmt, root);
8
+ }
9
+ return root;
10
+ }
11
+ function executeStatement(stmt, node) {
12
+ switch (stmt.kind) {
13
+ case "setEq":
14
+ executeSetEq(node, stmt.path, stmt.value, stmt.properties, stmt.preserveProperties);
15
+ break;
16
+ case "replaceProperties":
17
+ executeReplaceProperties(node, stmt.path, stmt.properties, stmt.preserveValue);
18
+ break;
19
+ case "updateProperties":
20
+ executeUpdateProperties(node, stmt.path, stmt.properties);
21
+ break;
22
+ case "define":
23
+ executeDefine(node, stmt.path, stmt.deleted);
24
+ break;
25
+ case "clearAll":
26
+ node.properties = {};
27
+ break;
28
+ }
29
+ }
30
+ function executeSetEq(node, path, value, properties, preserveProperties) {
31
+ // Reference without properties → produce a link
32
+ if (value.kind === "scalar" &&
33
+ value.value.kind === "reference" &&
34
+ properties === null &&
35
+ !preserveProperties) {
36
+ const [writeKey, parent] = buildAccessPath(node, path);
37
+ const props = getOrCreateProperties(parent);
38
+ props[writeKey] = {
39
+ linkTo: formatRefString(value.value.ups, value.value.path),
40
+ };
41
+ return;
42
+ }
43
+ const [writeKey, parent] = buildAccessPath(node, path);
44
+ if (properties !== null) {
45
+ // name = value { new_properties } - set value and replace properties
46
+ const result = createValueNode(value);
47
+ for (const s of properties) {
48
+ executeStatement(s, result);
49
+ }
50
+ const props = getOrCreateProperties(parent);
51
+ props[writeKey] = result;
52
+ }
53
+ else if (preserveProperties) {
54
+ // name = value { ... } - update value, preserve existing properties
55
+ const props = getOrCreateProperties(parent);
56
+ const existing = props[writeKey];
57
+ if (existing !== undefined && !isRef(existing)) {
58
+ const result = createValueNode(value);
59
+ if (existing.properties) {
60
+ result.properties = existing.properties;
61
+ }
62
+ props[writeKey] = result;
63
+ }
64
+ else {
65
+ props[writeKey] = createValueNode(value);
66
+ }
67
+ }
68
+ else {
69
+ // name = value - simple assignment
70
+ const props = getOrCreateProperties(parent);
71
+ props[writeKey] = createValueNode(value);
72
+ }
73
+ }
74
+ function executeReplaceProperties(node, path, properties, preserveValue) {
75
+ const [writeKey, parent] = buildAccessPath(node, path);
76
+ const result = {};
77
+ if (preserveValue) {
78
+ const props = getOrCreateProperties(parent);
79
+ const existing = props[writeKey];
80
+ if (existing !== undefined && !isRef(existing)) {
81
+ result.eq = existing.eq;
82
+ }
83
+ }
84
+ for (const stmt of properties) {
85
+ executeStatement(stmt, result);
86
+ }
87
+ const props = getOrCreateProperties(parent);
88
+ props[writeKey] = result;
89
+ }
90
+ function executeUpdateProperties(node, path, properties) {
91
+ const [writeKey, parent] = buildAccessPath(node, path);
92
+ const props = getOrCreateProperties(parent);
93
+ let target = props[writeKey];
94
+ if (target === undefined) {
95
+ target = {};
96
+ props[writeKey] = target;
97
+ }
98
+ if (isRef(target)) {
99
+ const newNode = {};
100
+ for (const stmt of properties) {
101
+ executeStatement(stmt, newNode);
102
+ }
103
+ props[writeKey] = newNode;
104
+ }
105
+ else {
106
+ for (const stmt of properties) {
107
+ executeStatement(stmt, target);
108
+ }
109
+ }
110
+ }
111
+ function executeDefine(node, path, deleted) {
112
+ const [writeKey, parent] = buildAccessPath(node, path);
113
+ const props = getOrCreateProperties(parent);
114
+ if (deleted) {
115
+ props[writeKey] = { deleted: true };
116
+ }
117
+ else {
118
+ props[writeKey] = {};
119
+ }
120
+ }
121
+ /** Navigate to the parent of the final path segment, creating intermediate nodes. */
122
+ function buildAccessPath(node, path) {
123
+ let current = node;
124
+ for (let i = 0; i < path.length - 1; i++) {
125
+ const segment = path[i];
126
+ const props = getOrCreateProperties(current);
127
+ let entry = props[segment];
128
+ if (entry === undefined) {
129
+ entry = {};
130
+ props[segment] = entry;
131
+ }
132
+ if (isRef(entry)) {
133
+ entry = {};
134
+ props[segment] = entry;
135
+ }
136
+ current = entry;
137
+ }
138
+ return [path[path.length - 1], current];
139
+ }
140
+ /** Convert an AST TagValue to a MOTLYValue. */
141
+ function createValueNode(value) {
142
+ if (value.kind === "array") {
143
+ return { eq: resolveArray(value.elements) };
144
+ }
145
+ const sv = value.value;
146
+ switch (sv.kind) {
147
+ case "string":
148
+ return { eq: sv.value };
149
+ case "number":
150
+ return { eq: sv.value };
151
+ case "boolean":
152
+ return { eq: sv.value };
153
+ case "date":
154
+ return { eq: new Date(sv.value) };
155
+ case "reference":
156
+ // Should not be reached for simple ref assignments (handled above).
157
+ return {};
158
+ }
159
+ }
160
+ /** Resolve an array of AST elements to MOTLYNodes. */
161
+ function resolveArray(elements) {
162
+ return elements.map(resolveArrayElement);
163
+ }
164
+ function resolveArrayElement(el) {
165
+ // Reference without properties becomes a link
166
+ if (el.value !== null &&
167
+ el.value.kind === "scalar" &&
168
+ el.value.value.kind === "reference" &&
169
+ el.properties === null) {
170
+ return {
171
+ linkTo: formatRefString(el.value.value.ups, el.value.value.path),
172
+ };
173
+ }
174
+ const node = {};
175
+ if (el.value !== null) {
176
+ if (el.value.kind === "array") {
177
+ node.eq = resolveArray(el.value.elements);
178
+ }
179
+ else {
180
+ const sv = el.value.value;
181
+ switch (sv.kind) {
182
+ case "string":
183
+ node.eq = sv.value;
184
+ break;
185
+ case "number":
186
+ node.eq = sv.value;
187
+ break;
188
+ case "boolean":
189
+ node.eq = sv.value;
190
+ break;
191
+ case "date":
192
+ node.eq = new Date(sv.value);
193
+ break;
194
+ case "reference":
195
+ // Reference with properties: ignore the reference value
196
+ break;
197
+ }
198
+ }
199
+ }
200
+ if (el.properties !== null) {
201
+ for (const stmt of el.properties) {
202
+ executeStatement(stmt, node);
203
+ }
204
+ }
205
+ return node;
206
+ }
207
+ /** Format a reference path back to its string form: `$^^name[0].sub` */
208
+ function formatRefString(ups, path) {
209
+ let s = "$";
210
+ for (let i = 0; i < ups; i++)
211
+ s += "^";
212
+ let first = true;
213
+ for (const seg of path) {
214
+ if (seg.kind === "name") {
215
+ if (!first)
216
+ s += ".";
217
+ s += seg.name;
218
+ first = false;
219
+ }
220
+ else {
221
+ s += `[${seg.index}]`;
222
+ }
223
+ }
224
+ return s;
225
+ }
226
+ /** Check if a MOTLYNode is a MOTLYRef. */
227
+ function isRef(node) {
228
+ return "linkTo" in node;
229
+ }
230
+ /** Get or create the properties object on a MOTLYValue. */
231
+ function getOrCreateProperties(node) {
232
+ if (!node.properties) {
233
+ node.properties = {};
234
+ }
235
+ return node.properties;
236
+ }
@@ -0,0 +1,3 @@
1
+ import { Statement } from "./ast";
2
+ /** Parse a MOTLY input string into a list of statements. */
3
+ export declare function parse(input: string): Statement[];