@listen-fire/movement-lang 0.12.0-rc.8 → 0.12.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/dist/checker/rust_checker.d.ts +6 -0
- package/dist/checker/rust_checker.js +12 -7
- package/dist/checker/story_analysis.d.ts +236 -0
- package/dist/checker/story_analysis.js +168 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/language_version_value.d.ts +1 -1
- package/dist/language_version_value.js +1 -1
- package/dist/service/language_service.d.ts +82 -0
- package/dist/service/language_service.js +186 -0
- package/dist/wasm/loader.d.ts +25 -0
- package/dist/wasm/loader.js +35 -1
- package/dist/wasm/movement.wasm +0 -0
- package/dist/wasm/movement_parser.wasm +0 -0
- package/package.json +3 -4
|
@@ -12,6 +12,8 @@ export interface RustCheckOptions {
|
|
|
12
12
|
* snapshot does not load. */
|
|
13
13
|
export declare class RustCheckRefused extends Error {
|
|
14
14
|
constructor(message: string);
|
|
15
|
+
/** The refusal an artifact's `{error: {message}}` reply carries. */
|
|
16
|
+
static of(json: unknown): RustCheckRefused;
|
|
15
17
|
}
|
|
16
18
|
/** The language the artifact reads: its release's version. */
|
|
17
19
|
export declare function languageVersion(): LanguageVersion;
|
|
@@ -87,3 +89,7 @@ export declare function compatibleInRust(request: {
|
|
|
87
89
|
} | {
|
|
88
90
|
undescribed: true;
|
|
89
91
|
};
|
|
92
|
+
/** One diagnostic of an artifact's reply, validated.
|
|
93
|
+
*
|
|
94
|
+
* @internal Used by the story analysis's wrapper (checker/story_analysis.ts). */
|
|
95
|
+
export declare function diagnosticOf(value: unknown): Diagnostic;
|
|
@@ -15,6 +15,7 @@ exports.languageStanding = languageStanding;
|
|
|
15
15
|
exports.checkSourceInRust = checkSourceInRust;
|
|
16
16
|
exports.checkAnswerInRust = checkAnswerInRust;
|
|
17
17
|
exports.compatibleInRust = compatibleInRust;
|
|
18
|
+
exports.diagnosticOf = diagnosticOf;
|
|
18
19
|
const loader_1 = require("../wasm/loader");
|
|
19
20
|
const language_version_1 = require("../language_version");
|
|
20
21
|
/** A source the Rust checker could not check: it does not parse, or the
|
|
@@ -24,6 +25,11 @@ class RustCheckRefused extends Error {
|
|
|
24
25
|
super(message);
|
|
25
26
|
this.name = 'RustCheckRefused';
|
|
26
27
|
}
|
|
28
|
+
/** The refusal an artifact's `{error: {message}}` reply carries. */
|
|
29
|
+
static of(json) {
|
|
30
|
+
const error = isRecord(json) && isRecord(json.error) ? json.error : undefined;
|
|
31
|
+
return new RustCheckRefused(typeof error?.message === 'string' ? error.message : 'the Rust checker refused the check');
|
|
32
|
+
}
|
|
27
33
|
}
|
|
28
34
|
exports.RustCheckRefused = RustCheckRefused;
|
|
29
35
|
/** The language the artifact reads: its release's version. */
|
|
@@ -57,7 +63,7 @@ function checkSourceInRust(source, snapshot, options) {
|
|
|
57
63
|
const reply = (0, loader_1.callCheckProgram)(source, JSON.stringify(snapshot), callOptions(options));
|
|
58
64
|
const json = reply.json;
|
|
59
65
|
if (!reply.ok)
|
|
60
|
-
throw
|
|
66
|
+
throw RustCheckRefused.of(json);
|
|
61
67
|
const diagnostics = isRecord(json) ? json.diagnostics : undefined;
|
|
62
68
|
if (!Array.isArray(diagnostics))
|
|
63
69
|
throw new Error('the Rust checker answered no diagnostics');
|
|
@@ -71,7 +77,7 @@ function checkAnswerInRust(source, snapshot, options) {
|
|
|
71
77
|
const reply = (0, loader_1.callCheckAnswer)(source, JSON.stringify(snapshot), callOptions(options));
|
|
72
78
|
const json = reply.json;
|
|
73
79
|
if (!reply.ok)
|
|
74
|
-
throw
|
|
80
|
+
throw RustCheckRefused.of(json);
|
|
75
81
|
if (!isRecord(json) || !Array.isArray(json.diagnostics) || !Array.isArray(json.stamps)) {
|
|
76
82
|
throw new Error(`the Rust checker answered no check: ${JSON.stringify(json)?.slice(0, 200)}`);
|
|
77
83
|
}
|
|
@@ -89,7 +95,7 @@ function compatibleInRust(request) {
|
|
|
89
95
|
const reply = (0, loader_1.callCheckCompatible)(JSON.stringify(request));
|
|
90
96
|
const json = reply.json;
|
|
91
97
|
if (!reply.ok)
|
|
92
|
-
throw
|
|
98
|
+
throw RustCheckRefused.of(json);
|
|
93
99
|
if (isRecord(json) && json.undescribed === true)
|
|
94
100
|
return { undescribed: true };
|
|
95
101
|
if (isRecord(json) && typeof json.fits === 'boolean')
|
|
@@ -99,10 +105,6 @@ function compatibleInRust(request) {
|
|
|
99
105
|
function callOptions(options) {
|
|
100
106
|
return options.upgradingFrom === undefined ? {} : { upgradingFrom: options.upgradingFrom };
|
|
101
107
|
}
|
|
102
|
-
function refused(json) {
|
|
103
|
-
const error = isRecord(json) && isRecord(json.error) ? json.error : undefined;
|
|
104
|
-
return new RustCheckRefused(typeof error?.message === 'string' ? error.message : 'the Rust checker refused the check');
|
|
105
|
-
}
|
|
106
108
|
function stampOf(value) {
|
|
107
109
|
if (!isRecord(value) || typeof value.path !== 'string' || (value.file !== undefined && typeof value.file !== 'string')) {
|
|
108
110
|
throw new Error(`the Rust checker answered a stamp that is not one: ${JSON.stringify(value)}`);
|
|
@@ -156,6 +158,9 @@ function linkedKindOf(value) {
|
|
|
156
158
|
return value;
|
|
157
159
|
throw new Error(`the Rust checker answered the import kind ${JSON.stringify(value)}`);
|
|
158
160
|
}
|
|
161
|
+
/** One diagnostic of an artifact's reply, validated.
|
|
162
|
+
*
|
|
163
|
+
* @internal Used by the story analysis's wrapper (checker/story_analysis.ts). */
|
|
159
164
|
function diagnosticOf(value) {
|
|
160
165
|
if (!isRecord(value) || typeof value.code !== 'string' || typeof value.message !== 'string') {
|
|
161
166
|
throw new Error(`the Rust checker answered a diagnostic that is not one: ${JSON.stringify(value)}`);
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import type { CollectionOpExpression, CombinatorKind, ExprSlot, FieldEntry, MembersExpression, ShapeNode, Span } from '../parser/ast';
|
|
2
|
+
import type { TraversalStep } from '../expression/types';
|
|
3
|
+
import type { CatalogSnapshot } from '../service/snapshot';
|
|
4
|
+
import type { Diagnostic } from './diagnostic';
|
|
5
|
+
import { type RustCheckOptions } from './rust_checker';
|
|
6
|
+
/** The analysis's shape version this binding writes and reads. */
|
|
7
|
+
export declare const STORY_ANALYSIS_VERSION = 1;
|
|
8
|
+
/** Every analysis version this binding reads; any other is refused by name. */
|
|
9
|
+
export declare const SUPPORTED_STORY_ANALYSIS_VERSIONS: readonly number[];
|
|
10
|
+
export interface StoryAnalysis {
|
|
11
|
+
storyAnalysisVersion: number;
|
|
12
|
+
diagnostics: Diagnostic[];
|
|
13
|
+
/** The file's scope is 0. */
|
|
14
|
+
scopes: StoryScope[];
|
|
15
|
+
symbols: StorySymbol[];
|
|
16
|
+
writes: StoryWrite[];
|
|
17
|
+
nodes: StoryNode[];
|
|
18
|
+
bodies: StoryBody[];
|
|
19
|
+
shapes: StoryShape[];
|
|
20
|
+
}
|
|
21
|
+
/** One scope: the last binding of each name in it, as the walk left it. A
|
|
22
|
+
* scope with no text of its own has no `span`. */
|
|
23
|
+
export interface StoryScope {
|
|
24
|
+
parent?: number;
|
|
25
|
+
span?: Span;
|
|
26
|
+
symbols: Record<string, number>;
|
|
27
|
+
}
|
|
28
|
+
export type StorySymbolKind = 'adapter' | 'credential' | 'plugin' | 'fileImport' | 'binding' | 'instance' | 'param' | 'movement' | 'shape' | 'alias' | 'type';
|
|
29
|
+
/** Where a record is: a system's binding name, and its record type where one place holds it. */
|
|
30
|
+
export interface StoryAddress {
|
|
31
|
+
instance: string;
|
|
32
|
+
position?: string;
|
|
33
|
+
}
|
|
34
|
+
/** What a movement may do. `reads` and `writes` name instance bindings;
|
|
35
|
+
* `partial` is set where a callee or a site could not be placed. */
|
|
36
|
+
export interface StoryEffects {
|
|
37
|
+
reads: string[];
|
|
38
|
+
writes: string[];
|
|
39
|
+
ai: boolean;
|
|
40
|
+
now: boolean;
|
|
41
|
+
suspend: boolean;
|
|
42
|
+
partial: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface StorySymbol {
|
|
45
|
+
name: string;
|
|
46
|
+
kind: StorySymbolKind;
|
|
47
|
+
span: Span;
|
|
48
|
+
adapter?: string;
|
|
49
|
+
address?: StoryAddress;
|
|
50
|
+
/** The binding holds an extraction's graph; `entity` the node's name, absent at its root. */
|
|
51
|
+
extracted?: {
|
|
52
|
+
entity?: string;
|
|
53
|
+
};
|
|
54
|
+
/** Movements only. */
|
|
55
|
+
effects?: StoryEffects;
|
|
56
|
+
}
|
|
57
|
+
export interface StoryWrite {
|
|
58
|
+
span: Span;
|
|
59
|
+
scope: number;
|
|
60
|
+
/** The field lines, spreads written out (a spread's line carries `spread`). */
|
|
61
|
+
fields: FieldEntry[];
|
|
62
|
+
hasUniqueBy: boolean;
|
|
63
|
+
/** `update` is a write to a position; `find` a match. */
|
|
64
|
+
action: 'create' | 'update' | 'find';
|
|
65
|
+
parents: Array<{
|
|
66
|
+
edge: string;
|
|
67
|
+
type?: string;
|
|
68
|
+
}>;
|
|
69
|
+
binding?: string;
|
|
70
|
+
target?: {
|
|
71
|
+
instance: string;
|
|
72
|
+
recordType?: string;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** Every node has its `span` and the `scope` it sits in. */
|
|
76
|
+
export type StoryNode = {
|
|
77
|
+
span: Span;
|
|
78
|
+
scope: number;
|
|
79
|
+
} & ({
|
|
80
|
+
kind: 'instance';
|
|
81
|
+
name: string;
|
|
82
|
+
adapterType?: string;
|
|
83
|
+
} | {
|
|
84
|
+
kind: 'listen';
|
|
85
|
+
instance: string;
|
|
86
|
+
adapterType?: string;
|
|
87
|
+
fires: string;
|
|
88
|
+
firesMovement: boolean;
|
|
89
|
+
events?: string[];
|
|
90
|
+
narrowing: Record<string, string>;
|
|
91
|
+
eventTypes: Array<{
|
|
92
|
+
key: string;
|
|
93
|
+
display?: string;
|
|
94
|
+
}>;
|
|
95
|
+
} | {
|
|
96
|
+
kind: 'link';
|
|
97
|
+
from: string;
|
|
98
|
+
edge: string;
|
|
99
|
+
to: {
|
|
100
|
+
kind: 'handle';
|
|
101
|
+
name: string;
|
|
102
|
+
} | {
|
|
103
|
+
kind: 'match';
|
|
104
|
+
span: Span;
|
|
105
|
+
};
|
|
106
|
+
} | {
|
|
107
|
+
kind: 'unlink';
|
|
108
|
+
from: string;
|
|
109
|
+
edge: string;
|
|
110
|
+
to: {
|
|
111
|
+
kind: 'handle';
|
|
112
|
+
name: string;
|
|
113
|
+
};
|
|
114
|
+
} | {
|
|
115
|
+
kind: 'await';
|
|
116
|
+
binding?: string;
|
|
117
|
+
source: StoryAwaitedSource;
|
|
118
|
+
} | {
|
|
119
|
+
kind: 'combinator';
|
|
120
|
+
combinator: CombinatorKind;
|
|
121
|
+
binding?: string;
|
|
122
|
+
arms: Array<{
|
|
123
|
+
span: Span;
|
|
124
|
+
scope?: number;
|
|
125
|
+
}>;
|
|
126
|
+
} | {
|
|
127
|
+
kind: 'branch';
|
|
128
|
+
arms: Array<{
|
|
129
|
+
span: Span;
|
|
130
|
+
scope: number;
|
|
131
|
+
conditionSpan: Span;
|
|
132
|
+
}>;
|
|
133
|
+
otherwise?: {
|
|
134
|
+
span: Span;
|
|
135
|
+
scope: number;
|
|
136
|
+
};
|
|
137
|
+
} | {
|
|
138
|
+
kind: 'extract';
|
|
139
|
+
binding?: string;
|
|
140
|
+
node?: StoryExtractNode;
|
|
141
|
+
}
|
|
142
|
+
/** `argParams[i]` is null for an argument past the callee's last parameter. */
|
|
143
|
+
| {
|
|
144
|
+
kind: 'call';
|
|
145
|
+
binding?: string;
|
|
146
|
+
callee: string;
|
|
147
|
+
isMovement: boolean;
|
|
148
|
+
argParams?: Array<string | null>;
|
|
149
|
+
} | {
|
|
150
|
+
kind: 'callReading';
|
|
151
|
+
reading: StoryCallReading;
|
|
152
|
+
} | (StoryWalk & {
|
|
153
|
+
kind: 'traversal';
|
|
154
|
+
from?: 'graph' | 'result';
|
|
155
|
+
})
|
|
156
|
+
/** `key` is the walk's comparable token (its root and steps as JSON, keys sorted). */
|
|
157
|
+
| (StoryWalk & {
|
|
158
|
+
kind: 'valuePath';
|
|
159
|
+
key: string;
|
|
160
|
+
}) | {
|
|
161
|
+
kind: 'delete';
|
|
162
|
+
subject: string;
|
|
163
|
+
} | {
|
|
164
|
+
kind: 'refresh';
|
|
165
|
+
subject: string;
|
|
166
|
+
});
|
|
167
|
+
export type StoryNodeKind = StoryNode['kind'];
|
|
168
|
+
/** A walk's root, its hops, and where each hop landed (null where it did not type). */
|
|
169
|
+
export interface StoryWalk {
|
|
170
|
+
root?: string;
|
|
171
|
+
steps: TraversalStep[];
|
|
172
|
+
landings: Array<StoryAddress | null>;
|
|
173
|
+
}
|
|
174
|
+
export type StoryAwaitedSource = {
|
|
175
|
+
kind: 'traversal';
|
|
176
|
+
root?: string;
|
|
177
|
+
edge?: string;
|
|
178
|
+
awaitable?: boolean;
|
|
179
|
+
resolvesEmpty?: boolean;
|
|
180
|
+
} | {
|
|
181
|
+
kind: 'sleep';
|
|
182
|
+
duration: string;
|
|
183
|
+
} | {
|
|
184
|
+
kind: 'until';
|
|
185
|
+
every?: string;
|
|
186
|
+
};
|
|
187
|
+
export type StoryCallReading = {
|
|
188
|
+
kind: 'value';
|
|
189
|
+
expr: ExprSlot;
|
|
190
|
+
} | {
|
|
191
|
+
kind: 'collection';
|
|
192
|
+
collection: CollectionOpExpression;
|
|
193
|
+
} | {
|
|
194
|
+
kind: 'members';
|
|
195
|
+
members: MembersExpression;
|
|
196
|
+
} | {
|
|
197
|
+
kind: 'refused';
|
|
198
|
+
refusal: {
|
|
199
|
+
code: string;
|
|
200
|
+
message: string;
|
|
201
|
+
span: Span;
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
/** An extraction's graph: each field's `type` in the author's words. */
|
|
205
|
+
export interface StoryExtractNode {
|
|
206
|
+
name: string;
|
|
207
|
+
fields: Array<{
|
|
208
|
+
name: string;
|
|
209
|
+
at: Span;
|
|
210
|
+
type?: string;
|
|
211
|
+
}>;
|
|
212
|
+
children: StoryExtractNode[];
|
|
213
|
+
}
|
|
214
|
+
/** A statement list the story caps a lane by, and whether it leaves. */
|
|
215
|
+
export interface StoryBody {
|
|
216
|
+
span: Span;
|
|
217
|
+
kind: 'movement' | 'arm' | 'else' | 'combinatorArm' | 'callback';
|
|
218
|
+
terminates: boolean;
|
|
219
|
+
}
|
|
220
|
+
/** A file-level node declaration, `extends` folded into `root`. */
|
|
221
|
+
export interface StoryShape {
|
|
222
|
+
name: string;
|
|
223
|
+
span: Span;
|
|
224
|
+
root: ShapeNode;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* The story analysis of `source` against `snapshot`. A pin this release
|
|
228
|
+
* does not read is its version diagnostic alone, every list empty, the
|
|
229
|
+
* artifact not asked (`refused_story_analysis` in Rust). Throws
|
|
230
|
+
* `RustCheckRefused` for a source that does not parse or a snapshot that
|
|
231
|
+
* does not load, and for an analysis version this binding does not read.
|
|
232
|
+
*/
|
|
233
|
+
export declare function storyAnalysisInRust(source: string, snapshot: CatalogSnapshot, options: RustCheckOptions): StoryAnalysis;
|
|
234
|
+
/** `name` from scope `scope`, through the table: the last binding of the
|
|
235
|
+
* name in the nearest enclosing scope that binds it. */
|
|
236
|
+
export declare function resolveInAnalysis(analysis: StoryAnalysis, scope: number, name: string): StorySymbol | undefined;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The story analysis (crates/movement-checker/src/story.rs), through the
|
|
3
|
+
// movement artifact's `story_analysis` export: what the one check walk
|
|
4
|
+
// resolved, as data a story projection reads. A scope is an id into
|
|
5
|
+
// `scopes`, a symbol an id into `symbols`; spans are the AST's (1-based).
|
|
6
|
+
// The diagnostics are the check's own, so one call gives both the verdict
|
|
7
|
+
// and the picture.
|
|
8
|
+
//
|
|
9
|
+
// The envelope and every record's identity (its kind, span and scope) are
|
|
10
|
+
// validated here. What a record carries from the AST (a write's fields, a
|
|
11
|
+
// walk's steps, a node declaration's tree) is trusted as the Rust's own
|
|
12
|
+
// output, as the parser's tree is (parser/rust_parser.ts).
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.SUPPORTED_STORY_ANALYSIS_VERSIONS = exports.STORY_ANALYSIS_VERSION = void 0;
|
|
15
|
+
exports.storyAnalysisInRust = storyAnalysisInRust;
|
|
16
|
+
exports.resolveInAnalysis = resolveInAnalysis;
|
|
17
|
+
const loader_1 = require("../wasm/loader");
|
|
18
|
+
const language_version_1 = require("../language_version");
|
|
19
|
+
const rust_checker_1 = require("./rust_checker");
|
|
20
|
+
/** The analysis's shape version this binding writes and reads. */
|
|
21
|
+
exports.STORY_ANALYSIS_VERSION = 1;
|
|
22
|
+
/** Every analysis version this binding reads; any other is refused by name. */
|
|
23
|
+
exports.SUPPORTED_STORY_ANALYSIS_VERSIONS = [exports.STORY_ANALYSIS_VERSION];
|
|
24
|
+
/**
|
|
25
|
+
* The story analysis of `source` against `snapshot`. A pin this release
|
|
26
|
+
* does not read is its version diagnostic alone, every list empty, the
|
|
27
|
+
* artifact not asked (`refused_story_analysis` in Rust). Throws
|
|
28
|
+
* `RustCheckRefused` for a source that does not parse or a snapshot that
|
|
29
|
+
* does not load, and for an analysis version this binding does not read.
|
|
30
|
+
*/
|
|
31
|
+
function storyAnalysisInRust(source, snapshot, options) {
|
|
32
|
+
const refusal = (0, language_version_1.languageVersionDiagnostic)(options.languageVersion);
|
|
33
|
+
if (refusal !== undefined) {
|
|
34
|
+
return {
|
|
35
|
+
storyAnalysisVersion: exports.STORY_ANALYSIS_VERSION,
|
|
36
|
+
diagnostics: [refusal],
|
|
37
|
+
scopes: [],
|
|
38
|
+
symbols: [],
|
|
39
|
+
writes: [],
|
|
40
|
+
nodes: [],
|
|
41
|
+
bodies: [],
|
|
42
|
+
shapes: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const reply = (0, loader_1.callStoryAnalysis)(source, JSON.stringify(snapshot), options.upgradingFrom === undefined ? {} : { upgradingFrom: options.upgradingFrom });
|
|
46
|
+
const json = reply.json;
|
|
47
|
+
if (!reply.ok)
|
|
48
|
+
throw rust_checker_1.RustCheckRefused.of(json);
|
|
49
|
+
return analysisOf(json);
|
|
50
|
+
}
|
|
51
|
+
/** `name` from scope `scope`, through the table: the last binding of the
|
|
52
|
+
* name in the nearest enclosing scope that binds it. */
|
|
53
|
+
function resolveInAnalysis(analysis, scope, name) {
|
|
54
|
+
for (let at = scope; at !== undefined; at = analysis.scopes[at]?.parent) {
|
|
55
|
+
const bound = analysis.scopes[at]?.symbols[name];
|
|
56
|
+
if (bound !== undefined)
|
|
57
|
+
return analysis.symbols[bound];
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
function analysisOf(json) {
|
|
62
|
+
if (!isRecord(json))
|
|
63
|
+
throw malformed('analysis', json);
|
|
64
|
+
const version = json.storyAnalysisVersion;
|
|
65
|
+
if (typeof version !== 'number' || !exports.SUPPORTED_STORY_ANALYSIS_VERSIONS.includes(version)) {
|
|
66
|
+
throw new rust_checker_1.RustCheckRefused(`the story analysis is version ${JSON.stringify(version)}, which this binding does not read (it reads ${exports.SUPPORTED_STORY_ANALYSIS_VERSIONS.join(', ')})`);
|
|
67
|
+
}
|
|
68
|
+
const { diagnostics, scopes, symbols, writes, nodes, bodies, shapes } = json;
|
|
69
|
+
if (!Array.isArray(diagnostics) ||
|
|
70
|
+
!Array.isArray(scopes) ||
|
|
71
|
+
!Array.isArray(symbols) ||
|
|
72
|
+
!Array.isArray(writes) ||
|
|
73
|
+
!Array.isArray(nodes) ||
|
|
74
|
+
!Array.isArray(bodies) ||
|
|
75
|
+
!Array.isArray(shapes)) {
|
|
76
|
+
throw malformed('analysis', json);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
storyAnalysisVersion: version,
|
|
80
|
+
diagnostics: diagnostics.map(rust_checker_1.diagnosticOf),
|
|
81
|
+
scopes: scopes.map(each(isScope, 'scope')),
|
|
82
|
+
symbols: symbols.map(each(isSymbol, 'symbol')),
|
|
83
|
+
writes: writes.map(each(isWrite, 'write')),
|
|
84
|
+
nodes: nodes.map(each(isNode, 'node')),
|
|
85
|
+
bodies: bodies.map(each(isBody, 'body')),
|
|
86
|
+
shapes: shapes.map(each(isShape, 'shape')),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function each(is, what) {
|
|
90
|
+
return value => {
|
|
91
|
+
if (!is(value))
|
|
92
|
+
throw malformed(what, value);
|
|
93
|
+
return value;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const SYMBOL_KINDS = new Set([
|
|
97
|
+
'adapter',
|
|
98
|
+
'credential',
|
|
99
|
+
'plugin',
|
|
100
|
+
'fileImport',
|
|
101
|
+
'binding',
|
|
102
|
+
'instance',
|
|
103
|
+
'param',
|
|
104
|
+
'movement',
|
|
105
|
+
'shape',
|
|
106
|
+
'alias',
|
|
107
|
+
'type',
|
|
108
|
+
]);
|
|
109
|
+
const NODE_KINDS = new Set([
|
|
110
|
+
'instance',
|
|
111
|
+
'listen',
|
|
112
|
+
'link',
|
|
113
|
+
'unlink',
|
|
114
|
+
'await',
|
|
115
|
+
'combinator',
|
|
116
|
+
'branch',
|
|
117
|
+
'extract',
|
|
118
|
+
'call',
|
|
119
|
+
'callReading',
|
|
120
|
+
'traversal',
|
|
121
|
+
'valuePath',
|
|
122
|
+
'delete',
|
|
123
|
+
'refresh',
|
|
124
|
+
]);
|
|
125
|
+
const BODY_KINDS = new Set(['movement', 'arm', 'else', 'combinatorArm', 'callback']);
|
|
126
|
+
function isScope(value) {
|
|
127
|
+
return (isRecord(value) &&
|
|
128
|
+
(value.parent === undefined || isId(value.parent)) &&
|
|
129
|
+
(value.span === undefined || isSpan(value.span)) &&
|
|
130
|
+
isRecord(value.symbols) &&
|
|
131
|
+
Object.values(value.symbols).every(isId));
|
|
132
|
+
}
|
|
133
|
+
function isSymbol(value) {
|
|
134
|
+
return isRecord(value) && typeof value.name === 'string' && typeof value.kind === 'string' && SYMBOL_KINDS.has(value.kind) && isSpan(value.span);
|
|
135
|
+
}
|
|
136
|
+
function isWrite(value) {
|
|
137
|
+
return (isRecord(value) &&
|
|
138
|
+
isSpan(value.span) &&
|
|
139
|
+
isId(value.scope) &&
|
|
140
|
+
Array.isArray(value.fields) &&
|
|
141
|
+
typeof value.hasUniqueBy === 'boolean' &&
|
|
142
|
+
(value.action === 'create' || value.action === 'update' || value.action === 'find') &&
|
|
143
|
+
Array.isArray(value.parents));
|
|
144
|
+
}
|
|
145
|
+
function isNode(value) {
|
|
146
|
+
return isRecord(value) && typeof value.kind === 'string' && NODE_KINDS.has(value.kind) && isSpan(value.span) && isId(value.scope);
|
|
147
|
+
}
|
|
148
|
+
function isBody(value) {
|
|
149
|
+
return isRecord(value) && isSpan(value.span) && typeof value.kind === 'string' && BODY_KINDS.has(value.kind) && typeof value.terminates === 'boolean';
|
|
150
|
+
}
|
|
151
|
+
function isShape(value) {
|
|
152
|
+
return isRecord(value) && typeof value.name === 'string' && isSpan(value.span) && isRecord(value.root);
|
|
153
|
+
}
|
|
154
|
+
function isId(value) {
|
|
155
|
+
return typeof value === 'number' && Number.isInteger(value) && value >= 0;
|
|
156
|
+
}
|
|
157
|
+
function isSpan(value) {
|
|
158
|
+
return isRecord(value) && isLoc(value.start) && isLoc(value.end);
|
|
159
|
+
}
|
|
160
|
+
function isLoc(value) {
|
|
161
|
+
return isRecord(value) && typeof value.line === 'number' && typeof value.col === 'number';
|
|
162
|
+
}
|
|
163
|
+
function isRecord(value) {
|
|
164
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
165
|
+
}
|
|
166
|
+
function malformed(what, value) {
|
|
167
|
+
return new Error(`the story analysis answered a ${what} that is not one: ${JSON.stringify(value)?.slice(0, 200)}`);
|
|
168
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ export * from './checker/fixes';
|
|
|
13
13
|
export * from './checker/diagnostic';
|
|
14
14
|
export * from './checker/linked_types';
|
|
15
15
|
export * from './checker/rust_checker';
|
|
16
|
+
export * from './checker/story_analysis';
|
|
16
17
|
export * from './checker/standard_library';
|
|
17
18
|
export type { MExpr } from './parser/expression/tree';
|
|
18
19
|
export * from './service/snapshot';
|
|
20
|
+
export * from './service/language_service';
|
|
19
21
|
export * from './language_version';
|
package/dist/index.js
CHANGED
|
@@ -36,6 +36,8 @@ __exportStar(require("./checker/fixes"), exports);
|
|
|
36
36
|
__exportStar(require("./checker/diagnostic"), exports);
|
|
37
37
|
__exportStar(require("./checker/linked_types"), exports);
|
|
38
38
|
__exportStar(require("./checker/rust_checker"), exports);
|
|
39
|
+
__exportStar(require("./checker/story_analysis"), exports);
|
|
39
40
|
__exportStar(require("./checker/standard_library"), exports);
|
|
40
41
|
__exportStar(require("./service/snapshot"), exports);
|
|
42
|
+
__exportStar(require("./service/language_service"), exports);
|
|
41
43
|
__exportStar(require("./language_version"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "0.12.
|
|
1
|
+
export declare const PACKAGE_VERSION = "0.12.1";
|
|
@@ -5,4 +5,4 @@ exports.PACKAGE_VERSION = void 0;
|
|
|
5
5
|
// scripts/build-packages.cjs (a source and its build sit at different depths
|
|
6
6
|
// beside package.json, so neither can require it by one relative path).
|
|
7
7
|
// __test__/language_version.unit.test.ts holds it equal to package.json.
|
|
8
|
-
exports.PACKAGE_VERSION = '0.12.
|
|
8
|
+
exports.PACKAGE_VERSION = '0.12.1';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type LanguageVersion } from '../language_version';
|
|
2
|
+
import type { CatalogSnapshot } from './snapshot';
|
|
3
|
+
export interface LanguageServiceOptions {
|
|
4
|
+
/** The program's pin. A pin this release does not read gets empty answers,
|
|
5
|
+
* the artifact not asked. */
|
|
6
|
+
languageVersion: LanguageVersion;
|
|
7
|
+
}
|
|
8
|
+
/** A 0-based line and a UTF-16 column within it, as the wasm counts. */
|
|
9
|
+
export interface LinePosition {
|
|
10
|
+
line: number;
|
|
11
|
+
character: number;
|
|
12
|
+
}
|
|
13
|
+
export type CompletionKind = 'variable' | 'function' | 'field' | 'edge' | 'module' | 'keyword' | 'property' | 'credential' | 'type' | 'namespace';
|
|
14
|
+
/** An entry that opens a connection flow rather than inserting text: the
|
|
15
|
+
* adapter's snapshot entry declares it (`connectActions`). */
|
|
16
|
+
export interface CompletionConnectAction {
|
|
17
|
+
kind: string;
|
|
18
|
+
adapter: string;
|
|
19
|
+
credential?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CompletionItem {
|
|
22
|
+
label: string;
|
|
23
|
+
kind: CompletionKind;
|
|
24
|
+
detail?: string;
|
|
25
|
+
/** The text to insert, where it is not the label. */
|
|
26
|
+
insert?: string;
|
|
27
|
+
/** What the item replaces: its own range, or the word before the cursor. */
|
|
28
|
+
from: number;
|
|
29
|
+
to: number;
|
|
30
|
+
connectAction?: CompletionConnectAction;
|
|
31
|
+
}
|
|
32
|
+
export interface Completions {
|
|
33
|
+
/** The word before the cursor, which an item replaces unless it says otherwise. */
|
|
34
|
+
from: number;
|
|
35
|
+
to: number;
|
|
36
|
+
items: CompletionItem[];
|
|
37
|
+
}
|
|
38
|
+
export interface HoverCard {
|
|
39
|
+
from: number;
|
|
40
|
+
to: number;
|
|
41
|
+
markdown: string;
|
|
42
|
+
}
|
|
43
|
+
export type ModuleNamespace = 'adapters' | 'credentials' | 'plugins';
|
|
44
|
+
/** Where a name is defined: this file (`file: null`), a library file of the
|
|
45
|
+
* snapshot (offsets into its text), or a module the name was imported from. */
|
|
46
|
+
export type Definition = {
|
|
47
|
+
file: null;
|
|
48
|
+
from: number;
|
|
49
|
+
to: number;
|
|
50
|
+
} | {
|
|
51
|
+
file: string;
|
|
52
|
+
from: number;
|
|
53
|
+
to: number;
|
|
54
|
+
} | {
|
|
55
|
+
module: {
|
|
56
|
+
namespace: ModuleNamespace;
|
|
57
|
+
name: string;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export interface QuickFix {
|
|
61
|
+
title: string;
|
|
62
|
+
/** The diagnostic code the fix answers. */
|
|
63
|
+
code: string;
|
|
64
|
+
preferred: boolean;
|
|
65
|
+
edits: Array<{
|
|
66
|
+
from: number;
|
|
67
|
+
to: number;
|
|
68
|
+
insert: string;
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
71
|
+
/** The completions at `offset`. */
|
|
72
|
+
export declare function completionsAt(source: string, offset: number, snapshot: CatalogSnapshot, options: LanguageServiceOptions): Completions;
|
|
73
|
+
/** The card for what is under `offset`, or null where there is none. */
|
|
74
|
+
export declare function hoverAt(source: string, offset: number, snapshot: CatalogSnapshot, options: LanguageServiceOptions): HoverCard | null;
|
|
75
|
+
/** Where the name under `offset` is defined, or null. */
|
|
76
|
+
export declare function definitionAt(source: string, offset: number, snapshot: CatalogSnapshot, options: LanguageServiceOptions): Definition | null;
|
|
77
|
+
/** The quick fixes for the diagnostics touching `from`..`to`. */
|
|
78
|
+
export declare function quickFixesIn(source: string, from: number, to: number, snapshot: CatalogSnapshot, options: LanguageServiceOptions): QuickFix[];
|
|
79
|
+
/** The UTF-16 offset of `position` in `text`, clamped to its line and to the text. */
|
|
80
|
+
export declare function offsetOf(text: string, position: LinePosition): number;
|
|
81
|
+
/** The line and UTF-16 column of `offset` in `text`, `offset` clamped to the text. */
|
|
82
|
+
export declare function positionOf(text: string, offset: number): LinePosition;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The language service (crates/movement-language), through the movement
|
|
3
|
+
// artifact's `language_query` export: completion, hover, go-to-definition
|
|
4
|
+
// and quick fixes for a program in this release's language, answered as the
|
|
5
|
+
// native service answers `lf lsp`.
|
|
6
|
+
//
|
|
7
|
+
// Positions are UTF-16 offsets into the source string (CodeMirror's unit,
|
|
8
|
+
// and a JavaScript string's own); the wasm counts 0-based lines and UTF-16
|
|
9
|
+
// columns, split on `\n` as the native service splits them. A definition in
|
|
10
|
+
// a library file is an offset into that file's text in `snapshot.files`.
|
|
11
|
+
//
|
|
12
|
+
// It loads with the engine artifact, which a browser never downloads: the
|
|
13
|
+
// editor asks it through the server, as it asks for the check.
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.completionsAt = completionsAt;
|
|
16
|
+
exports.hoverAt = hoverAt;
|
|
17
|
+
exports.definitionAt = definitionAt;
|
|
18
|
+
exports.quickFixesIn = quickFixesIn;
|
|
19
|
+
exports.offsetOf = offsetOf;
|
|
20
|
+
exports.positionOf = positionOf;
|
|
21
|
+
const loader_1 = require("../wasm/loader");
|
|
22
|
+
const language_version_1 = require("../language_version");
|
|
23
|
+
const rust_checker_1 = require("../checker/rust_checker");
|
|
24
|
+
/** The completions at `offset`. */
|
|
25
|
+
function completionsAt(source, offset, snapshot, options) {
|
|
26
|
+
if (unread(options))
|
|
27
|
+
return { from: offset, to: offset, items: [] };
|
|
28
|
+
const answer = ask(source, snapshot, { kind: 'complete', position: positionOf(source, offset) });
|
|
29
|
+
if (!isRecord(answer) || !Array.isArray(answer.items))
|
|
30
|
+
throw malformed('completion', answer);
|
|
31
|
+
const word = rangeOf(source, answer.word);
|
|
32
|
+
return { ...word, items: answer.items.map(item => itemOf(source, item, word)) };
|
|
33
|
+
}
|
|
34
|
+
/** The card for what is under `offset`, or null where there is none. */
|
|
35
|
+
function hoverAt(source, offset, snapshot, options) {
|
|
36
|
+
if (unread(options))
|
|
37
|
+
return null;
|
|
38
|
+
const answer = ask(source, snapshot, { kind: 'hover', position: positionOf(source, offset) });
|
|
39
|
+
if (answer === null)
|
|
40
|
+
return null;
|
|
41
|
+
if (!isRecord(answer) || typeof answer.contents !== 'string')
|
|
42
|
+
throw malformed('hover', answer);
|
|
43
|
+
return { ...rangeOf(source, answer.range), markdown: answer.contents };
|
|
44
|
+
}
|
|
45
|
+
/** Where the name under `offset` is defined, or null. */
|
|
46
|
+
function definitionAt(source, offset, snapshot, options) {
|
|
47
|
+
if (unread(options))
|
|
48
|
+
return null;
|
|
49
|
+
const answer = ask(source, snapshot, { kind: 'definition', position: positionOf(source, offset) });
|
|
50
|
+
if (answer === null)
|
|
51
|
+
return null;
|
|
52
|
+
if (!isRecord(answer) || (answer.file !== null && typeof answer.file !== 'string'))
|
|
53
|
+
throw malformed('definition', answer);
|
|
54
|
+
if (answer.module !== undefined)
|
|
55
|
+
return { module: moduleOf(answer.module) };
|
|
56
|
+
if (answer.file === null)
|
|
57
|
+
return { file: null, ...rangeOf(source, answer.range) };
|
|
58
|
+
// A library file the snapshot does not carry has no text: its offsets clamp to 0.
|
|
59
|
+
const text = snapshot.files?.[answer.file]?.source ?? '';
|
|
60
|
+
return { file: answer.file, ...rangeOf(text, answer.range) };
|
|
61
|
+
}
|
|
62
|
+
/** The quick fixes for the diagnostics touching `from`..`to`. */
|
|
63
|
+
function quickFixesIn(source, from, to, snapshot, options) {
|
|
64
|
+
if (unread(options))
|
|
65
|
+
return [];
|
|
66
|
+
const range = { start: positionOf(source, from), end: positionOf(source, to) };
|
|
67
|
+
const answer = ask(source, snapshot, { kind: 'codeActions', position: range.start, range });
|
|
68
|
+
if (!Array.isArray(answer))
|
|
69
|
+
throw malformed('code actions', answer);
|
|
70
|
+
return answer.map(action => {
|
|
71
|
+
if (!isRecord(action) ||
|
|
72
|
+
typeof action.title !== 'string' ||
|
|
73
|
+
typeof action.code !== 'string' ||
|
|
74
|
+
typeof action.preferred !== 'boolean' ||
|
|
75
|
+
!Array.isArray(action.edits)) {
|
|
76
|
+
throw malformed('code action', action);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
title: action.title,
|
|
80
|
+
code: action.code,
|
|
81
|
+
preferred: action.preferred,
|
|
82
|
+
edits: action.edits.map(edit => {
|
|
83
|
+
if (!isRecord(edit) || typeof edit.newText !== 'string')
|
|
84
|
+
throw malformed('edit', edit);
|
|
85
|
+
return { ...rangeOf(source, edit.range), insert: edit.newText };
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** The UTF-16 offset of `position` in `text`, clamped to its line and to the text. */
|
|
91
|
+
function offsetOf(text, position) {
|
|
92
|
+
let start = 0;
|
|
93
|
+
for (let line = 0; line < position.line; line++) {
|
|
94
|
+
const newline = text.indexOf('\n', start);
|
|
95
|
+
if (newline === -1)
|
|
96
|
+
return text.length;
|
|
97
|
+
start = newline + 1;
|
|
98
|
+
}
|
|
99
|
+
const newline = text.indexOf('\n', start);
|
|
100
|
+
const end = newline === -1 ? text.length : newline;
|
|
101
|
+
return Math.min(start + position.character, end);
|
|
102
|
+
}
|
|
103
|
+
/** The line and UTF-16 column of `offset` in `text`, `offset` clamped to the text. */
|
|
104
|
+
function positionOf(text, offset) {
|
|
105
|
+
const at = Math.max(0, Math.min(offset, text.length));
|
|
106
|
+
let line = 0;
|
|
107
|
+
let start = 0;
|
|
108
|
+
for (let newline = text.indexOf('\n'); newline !== -1 && newline < at; newline = text.indexOf('\n', start)) {
|
|
109
|
+
line += 1;
|
|
110
|
+
start = newline + 1;
|
|
111
|
+
}
|
|
112
|
+
return { line, character: at - start };
|
|
113
|
+
}
|
|
114
|
+
function unread(options) {
|
|
115
|
+
return (0, language_version_1.languageVersionDiagnostic)(options.languageVersion) !== undefined;
|
|
116
|
+
}
|
|
117
|
+
function ask(source, snapshot, query) {
|
|
118
|
+
const reply = (0, loader_1.callLanguageQuery)(source, JSON.stringify(snapshot), JSON.stringify(query));
|
|
119
|
+
if (!reply.ok)
|
|
120
|
+
throw rust_checker_1.RustCheckRefused.of(reply.json);
|
|
121
|
+
return reply.json;
|
|
122
|
+
}
|
|
123
|
+
const KINDS = new Set([
|
|
124
|
+
'variable',
|
|
125
|
+
'function',
|
|
126
|
+
'field',
|
|
127
|
+
'edge',
|
|
128
|
+
'module',
|
|
129
|
+
'keyword',
|
|
130
|
+
'property',
|
|
131
|
+
'credential',
|
|
132
|
+
'type',
|
|
133
|
+
'namespace',
|
|
134
|
+
]);
|
|
135
|
+
function isKind(value) {
|
|
136
|
+
return typeof value === 'string' && KINDS.has(value);
|
|
137
|
+
}
|
|
138
|
+
function itemOf(source, value, word) {
|
|
139
|
+
if (!isRecord(value) || typeof value.label !== 'string' || !isKind(value.kind))
|
|
140
|
+
throw malformed('completion item', value);
|
|
141
|
+
const { label, kind, detail, insert, replace, connectAction } = value;
|
|
142
|
+
if ((detail !== undefined && typeof detail !== 'string') || (insert !== undefined && typeof insert !== 'string')) {
|
|
143
|
+
throw malformed('completion item', value);
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
label,
|
|
147
|
+
kind,
|
|
148
|
+
...(detail !== undefined ? { detail } : {}),
|
|
149
|
+
...(insert !== undefined ? { insert } : {}),
|
|
150
|
+
...(replace !== undefined ? rangeOf(source, replace) : word),
|
|
151
|
+
...(connectAction !== undefined ? { connectAction: connectActionOf(connectAction) } : {}),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function connectActionOf(value) {
|
|
155
|
+
if (!isRecord(value) ||
|
|
156
|
+
typeof value.kind !== 'string' ||
|
|
157
|
+
typeof value.adapter !== 'string' ||
|
|
158
|
+
(value.credential !== undefined && typeof value.credential !== 'string')) {
|
|
159
|
+
throw malformed('connect action', value);
|
|
160
|
+
}
|
|
161
|
+
return { kind: value.kind, adapter: value.adapter, ...(value.credential !== undefined ? { credential: value.credential } : {}) };
|
|
162
|
+
}
|
|
163
|
+
function moduleOf(value) {
|
|
164
|
+
if (!isRecord(value) || typeof value.name !== 'string')
|
|
165
|
+
throw malformed('module', value);
|
|
166
|
+
const { namespace, name } = value;
|
|
167
|
+
if (namespace !== 'adapters' && namespace !== 'credentials' && namespace !== 'plugins')
|
|
168
|
+
throw malformed('module', value);
|
|
169
|
+
return { namespace, name };
|
|
170
|
+
}
|
|
171
|
+
function rangeOf(text, value) {
|
|
172
|
+
if (!isRecord(value))
|
|
173
|
+
throw malformed('range', value);
|
|
174
|
+
return { from: offsetOf(text, linePositionOf(value.start)), to: offsetOf(text, linePositionOf(value.end)) };
|
|
175
|
+
}
|
|
176
|
+
function linePositionOf(value) {
|
|
177
|
+
if (!isRecord(value) || typeof value.line !== 'number' || typeof value.character !== 'number')
|
|
178
|
+
throw malformed('position', value);
|
|
179
|
+
return { line: value.line, character: value.character };
|
|
180
|
+
}
|
|
181
|
+
function isRecord(value) {
|
|
182
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
183
|
+
}
|
|
184
|
+
function malformed(what, value) {
|
|
185
|
+
return new Error(`the language service answered a ${what} that is not one: ${JSON.stringify(value)?.slice(0, 200)}`);
|
|
186
|
+
}
|
package/dist/wasm/loader.d.ts
CHANGED
|
@@ -128,6 +128,31 @@ export declare function callLanguageStanding(pin: string): {
|
|
|
128
128
|
ok: boolean;
|
|
129
129
|
json: unknown;
|
|
130
130
|
};
|
|
131
|
+
/**
|
|
132
|
+
* The language service (crates/movement-language/src/abi.rs
|
|
133
|
+
* `language_query`): the source, the snapshot and the query as JSON, and the
|
|
134
|
+
* reply — the native service's answer (an object, an array or `null`), or
|
|
135
|
+
* `{error}` for a snapshot or a query that does not read.
|
|
136
|
+
*
|
|
137
|
+
* @internal Used by the service's wrapper (service/language_service.ts).
|
|
138
|
+
*/
|
|
139
|
+
export declare function callLanguageQuery(source: string, snapshotJson: string, queryJson: string): {
|
|
140
|
+
ok: boolean;
|
|
141
|
+
json: unknown;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* The checker's story analysis (crates/movement-checker/src/story.rs
|
|
145
|
+
* `story_analysis`): `callCheckProgram`'s inputs, and the reply —
|
|
146
|
+
* `{storyAnalysisVersion, diagnostics, scopes, symbols, writes, nodes,
|
|
147
|
+
* bodies, shapes}`, or `{error}` for a source that does not parse or a
|
|
148
|
+
* snapshot that does not load.
|
|
149
|
+
*
|
|
150
|
+
* @internal Used by the analysis's wrapper (checker/story_analysis.ts).
|
|
151
|
+
*/
|
|
152
|
+
export declare function callStoryAnalysis(source: string, snapshotJson: string, options: CheckCallOptions): {
|
|
153
|
+
ok: boolean;
|
|
154
|
+
json: unknown;
|
|
155
|
+
};
|
|
131
156
|
/**
|
|
132
157
|
* One engine segment's conversation (crates/movement-engine/src/abi.rs):
|
|
133
158
|
* `start` and `answer` each return the next step as `{ok: true, json}` —
|
package/dist/wasm/loader.js
CHANGED
|
@@ -34,6 +34,8 @@ exports.callCheckCompatible = callCheckCompatible;
|
|
|
34
34
|
exports.callStandardLibrary = callStandardLibrary;
|
|
35
35
|
exports.callLanguageVersion = callLanguageVersion;
|
|
36
36
|
exports.callLanguageStanding = callLanguageStanding;
|
|
37
|
+
exports.callLanguageQuery = callLanguageQuery;
|
|
38
|
+
exports.callStoryAnalysis = callStoryAnalysis;
|
|
37
39
|
exports.initEngine = initEngine;
|
|
38
40
|
exports.openEngineSegment = openEngineSegment;
|
|
39
41
|
exports.parserGeneration = parserGeneration;
|
|
@@ -185,6 +187,29 @@ function callLanguageVersion() {
|
|
|
185
187
|
function callLanguageStanding(pin) {
|
|
186
188
|
return callChecker('language_standing', [pin], []);
|
|
187
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* The language service (crates/movement-language/src/abi.rs
|
|
192
|
+
* `language_query`): the source, the snapshot and the query as JSON, and the
|
|
193
|
+
* reply — the native service's answer (an object, an array or `null`), or
|
|
194
|
+
* `{error}` for a snapshot or a query that does not read.
|
|
195
|
+
*
|
|
196
|
+
* @internal Used by the service's wrapper (service/language_service.ts).
|
|
197
|
+
*/
|
|
198
|
+
function callLanguageQuery(source, snapshotJson, queryJson) {
|
|
199
|
+
return callChecker('language_query', [source, snapshotJson, queryJson], []);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The checker's story analysis (crates/movement-checker/src/story.rs
|
|
203
|
+
* `story_analysis`): `callCheckProgram`'s inputs, and the reply —
|
|
204
|
+
* `{storyAnalysisVersion, diagnostics, scopes, symbols, writes, nodes,
|
|
205
|
+
* bodies, shapes}`, or `{error}` for a source that does not parse or a
|
|
206
|
+
* snapshot that does not load.
|
|
207
|
+
*
|
|
208
|
+
* @internal Used by the analysis's wrapper (checker/story_analysis.ts).
|
|
209
|
+
*/
|
|
210
|
+
function callStoryAnalysis(source, snapshotJson, options) {
|
|
211
|
+
return callChecker('story_analysis', [source, snapshotJson, optionsJson(options)], []);
|
|
212
|
+
}
|
|
188
213
|
function callChecker(fn, inputs, extra) {
|
|
189
214
|
const exports = currentExports();
|
|
190
215
|
const checker = exports.checker[fn];
|
|
@@ -400,7 +425,16 @@ function readExports(raw) {
|
|
|
400
425
|
catalog_query: exported('catalog_query'),
|
|
401
426
|
};
|
|
402
427
|
const checker = {};
|
|
403
|
-
for (const fn of [
|
|
428
|
+
for (const fn of [
|
|
429
|
+
'check_program',
|
|
430
|
+
'check_answer',
|
|
431
|
+
'check_compatible',
|
|
432
|
+
'standard_library',
|
|
433
|
+
'language_version',
|
|
434
|
+
'language_standing',
|
|
435
|
+
'language_query',
|
|
436
|
+
'story_analysis',
|
|
437
|
+
]) {
|
|
404
438
|
if (typeof raw[fn] !== 'function')
|
|
405
439
|
continue;
|
|
406
440
|
const call = exported(fn);
|
package/dist/wasm/movement.wasm
CHANGED
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@listen-fire/movement-lang",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "0.12.
|
|
4
|
+
"version": "0.12.1",
|
|
5
5
|
"description": "The data-movement language: the wasm parser, checker and engine with their loader and bindings, the version rule, the expression AST and the types the contracts name",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": "22.
|
|
7
|
+
"node": ">=22.23.4 <23"
|
|
8
8
|
},
|
|
9
9
|
"main": "./dist/index.js",
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
@@ -26,8 +26,7 @@
|
|
|
26
26
|
"dist"
|
|
27
27
|
],
|
|
28
28
|
"publishConfig": {
|
|
29
|
-
"access": "public"
|
|
30
|
-
"tag": "next"
|
|
29
|
+
"access": "public"
|
|
31
30
|
},
|
|
32
31
|
"scripts": {
|
|
33
32
|
"build": "tsc --project tsconfig.build.json && cp wasm/*.wasm dist/wasm/",
|