@chestnutlabs/gcode-parser 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/parse.d.ts +25 -0
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +97 -3
- package/dist/rs274-flow.d.ts +69 -0
- package/dist/rs274-flow.d.ts.map +1 -0
- package/dist/rs274-flow.js +702 -0
- package/dist/rs274.d.ts +107 -0
- package/dist/rs274.d.ts.map +1 -0
- package/dist/rs274.js +723 -0
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +5 -0
- package/package.json +5 -5
package/dist/parse.d.ts
CHANGED
|
@@ -34,6 +34,19 @@ export interface ParseLimits {
|
|
|
34
34
|
maxBufferBytes: number;
|
|
35
35
|
maxLineLength: number;
|
|
36
36
|
maxWarnings: number;
|
|
37
|
+
/**
|
|
38
|
+
* Total loop work across an RS274NGC O-word program (DD-017 §4.5, Phase 2). Charged per loop pass and
|
|
39
|
+
* per statement executed inside a loop body; exceeding it stops the program with a partial IR and a
|
|
40
|
+
* `rs274-iteration-limit` disclosure, so a hostile `o while [1]` wastes only bounded CPU. Irrelevant to
|
|
41
|
+
* FDM / non-parametric input, which never enters the interpreter.
|
|
42
|
+
*/
|
|
43
|
+
maxProgramIterations: number;
|
|
44
|
+
/**
|
|
45
|
+
* Subroutine call-recursion depth for an RS274NGC O-word program (DD-017 §4.5, Phase 3). A `call` that
|
|
46
|
+
* would exceed this depth is disclosed (`rs274-call-depth`) and skipped, so unbounded recursion cannot
|
|
47
|
+
* exhaust the stack. Irrelevant to FDM / non-parametric input.
|
|
48
|
+
*/
|
|
49
|
+
maxCallDepth: number;
|
|
37
50
|
}
|
|
38
51
|
export declare const DEFAULT_LIMITS: ParseLimits;
|
|
39
52
|
/**
|
|
@@ -144,6 +157,18 @@ export interface Engine {
|
|
|
144
157
|
processLine(rawLine: string, offset: number): void;
|
|
145
158
|
stopped(): boolean;
|
|
146
159
|
markInputTooBig(): void;
|
|
160
|
+
/**
|
|
161
|
+
* Interpret the buffered `text` as an RS274NGC O-word program (DD-017 Phase 2). Called by the
|
|
162
|
+
* in-memory drivers INSTEAD of the forward-only line loop when `programUsesOWords(text)` is true;
|
|
163
|
+
* the interpreter re-feeds every plain line through `processLine`, so geometry stays on one path.
|
|
164
|
+
*/
|
|
165
|
+
runOWordFlow(): void;
|
|
166
|
+
/**
|
|
167
|
+
* Streaming only: record that an O-word control line was seen but NOT interpreted (control flow needs a
|
|
168
|
+
* fully-buffered program — DD-017 §4.1). Discloses once and marks the parametric capability degraded;
|
|
169
|
+
* the line still runs linearly (the honest pre-Phase-2 behaviour), never silently as if flow had run.
|
|
170
|
+
*/
|
|
171
|
+
noteUninterpretedOWord(srcByte: number): void;
|
|
147
172
|
/** Streaming driver: record the true source byte count as it becomes known. */
|
|
148
173
|
setSourceBytes(bytes: number): void;
|
|
149
174
|
/**
|
package/dist/parse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
|
package/dist/parse.js
CHANGED
|
@@ -27,14 +27,18 @@
|
|
|
27
27
|
* output; multi-byte comment bytes would skew offsets and are a documented
|
|
28
28
|
* limitation until the streaming decoder lands (phase 3).
|
|
29
29
|
*/
|
|
30
|
-
import { IR_SCHEMA_VERSION, MoveKind, buildSourceIndex, computeSegmentBounds } from '@chestnutlabs/toolpath-core';
|
|
30
|
+
import { IR_SCHEMA_VERSION, MoveKind, buildSourceIndex, classifyModelBounds, computeSegmentBounds, emptyBounds } from '@chestnutlabs/toolpath-core';
|
|
31
31
|
import { BudgetExceededError, SegmentWriter } from './growable.js';
|
|
32
|
+
import { Rs274Context, lineUsesParametric } from './rs274.js';
|
|
33
|
+
import { RS274_FLOW_WARN, interpretOWordProgram, programUsesOWords } from './rs274-flow.js';
|
|
32
34
|
export const DEFAULT_LIMITS = {
|
|
33
35
|
maxInputBytes: 512 * 1024 * 1024,
|
|
34
36
|
maxSegments: 20_000_000,
|
|
35
37
|
maxBufferBytes: 1536 * 1024 * 1024,
|
|
36
38
|
maxLineLength: 64 * 1024,
|
|
37
|
-
maxWarnings: 10_000
|
|
39
|
+
maxWarnings: 10_000,
|
|
40
|
+
maxProgramIterations: 1_000_000,
|
|
41
|
+
maxCallDepth: 50
|
|
38
42
|
};
|
|
39
43
|
const LAYER_TOLERANCE = 0.05;
|
|
40
44
|
const UNRESOLVED_LAYER = 0xffffffff;
|
|
@@ -186,6 +190,10 @@ export function createEngine(input, opts) {
|
|
|
186
190
|
let cannedInitialZ = 0; // Z when the cycle activated — the G98 retract plane
|
|
187
191
|
let cannedRetractInitial = true; // G98 (retract to initial Z) vs G99 (retract to R); modal, default G98
|
|
188
192
|
let cannedCyclesSeen = false; // any G81/G82/G83 expanded → cannedCycles capability 'known'
|
|
193
|
+
// RS274NGC parametric programs (DD-017 Phase 1, #189 phase 7). The store + expression evaluator resolve
|
|
194
|
+
// `#params`/`[expr]` on any line that uses them; non-parametric lines never touch it (byte-identical).
|
|
195
|
+
// The system-parameter allow-list (#5420–#5422 = current position) reads live engine position.
|
|
196
|
+
const rs274 = new Rs274Context(warn, () => ({ x: sx, y: sy, z: sz }));
|
|
189
197
|
// Per-axis position certainty (DD-010 D4 amendment, #158). A G31 probe endpoint is reached at
|
|
190
198
|
// RUNTIME (workpiece contact), not the commanded value, so it marks the probed axes uncertain. A
|
|
191
199
|
// following G92 then RESYNCS the logical frame at the datum (rather than datum-shifting a stale
|
|
@@ -573,7 +581,22 @@ export function createEngine(input, opts) {
|
|
|
573
581
|
if (ci !== -1)
|
|
574
582
|
onComment(rawLine.slice(ci + 1), offset);
|
|
575
583
|
}
|
|
576
|
-
|
|
584
|
+
// RS274NGC parametric programs (DD-017 Phase 1): a line using `#`/`[` is resolved by the parameter +
|
|
585
|
+
// expression interpreter; a `#n = <expr>` assignment executes and the line ends there. Every other
|
|
586
|
+
// line (all FDM, all simple CNC) takes the untouched lexer below — byte-identical.
|
|
587
|
+
let cmd;
|
|
588
|
+
// Hot-path gate: a cheap `#`/`[` scan of the RAW line — no allocation. Every FDM line fails this and
|
|
589
|
+
// takes the untouched lexer (byte-identical). A `#`/`[` that turns out to be only inside a comment
|
|
590
|
+
// routes through the resolver, which strips comments and returns 'plain' → the normal lexer.
|
|
591
|
+
if (lineUsesParametric(rawLine)) {
|
|
592
|
+
const r = rs274.lexLine(rawLine, offset);
|
|
593
|
+
if (r.kind === 'assign' || r.kind === 'empty')
|
|
594
|
+
return; // assignment applied / nothing to do
|
|
595
|
+
cmd = r.kind === 'cmd' ? { codes: r.codes, params: r.params } : lexLine(rawLine); // 'plain' → normal
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
cmd = lexLine(rawLine);
|
|
599
|
+
}
|
|
577
600
|
// Skip only genuinely empty lines. A line with no lexable words but real content (a dialect
|
|
578
601
|
// extended command like `EXCLUDE_OBJECT_START NAME=cube`, a macro) must NOT be dropped — the
|
|
579
602
|
// observer below still forwards its raw line. The body split runs only when there are no words.
|
|
@@ -838,6 +861,46 @@ export function createEngine(input, opts) {
|
|
|
838
861
|
truncatedAtByte = offset;
|
|
839
862
|
}
|
|
840
863
|
};
|
|
864
|
+
// RS274NGC O-word control flow (DD-017 Phase 2). Engaged only when the buffered program actually
|
|
865
|
+
// contains a control line; it buffers the lines with their byte offsets and hands them to the tree
|
|
866
|
+
// interpreter, which re-feeds each plain line through `processLine` (one geometry path) and evaluates
|
|
867
|
+
// conditions/counts against the SAME parameter store. `used`/`degraded` tier the capability.
|
|
868
|
+
const runOWordFlow = () => {
|
|
869
|
+
rs274.used = true;
|
|
870
|
+
const flowLines = [];
|
|
871
|
+
const len = text.length;
|
|
872
|
+
let offset = 0;
|
|
873
|
+
while (offset < len) {
|
|
874
|
+
let nl = text.indexOf('\n', offset);
|
|
875
|
+
if (nl === -1)
|
|
876
|
+
nl = len;
|
|
877
|
+
flowLines.push({ text: text.slice(offset, nl), offset });
|
|
878
|
+
offset = nl + 1;
|
|
879
|
+
}
|
|
880
|
+
const { degraded } = interpretOWordProgram(flowLines, {
|
|
881
|
+
processLine,
|
|
882
|
+
stopped: () => stopReason !== undefined,
|
|
883
|
+
evalExpression: (t, b) => rs274.evalExpression(t, b),
|
|
884
|
+
pushScope: (args) => rs274.pushScope(args),
|
|
885
|
+
popScope: () => rs274.popScope(),
|
|
886
|
+
warn,
|
|
887
|
+
maxProgramIterations: limits.maxProgramIterations,
|
|
888
|
+
maxCallDepth: limits.maxCallDepth
|
|
889
|
+
});
|
|
890
|
+
if (degraded)
|
|
891
|
+
rs274.degraded = true;
|
|
892
|
+
};
|
|
893
|
+
// Streaming can't interpret control flow from a partial stream (a loop needs its whole body — DD-017
|
|
894
|
+
// §4.1); disclose once and let the program run linearly (control lines are inert). Honest, bounded.
|
|
895
|
+
let streamingOWordWarned = false;
|
|
896
|
+
const noteUninterpretedOWord = (srcByte) => {
|
|
897
|
+
if (streamingOWordWarned)
|
|
898
|
+
return;
|
|
899
|
+
streamingOWordWarned = true;
|
|
900
|
+
rs274.used = true;
|
|
901
|
+
rs274.degraded = true;
|
|
902
|
+
warn(RS274_FLOW_WARN.streamingUnsupported, 'O-word control flow is only interpreted for fully-buffered input; this streamed program ran linearly (control flow not executed).', srcByte);
|
|
903
|
+
};
|
|
841
904
|
const finish = (cancelledAtByte) => {
|
|
842
905
|
if (stopReason !== undefined && truncatedAtByte === undefined) {
|
|
843
906
|
truncatedAtByte = currentSrcByte;
|
|
@@ -854,6 +917,7 @@ export function createEngine(input, opts) {
|
|
|
854
917
|
stats.segments = segments.count;
|
|
855
918
|
const origin = { x: ox, y: oy, z: oz };
|
|
856
919
|
const { bounds, boundsWithTravel, objectBounds } = computeSegmentBounds(segments, origin);
|
|
920
|
+
const { modelBounds, classification: nonModelClassification } = classifyModelBounds(segments, origin);
|
|
857
921
|
const sourceIndex = buildSourceIndex(segments.srcByte, segments.count);
|
|
858
922
|
// Layer table for the IR (absolute z; seg ranges accumulated above).
|
|
859
923
|
const irLayers = layersCleared || layers.length === 0
|
|
@@ -872,6 +936,10 @@ export function createEngine(input, opts) {
|
|
|
872
936
|
sourcePositions: 'known',
|
|
873
937
|
featureRoles: 'unavailable',
|
|
874
938
|
objects: 'unavailable',
|
|
939
|
+
// Non-model (model-bounds) classification (DD-026 D7). At parse time the object/feature channels
|
|
940
|
+
// are usually empty, so this is 'unavailable' until a dialect adapter annotates them and the
|
|
941
|
+
// runner refreshes it (gcode-dialects finalize). Honest either way — never a guess.
|
|
942
|
+
nonModelClassification,
|
|
875
943
|
retractions: retractionEvents.length > 0 ? 'known' : 'unavailable',
|
|
876
944
|
colorChanges: colorChangeEvents.length > 0 ? 'known' : 'unavailable',
|
|
877
945
|
// Annotation-derived move kinds (DD-016, #182). The parser never sets these — they come from a
|
|
@@ -886,6 +954,11 @@ export function createEngine(input, opts) {
|
|
|
886
954
|
// Canned drilling cycles expanded to geometry (DD-012 phase 2, #189). 'known' once a G81/G82/G83
|
|
887
955
|
// was seen (holes are real segments), else 'unavailable' (no cycles / FDM).
|
|
888
956
|
cannedCycles: cannedCyclesSeen ? 'known' : 'unavailable',
|
|
957
|
+
// RS274NGC parametric programs (DD-017, #189 phase 7). 'known' once a `#param`/`[expr]`/O-word was
|
|
958
|
+
// executed deterministically within limits (the interpreter computes geometry — it does not infer);
|
|
959
|
+
// 'approximated' when Phase 2 flow was degraded (a hit iteration cap, an unbalanced/unsupported
|
|
960
|
+
// O-word, or a malformed condition — always disclosed); 'unavailable' for non-parametric input.
|
|
961
|
+
parametricProgram: rs274.parametricConfidence(),
|
|
889
962
|
// Motion-model modes (DD-010 E10 phase 1). 'known' when the governing command was seen
|
|
890
963
|
// (M82/M83, or a firmware-known G90/G91 for E); 'inferred' when defaulted (absolute).
|
|
891
964
|
extrusionMode: eModeExplicit !== null || (extruderFollowsPositioning && positioningSeen) ? 'known' : 'inferred',
|
|
@@ -945,6 +1018,7 @@ export function createEngine(input, opts) {
|
|
|
945
1018
|
bounds,
|
|
946
1019
|
boundsWithTravel,
|
|
947
1020
|
objectBounds,
|
|
1021
|
+
modelBounds,
|
|
948
1022
|
sourceIndex
|
|
949
1023
|
};
|
|
950
1024
|
if (stopReason !== undefined) {
|
|
@@ -999,12 +1073,14 @@ export function createEngine(input, opts) {
|
|
|
999
1073
|
sourcePositions: 'unavailable', // no per-slice source index is built
|
|
1000
1074
|
featureRoles: 'unavailable',
|
|
1001
1075
|
objects: 'unavailable',
|
|
1076
|
+
nonModelClassification: 'unavailable', // model classification resolves on the final IR (DD-026)
|
|
1002
1077
|
retractions: 'unavailable', // markers resolve on the final IR, not on preview slices
|
|
1003
1078
|
colorChanges: 'unavailable', // color-change boundaries resolve on the final IR
|
|
1004
1079
|
wipeMoves: 'unavailable', // annotation move kinds resolve on the final IR (DD-016)
|
|
1005
1080
|
seamMoves: 'unavailable',
|
|
1006
1081
|
cutMoves: 'unavailable', // non-extrusion classification resolves on the final IR (DD-012)
|
|
1007
1082
|
cannedCycles: 'unavailable', // canned-cycle expansion resolves on the final IR (DD-012)
|
|
1083
|
+
parametricProgram: 'unavailable', // parametric resolution resolves on the final IR (DD-017)
|
|
1008
1084
|
extrusionMode: 'inferred', // motion modes resolve fully on the final IR (E10)
|
|
1009
1085
|
positioningMode: 'inferred',
|
|
1010
1086
|
arcPlanes: 'inferred',
|
|
@@ -1020,6 +1096,7 @@ export function createEngine(input, opts) {
|
|
|
1020
1096
|
bounds,
|
|
1021
1097
|
boundsWithTravel,
|
|
1022
1098
|
objectBounds,
|
|
1099
|
+
modelBounds: emptyBounds(),
|
|
1023
1100
|
sourceIndex: { byteOffsets: new Uint32Array(0), segmentIndices: new Uint32Array(0) }
|
|
1024
1101
|
};
|
|
1025
1102
|
return { slice, upTo: cut };
|
|
@@ -1029,6 +1106,8 @@ export function createEngine(input, opts) {
|
|
|
1029
1106
|
limits,
|
|
1030
1107
|
processLine,
|
|
1031
1108
|
stopped: () => stopReason !== undefined,
|
|
1109
|
+
runOWordFlow,
|
|
1110
|
+
noteUninterpretedOWord,
|
|
1032
1111
|
snapshot,
|
|
1033
1112
|
markInputTooBig: () => {
|
|
1034
1113
|
stopReason = {
|
|
@@ -1054,6 +1133,13 @@ export function parseGcodeToIR(input, opts = {}) {
|
|
|
1054
1133
|
return engine.finish();
|
|
1055
1134
|
}
|
|
1056
1135
|
const text = engine.text;
|
|
1136
|
+
// RS274NGC O-word program (DD-017 Phase 2): interpret with control flow instead of the forward-only
|
|
1137
|
+
// loop. The cheap detection scan fails for every FDM / non-parametric file, which takes the untouched
|
|
1138
|
+
// loop below (byte-identical).
|
|
1139
|
+
if (programUsesOWords(text)) {
|
|
1140
|
+
engine.runOWordFlow();
|
|
1141
|
+
return engine.finish();
|
|
1142
|
+
}
|
|
1057
1143
|
const len = text.length;
|
|
1058
1144
|
let offset = 0;
|
|
1059
1145
|
while (offset < len && !engine.stopped()) {
|
|
@@ -1093,6 +1179,14 @@ export async function parseGcodeToIRAsync(input, opts = {}, hooks = {}) {
|
|
|
1093
1179
|
const yieldEvery = hooks.yieldIntervalMs ?? 50;
|
|
1094
1180
|
const partial = makePartialEmitter(engine, hooks);
|
|
1095
1181
|
const text = engine.text;
|
|
1182
|
+
// RS274NGC O-word program (DD-017 Phase 2): a bounded synchronous interpretation section (loops are
|
|
1183
|
+
// capped by `maxProgramIterations`, and these programs are small — hand/CAM-written). Non-parametric
|
|
1184
|
+
// input takes the cooperative line loop below unchanged.
|
|
1185
|
+
if (programUsesOWords(text)) {
|
|
1186
|
+
engine.runOWordFlow();
|
|
1187
|
+
hooks.onProgress?.(text.length, byteLength, 0);
|
|
1188
|
+
return { ...engine.finish(), cancelled: false };
|
|
1189
|
+
}
|
|
1096
1190
|
const len = text.length;
|
|
1097
1191
|
let offset = 0;
|
|
1098
1192
|
let sliceStart = Date.now();
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** Warning codes surfaced to the IR by the flow interpreter (DD-017 §4.6). */
|
|
2
|
+
export declare const RS274_FLOW_WARN: {
|
|
3
|
+
readonly iterationLimit: "rs274-iteration-limit";
|
|
4
|
+
readonly unbalancedOword: "rs274-unbalanced-oword";
|
|
5
|
+
readonly unsupportedOword: "rs274-unsupported-oword";
|
|
6
|
+
readonly controlNesting: "rs274-control-nesting";
|
|
7
|
+
readonly misplacedControl: "rs274-misplaced-control";
|
|
8
|
+
readonly streamingUnsupported: "rs274-streaming-flow-unsupported";
|
|
9
|
+
readonly callDepth: "rs274-call-depth";
|
|
10
|
+
readonly unknownSub: "rs274-unknown-sub";
|
|
11
|
+
readonly duplicateSub: "rs274-duplicate-sub";
|
|
12
|
+
};
|
|
13
|
+
/** The recognized O-word keywords. An `o<id>` line with any OTHER trailing word is NOT a control line
|
|
14
|
+
* (e.g. a stray `o5 g1`) — it falls through to the normal lexer, so detection never hijacks plain code. */
|
|
15
|
+
export declare const KNOWN_OWORD_KEYWORDS: ReadonlySet<string>;
|
|
16
|
+
export type WarnFn = (code: string, message: string, srcByte?: number) => void;
|
|
17
|
+
/** A classified O-word control line: its keyword, its (opaque, normalized) id token, and any trailing
|
|
18
|
+
* expression (an `if`/`while` condition or a `repeat` count). */
|
|
19
|
+
export interface OwInfo {
|
|
20
|
+
keyword: string;
|
|
21
|
+
/** The id token verbatim-normalized: `100`, `<name>`, or a literal `[expr]` — matched, never evaluated. */
|
|
22
|
+
id: string;
|
|
23
|
+
expr: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Classify one raw line as an O-word control line, or `null` when it is not one. `null` covers every
|
|
27
|
+
* plain line (all FDM, all geometry/param lines) AND an `o<id>` whose trailing word is not a recognized
|
|
28
|
+
* keyword (e.g. `o5 g1 x10`) — those fall through to the normal lexer, so classification never hijacks
|
|
29
|
+
* ordinary code. The id is read as an opaque token (numeric / `<name>` / balanced `[expr]`) and is used
|
|
30
|
+
* only to pair an opener with its closer — it is deliberately NOT evaluated here.
|
|
31
|
+
*/
|
|
32
|
+
export declare function classifyOWord(rawLine: string): OwInfo | null;
|
|
33
|
+
/** One buffered source line with its byte offset (for `srcByte` on any geometry it emits). */
|
|
34
|
+
export interface FlowLine {
|
|
35
|
+
text: string;
|
|
36
|
+
offset: number;
|
|
37
|
+
}
|
|
38
|
+
/** The engine surface the interpreter drives — supplied by `createEngine` with its closures. */
|
|
39
|
+
export interface FlowHost {
|
|
40
|
+
/** Execute one plain (non-control) source line through the normal engine path. */
|
|
41
|
+
processLine(text: string, offset: number): void;
|
|
42
|
+
/** True once a geometry limit (segments/buffer) has stopped emission — the interpreter then halts. */
|
|
43
|
+
stopped(): boolean;
|
|
44
|
+
/** Evaluate a control expression against the shared parameter store; `null` = malformed/non-finite. */
|
|
45
|
+
evalExpression(text: string, srcByte: number): number | null;
|
|
46
|
+
/** Push a subroutine call-frame scope with `args` bound to `#1`..`#30` (Phase 3). */
|
|
47
|
+
pushScope(args: readonly number[]): void;
|
|
48
|
+
/** Pop the current call-frame scope (Phase 3). */
|
|
49
|
+
popScope(): void;
|
|
50
|
+
warn: WarnFn;
|
|
51
|
+
maxProgramIterations: number;
|
|
52
|
+
/** Subroutine recursion depth cap (Phase 3); exceeding it discloses and skips the call. */
|
|
53
|
+
maxCallDepth: number;
|
|
54
|
+
}
|
|
55
|
+
export interface FlowOutcome {
|
|
56
|
+
/** True when execution was degraded (limit hit, unbalanced/unsupported O-word, malformed condition). */
|
|
57
|
+
degraded: boolean;
|
|
58
|
+
}
|
|
59
|
+
/** True once the built tree used any recognized control construct (drives the capability). */
|
|
60
|
+
export declare function programUsesOWords(text: string): boolean;
|
|
61
|
+
/** True when a single line is a recognized O-word control line (used by the streaming disclosure). */
|
|
62
|
+
export declare function isOWordControlLine(line: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Interpret a fully-buffered O-word program. Builds the block tree (tolerant of unbalanced O-words —
|
|
65
|
+
* disclosed, never thrown out), then executes it with bounded iteration. Returns whether execution was
|
|
66
|
+
* degraded so the caller can tier the `parametricProgram` capability.
|
|
67
|
+
*/
|
|
68
|
+
export declare function interpretOWordProgram(lines: readonly FlowLine[], host: FlowHost): FlowOutcome;
|
|
69
|
+
//# sourceMappingURL=rs274-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rs274-flow.d.ts","sourceRoot":"","sources":["../src/rs274-flow.ts"],"names":[],"mappings":"AAgCA,8EAA8E;AAC9E,eAAO,MAAM,eAAe;;;;;;;;;;CAUlB,CAAC;AAeX;4GAC4G;AAC5G,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAgBnD,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAE/E;kEACkE;AAClE,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,2GAA2G;IAC3G,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkD5D;AAED,8FAA8F;AAC9F,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gGAAgG;AAChG,MAAM,WAAW,QAAQ;IACvB,kFAAkF;IAClF,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,sGAAsG;IACtG,OAAO,IAAI,OAAO,CAAC;IACnB,uGAAuG;IACvG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC7D,qFAAqF;IACrF,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACzC,kDAAkD;IAClD,QAAQ,IAAI,IAAI,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,EAAE,MAAM,CAAC;IAC7B,2FAA2F;IAC3F,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,wGAAwG;IACxG,QAAQ,EAAE,OAAO,CAAC;CACnB;AAoCD,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUvD;AAED,sGAAsG;AACtG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AA2FD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,GAAG,WAAW,CAmb7F"}
|