dirac-browser 0.1.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/README.md +88 -0
- package/dist/chatbox.d.ts +1 -0
- package/dist/chatbox.js +81 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +39 -0
- package/dist/runtime/braket-parser.d.ts +44 -0
- package/dist/runtime/braket-parser.js +208 -0
- package/dist/runtime/interpreter.d.ts +6 -0
- package/dist/runtime/interpreter.js +110 -0
- package/dist/runtime/parser.d.ts +11 -0
- package/dist/runtime/parser.js +135 -0
- package/dist/runtime/session.d.ts +43 -0
- package/dist/runtime/session.js +171 -0
- package/dist/tags/assign.d.ts +6 -0
- package/dist/tags/assign.js +32 -0
- package/dist/tags/call.d.ts +6 -0
- package/dist/tags/call.js +130 -0
- package/dist/tags/defvar.d.ts +6 -0
- package/dist/tags/defvar.js +27 -0
- package/dist/tags/eval.d.ts +6 -0
- package/dist/tags/eval.js +55 -0
- package/dist/tags/execute.d.ts +6 -0
- package/dist/tags/execute.js +45 -0
- package/dist/tags/expr.d.ts +8 -0
- package/dist/tags/expr.js +107 -0
- package/dist/tags/if.d.ts +6 -0
- package/dist/tags/if.js +46 -0
- package/dist/tags/llm.d.ts +6 -0
- package/dist/tags/llm.js +73 -0
- package/dist/tags/loop.d.ts +6 -0
- package/dist/tags/loop.js +31 -0
- package/dist/tags/output.d.ts +6 -0
- package/dist/tags/output.js +19 -0
- package/dist/tags/parameters.d.ts +6 -0
- package/dist/tags/parameters.js +63 -0
- package/dist/tags/subroutine.d.ts +6 -0
- package/dist/tags/subroutine.js +41 -0
- package/dist/tags/variable.d.ts +6 -0
- package/dist/tags/variable.js +20 -0
- package/dist/types/index.d.ts +76 -0
- package/dist/types/index.js +5 -0
- package/dist/utils/llm-adapter.d.ts +22 -0
- package/dist/utils/llm-adapter.js +91 -0
- package/examples/llm-reflection-test.di +18 -0
- package/package.json +21 -0
- package/src/chatbox.ts +89 -0
- package/src/index.ts +47 -0
- package/src/runtime/braket-parser.ts +234 -0
- package/src/runtime/interpreter.ts +129 -0
- package/src/runtime/parser.ts +151 -0
- package/src/runtime/session.ts +209 -0
- package/src/tags/assign.ts +37 -0
- package/src/tags/call.ts +166 -0
- package/src/tags/defvar.ts +32 -0
- package/src/tags/eval.ts +65 -0
- package/src/tags/execute.ts +52 -0
- package/src/tags/expr.ts +128 -0
- package/src/tags/if.ts +58 -0
- package/src/tags/llm.ts +88 -0
- package/src/tags/loop.ts +43 -0
- package/src/tags/output.ts +23 -0
- package/src/tags/parameters.ts +77 -0
- package/src/tags/subroutine.ts +52 -0
- package/src/tags/variable.ts +27 -0
- package/src/types/index.ts +103 -0
- package/src/utils/llm-adapter.ts +118 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session management - Browser edition (no LLM SDKs)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { DiracSession, DiracConfig, Variable, Subroutine, DiracElement } from '../types/index.js';
|
|
6
|
+
|
|
7
|
+
export function createSession(config: DiracConfig = {}): DiracSession {
|
|
8
|
+
// Browser version - no LLM client
|
|
9
|
+
let llmClient: any = null;
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
variables: [],
|
|
13
|
+
subroutines: [],
|
|
14
|
+
varBoundary: 0,
|
|
15
|
+
subBoundary: 0,
|
|
16
|
+
parameterStack: [],
|
|
17
|
+
output: [],
|
|
18
|
+
llmClient,
|
|
19
|
+
limits: {
|
|
20
|
+
maxLLMCalls: config.maxLLMCalls || 100,
|
|
21
|
+
currentLLMCalls: 0,
|
|
22
|
+
maxDepth: config.maxDepth || 50,
|
|
23
|
+
currentDepth: 0,
|
|
24
|
+
},
|
|
25
|
+
isReturn: false,
|
|
26
|
+
isBreak: false,
|
|
27
|
+
debug: config.debug || false,
|
|
28
|
+
config, // Store config reference
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Variable management (maps to var_info functions in MASK)
|
|
33
|
+
|
|
34
|
+
export function setVariable(session: DiracSession, name: string, value: any, visible: boolean = false): void {
|
|
35
|
+
session.variables.push({
|
|
36
|
+
name,
|
|
37
|
+
value,
|
|
38
|
+
visible,
|
|
39
|
+
boundary: session.varBoundary,
|
|
40
|
+
passby: 'value',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getVariable(session: DiracSession, name: string): any {
|
|
45
|
+
// Search from end (most recent) to beginning
|
|
46
|
+
for (let i = session.variables.length - 1; i >= 0; i--) {
|
|
47
|
+
if (session.variables[i].name === name) {
|
|
48
|
+
return session.variables[i].value;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function hasVariable(session: DiracSession, name: string): boolean {
|
|
55
|
+
return session.variables.some(v => v.name === name);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Set boundary marker for local variables
|
|
60
|
+
* Maps to var_info_set_boundary in MASK
|
|
61
|
+
*/
|
|
62
|
+
export function setBoundary(session: DiracSession): number {
|
|
63
|
+
const oldBoundary = session.varBoundary;
|
|
64
|
+
session.varBoundary = session.variables.length;
|
|
65
|
+
return oldBoundary;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Pop variables back to boundary (cleanup local scope)
|
|
70
|
+
* Maps to var_info_pop_to_boundary in MASK
|
|
71
|
+
*/
|
|
72
|
+
export function popToBoundary(session: DiracSession): void {
|
|
73
|
+
session.variables = session.variables.slice(0, session.varBoundary);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Clean private variables but keep visible ones
|
|
78
|
+
* Maps to var_info_clean_to_boundary in MASK
|
|
79
|
+
*/
|
|
80
|
+
export function cleanToBoundary(session: DiracSession): void {
|
|
81
|
+
const kept: Variable[] = [];
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < session.varBoundary; i++) {
|
|
84
|
+
kept.push(session.variables[i]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (let i = session.varBoundary; i < session.variables.length; i++) {
|
|
88
|
+
if (session.variables[i].visible) {
|
|
89
|
+
kept.push(session.variables[i]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
session.variables = kept;
|
|
94
|
+
session.varBoundary = kept.length;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Subroutine management (maps to subroutine functions in MASK)
|
|
98
|
+
|
|
99
|
+
export function registerSubroutine(
|
|
100
|
+
session: DiracSession,
|
|
101
|
+
name: string,
|
|
102
|
+
element: DiracElement,
|
|
103
|
+
description?: string,
|
|
104
|
+
parameters?: any[]
|
|
105
|
+
): void {
|
|
106
|
+
console.log(`📝 Registering subroutine: ${name}`, { description, paramCount: parameters?.length || 0 });
|
|
107
|
+
session.subroutines.push({
|
|
108
|
+
name,
|
|
109
|
+
element,
|
|
110
|
+
boundary: session.subBoundary,
|
|
111
|
+
description,
|
|
112
|
+
parameters,
|
|
113
|
+
});
|
|
114
|
+
console.log(`✅ Total subroutines registered: ${session.subroutines.length}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function getSubroutine(session: DiracSession, name: string): DiracElement | undefined {
|
|
118
|
+
console.log(`🔍 Looking up subroutine: ${name} (${session.subroutines.length} registered)`);
|
|
119
|
+
console.log('Available subroutines:', session.subroutines.map(s => s.name));
|
|
120
|
+
|
|
121
|
+
// Search from end (most recent) to beginning
|
|
122
|
+
for (let i = session.subroutines.length - 1; i >= 0; i--) {
|
|
123
|
+
if (session.subroutines[i].name === name) {
|
|
124
|
+
console.log(`✅ Found subroutine: ${name}`);
|
|
125
|
+
return session.subroutines[i].element;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
console.log(`❌ Subroutine not found: ${name}`);
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function setSubroutineBoundary(session: DiracSession): number {
|
|
133
|
+
const oldBoundary = session.subBoundary;
|
|
134
|
+
session.subBoundary = session.subroutines.length;
|
|
135
|
+
return oldBoundary;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function popSubroutinesToBoundary(session: DiracSession): void {
|
|
139
|
+
session.subroutines = session.subroutines.slice(0, session.subBoundary);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function cleanSubroutinesToBoundary(session: DiracSession): void {
|
|
143
|
+
// For now, same as pop (visibility not implemented for subroutines yet)
|
|
144
|
+
popSubroutinesToBoundary(session);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Variable substitution (maps to var_replace functions in MASK)
|
|
148
|
+
|
|
149
|
+
export function substituteVariables(session: DiracSession, text: string): string {
|
|
150
|
+
// Decode HTML entities first
|
|
151
|
+
let decoded = text
|
|
152
|
+
.replace(/ /g, '\n')
|
|
153
|
+
.replace(/ /g, '\r')
|
|
154
|
+
.replace(/	/g, '\t')
|
|
155
|
+
.replace(/</g, '<')
|
|
156
|
+
.replace(/>/g, '>')
|
|
157
|
+
.replace(/&/g, '&')
|
|
158
|
+
.replace(/"/g, '"')
|
|
159
|
+
.replace(/'/g, "'");
|
|
160
|
+
|
|
161
|
+
// Substitute both ${var}/$var and {var} patterns
|
|
162
|
+
return decoded
|
|
163
|
+
.replace(/\$\{?(\w+)\}?/g, (match, varName) => {
|
|
164
|
+
const value = getVariable(session, varName);
|
|
165
|
+
return value !== undefined ? String(value) : match;
|
|
166
|
+
})
|
|
167
|
+
.replace(/\{(\w+)\}/g, (match, varName) => {
|
|
168
|
+
const value = getVariable(session, varName);
|
|
169
|
+
return value !== undefined ? String(value) : match;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Output management
|
|
174
|
+
|
|
175
|
+
export function emit(session: DiracSession, content: string): void {
|
|
176
|
+
session.output.push(content);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function getOutput(session: DiracSession): string {
|
|
180
|
+
return session.output.join('');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get all available subroutines for reflection
|
|
185
|
+
* Used by LLM adapter to auto-generate prompts
|
|
186
|
+
*/
|
|
187
|
+
export function getAvailableSubroutines(session: DiracSession) {
|
|
188
|
+
return session.subroutines
|
|
189
|
+
.filter(sub => sub.description || sub.parameters)
|
|
190
|
+
.map(sub => ({
|
|
191
|
+
name: sub.name,
|
|
192
|
+
description: sub.description,
|
|
193
|
+
parameters: sub.parameters || []
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Parameter stack (for subroutine calls)
|
|
198
|
+
|
|
199
|
+
export function pushParameters(session: DiracSession, params: DiracElement[]): void {
|
|
200
|
+
session.parameterStack.push(params);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function popParameters(session: DiracSession): DiracElement[] | undefined {
|
|
204
|
+
return session.parameterStack.pop();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function getCurrentParameters(session: DiracSession): DiracElement[] | undefined {
|
|
208
|
+
return session.parameterStack[session.parameterStack.length - 1];
|
|
209
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <assign> tag - assign value to existing variable
|
|
3
|
+
* Maps to mask_tag_assign in MASK
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import { getVariable, setVariable, substituteVariables } from '../runtime/session.js';
|
|
8
|
+
|
|
9
|
+
export function executeAssign(session: DiracSession, element: DiracElement): void {
|
|
10
|
+
const name = element.attributes.name;
|
|
11
|
+
const valueAttr = element.attributes.value;
|
|
12
|
+
|
|
13
|
+
if (!name) {
|
|
14
|
+
throw new Error('<assign> requires name attribute');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Get value
|
|
18
|
+
let value: any;
|
|
19
|
+
if (valueAttr !== undefined) {
|
|
20
|
+
value = substituteVariables(session, valueAttr);
|
|
21
|
+
} else if (element.text) {
|
|
22
|
+
value = substituteVariables(session, element.text);
|
|
23
|
+
} else {
|
|
24
|
+
value = '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Find existing variable and update it
|
|
28
|
+
for (let i = session.variables.length - 1; i >= 0; i--) {
|
|
29
|
+
if (session.variables[i].name === name) {
|
|
30
|
+
session.variables[i].value = value;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Variable not found - create it (same as MASK behavior)
|
|
36
|
+
setVariable(session, name, value, false);
|
|
37
|
+
}
|
package/src/tags/call.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <call> tag - invoke subroutine
|
|
3
|
+
* Maps to mask_call_integrate in MASK
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import {
|
|
8
|
+
getSubroutine,
|
|
9
|
+
setBoundary,
|
|
10
|
+
popToBoundary,
|
|
11
|
+
cleanToBoundary,
|
|
12
|
+
pushParameters,
|
|
13
|
+
popParameters,
|
|
14
|
+
substituteVariables,
|
|
15
|
+
setVariable,
|
|
16
|
+
getVariable,
|
|
17
|
+
} from '../runtime/session.js';
|
|
18
|
+
import { integrateChildren } from '../runtime/interpreter.js';
|
|
19
|
+
|
|
20
|
+
export async function executeCall(session: DiracSession, element: DiracElement): Promise<void> {
|
|
21
|
+
// Support both <call name="FOO" /> and direct <FOO /> syntax
|
|
22
|
+
// For <call> tag, use name/subroutine attribute
|
|
23
|
+
// For direct syntax, use element.tag
|
|
24
|
+
let name: string;
|
|
25
|
+
|
|
26
|
+
if (element.tag === 'call') {
|
|
27
|
+
// Explicit <call> tag - use name or subroutine attribute
|
|
28
|
+
name = element.attributes.name || element.attributes.subroutine || '';
|
|
29
|
+
} else {
|
|
30
|
+
// Direct tag syntax - use tag name itself, ignore name attribute
|
|
31
|
+
name = element.tag;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!name) {
|
|
35
|
+
throw new Error('<call> requires name or subroutine attribute');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const subroutine = getSubroutine(session, name);
|
|
39
|
+
if (!subroutine) {
|
|
40
|
+
throw new Error(`Subroutine '${name}' not found`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Handle extension (parent subroutine)
|
|
44
|
+
const extendsName = subroutine.attributes.extends;
|
|
45
|
+
if (extendsName) {
|
|
46
|
+
const parent = getSubroutine(session, extendsName);
|
|
47
|
+
if (parent) {
|
|
48
|
+
// Call parent first
|
|
49
|
+
await executeCallInternal(session, parent, element);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Call this subroutine
|
|
54
|
+
await executeCallInternal(session, subroutine, element);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function executeCallInternal(
|
|
58
|
+
session: DiracSession,
|
|
59
|
+
subroutine: DiracElement,
|
|
60
|
+
callElement: DiracElement
|
|
61
|
+
): Promise<void> {
|
|
62
|
+
// Set boundary for local scope
|
|
63
|
+
const oldBoundary = setBoundary(session);
|
|
64
|
+
const wasReturn = session.isReturn;
|
|
65
|
+
session.isReturn = false;
|
|
66
|
+
|
|
67
|
+
// Substitute variables in call element attributes before pushing to parameter stack
|
|
68
|
+
const substitutedElement: DiracElement = {
|
|
69
|
+
tag: callElement.tag,
|
|
70
|
+
attributes: {},
|
|
71
|
+
children: callElement.children
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Process each attribute for {variable} substitution
|
|
75
|
+
for (const [key, value] of Object.entries(callElement.attributes)) {
|
|
76
|
+
if (typeof value === 'string') {
|
|
77
|
+
substitutedElement.attributes[key] = substituteVariables(session, value);
|
|
78
|
+
} else {
|
|
79
|
+
substitutedElement.attributes[key] = value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Push caller element onto parameter stack for <parameters select="*|@*|@attr"/> access
|
|
84
|
+
pushParameters(session, [substitutedElement]);
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
// Bind call attributes as variables (for <call name="foo" x="1" y="2"/> style)
|
|
88
|
+
// This allows ${x} and ${y} to be used in the subroutine body
|
|
89
|
+
for (const [key, value] of Object.entries(substitutedElement.attributes)) {
|
|
90
|
+
// Skip special attributes
|
|
91
|
+
if (key === 'name' || key === 'subroutine') continue;
|
|
92
|
+
|
|
93
|
+
// Set as local variable
|
|
94
|
+
setVariable(session, key, value, false);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Bind parameters (for <call><parameters>...</parameters></call> style)
|
|
98
|
+
const paramElements = callElement.children.filter(c => c.tag === 'parameters');
|
|
99
|
+
if (paramElements.length > 0) {
|
|
100
|
+
await bindParameters(session, subroutine, paramElements[0]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Execute subroutine body
|
|
104
|
+
await integrateChildren(session, subroutine);
|
|
105
|
+
|
|
106
|
+
} finally {
|
|
107
|
+
// Pop parameter stack
|
|
108
|
+
popParameters(session);
|
|
109
|
+
|
|
110
|
+
// Clean up scope (keep visible variables)
|
|
111
|
+
session.varBoundary = oldBoundary;
|
|
112
|
+
cleanToBoundary(session);
|
|
113
|
+
session.isReturn = wasReturn;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function bindParameters(
|
|
118
|
+
session: DiracSession,
|
|
119
|
+
subroutine: DiracElement,
|
|
120
|
+
callParams: DiracElement
|
|
121
|
+
): Promise<void> {
|
|
122
|
+
// Find <parameters> definition in subroutine
|
|
123
|
+
const paramDef = subroutine.children.find(c => c.tag === 'parameters');
|
|
124
|
+
if (!paramDef) {
|
|
125
|
+
return; // No parameters defined
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Get variable definitions from parameter declaration
|
|
129
|
+
const paramVars = paramDef.children.filter(c => c.tag === 'variable');
|
|
130
|
+
const callVars = callParams.children.filter(c => c.tag === 'variable');
|
|
131
|
+
|
|
132
|
+
// Bind each parameter
|
|
133
|
+
for (let i = 0; i < paramVars.length; i++) {
|
|
134
|
+
const paramVar = paramVars[i];
|
|
135
|
+
const callVar = callVars[i];
|
|
136
|
+
|
|
137
|
+
const paramName = paramVar.attributes.name;
|
|
138
|
+
const passby = paramVar.attributes.passby || 'value';
|
|
139
|
+
|
|
140
|
+
if (!paramName) continue;
|
|
141
|
+
|
|
142
|
+
let value: any;
|
|
143
|
+
|
|
144
|
+
if (callVar) {
|
|
145
|
+
// Get value from call site
|
|
146
|
+
const callValue = callVar.attributes.value;
|
|
147
|
+
if (callValue) {
|
|
148
|
+
if (passby === 'ref') {
|
|
149
|
+
// Pass by reference - store variable name
|
|
150
|
+
setVariable(session, paramName, getVariable(session, callValue), false);
|
|
151
|
+
session.variables[session.variables.length - 1].passby = 'ref';
|
|
152
|
+
session.variables[session.variables.length - 1].refName = callValue;
|
|
153
|
+
} else {
|
|
154
|
+
// Pass by value
|
|
155
|
+
value = substituteVariables(session, callValue);
|
|
156
|
+
setVariable(session, paramName, value, false);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
// No value provided - use default if available
|
|
161
|
+
const defaultValue = paramVar.attributes.default || '';
|
|
162
|
+
value = substituteVariables(session, defaultValue);
|
|
163
|
+
setVariable(session, paramName, value, false);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <defvar> tag - define variable
|
|
3
|
+
* Maps to mask_tag_defvar in MASK
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import { setVariable, substituteVariables } from '../runtime/session.js';
|
|
8
|
+
|
|
9
|
+
export function executeDefvar(session: DiracSession, element: DiracElement): void {
|
|
10
|
+
const name = element.attributes.name;
|
|
11
|
+
const valueAttr = element.attributes.value;
|
|
12
|
+
const visibleAttr = element.attributes.visible || 'false';
|
|
13
|
+
|
|
14
|
+
if (!name) {
|
|
15
|
+
throw new Error('<defvar> requires name attribute');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Determine visibility
|
|
19
|
+
const visible = visibleAttr === 'true' || visibleAttr === 'variable' || visibleAttr === 'both';
|
|
20
|
+
|
|
21
|
+
// Get value (from attribute or text content)
|
|
22
|
+
let value: any;
|
|
23
|
+
if (valueAttr !== undefined) {
|
|
24
|
+
value = substituteVariables(session, valueAttr);
|
|
25
|
+
} else if (element.text) {
|
|
26
|
+
value = substituteVariables(session, element.text);
|
|
27
|
+
} else {
|
|
28
|
+
value = '';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setVariable(session, name, value, visible);
|
|
32
|
+
}
|
package/src/tags/eval.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <eval> tag - evaluate JavaScript expression
|
|
3
|
+
* Maps to mask_tag_eval in MASK (but for JS, not C)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import { setVariable, getVariable, substituteVariables } from '../runtime/session.js';
|
|
8
|
+
|
|
9
|
+
// AsyncFunction constructor
|
|
10
|
+
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
|
11
|
+
|
|
12
|
+
export async function executeEval(session: DiracSession, element: DiracElement): Promise<void> {
|
|
13
|
+
const name = element.attributes.name;
|
|
14
|
+
const exprAttr = element.attributes.expr;
|
|
15
|
+
|
|
16
|
+
// Get expression - replace ${var} with var (will be passed as actual JS vars)
|
|
17
|
+
let expr: string;
|
|
18
|
+
if (exprAttr) {
|
|
19
|
+
expr = exprAttr.replace(/\$\{(\w+)\}/g, '$1');
|
|
20
|
+
} else if (element.text) {
|
|
21
|
+
expr = element.text.replace(/\$\{(\w+)\}/g, '$1');
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error('<eval> requires expr attribute or text content');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (session.debug) {
|
|
27
|
+
console.error(`[EVAL] Code after substitution:\n${expr}\n`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Build context object with all variables
|
|
32
|
+
const context: Record<string, any> = {};
|
|
33
|
+
for (const v of session.variables) {
|
|
34
|
+
context[v.name] = v.value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Add customContext if provided (for Angular services, etc.)
|
|
38
|
+
if (session.config.customContext) {
|
|
39
|
+
Object.assign(context, session.config.customContext);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Add helper to get current parameters from stack
|
|
43
|
+
context.getParams = () => {
|
|
44
|
+
const params = session.parameterStack[session.parameterStack.length - 1];
|
|
45
|
+
return params && params[0] ? params[0] : null;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let result: any;
|
|
49
|
+
// Execute as async function to support top-level await
|
|
50
|
+
const func = new AsyncFunction(...Object.keys(context), expr);
|
|
51
|
+
result = await func(...Object.values(context));
|
|
52
|
+
|
|
53
|
+
if (session.debug) {
|
|
54
|
+
console.error(`[EVAL] Result: ${JSON.stringify(result)}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Store result if name provided
|
|
58
|
+
if (name) {
|
|
59
|
+
setVariable(session, name, result, false);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw new Error(`Eval error: ${error instanceof Error ? error.message : String(error)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <execute> tag - Execute dynamically generated Dirac code
|
|
3
|
+
* Takes LLM-generated or variable content and interprets it as Dirac XML
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import { getVariable, substituteVariables } from '../runtime/session.js';
|
|
8
|
+
import { DiracParser } from '../runtime/parser.js';
|
|
9
|
+
import { integrate } from '../runtime/interpreter.js';
|
|
10
|
+
|
|
11
|
+
export async function executeExecute(session: DiracSession, element: DiracElement): Promise<void> {
|
|
12
|
+
const sourceAttr = element.attributes.source;
|
|
13
|
+
|
|
14
|
+
// Get the Dirac code to execute
|
|
15
|
+
let diracCode: string;
|
|
16
|
+
|
|
17
|
+
if (sourceAttr) {
|
|
18
|
+
// Get from variable
|
|
19
|
+
diracCode = getVariable(session, sourceAttr);
|
|
20
|
+
if (!diracCode) {
|
|
21
|
+
throw new Error(`<execute> source variable '${sourceAttr}' not found`);
|
|
22
|
+
}
|
|
23
|
+
} else if (element.text) {
|
|
24
|
+
// Get from text content (with variable substitution)
|
|
25
|
+
diracCode = substituteVariables(session, element.text);
|
|
26
|
+
} else {
|
|
27
|
+
throw new Error('<execute> requires source attribute or text content');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (session.debug) {
|
|
31
|
+
console.error(`[EXECUTE] Interpreting dynamic code:\n${diracCode}\n`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Strip markdown code blocks if present
|
|
35
|
+
diracCode = diracCode.trim();
|
|
36
|
+
if (diracCode.startsWith('```')) {
|
|
37
|
+
// Remove markdown code fences
|
|
38
|
+
diracCode = diracCode.replace(/^```(?:xml|html)?\n?/m, '').replace(/\n?```$/m, '').trim();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Parse the dynamic Dirac code
|
|
43
|
+
const parser = new DiracParser();
|
|
44
|
+
const dynamicAST = parser.parse(diracCode);
|
|
45
|
+
|
|
46
|
+
// Execute the dynamically generated code
|
|
47
|
+
await integrate(session, dynamicAST);
|
|
48
|
+
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throw new Error(`Execute error: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/tags/expr.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <expr> tag - arithmetic and logical operations
|
|
3
|
+
* Maps to mask_tag_expr in MASK
|
|
4
|
+
*
|
|
5
|
+
* Supports: plus, minus, times, divide, mod, lt, gt, eq, and, or, not
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
9
|
+
import { emit, substituteVariables } from '../runtime/session.js';
|
|
10
|
+
import { integrate } from '../runtime/interpreter.js';
|
|
11
|
+
|
|
12
|
+
export async function executeExpr(session: DiracSession, element: DiracElement): Promise<void> {
|
|
13
|
+
const op = element.attributes.eval || element.attributes.op;
|
|
14
|
+
|
|
15
|
+
if (!op) {
|
|
16
|
+
throw new Error('<expr> requires eval or op attribute');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Collect arguments from <arg> children
|
|
20
|
+
const args: number[] = [];
|
|
21
|
+
const stringArgs: string[] = [];
|
|
22
|
+
|
|
23
|
+
for (const child of element.children) {
|
|
24
|
+
if (child.tag === 'arg') {
|
|
25
|
+
let argValue = '';
|
|
26
|
+
|
|
27
|
+
// Check if arg has text content directly
|
|
28
|
+
if (child.text) {
|
|
29
|
+
argValue = substituteVariables(session, child.text);
|
|
30
|
+
} else {
|
|
31
|
+
// Capture output from arg children
|
|
32
|
+
const oldOutput = session.output;
|
|
33
|
+
session.output = [];
|
|
34
|
+
|
|
35
|
+
for (const argChild of child.children) {
|
|
36
|
+
await integrate(session, argChild);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
argValue = session.output.join('');
|
|
40
|
+
session.output = oldOutput;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
stringArgs.push(argValue);
|
|
44
|
+
const numValue = parseFloat(argValue);
|
|
45
|
+
args.push(isNaN(numValue) ? 0 : numValue);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let result: number | boolean = 0;
|
|
50
|
+
|
|
51
|
+
switch (op.toLowerCase()) {
|
|
52
|
+
case 'plus':
|
|
53
|
+
case 'add':
|
|
54
|
+
result = args.reduce((a, b) => a + b, 0);
|
|
55
|
+
break;
|
|
56
|
+
|
|
57
|
+
case 'minus':
|
|
58
|
+
case 'subtract':
|
|
59
|
+
result = args.length > 0 ? args[0] - args.slice(1).reduce((a, b) => a + b, 0) : 0;
|
|
60
|
+
break;
|
|
61
|
+
|
|
62
|
+
case 'times':
|
|
63
|
+
case 'multiply':
|
|
64
|
+
case 'mul':
|
|
65
|
+
result = args.reduce((a, b) => a * b, 1);
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
case 'divide':
|
|
69
|
+
case 'div':
|
|
70
|
+
if (args.length >= 2 && args[1] !== 0) {
|
|
71
|
+
result = args[0] / args[1];
|
|
72
|
+
} else {
|
|
73
|
+
result = 0;
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
case 'mod':
|
|
78
|
+
case 'modulo':
|
|
79
|
+
if (args.length >= 2 && args[1] !== 0) {
|
|
80
|
+
result = args[0] % args[1];
|
|
81
|
+
} else {
|
|
82
|
+
result = 0;
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
case 'lt':
|
|
87
|
+
case 'less':
|
|
88
|
+
result = args.length >= 2 ? args[0] < args[1] : false;
|
|
89
|
+
break;
|
|
90
|
+
|
|
91
|
+
case 'gt':
|
|
92
|
+
case 'greater':
|
|
93
|
+
result = args.length >= 2 ? args[0] > args[1] : false;
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case 'eq':
|
|
97
|
+
case 'equal':
|
|
98
|
+
result = args.length >= 2 ? args[0] === args[1] : false;
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case 'and':
|
|
102
|
+
result = args.every(a => a !== 0);
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case 'or':
|
|
106
|
+
result = args.some(a => a !== 0);
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case 'not':
|
|
110
|
+
result = args.length > 0 ? args[0] === 0 : true;
|
|
111
|
+
break;
|
|
112
|
+
|
|
113
|
+
case 'same':
|
|
114
|
+
case 'strcmp':
|
|
115
|
+
result = stringArgs.length >= 2 ? stringArgs[0] === stringArgs[1] : false;
|
|
116
|
+
break;
|
|
117
|
+
|
|
118
|
+
default:
|
|
119
|
+
throw new Error(`<expr> unknown operation: ${op}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Emit result
|
|
123
|
+
if (typeof result === 'boolean') {
|
|
124
|
+
emit(session, result ? '1' : '0');
|
|
125
|
+
} else {
|
|
126
|
+
emit(session, String(result));
|
|
127
|
+
}
|
|
128
|
+
}
|