worldorbit 2.5.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/LICENSE.md +5 -0
- package/README.md +250 -0
- package/dist/browser/core/dist/index.js +4009 -0
- package/dist/browser/markdown/dist/index.js +3951 -0
- package/dist/browser/viewer/dist/index.js +5981 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.js +84 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.js +16 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +25 -0
- package/dist/normalize.d.ts +2 -0
- package/dist/normalize.js +243 -0
- package/dist/parse.d.ts +2 -0
- package/dist/parse.js +126 -0
- package/dist/render.d.ts +6 -0
- package/dist/render.js +683 -0
- package/dist/tokenize.d.ts +4 -0
- package/dist/tokenize.js +68 -0
- package/dist/types.d.ts +208 -0
- package/dist/types.js +1 -0
- package/dist/unpkg/core/dist/index.js +4081 -0
- package/dist/unpkg/markdown/dist/index.js +3979 -0
- package/dist/unpkg/test.html +1 -0
- package/dist/unpkg/viewer/dist/index.js +6038 -0
- package/dist/unpkg/worldorbit-core.min.js +5 -0
- package/dist/unpkg/worldorbit-markdown.min.js +81 -0
- package/dist/unpkg/worldorbit-viewer.min.js +232 -0
- package/dist/unpkg/worldorbit.d.ts +2 -0
- package/dist/unpkg/worldorbit.js +2 -0
- package/dist/unpkg/worldorbit.min.js +236 -0
- package/dist/validate.d.ts +2 -0
- package/dist/validate.js +31 -0
- package/dist/viewer-state.d.ts +16 -0
- package/dist/viewer-state.js +130 -0
- package/dist/viewer.d.ts +2 -0
- package/dist/viewer.js +434 -0
- package/package.json +64 -0
- package/packages/core/README.md +13 -0
- package/packages/core/dist/atlas-edit.d.ts +11 -0
- package/packages/core/dist/atlas-edit.js +210 -0
- package/packages/core/dist/diagnostics.d.ts +10 -0
- package/packages/core/dist/diagnostics.js +109 -0
- package/packages/core/dist/draft-parse.d.ts +3 -0
- package/packages/core/dist/draft-parse.js +642 -0
- package/packages/core/dist/draft.d.ts +15 -0
- package/packages/core/dist/draft.js +343 -0
- package/packages/core/dist/errors.d.ts +7 -0
- package/packages/core/dist/errors.js +16 -0
- package/packages/core/dist/format.d.ts +4 -0
- package/packages/core/dist/format.js +364 -0
- package/packages/core/dist/index.d.ts +28 -0
- package/packages/core/dist/index.js +44 -0
- package/packages/core/dist/load.d.ts +4 -0
- package/packages/core/dist/load.js +130 -0
- package/packages/core/dist/markdown.d.ts +2 -0
- package/packages/core/dist/markdown.js +37 -0
- package/packages/core/dist/normalize.d.ts +2 -0
- package/packages/core/dist/normalize.js +304 -0
- package/packages/core/dist/parse.d.ts +2 -0
- package/packages/core/dist/parse.js +133 -0
- package/packages/core/dist/scene.d.ts +3 -0
- package/packages/core/dist/scene.js +1484 -0
- package/packages/core/dist/schema.d.ts +8 -0
- package/packages/core/dist/schema.js +298 -0
- package/packages/core/dist/tokenize.d.ts +4 -0
- package/packages/core/dist/tokenize.js +68 -0
- package/packages/core/dist/types.d.ts +382 -0
- package/packages/core/dist/types.js +1 -0
- package/packages/core/dist/validate.d.ts +2 -0
- package/packages/core/dist/validate.js +56 -0
- package/packages/editor/dist/editor.d.ts +2 -0
- package/packages/editor/dist/editor.js +2620 -0
- package/packages/editor/dist/index.d.ts +2 -0
- package/packages/editor/dist/index.js +1 -0
- package/packages/editor/dist/types.d.ts +53 -0
- package/packages/editor/dist/types.js +1 -0
- package/packages/markdown/README.md +9 -0
- package/packages/markdown/dist/html.d.ts +3 -0
- package/packages/markdown/dist/html.js +57 -0
- package/packages/markdown/dist/index.d.ts +4 -0
- package/packages/markdown/dist/index.js +3 -0
- package/packages/markdown/dist/rehype.d.ts +10 -0
- package/packages/markdown/dist/rehype.js +49 -0
- package/packages/markdown/dist/remark.d.ts +9 -0
- package/packages/markdown/dist/remark.js +28 -0
- package/packages/markdown/dist/types.d.ts +11 -0
- package/packages/markdown/dist/types.js +1 -0
- package/packages/viewer/README.md +12 -0
- package/packages/viewer/dist/atlas-state.d.ts +12 -0
- package/packages/viewer/dist/atlas-state.js +251 -0
- package/packages/viewer/dist/atlas-viewer.d.ts +2 -0
- package/packages/viewer/dist/atlas-viewer.js +448 -0
- package/packages/viewer/dist/custom-element.d.ts +1 -0
- package/packages/viewer/dist/custom-element.js +64 -0
- package/packages/viewer/dist/embed.d.ts +20 -0
- package/packages/viewer/dist/embed.js +138 -0
- package/packages/viewer/dist/index.d.ts +9 -0
- package/packages/viewer/dist/index.js +8 -0
- package/packages/viewer/dist/minimap.d.ts +3 -0
- package/packages/viewer/dist/minimap.js +63 -0
- package/packages/viewer/dist/render.d.ts +6 -0
- package/packages/viewer/dist/render.js +585 -0
- package/packages/viewer/dist/theme.d.ts +4 -0
- package/packages/viewer/dist/theme.js +98 -0
- package/packages/viewer/dist/tooltip.d.ts +3 -0
- package/packages/viewer/dist/tooltip.js +154 -0
- package/packages/viewer/dist/types.d.ts +256 -0
- package/packages/viewer/dist/types.js +1 -0
- package/packages/viewer/dist/viewer-state.d.ts +19 -0
- package/packages/viewer/dist/viewer-state.js +162 -0
- package/packages/viewer/dist/viewer.d.ts +2 -0
- package/packages/viewer/dist/viewer.js +1156 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { WorldOrbitError } from "./errors.js";
|
|
2
|
+
import { getFieldSchema, supportsObjectType, unitFamilyAllowsUnit, } from "./schema.js";
|
|
3
|
+
const UNIT_PATTERN = /^(-?\d+(?:\.\d+)?)(au|km|re|sol|me|d|y|h|deg)?$/;
|
|
4
|
+
const BOOLEAN_VALUES = new Map([
|
|
5
|
+
["true", true],
|
|
6
|
+
["false", false],
|
|
7
|
+
["yes", true],
|
|
8
|
+
["no", false],
|
|
9
|
+
]);
|
|
10
|
+
const URL_SCHEME_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
11
|
+
export function normalizeDocument(ast) {
|
|
12
|
+
let system = null;
|
|
13
|
+
const objects = [];
|
|
14
|
+
for (const node of ast.objects) {
|
|
15
|
+
const normalized = normalizeObject(node);
|
|
16
|
+
if (node.objectType === "system") {
|
|
17
|
+
if (system) {
|
|
18
|
+
throw WorldOrbitError.fromLocation("Only one system object is allowed", node.location);
|
|
19
|
+
}
|
|
20
|
+
system = normalized;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
objects.push(normalized);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
format: "worldorbit",
|
|
28
|
+
version: "1.0",
|
|
29
|
+
system,
|
|
30
|
+
objects,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function normalizeObject(node) {
|
|
34
|
+
const mergedFields = [...node.inlineFields, ...node.blockFields];
|
|
35
|
+
validateFieldCompatibility(node.objectType, mergedFields);
|
|
36
|
+
const fieldMap = collectFields(mergedFields);
|
|
37
|
+
const placement = extractPlacement(node.objectType, fieldMap);
|
|
38
|
+
const properties = normalizeProperties(fieldMap);
|
|
39
|
+
const info = normalizeInfo(node.infoEntries);
|
|
40
|
+
if (node.objectType === "system") {
|
|
41
|
+
return {
|
|
42
|
+
type: "system",
|
|
43
|
+
id: node.name,
|
|
44
|
+
properties,
|
|
45
|
+
info,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
type: node.objectType,
|
|
50
|
+
id: node.name,
|
|
51
|
+
properties,
|
|
52
|
+
placement,
|
|
53
|
+
info,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function validateFieldCompatibility(objectType, fields) {
|
|
57
|
+
for (const field of fields) {
|
|
58
|
+
const schema = getFieldSchema(field.key);
|
|
59
|
+
if (!schema) {
|
|
60
|
+
throw WorldOrbitError.fromLocation(`Unknown field "${field.key}"`, field.location);
|
|
61
|
+
}
|
|
62
|
+
if (!supportsObjectType(schema, objectType)) {
|
|
63
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" is not valid on "${objectType}"`, field.location);
|
|
64
|
+
}
|
|
65
|
+
if (schema.arity === "single" && field.values.length !== 1) {
|
|
66
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function collectFields(fields) {
|
|
71
|
+
const map = new Map();
|
|
72
|
+
for (const field of fields) {
|
|
73
|
+
if (map.has(field.key)) {
|
|
74
|
+
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
75
|
+
}
|
|
76
|
+
map.set(field.key, field);
|
|
77
|
+
}
|
|
78
|
+
return map;
|
|
79
|
+
}
|
|
80
|
+
function extractPlacement(objectType, fieldMap) {
|
|
81
|
+
const hasOrbit = fieldMap.has("orbit");
|
|
82
|
+
const hasAt = fieldMap.has("at");
|
|
83
|
+
const hasSurface = fieldMap.has("surface");
|
|
84
|
+
const hasFree = fieldMap.has("free");
|
|
85
|
+
const count = [hasOrbit, hasAt, hasSurface, hasFree].filter(Boolean).length;
|
|
86
|
+
if (count > 1) {
|
|
87
|
+
const conflictingField = fieldMap.get("orbit") ??
|
|
88
|
+
fieldMap.get("at") ??
|
|
89
|
+
fieldMap.get("surface") ??
|
|
90
|
+
fieldMap.get("free");
|
|
91
|
+
throw WorldOrbitError.fromLocation("Object has multiple placement modes", conflictingField?.location);
|
|
92
|
+
}
|
|
93
|
+
if (objectType === "system" && count > 0) {
|
|
94
|
+
throw WorldOrbitError.fromLocation("System objects cannot declare placement", [...fieldMap.values()][0]?.location);
|
|
95
|
+
}
|
|
96
|
+
if (hasOrbit) {
|
|
97
|
+
return {
|
|
98
|
+
mode: "orbit",
|
|
99
|
+
target: singleValue(fieldMap, "orbit"),
|
|
100
|
+
distance: parseOptionalUnitValue(fieldMap, "distance"),
|
|
101
|
+
semiMajor: parseOptionalUnitValue(fieldMap, "semiMajor"),
|
|
102
|
+
eccentricity: parseOptionalNumber(fieldMap, "eccentricity"),
|
|
103
|
+
period: parseOptionalUnitValue(fieldMap, "period"),
|
|
104
|
+
angle: parseOptionalUnitValue(fieldMap, "angle"),
|
|
105
|
+
inclination: parseOptionalUnitValue(fieldMap, "inclination"),
|
|
106
|
+
phase: parseOptionalUnitValue(fieldMap, "phase"),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (hasAt) {
|
|
110
|
+
const field = getField(fieldMap, "at");
|
|
111
|
+
const target = singleValue(fieldMap, "at");
|
|
112
|
+
return {
|
|
113
|
+
mode: "at",
|
|
114
|
+
target,
|
|
115
|
+
reference: parseAtReference(target, field.location),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (hasSurface) {
|
|
119
|
+
return {
|
|
120
|
+
mode: "surface",
|
|
121
|
+
target: singleValue(fieldMap, "surface"),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (hasFree) {
|
|
125
|
+
const raw = singleValue(fieldMap, "free");
|
|
126
|
+
const distance = tryParseUnitValue(raw);
|
|
127
|
+
return {
|
|
128
|
+
mode: "free",
|
|
129
|
+
distance: distance ?? undefined,
|
|
130
|
+
descriptor: distance ? undefined : raw,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
function normalizeProperties(fieldMap) {
|
|
136
|
+
const result = {};
|
|
137
|
+
for (const [key, field] of fieldMap.entries()) {
|
|
138
|
+
const schema = getFieldSchema(key);
|
|
139
|
+
if (!schema || schema.placement) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
switch (schema.kind) {
|
|
143
|
+
case "list":
|
|
144
|
+
result[key] = field.values;
|
|
145
|
+
break;
|
|
146
|
+
case "boolean":
|
|
147
|
+
result[key] = parseBoolean(field);
|
|
148
|
+
break;
|
|
149
|
+
case "number":
|
|
150
|
+
result[key] = parseNumber(singleFieldValue(field), key, field.location);
|
|
151
|
+
break;
|
|
152
|
+
case "unit":
|
|
153
|
+
result[key] = parseUnitValue(singleFieldValue(field), field.location, key);
|
|
154
|
+
break;
|
|
155
|
+
case "string":
|
|
156
|
+
result[key] = normalizeStringValue(key, field);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
function normalizeStringValue(key, field) {
|
|
163
|
+
const value = field.values.join(" ").trim();
|
|
164
|
+
if (key === "image") {
|
|
165
|
+
validateImageSource(value, field.location);
|
|
166
|
+
}
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
function validateImageSource(value, location) {
|
|
170
|
+
if (!value) {
|
|
171
|
+
throw WorldOrbitError.fromLocation('Field "image" must not be empty', location);
|
|
172
|
+
}
|
|
173
|
+
if (value.startsWith("//")) {
|
|
174
|
+
throw WorldOrbitError.fromLocation('Field "image" must use a relative path, root-relative path, or an http/https URL', location);
|
|
175
|
+
}
|
|
176
|
+
const schemeMatch = value.match(URL_SCHEME_PATTERN);
|
|
177
|
+
if (!schemeMatch) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const scheme = schemeMatch[0].slice(0, -1).toLowerCase();
|
|
181
|
+
if (scheme !== "http" && scheme !== "https") {
|
|
182
|
+
throw WorldOrbitError.fromLocation(`Field "image" does not support the "${scheme}" scheme`, location);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function normalizeInfo(entries) {
|
|
186
|
+
const info = {};
|
|
187
|
+
for (const entry of entries) {
|
|
188
|
+
if (entry.key in info) {
|
|
189
|
+
throw WorldOrbitError.fromLocation(`Duplicate info key "${entry.key}"`, entry.location);
|
|
190
|
+
}
|
|
191
|
+
info[entry.key] = entry.value;
|
|
192
|
+
}
|
|
193
|
+
return info;
|
|
194
|
+
}
|
|
195
|
+
function parseAtReference(target, location) {
|
|
196
|
+
if (/^[A-Za-z0-9._-]+-[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
197
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
198
|
+
}
|
|
199
|
+
const pairedMatch = target.match(/^([A-Za-z0-9._-]+)-([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
200
|
+
if (pairedMatch) {
|
|
201
|
+
return {
|
|
202
|
+
kind: "lagrange",
|
|
203
|
+
primary: pairedMatch[1],
|
|
204
|
+
secondary: pairedMatch[2],
|
|
205
|
+
point: pairedMatch[3],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const simpleMatch = target.match(/^([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
209
|
+
if (simpleMatch) {
|
|
210
|
+
return {
|
|
211
|
+
kind: "lagrange",
|
|
212
|
+
primary: simpleMatch[1],
|
|
213
|
+
secondary: null,
|
|
214
|
+
point: simpleMatch[2],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (/^[A-Za-z0-9._-]+:L\d+$/i.test(target)) {
|
|
218
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
219
|
+
}
|
|
220
|
+
const anchorMatch = target.match(/^([A-Za-z0-9._-]+):([A-Za-z0-9._-]+)$/);
|
|
221
|
+
if (anchorMatch) {
|
|
222
|
+
return {
|
|
223
|
+
kind: "anchor",
|
|
224
|
+
objectId: anchorMatch[1],
|
|
225
|
+
anchor: anchorMatch[2],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
kind: "named",
|
|
230
|
+
name: target,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function parseUnitValue(input, location, fieldKey) {
|
|
234
|
+
const match = input.match(UNIT_PATTERN);
|
|
235
|
+
if (!match) {
|
|
236
|
+
throw WorldOrbitError.fromLocation(`Invalid unit value "${input}"`, location);
|
|
237
|
+
}
|
|
238
|
+
const unitValue = {
|
|
239
|
+
value: Number(match[1]),
|
|
240
|
+
unit: match[2] ?? null,
|
|
241
|
+
};
|
|
242
|
+
if (fieldKey) {
|
|
243
|
+
const schema = getFieldSchema(fieldKey);
|
|
244
|
+
if (schema?.unitFamily && !unitFamilyAllowsUnit(schema.unitFamily, unitValue.unit)) {
|
|
245
|
+
throw WorldOrbitError.fromLocation(`Unit "${unitValue.unit ?? "none"}" is not valid for "${fieldKey}"`, location);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return unitValue;
|
|
249
|
+
}
|
|
250
|
+
function tryParseUnitValue(input) {
|
|
251
|
+
const match = input.match(UNIT_PATTERN);
|
|
252
|
+
if (!match) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
value: Number(match[1]),
|
|
257
|
+
unit: match[2] ?? null,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function parseOptionalUnitValue(fieldMap, key) {
|
|
261
|
+
if (!fieldMap.has(key)) {
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
const field = getField(fieldMap, key);
|
|
265
|
+
return parseUnitValue(singleFieldValue(field), field.location, key);
|
|
266
|
+
}
|
|
267
|
+
function parseOptionalNumber(fieldMap, key) {
|
|
268
|
+
if (!fieldMap.has(key)) {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
const field = getField(fieldMap, key);
|
|
272
|
+
return parseNumber(singleFieldValue(field), key, field.location);
|
|
273
|
+
}
|
|
274
|
+
function parseNumber(input, key, location) {
|
|
275
|
+
const value = Number(input);
|
|
276
|
+
if (!Number.isFinite(value)) {
|
|
277
|
+
throw WorldOrbitError.fromLocation(`Invalid numeric value "${input}" for "${key}"`, location);
|
|
278
|
+
}
|
|
279
|
+
return value;
|
|
280
|
+
}
|
|
281
|
+
function parseBoolean(field) {
|
|
282
|
+
const rawValue = singleFieldValue(field).toLowerCase();
|
|
283
|
+
const parsed = BOOLEAN_VALUES.get(rawValue);
|
|
284
|
+
if (parsed === undefined) {
|
|
285
|
+
throw WorldOrbitError.fromLocation(`Invalid boolean value "${rawValue}" for "${field.key}"`, field.location);
|
|
286
|
+
}
|
|
287
|
+
return parsed;
|
|
288
|
+
}
|
|
289
|
+
function getField(fieldMap, key) {
|
|
290
|
+
const field = fieldMap.get(key);
|
|
291
|
+
if (!field) {
|
|
292
|
+
throw new WorldOrbitError(`Missing value for key "${key}"`);
|
|
293
|
+
}
|
|
294
|
+
return field;
|
|
295
|
+
}
|
|
296
|
+
function singleValue(fieldMap, key) {
|
|
297
|
+
return singleFieldValue(getField(fieldMap, key));
|
|
298
|
+
}
|
|
299
|
+
function singleFieldValue(field) {
|
|
300
|
+
if (field.values.length !== 1) {
|
|
301
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
302
|
+
}
|
|
303
|
+
return field.values[0];
|
|
304
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { WorldOrbitError } from "./errors.js";
|
|
2
|
+
import { getFieldSchema, isKnownFieldKey, WORLDORBIT_OBJECT_TYPES } from "./schema.js";
|
|
3
|
+
import { getIndent, tokenizeLineDetailed } from "./tokenize.js";
|
|
4
|
+
export function parseWorldOrbit(source) {
|
|
5
|
+
const lines = source.split(/\r?\n/);
|
|
6
|
+
const objects = [];
|
|
7
|
+
let currentObject = null;
|
|
8
|
+
let inInfoBlock = false;
|
|
9
|
+
let infoIndent = null;
|
|
10
|
+
for (let index = 0; index < lines.length; index++) {
|
|
11
|
+
const rawLine = lines[index];
|
|
12
|
+
const lineNumber = index + 1;
|
|
13
|
+
if (!rawLine.trim()) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const indent = getIndent(rawLine);
|
|
17
|
+
const tokens = tokenizeLineDetailed(rawLine.slice(indent), {
|
|
18
|
+
line: lineNumber,
|
|
19
|
+
columnOffset: indent,
|
|
20
|
+
});
|
|
21
|
+
if (tokens.length === 0) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (indent === 0) {
|
|
25
|
+
inInfoBlock = false;
|
|
26
|
+
infoIndent = null;
|
|
27
|
+
const objectNode = parseObjectHeader(tokens, lineNumber);
|
|
28
|
+
objects.push(objectNode);
|
|
29
|
+
currentObject = objectNode;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!currentObject) {
|
|
33
|
+
throw new WorldOrbitError("Indented line without parent object", lineNumber, indent + 1);
|
|
34
|
+
}
|
|
35
|
+
if (tokens.length === 1 && tokens[0].value === "info") {
|
|
36
|
+
inInfoBlock = true;
|
|
37
|
+
infoIndent = indent;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (inInfoBlock && indent <= (infoIndent ?? 0)) {
|
|
41
|
+
inInfoBlock = false;
|
|
42
|
+
}
|
|
43
|
+
if (inInfoBlock) {
|
|
44
|
+
currentObject.infoEntries.push(parseInfoEntry(tokens, lineNumber));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
currentObject.blockFields.push(parseField(tokens, lineNumber));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
type: "document",
|
|
52
|
+
objects,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function parseObjectHeader(tokens, line) {
|
|
56
|
+
if (tokens.length < 2) {
|
|
57
|
+
throw new WorldOrbitError("Invalid object declaration", line, tokens[0]?.column ?? 1);
|
|
58
|
+
}
|
|
59
|
+
const [objectTypeToken, nameToken, ...rest] = tokens;
|
|
60
|
+
if (!WORLDORBIT_OBJECT_TYPES.has(objectTypeToken.value)) {
|
|
61
|
+
throw new WorldOrbitError(`Unknown object type "${objectTypeToken.value}"`, line, objectTypeToken.column);
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
type: "object",
|
|
65
|
+
objectType: objectTypeToken.value,
|
|
66
|
+
name: nameToken.value,
|
|
67
|
+
inlineFields: parseInlineFields(rest, line),
|
|
68
|
+
blockFields: [],
|
|
69
|
+
infoEntries: [],
|
|
70
|
+
location: { line, column: objectTypeToken.column },
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function parseInlineFields(tokens, line) {
|
|
74
|
+
const fields = [];
|
|
75
|
+
let index = 0;
|
|
76
|
+
while (index < tokens.length) {
|
|
77
|
+
const keyToken = tokens[index];
|
|
78
|
+
const schema = getFieldSchema(keyToken.value);
|
|
79
|
+
if (!schema) {
|
|
80
|
+
throw new WorldOrbitError(`Unknown field "${keyToken.value}"`, line, keyToken.column);
|
|
81
|
+
}
|
|
82
|
+
index++;
|
|
83
|
+
const valueTokens = [];
|
|
84
|
+
if (schema.arity === "multiple") {
|
|
85
|
+
while (index < tokens.length && !isKnownFieldKey(tokens[index].value)) {
|
|
86
|
+
valueTokens.push(tokens[index]);
|
|
87
|
+
index++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const nextToken = tokens[index];
|
|
92
|
+
if (nextToken) {
|
|
93
|
+
valueTokens.push(nextToken);
|
|
94
|
+
index++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (valueTokens.length === 0) {
|
|
98
|
+
throw new WorldOrbitError(`Missing value for field "${keyToken.value}"`, line, keyToken.column);
|
|
99
|
+
}
|
|
100
|
+
fields.push({
|
|
101
|
+
type: "field",
|
|
102
|
+
key: keyToken.value,
|
|
103
|
+
values: valueTokens.map((token) => token.value),
|
|
104
|
+
location: { line, column: keyToken.column },
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return fields;
|
|
108
|
+
}
|
|
109
|
+
function parseField(tokens, line) {
|
|
110
|
+
if (tokens.length < 2) {
|
|
111
|
+
throw new WorldOrbitError("Invalid field line", line, tokens[0]?.column ?? 1);
|
|
112
|
+
}
|
|
113
|
+
if (!getFieldSchema(tokens[0].value)) {
|
|
114
|
+
throw new WorldOrbitError(`Unknown field "${tokens[0].value}"`, line, tokens[0].column);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
type: "field",
|
|
118
|
+
key: tokens[0].value,
|
|
119
|
+
values: tokens.slice(1).map((token) => token.value),
|
|
120
|
+
location: { line, column: tokens[0].column },
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function parseInfoEntry(tokens, line) {
|
|
124
|
+
if (tokens.length < 2) {
|
|
125
|
+
throw new WorldOrbitError("Invalid info entry", line, tokens[0]?.column ?? 1);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
type: "info-entry",
|
|
129
|
+
key: tokens[0].value,
|
|
130
|
+
value: tokens.slice(1).map((token) => token.value).join(" "),
|
|
131
|
+
location: { line, column: tokens[0].column },
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CoordinatePoint, RenderScene, SceneRenderOptions, WorldOrbitDocument } from "./types.js";
|
|
2
|
+
export declare function renderDocumentToScene(document: WorldOrbitDocument, options?: SceneRenderOptions): RenderScene;
|
|
3
|
+
export declare function rotatePoint(point: CoordinatePoint, center: CoordinatePoint, rotationDeg: number): CoordinatePoint;
|