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,8 @@
|
|
|
1
|
+
export declare const OBJECT_TYPES: ReadonlySet<string>;
|
|
2
|
+
export declare const KNOWN_FIELD_KEYS: ReadonlySet<string>;
|
|
3
|
+
export declare const LIST_KEYS: ReadonlySet<string>;
|
|
4
|
+
export declare const BOOLEAN_KEYS: ReadonlySet<string>;
|
|
5
|
+
export declare const INLINE_MULTI_VALUE_KEYS: ReadonlySet<string>;
|
|
6
|
+
export declare const UNIT_VALUE_KEYS: ReadonlySet<string>;
|
|
7
|
+
export declare const NUMBER_KEYS: ReadonlySet<string>;
|
|
8
|
+
export declare const PLACEMENT_KEYS: ReadonlySet<string>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export const OBJECT_TYPES = new Set([
|
|
2
|
+
"system",
|
|
3
|
+
"star",
|
|
4
|
+
"planet",
|
|
5
|
+
"moon",
|
|
6
|
+
"belt",
|
|
7
|
+
"asteroid",
|
|
8
|
+
"comet",
|
|
9
|
+
"ring",
|
|
10
|
+
"structure",
|
|
11
|
+
"phenomenon",
|
|
12
|
+
]);
|
|
13
|
+
export const KNOWN_FIELD_KEYS = new Set([
|
|
14
|
+
"orbit",
|
|
15
|
+
"distance",
|
|
16
|
+
"semiMajor",
|
|
17
|
+
"eccentricity",
|
|
18
|
+
"period",
|
|
19
|
+
"angle",
|
|
20
|
+
"inclination",
|
|
21
|
+
"phase",
|
|
22
|
+
"at",
|
|
23
|
+
"surface",
|
|
24
|
+
"free",
|
|
25
|
+
"kind",
|
|
26
|
+
"class",
|
|
27
|
+
"tags",
|
|
28
|
+
"color",
|
|
29
|
+
"hidden",
|
|
30
|
+
"radius",
|
|
31
|
+
"mass",
|
|
32
|
+
"density",
|
|
33
|
+
"gravity",
|
|
34
|
+
"temperature",
|
|
35
|
+
"albedo",
|
|
36
|
+
"atmosphere",
|
|
37
|
+
"inner",
|
|
38
|
+
"outer",
|
|
39
|
+
"view",
|
|
40
|
+
"scale",
|
|
41
|
+
"units",
|
|
42
|
+
"title",
|
|
43
|
+
"on",
|
|
44
|
+
"source",
|
|
45
|
+
"cycle",
|
|
46
|
+
]);
|
|
47
|
+
export const LIST_KEYS = new Set(["tags"]);
|
|
48
|
+
export const BOOLEAN_KEYS = new Set(["hidden"]);
|
|
49
|
+
export const INLINE_MULTI_VALUE_KEYS = new Set([
|
|
50
|
+
...LIST_KEYS,
|
|
51
|
+
]);
|
|
52
|
+
export const UNIT_VALUE_KEYS = new Set([
|
|
53
|
+
"distance",
|
|
54
|
+
"semiMajor",
|
|
55
|
+
"inner",
|
|
56
|
+
"outer",
|
|
57
|
+
"radius",
|
|
58
|
+
"mass",
|
|
59
|
+
"density",
|
|
60
|
+
"gravity",
|
|
61
|
+
"temperature",
|
|
62
|
+
"period",
|
|
63
|
+
"angle",
|
|
64
|
+
"inclination",
|
|
65
|
+
"phase",
|
|
66
|
+
"cycle",
|
|
67
|
+
]);
|
|
68
|
+
export const NUMBER_KEYS = new Set([
|
|
69
|
+
"eccentricity",
|
|
70
|
+
"albedo",
|
|
71
|
+
]);
|
|
72
|
+
export const PLACEMENT_KEYS = new Set([
|
|
73
|
+
"orbit",
|
|
74
|
+
"at",
|
|
75
|
+
"surface",
|
|
76
|
+
"free",
|
|
77
|
+
"distance",
|
|
78
|
+
"semiMajor",
|
|
79
|
+
"eccentricity",
|
|
80
|
+
"period",
|
|
81
|
+
"angle",
|
|
82
|
+
"inclination",
|
|
83
|
+
"phase",
|
|
84
|
+
]);
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AstSourceLocation } from "./types.js";
|
|
2
|
+
export declare class WorldOrbitError extends Error {
|
|
3
|
+
readonly line?: number;
|
|
4
|
+
readonly column?: number;
|
|
5
|
+
constructor(message: string, line?: number, column?: number);
|
|
6
|
+
static fromLocation(message: string, location?: AstSourceLocation): WorldOrbitError;
|
|
7
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class WorldOrbitError extends Error {
|
|
2
|
+
line;
|
|
3
|
+
column;
|
|
4
|
+
constructor(message, line, column) {
|
|
5
|
+
const locationSuffix = line === undefined
|
|
6
|
+
? ""
|
|
7
|
+
: ` (line ${line}${column === undefined ? "" : `, column ${column}`})`;
|
|
8
|
+
super(`${message}${locationSuffix}`);
|
|
9
|
+
this.name = "WorldOrbitError";
|
|
10
|
+
this.line = line;
|
|
11
|
+
this.column = column;
|
|
12
|
+
}
|
|
13
|
+
static fromLocation(message, location) {
|
|
14
|
+
return new WorldOrbitError(message, location?.line, location?.column);
|
|
15
|
+
}
|
|
16
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export * from "./errors.js";
|
|
3
|
+
export { tokenizeLine, tokenizeLineDetailed } from "./tokenize.js";
|
|
4
|
+
export { parseWorldOrbit } from "./parse.js";
|
|
5
|
+
export { normalizeDocument } from "./normalize.js";
|
|
6
|
+
export { validateDocument } from "./validate.js";
|
|
7
|
+
export { renderDocumentToScene, renderSceneToSvg, renderDocumentToSvg, renderSourceToSvg, } from "./render.js";
|
|
8
|
+
export { createInteractiveViewer } from "./viewer.js";
|
|
9
|
+
import type { AstDocument, WorldOrbitDocument } from "./types.js";
|
|
10
|
+
export interface ParseResult {
|
|
11
|
+
ast: AstDocument;
|
|
12
|
+
document: WorldOrbitDocument;
|
|
13
|
+
}
|
|
14
|
+
export interface RenderResult extends ParseResult {
|
|
15
|
+
svg: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function parse(source: string): ParseResult;
|
|
18
|
+
export declare function render(source: string): RenderResult;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export * from "./errors.js";
|
|
3
|
+
export { tokenizeLine, tokenizeLineDetailed } from "./tokenize.js";
|
|
4
|
+
export { parseWorldOrbit } from "./parse.js";
|
|
5
|
+
export { normalizeDocument } from "./normalize.js";
|
|
6
|
+
export { validateDocument } from "./validate.js";
|
|
7
|
+
export { renderDocumentToScene, renderSceneToSvg, renderDocumentToSvg, renderSourceToSvg, } from "./render.js";
|
|
8
|
+
export { createInteractiveViewer } from "./viewer.js";
|
|
9
|
+
import { normalizeDocument } from "./normalize.js";
|
|
10
|
+
import { parseWorldOrbit } from "./parse.js";
|
|
11
|
+
import { renderDocumentToSvg } from "./render.js";
|
|
12
|
+
import { validateDocument } from "./validate.js";
|
|
13
|
+
export function parse(source) {
|
|
14
|
+
const ast = parseWorldOrbit(source);
|
|
15
|
+
const document = normalizeDocument(ast);
|
|
16
|
+
validateDocument(document);
|
|
17
|
+
return { ast, document };
|
|
18
|
+
}
|
|
19
|
+
export function render(source) {
|
|
20
|
+
const result = parse(source);
|
|
21
|
+
return {
|
|
22
|
+
...result,
|
|
23
|
+
svg: renderDocumentToSvg(result.document),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { BOOLEAN_KEYS, LIST_KEYS, NUMBER_KEYS, PLACEMENT_KEYS, UNIT_VALUE_KEYS, } from "./constants.js";
|
|
2
|
+
import { WorldOrbitError } from "./errors.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
|
+
export function normalizeDocument(ast) {
|
|
11
|
+
let system = null;
|
|
12
|
+
const objects = [];
|
|
13
|
+
for (const node of ast.objects) {
|
|
14
|
+
const normalized = normalizeObject(node);
|
|
15
|
+
if (node.objectType === "system") {
|
|
16
|
+
if (system) {
|
|
17
|
+
throw WorldOrbitError.fromLocation("Only one system object is allowed", node.location);
|
|
18
|
+
}
|
|
19
|
+
system = normalized;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
objects.push(normalized);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
format: "worldorbit",
|
|
27
|
+
version: "0.1",
|
|
28
|
+
system,
|
|
29
|
+
objects,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function normalizeObject(node) {
|
|
33
|
+
const mergedFields = [...node.inlineFields, ...node.blockFields];
|
|
34
|
+
const fieldMap = collectFields(mergedFields);
|
|
35
|
+
const placement = extractPlacement(fieldMap);
|
|
36
|
+
const properties = normalizeProperties(fieldMap);
|
|
37
|
+
const info = normalizeInfo(node.infoEntries);
|
|
38
|
+
if (node.objectType === "system") {
|
|
39
|
+
return {
|
|
40
|
+
type: "system",
|
|
41
|
+
id: node.name,
|
|
42
|
+
properties,
|
|
43
|
+
info,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
type: node.objectType,
|
|
48
|
+
id: node.name,
|
|
49
|
+
properties,
|
|
50
|
+
placement,
|
|
51
|
+
info,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function collectFields(fields) {
|
|
55
|
+
const map = new Map();
|
|
56
|
+
for (const field of fields) {
|
|
57
|
+
if (map.has(field.key)) {
|
|
58
|
+
throw WorldOrbitError.fromLocation(`Duplicate field "${field.key}"`, field.location);
|
|
59
|
+
}
|
|
60
|
+
map.set(field.key, field);
|
|
61
|
+
}
|
|
62
|
+
return map;
|
|
63
|
+
}
|
|
64
|
+
function extractPlacement(fieldMap) {
|
|
65
|
+
const hasOrbit = fieldMap.has("orbit");
|
|
66
|
+
const hasAt = fieldMap.has("at");
|
|
67
|
+
const hasSurface = fieldMap.has("surface");
|
|
68
|
+
const hasFree = fieldMap.has("free");
|
|
69
|
+
const count = [hasOrbit, hasAt, hasSurface, hasFree].filter(Boolean).length;
|
|
70
|
+
if (count > 1) {
|
|
71
|
+
const conflictingField = fieldMap.get("orbit") ??
|
|
72
|
+
fieldMap.get("at") ??
|
|
73
|
+
fieldMap.get("surface") ??
|
|
74
|
+
fieldMap.get("free");
|
|
75
|
+
throw WorldOrbitError.fromLocation("Object has multiple placement modes", conflictingField?.location);
|
|
76
|
+
}
|
|
77
|
+
if (hasOrbit) {
|
|
78
|
+
return {
|
|
79
|
+
mode: "orbit",
|
|
80
|
+
target: singleValue(fieldMap, "orbit"),
|
|
81
|
+
distance: parseOptionalUnitValue(fieldMap, "distance"),
|
|
82
|
+
semiMajor: parseOptionalUnitValue(fieldMap, "semiMajor"),
|
|
83
|
+
eccentricity: parseOptionalNumber(fieldMap, "eccentricity"),
|
|
84
|
+
period: parseOptionalUnitValue(fieldMap, "period"),
|
|
85
|
+
angle: parseOptionalUnitValue(fieldMap, "angle"),
|
|
86
|
+
inclination: parseOptionalUnitValue(fieldMap, "inclination"),
|
|
87
|
+
phase: parseOptionalUnitValue(fieldMap, "phase"),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (hasAt) {
|
|
91
|
+
const field = getField(fieldMap, "at");
|
|
92
|
+
const target = singleValue(fieldMap, "at");
|
|
93
|
+
return {
|
|
94
|
+
mode: "at",
|
|
95
|
+
target,
|
|
96
|
+
reference: parseAtReference(target, field.location),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (hasSurface) {
|
|
100
|
+
return {
|
|
101
|
+
mode: "surface",
|
|
102
|
+
target: singleValue(fieldMap, "surface"),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (hasFree) {
|
|
106
|
+
const raw = singleValue(fieldMap, "free");
|
|
107
|
+
const distance = tryParseUnitValue(raw);
|
|
108
|
+
return {
|
|
109
|
+
mode: "free",
|
|
110
|
+
distance: distance ?? undefined,
|
|
111
|
+
descriptor: distance ? undefined : raw,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
function normalizeProperties(fieldMap) {
|
|
117
|
+
const result = {};
|
|
118
|
+
for (const [key, field] of fieldMap.entries()) {
|
|
119
|
+
if (PLACEMENT_KEYS.has(key)) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (LIST_KEYS.has(key)) {
|
|
123
|
+
result[key] = field.values;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (BOOLEAN_KEYS.has(key)) {
|
|
127
|
+
result[key] = parseBoolean(field);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (NUMBER_KEYS.has(key)) {
|
|
131
|
+
result[key] = parseNumber(singleFieldValue(field), key, field.location);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (UNIT_VALUE_KEYS.has(key)) {
|
|
135
|
+
result[key] = parseUnitValue(singleFieldValue(field), field.location);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
result[key] = field.values.join(" ");
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
function normalizeInfo(entries) {
|
|
143
|
+
const info = {};
|
|
144
|
+
for (const entry of entries) {
|
|
145
|
+
if (entry.key in info) {
|
|
146
|
+
throw WorldOrbitError.fromLocation(`Duplicate info key "${entry.key}"`, entry.location);
|
|
147
|
+
}
|
|
148
|
+
info[entry.key] = entry.value;
|
|
149
|
+
}
|
|
150
|
+
return info;
|
|
151
|
+
}
|
|
152
|
+
function parseAtReference(target, location) {
|
|
153
|
+
const pairedMatch = target.match(/^([A-Za-z0-9._-]+)-([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
154
|
+
if (pairedMatch) {
|
|
155
|
+
return {
|
|
156
|
+
kind: "lagrange",
|
|
157
|
+
primary: pairedMatch[1],
|
|
158
|
+
secondary: pairedMatch[2],
|
|
159
|
+
point: pairedMatch[3],
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const simpleMatch = target.match(/^([A-Za-z0-9._-]+):(L[1-5])$/);
|
|
163
|
+
if (simpleMatch) {
|
|
164
|
+
return {
|
|
165
|
+
kind: "lagrange",
|
|
166
|
+
primary: simpleMatch[1],
|
|
167
|
+
secondary: null,
|
|
168
|
+
point: simpleMatch[2],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (target.includes(":L")) {
|
|
172
|
+
throw WorldOrbitError.fromLocation(`Invalid special position "${target}"`, location);
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
kind: "named",
|
|
176
|
+
name: target,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function parseUnitValue(input, location) {
|
|
180
|
+
const match = input.match(UNIT_PATTERN);
|
|
181
|
+
if (!match) {
|
|
182
|
+
throw WorldOrbitError.fromLocation(`Invalid unit value "${input}"`, location);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
value: Number(match[1]),
|
|
186
|
+
unit: match[2] ?? null,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function tryParseUnitValue(input) {
|
|
190
|
+
const match = input.match(UNIT_PATTERN);
|
|
191
|
+
if (!match) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
value: Number(match[1]),
|
|
196
|
+
unit: match[2] ?? null,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function parseOptionalUnitValue(fieldMap, key) {
|
|
200
|
+
if (!fieldMap.has(key)) {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
const field = getField(fieldMap, key);
|
|
204
|
+
return parseUnitValue(singleFieldValue(field), field.location);
|
|
205
|
+
}
|
|
206
|
+
function parseOptionalNumber(fieldMap, key) {
|
|
207
|
+
if (!fieldMap.has(key)) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
const field = getField(fieldMap, key);
|
|
211
|
+
return parseNumber(singleFieldValue(field), key, field.location);
|
|
212
|
+
}
|
|
213
|
+
function parseNumber(input, key, location) {
|
|
214
|
+
const value = Number(input);
|
|
215
|
+
if (!Number.isFinite(value)) {
|
|
216
|
+
throw WorldOrbitError.fromLocation(`Invalid numeric value "${input}" for "${key}"`, location);
|
|
217
|
+
}
|
|
218
|
+
return value;
|
|
219
|
+
}
|
|
220
|
+
function parseBoolean(field) {
|
|
221
|
+
const rawValue = singleFieldValue(field).toLowerCase();
|
|
222
|
+
const parsed = BOOLEAN_VALUES.get(rawValue);
|
|
223
|
+
if (parsed === undefined) {
|
|
224
|
+
throw WorldOrbitError.fromLocation(`Invalid boolean value "${rawValue}" for "${field.key}"`, field.location);
|
|
225
|
+
}
|
|
226
|
+
return parsed;
|
|
227
|
+
}
|
|
228
|
+
function getField(fieldMap, key) {
|
|
229
|
+
const field = fieldMap.get(key);
|
|
230
|
+
if (!field) {
|
|
231
|
+
throw new WorldOrbitError(`Missing value for key "${key}"`);
|
|
232
|
+
}
|
|
233
|
+
return field;
|
|
234
|
+
}
|
|
235
|
+
function singleValue(fieldMap, key) {
|
|
236
|
+
return singleFieldValue(getField(fieldMap, key));
|
|
237
|
+
}
|
|
238
|
+
function singleFieldValue(field) {
|
|
239
|
+
if (field.values.length !== 1) {
|
|
240
|
+
throw WorldOrbitError.fromLocation(`Field "${field.key}" expects exactly one value`, field.location);
|
|
241
|
+
}
|
|
242
|
+
return field.values[0];
|
|
243
|
+
}
|
package/dist/parse.d.ts
ADDED
package/dist/parse.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { INLINE_MULTI_VALUE_KEYS, KNOWN_FIELD_KEYS, OBJECT_TYPES, } from "./constants.js";
|
|
2
|
+
import { WorldOrbitError } from "./errors.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 (!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
|
+
index++;
|
|
79
|
+
const valueTokens = [];
|
|
80
|
+
if (INLINE_MULTI_VALUE_KEYS.has(keyToken.value)) {
|
|
81
|
+
while (index < tokens.length && !KNOWN_FIELD_KEYS.has(tokens[index].value)) {
|
|
82
|
+
valueTokens.push(tokens[index]);
|
|
83
|
+
index++;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const nextToken = tokens[index];
|
|
88
|
+
if (nextToken) {
|
|
89
|
+
valueTokens.push(nextToken);
|
|
90
|
+
index++;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (valueTokens.length === 0) {
|
|
94
|
+
throw new WorldOrbitError(`Missing value for field "${keyToken.value}"`, line, keyToken.column);
|
|
95
|
+
}
|
|
96
|
+
fields.push({
|
|
97
|
+
type: "field",
|
|
98
|
+
key: keyToken.value,
|
|
99
|
+
values: valueTokens.map((token) => token.value),
|
|
100
|
+
location: { line, column: keyToken.column },
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return fields;
|
|
104
|
+
}
|
|
105
|
+
function parseField(tokens, line) {
|
|
106
|
+
if (tokens.length < 2) {
|
|
107
|
+
throw new WorldOrbitError("Invalid field line", line, tokens[0]?.column ?? 1);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
type: "field",
|
|
111
|
+
key: tokens[0].value,
|
|
112
|
+
values: tokens.slice(1).map((token) => token.value),
|
|
113
|
+
location: { line, column: tokens[0].column },
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function parseInfoEntry(tokens, line) {
|
|
117
|
+
if (tokens.length < 2) {
|
|
118
|
+
throw new WorldOrbitError("Invalid info entry", line, tokens[0]?.column ?? 1);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
type: "info-entry",
|
|
122
|
+
key: tokens[0].value,
|
|
123
|
+
value: tokens.slice(1).map((token) => token.value).join(" "),
|
|
124
|
+
location: { line, column: tokens[0].column },
|
|
125
|
+
};
|
|
126
|
+
}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CoordinatePoint, RenderScene, SvgRenderOptions, WorldOrbitDocument } from "./types.js";
|
|
2
|
+
export declare function renderDocumentToScene(document: WorldOrbitDocument, options?: SvgRenderOptions): RenderScene;
|
|
3
|
+
export declare function renderSceneToSvg(scene: RenderScene): string;
|
|
4
|
+
export declare function renderDocumentToSvg(document: WorldOrbitDocument, options?: SvgRenderOptions): string;
|
|
5
|
+
export declare function renderSourceToSvg(source: string, options?: SvgRenderOptions): string;
|
|
6
|
+
export declare function rotatePoint(point: CoordinatePoint, center: CoordinatePoint, rotationDeg: number): CoordinatePoint;
|