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.
Files changed (67) hide show
  1. package/README.md +88 -0
  2. package/dist/chatbox.d.ts +1 -0
  3. package/dist/chatbox.js +81 -0
  4. package/dist/index.d.ts +33 -0
  5. package/dist/index.js +39 -0
  6. package/dist/runtime/braket-parser.d.ts +44 -0
  7. package/dist/runtime/braket-parser.js +208 -0
  8. package/dist/runtime/interpreter.d.ts +6 -0
  9. package/dist/runtime/interpreter.js +110 -0
  10. package/dist/runtime/parser.d.ts +11 -0
  11. package/dist/runtime/parser.js +135 -0
  12. package/dist/runtime/session.d.ts +43 -0
  13. package/dist/runtime/session.js +171 -0
  14. package/dist/tags/assign.d.ts +6 -0
  15. package/dist/tags/assign.js +32 -0
  16. package/dist/tags/call.d.ts +6 -0
  17. package/dist/tags/call.js +130 -0
  18. package/dist/tags/defvar.d.ts +6 -0
  19. package/dist/tags/defvar.js +27 -0
  20. package/dist/tags/eval.d.ts +6 -0
  21. package/dist/tags/eval.js +55 -0
  22. package/dist/tags/execute.d.ts +6 -0
  23. package/dist/tags/execute.js +45 -0
  24. package/dist/tags/expr.d.ts +8 -0
  25. package/dist/tags/expr.js +107 -0
  26. package/dist/tags/if.d.ts +6 -0
  27. package/dist/tags/if.js +46 -0
  28. package/dist/tags/llm.d.ts +6 -0
  29. package/dist/tags/llm.js +73 -0
  30. package/dist/tags/loop.d.ts +6 -0
  31. package/dist/tags/loop.js +31 -0
  32. package/dist/tags/output.d.ts +6 -0
  33. package/dist/tags/output.js +19 -0
  34. package/dist/tags/parameters.d.ts +6 -0
  35. package/dist/tags/parameters.js +63 -0
  36. package/dist/tags/subroutine.d.ts +6 -0
  37. package/dist/tags/subroutine.js +41 -0
  38. package/dist/tags/variable.d.ts +6 -0
  39. package/dist/tags/variable.js +20 -0
  40. package/dist/types/index.d.ts +76 -0
  41. package/dist/types/index.js +5 -0
  42. package/dist/utils/llm-adapter.d.ts +22 -0
  43. package/dist/utils/llm-adapter.js +91 -0
  44. package/examples/llm-reflection-test.di +18 -0
  45. package/package.json +21 -0
  46. package/src/chatbox.ts +89 -0
  47. package/src/index.ts +47 -0
  48. package/src/runtime/braket-parser.ts +234 -0
  49. package/src/runtime/interpreter.ts +129 -0
  50. package/src/runtime/parser.ts +151 -0
  51. package/src/runtime/session.ts +209 -0
  52. package/src/tags/assign.ts +37 -0
  53. package/src/tags/call.ts +166 -0
  54. package/src/tags/defvar.ts +32 -0
  55. package/src/tags/eval.ts +65 -0
  56. package/src/tags/execute.ts +52 -0
  57. package/src/tags/expr.ts +128 -0
  58. package/src/tags/if.ts +58 -0
  59. package/src/tags/llm.ts +88 -0
  60. package/src/tags/loop.ts +43 -0
  61. package/src/tags/output.ts +23 -0
  62. package/src/tags/parameters.ts +77 -0
  63. package/src/tags/subroutine.ts +52 -0
  64. package/src/tags/variable.ts +27 -0
  65. package/src/types/index.ts +103 -0
  66. package/src/utils/llm-adapter.ts +118 -0
  67. package/tsconfig.json +18 -0
@@ -0,0 +1,135 @@
1
+ /**
2
+ * XML Parser for Dirac (.di files)
3
+ */
4
+ import { XMLParser } from 'fast-xml-parser';
5
+ export class DiracParser {
6
+ constructor() {
7
+ this.parser = new XMLParser({
8
+ ignoreAttributes: false,
9
+ attributeNamePrefix: '@_',
10
+ trimValues: true,
11
+ parseAttributeValue: false,
12
+ parseTagValue: false,
13
+ textNodeName: '#text',
14
+ cdataPropName: '#cdata',
15
+ preserveOrder: true, // Preserve element order!
16
+ commentPropName: '#comment',
17
+ });
18
+ }
19
+ parse(source) {
20
+ // Strip shebang line if present
21
+ if (source.startsWith('#!')) {
22
+ source = source.replace(/^#!.*\n/, '');
23
+ }
24
+ const result = this.parser.parse(source);
25
+ // With preserveOrder, result is an array
26
+ if (!Array.isArray(result) || result.length === 0) {
27
+ throw new Error('Empty or invalid XML');
28
+ }
29
+ // Find the first non-comment element
30
+ for (const item of result) {
31
+ if (!item['#comment']) {
32
+ return this.convertOrderedToElement(item);
33
+ }
34
+ }
35
+ throw new Error('No root element found');
36
+ }
37
+ convertOrderedToElement(obj) {
38
+ // obj is like { "tagname": [...children], ":@": {...attributes} }
39
+ const tagName = Object.keys(obj).find(k => k !== ':@' && k !== '#comment');
40
+ if (!tagName) {
41
+ throw new Error('Invalid element structure');
42
+ }
43
+ const element = {
44
+ tag: tagName,
45
+ attributes: {},
46
+ children: [],
47
+ };
48
+ // Extract attributes
49
+ if (obj[':@']) {
50
+ for (const [key, value] of Object.entries(obj[':@'])) {
51
+ if (key.startsWith('@_')) {
52
+ element.attributes[key.slice(2)] = value;
53
+ }
54
+ }
55
+ }
56
+ // Extract children
57
+ const children = obj[tagName];
58
+ if (Array.isArray(children)) {
59
+ for (const child of children) {
60
+ if (child['#text']) {
61
+ // Text node - add as child AND to element.text for backward compat
62
+ element.children.push({
63
+ tag: '',
64
+ text: child['#text'],
65
+ attributes: {},
66
+ children: []
67
+ });
68
+ // Also set element.text if not set (for simple text-only elements)
69
+ if (!element.text) {
70
+ element.text = child['#text'];
71
+ }
72
+ else {
73
+ element.text += child['#text'];
74
+ }
75
+ }
76
+ else if (child['#comment']) {
77
+ // Skip comments
78
+ continue;
79
+ }
80
+ else {
81
+ // Child element
82
+ element.children.push(this.convertOrderedToElement(child));
83
+ }
84
+ }
85
+ }
86
+ return element;
87
+ }
88
+ // Old method - no longer used
89
+ convertToElement(obj, tagName) {
90
+ if (!tagName) {
91
+ // Root level - find the actual tag
92
+ const keys = Object.keys(obj);
93
+ if (keys.length === 0) {
94
+ throw new Error('Empty XML');
95
+ }
96
+ tagName = keys[0];
97
+ obj = obj[tagName];
98
+ }
99
+ const element = {
100
+ tag: tagName,
101
+ attributes: {},
102
+ children: [],
103
+ };
104
+ if (typeof obj === 'string') {
105
+ element.text = obj;
106
+ return element;
107
+ }
108
+ if (!obj) {
109
+ return element;
110
+ }
111
+ // Extract attributes and children
112
+ for (const key of Object.keys(obj)) {
113
+ const value = obj[key];
114
+ if (key === '#text') {
115
+ element.text = value;
116
+ }
117
+ else if (key.startsWith('@_')) {
118
+ // Attribute
119
+ element.attributes[key.slice(2)] = value;
120
+ }
121
+ else {
122
+ // Child element
123
+ if (Array.isArray(value)) {
124
+ for (const item of value) {
125
+ element.children.push(this.convertToElement(item, key));
126
+ }
127
+ }
128
+ else {
129
+ element.children.push(this.convertToElement(value, key));
130
+ }
131
+ }
132
+ }
133
+ return element;
134
+ }
135
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Session management - Browser edition (no LLM SDKs)
3
+ */
4
+ import type { DiracSession, DiracConfig, DiracElement } from '../types/index.js';
5
+ export declare function createSession(config?: DiracConfig): DiracSession;
6
+ export declare function setVariable(session: DiracSession, name: string, value: any, visible?: boolean): void;
7
+ export declare function getVariable(session: DiracSession, name: string): any;
8
+ export declare function hasVariable(session: DiracSession, name: string): boolean;
9
+ /**
10
+ * Set boundary marker for local variables
11
+ * Maps to var_info_set_boundary in MASK
12
+ */
13
+ export declare function setBoundary(session: DiracSession): number;
14
+ /**
15
+ * Pop variables back to boundary (cleanup local scope)
16
+ * Maps to var_info_pop_to_boundary in MASK
17
+ */
18
+ export declare function popToBoundary(session: DiracSession): void;
19
+ /**
20
+ * Clean private variables but keep visible ones
21
+ * Maps to var_info_clean_to_boundary in MASK
22
+ */
23
+ export declare function cleanToBoundary(session: DiracSession): void;
24
+ export declare function registerSubroutine(session: DiracSession, name: string, element: DiracElement, description?: string, parameters?: any[]): void;
25
+ export declare function getSubroutine(session: DiracSession, name: string): DiracElement | undefined;
26
+ export declare function setSubroutineBoundary(session: DiracSession): number;
27
+ export declare function popSubroutinesToBoundary(session: DiracSession): void;
28
+ export declare function cleanSubroutinesToBoundary(session: DiracSession): void;
29
+ export declare function substituteVariables(session: DiracSession, text: string): string;
30
+ export declare function emit(session: DiracSession, content: string): void;
31
+ export declare function getOutput(session: DiracSession): string;
32
+ /**
33
+ * Get all available subroutines for reflection
34
+ * Used by LLM adapter to auto-generate prompts
35
+ */
36
+ export declare function getAvailableSubroutines(session: DiracSession): {
37
+ name: string;
38
+ description: string | undefined;
39
+ parameters: import("../types/index.js").ParameterMetadata[];
40
+ }[];
41
+ export declare function pushParameters(session: DiracSession, params: DiracElement[]): void;
42
+ export declare function popParameters(session: DiracSession): DiracElement[] | undefined;
43
+ export declare function getCurrentParameters(session: DiracSession): DiracElement[] | undefined;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Session management - Browser edition (no LLM SDKs)
3
+ */
4
+ export function createSession(config = {}) {
5
+ // Browser version - no LLM client
6
+ let llmClient = null;
7
+ return {
8
+ variables: [],
9
+ subroutines: [],
10
+ varBoundary: 0,
11
+ subBoundary: 0,
12
+ parameterStack: [],
13
+ output: [],
14
+ llmClient,
15
+ limits: {
16
+ maxLLMCalls: config.maxLLMCalls || 100,
17
+ currentLLMCalls: 0,
18
+ maxDepth: config.maxDepth || 50,
19
+ currentDepth: 0,
20
+ },
21
+ isReturn: false,
22
+ isBreak: false,
23
+ debug: config.debug || false,
24
+ config, // Store config reference
25
+ };
26
+ }
27
+ // Variable management (maps to var_info functions in MASK)
28
+ export function setVariable(session, name, value, visible = false) {
29
+ session.variables.push({
30
+ name,
31
+ value,
32
+ visible,
33
+ boundary: session.varBoundary,
34
+ passby: 'value',
35
+ });
36
+ }
37
+ export function getVariable(session, name) {
38
+ // Search from end (most recent) to beginning
39
+ for (let i = session.variables.length - 1; i >= 0; i--) {
40
+ if (session.variables[i].name === name) {
41
+ return session.variables[i].value;
42
+ }
43
+ }
44
+ return undefined;
45
+ }
46
+ export function hasVariable(session, name) {
47
+ return session.variables.some(v => v.name === name);
48
+ }
49
+ /**
50
+ * Set boundary marker for local variables
51
+ * Maps to var_info_set_boundary in MASK
52
+ */
53
+ export function setBoundary(session) {
54
+ const oldBoundary = session.varBoundary;
55
+ session.varBoundary = session.variables.length;
56
+ return oldBoundary;
57
+ }
58
+ /**
59
+ * Pop variables back to boundary (cleanup local scope)
60
+ * Maps to var_info_pop_to_boundary in MASK
61
+ */
62
+ export function popToBoundary(session) {
63
+ session.variables = session.variables.slice(0, session.varBoundary);
64
+ }
65
+ /**
66
+ * Clean private variables but keep visible ones
67
+ * Maps to var_info_clean_to_boundary in MASK
68
+ */
69
+ export function cleanToBoundary(session) {
70
+ const kept = [];
71
+ for (let i = 0; i < session.varBoundary; i++) {
72
+ kept.push(session.variables[i]);
73
+ }
74
+ for (let i = session.varBoundary; i < session.variables.length; i++) {
75
+ if (session.variables[i].visible) {
76
+ kept.push(session.variables[i]);
77
+ }
78
+ }
79
+ session.variables = kept;
80
+ session.varBoundary = kept.length;
81
+ }
82
+ // Subroutine management (maps to subroutine functions in MASK)
83
+ export function registerSubroutine(session, name, element, description, parameters) {
84
+ console.log(`📝 Registering subroutine: ${name}`, { description, paramCount: parameters?.length || 0 });
85
+ session.subroutines.push({
86
+ name,
87
+ element,
88
+ boundary: session.subBoundary,
89
+ description,
90
+ parameters,
91
+ });
92
+ console.log(`✅ Total subroutines registered: ${session.subroutines.length}`);
93
+ }
94
+ export function getSubroutine(session, name) {
95
+ console.log(`🔍 Looking up subroutine: ${name} (${session.subroutines.length} registered)`);
96
+ console.log('Available subroutines:', session.subroutines.map(s => s.name));
97
+ // Search from end (most recent) to beginning
98
+ for (let i = session.subroutines.length - 1; i >= 0; i--) {
99
+ if (session.subroutines[i].name === name) {
100
+ console.log(`✅ Found subroutine: ${name}`);
101
+ return session.subroutines[i].element;
102
+ }
103
+ }
104
+ console.log(`❌ Subroutine not found: ${name}`);
105
+ return undefined;
106
+ }
107
+ export function setSubroutineBoundary(session) {
108
+ const oldBoundary = session.subBoundary;
109
+ session.subBoundary = session.subroutines.length;
110
+ return oldBoundary;
111
+ }
112
+ export function popSubroutinesToBoundary(session) {
113
+ session.subroutines = session.subroutines.slice(0, session.subBoundary);
114
+ }
115
+ export function cleanSubroutinesToBoundary(session) {
116
+ // For now, same as pop (visibility not implemented for subroutines yet)
117
+ popSubroutinesToBoundary(session);
118
+ }
119
+ // Variable substitution (maps to var_replace functions in MASK)
120
+ export function substituteVariables(session, text) {
121
+ // Decode HTML entities first
122
+ let decoded = text
123
+ .replace(/&#10;/g, '\n')
124
+ .replace(/&#13;/g, '\r')
125
+ .replace(/&#9;/g, '\t')
126
+ .replace(/&lt;/g, '<')
127
+ .replace(/&gt;/g, '>')
128
+ .replace(/&amp;/g, '&')
129
+ .replace(/&quot;/g, '"')
130
+ .replace(/&apos;/g, "'");
131
+ // Substitute both ${var}/$var and {var} patterns
132
+ return decoded
133
+ .replace(/\$\{?(\w+)\}?/g, (match, varName) => {
134
+ const value = getVariable(session, varName);
135
+ return value !== undefined ? String(value) : match;
136
+ })
137
+ .replace(/\{(\w+)\}/g, (match, varName) => {
138
+ const value = getVariable(session, varName);
139
+ return value !== undefined ? String(value) : match;
140
+ });
141
+ }
142
+ // Output management
143
+ export function emit(session, content) {
144
+ session.output.push(content);
145
+ }
146
+ export function getOutput(session) {
147
+ return session.output.join('');
148
+ }
149
+ /**
150
+ * Get all available subroutines for reflection
151
+ * Used by LLM adapter to auto-generate prompts
152
+ */
153
+ export function getAvailableSubroutines(session) {
154
+ return session.subroutines
155
+ .filter(sub => sub.description || sub.parameters)
156
+ .map(sub => ({
157
+ name: sub.name,
158
+ description: sub.description,
159
+ parameters: sub.parameters || []
160
+ }));
161
+ }
162
+ // Parameter stack (for subroutine calls)
163
+ export function pushParameters(session, params) {
164
+ session.parameterStack.push(params);
165
+ }
166
+ export function popParameters(session) {
167
+ return session.parameterStack.pop();
168
+ }
169
+ export function getCurrentParameters(session) {
170
+ return session.parameterStack[session.parameterStack.length - 1];
171
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * <assign> tag - assign value to existing variable
3
+ * Maps to mask_tag_assign in MASK
4
+ */
5
+ import type { DiracSession, DiracElement } from '../types/index.js';
6
+ export declare function executeAssign(session: DiracSession, element: DiracElement): void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * <assign> tag - assign value to existing variable
3
+ * Maps to mask_tag_assign in MASK
4
+ */
5
+ import { setVariable, substituteVariables } from '../runtime/session.js';
6
+ export function executeAssign(session, element) {
7
+ const name = element.attributes.name;
8
+ const valueAttr = element.attributes.value;
9
+ if (!name) {
10
+ throw new Error('<assign> requires name attribute');
11
+ }
12
+ // Get value
13
+ let value;
14
+ if (valueAttr !== undefined) {
15
+ value = substituteVariables(session, valueAttr);
16
+ }
17
+ else if (element.text) {
18
+ value = substituteVariables(session, element.text);
19
+ }
20
+ else {
21
+ value = '';
22
+ }
23
+ // Find existing variable and update it
24
+ for (let i = session.variables.length - 1; i >= 0; i--) {
25
+ if (session.variables[i].name === name) {
26
+ session.variables[i].value = value;
27
+ return;
28
+ }
29
+ }
30
+ // Variable not found - create it (same as MASK behavior)
31
+ setVariable(session, name, value, false);
32
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * <call> tag - invoke subroutine
3
+ * Maps to mask_call_integrate in MASK
4
+ */
5
+ import type { DiracSession, DiracElement } from '../types/index.js';
6
+ export declare function executeCall(session: DiracSession, element: DiracElement): Promise<void>;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * <call> tag - invoke subroutine
3
+ * Maps to mask_call_integrate in MASK
4
+ */
5
+ import { getSubroutine, setBoundary, cleanToBoundary, pushParameters, popParameters, substituteVariables, setVariable, getVariable, } from '../runtime/session.js';
6
+ import { integrateChildren } from '../runtime/interpreter.js';
7
+ export async function executeCall(session, element) {
8
+ // Support both <call name="FOO" /> and direct <FOO /> syntax
9
+ // For <call> tag, use name/subroutine attribute
10
+ // For direct syntax, use element.tag
11
+ let name;
12
+ if (element.tag === 'call') {
13
+ // Explicit <call> tag - use name or subroutine attribute
14
+ name = element.attributes.name || element.attributes.subroutine || '';
15
+ }
16
+ else {
17
+ // Direct tag syntax - use tag name itself, ignore name attribute
18
+ name = element.tag;
19
+ }
20
+ if (!name) {
21
+ throw new Error('<call> requires name or subroutine attribute');
22
+ }
23
+ const subroutine = getSubroutine(session, name);
24
+ if (!subroutine) {
25
+ throw new Error(`Subroutine '${name}' not found`);
26
+ }
27
+ // Handle extension (parent subroutine)
28
+ const extendsName = subroutine.attributes.extends;
29
+ if (extendsName) {
30
+ const parent = getSubroutine(session, extendsName);
31
+ if (parent) {
32
+ // Call parent first
33
+ await executeCallInternal(session, parent, element);
34
+ }
35
+ }
36
+ // Call this subroutine
37
+ await executeCallInternal(session, subroutine, element);
38
+ }
39
+ async function executeCallInternal(session, subroutine, callElement) {
40
+ // Set boundary for local scope
41
+ const oldBoundary = setBoundary(session);
42
+ const wasReturn = session.isReturn;
43
+ session.isReturn = false;
44
+ // Substitute variables in call element attributes before pushing to parameter stack
45
+ const substitutedElement = {
46
+ tag: callElement.tag,
47
+ attributes: {},
48
+ children: callElement.children
49
+ };
50
+ // Process each attribute for {variable} substitution
51
+ for (const [key, value] of Object.entries(callElement.attributes)) {
52
+ if (typeof value === 'string') {
53
+ substitutedElement.attributes[key] = substituteVariables(session, value);
54
+ }
55
+ else {
56
+ substitutedElement.attributes[key] = value;
57
+ }
58
+ }
59
+ // Push caller element onto parameter stack for <parameters select="*|@*|@attr"/> access
60
+ pushParameters(session, [substitutedElement]);
61
+ try {
62
+ // Bind call attributes as variables (for <call name="foo" x="1" y="2"/> style)
63
+ // This allows ${x} and ${y} to be used in the subroutine body
64
+ for (const [key, value] of Object.entries(substitutedElement.attributes)) {
65
+ // Skip special attributes
66
+ if (key === 'name' || key === 'subroutine')
67
+ continue;
68
+ // Set as local variable
69
+ setVariable(session, key, value, false);
70
+ }
71
+ // Bind parameters (for <call><parameters>...</parameters></call> style)
72
+ const paramElements = callElement.children.filter(c => c.tag === 'parameters');
73
+ if (paramElements.length > 0) {
74
+ await bindParameters(session, subroutine, paramElements[0]);
75
+ }
76
+ // Execute subroutine body
77
+ await integrateChildren(session, subroutine);
78
+ }
79
+ finally {
80
+ // Pop parameter stack
81
+ popParameters(session);
82
+ // Clean up scope (keep visible variables)
83
+ session.varBoundary = oldBoundary;
84
+ cleanToBoundary(session);
85
+ session.isReturn = wasReturn;
86
+ }
87
+ }
88
+ async function bindParameters(session, subroutine, callParams) {
89
+ // Find <parameters> definition in subroutine
90
+ const paramDef = subroutine.children.find(c => c.tag === 'parameters');
91
+ if (!paramDef) {
92
+ return; // No parameters defined
93
+ }
94
+ // Get variable definitions from parameter declaration
95
+ const paramVars = paramDef.children.filter(c => c.tag === 'variable');
96
+ const callVars = callParams.children.filter(c => c.tag === 'variable');
97
+ // Bind each parameter
98
+ for (let i = 0; i < paramVars.length; i++) {
99
+ const paramVar = paramVars[i];
100
+ const callVar = callVars[i];
101
+ const paramName = paramVar.attributes.name;
102
+ const passby = paramVar.attributes.passby || 'value';
103
+ if (!paramName)
104
+ continue;
105
+ let value;
106
+ if (callVar) {
107
+ // Get value from call site
108
+ const callValue = callVar.attributes.value;
109
+ if (callValue) {
110
+ if (passby === 'ref') {
111
+ // Pass by reference - store variable name
112
+ setVariable(session, paramName, getVariable(session, callValue), false);
113
+ session.variables[session.variables.length - 1].passby = 'ref';
114
+ session.variables[session.variables.length - 1].refName = callValue;
115
+ }
116
+ else {
117
+ // Pass by value
118
+ value = substituteVariables(session, callValue);
119
+ setVariable(session, paramName, value, false);
120
+ }
121
+ }
122
+ }
123
+ else {
124
+ // No value provided - use default if available
125
+ const defaultValue = paramVar.attributes.default || '';
126
+ value = substituteVariables(session, defaultValue);
127
+ setVariable(session, paramName, value, false);
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * <defvar> tag - define variable
3
+ * Maps to mask_tag_defvar in MASK
4
+ */
5
+ import type { DiracSession, DiracElement } from '../types/index.js';
6
+ export declare function executeDefvar(session: DiracSession, element: DiracElement): void;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * <defvar> tag - define variable
3
+ * Maps to mask_tag_defvar in MASK
4
+ */
5
+ import { setVariable, substituteVariables } from '../runtime/session.js';
6
+ export function executeDefvar(session, element) {
7
+ const name = element.attributes.name;
8
+ const valueAttr = element.attributes.value;
9
+ const visibleAttr = element.attributes.visible || 'false';
10
+ if (!name) {
11
+ throw new Error('<defvar> requires name attribute');
12
+ }
13
+ // Determine visibility
14
+ const visible = visibleAttr === 'true' || visibleAttr === 'variable' || visibleAttr === 'both';
15
+ // Get value (from attribute or text content)
16
+ let value;
17
+ if (valueAttr !== undefined) {
18
+ value = substituteVariables(session, valueAttr);
19
+ }
20
+ else if (element.text) {
21
+ value = substituteVariables(session, element.text);
22
+ }
23
+ else {
24
+ value = '';
25
+ }
26
+ setVariable(session, name, value, visible);
27
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * <eval> tag - evaluate JavaScript expression
3
+ * Maps to mask_tag_eval in MASK (but for JS, not C)
4
+ */
5
+ import type { DiracSession, DiracElement } from '../types/index.js';
6
+ export declare function executeEval(session: DiracSession, element: DiracElement): Promise<void>;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * <eval> tag - evaluate JavaScript expression
3
+ * Maps to mask_tag_eval in MASK (but for JS, not C)
4
+ */
5
+ import { setVariable } from '../runtime/session.js';
6
+ // AsyncFunction constructor
7
+ const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
8
+ export async function executeEval(session, element) {
9
+ const name = element.attributes.name;
10
+ const exprAttr = element.attributes.expr;
11
+ // Get expression - replace ${var} with var (will be passed as actual JS vars)
12
+ let expr;
13
+ if (exprAttr) {
14
+ expr = exprAttr.replace(/\$\{(\w+)\}/g, '$1');
15
+ }
16
+ else if (element.text) {
17
+ expr = element.text.replace(/\$\{(\w+)\}/g, '$1');
18
+ }
19
+ else {
20
+ throw new Error('<eval> requires expr attribute or text content');
21
+ }
22
+ if (session.debug) {
23
+ console.error(`[EVAL] Code after substitution:\n${expr}\n`);
24
+ }
25
+ try {
26
+ // Build context object with all variables
27
+ const context = {};
28
+ for (const v of session.variables) {
29
+ context[v.name] = v.value;
30
+ }
31
+ // Add customContext if provided (for Angular services, etc.)
32
+ if (session.config.customContext) {
33
+ Object.assign(context, session.config.customContext);
34
+ }
35
+ // Add helper to get current parameters from stack
36
+ context.getParams = () => {
37
+ const params = session.parameterStack[session.parameterStack.length - 1];
38
+ return params && params[0] ? params[0] : null;
39
+ };
40
+ let result;
41
+ // Execute as async function to support top-level await
42
+ const func = new AsyncFunction(...Object.keys(context), expr);
43
+ result = await func(...Object.values(context));
44
+ if (session.debug) {
45
+ console.error(`[EVAL] Result: ${JSON.stringify(result)}`);
46
+ }
47
+ // Store result if name provided
48
+ if (name) {
49
+ setVariable(session, name, result, false);
50
+ }
51
+ }
52
+ catch (error) {
53
+ throw new Error(`Eval error: ${error instanceof Error ? error.message : String(error)}`);
54
+ }
55
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * <execute> tag - Execute dynamically generated Dirac code
3
+ * Takes LLM-generated or variable content and interprets it as Dirac XML
4
+ */
5
+ import type { DiracSession, DiracElement } from '../types/index.js';
6
+ export declare function executeExecute(session: DiracSession, element: DiracElement): Promise<void>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * <execute> tag - Execute dynamically generated Dirac code
3
+ * Takes LLM-generated or variable content and interprets it as Dirac XML
4
+ */
5
+ import { getVariable, substituteVariables } from '../runtime/session.js';
6
+ import { DiracParser } from '../runtime/parser.js';
7
+ import { integrate } from '../runtime/interpreter.js';
8
+ export async function executeExecute(session, element) {
9
+ const sourceAttr = element.attributes.source;
10
+ // Get the Dirac code to execute
11
+ let diracCode;
12
+ if (sourceAttr) {
13
+ // Get from variable
14
+ diracCode = getVariable(session, sourceAttr);
15
+ if (!diracCode) {
16
+ throw new Error(`<execute> source variable '${sourceAttr}' not found`);
17
+ }
18
+ }
19
+ else if (element.text) {
20
+ // Get from text content (with variable substitution)
21
+ diracCode = substituteVariables(session, element.text);
22
+ }
23
+ else {
24
+ throw new Error('<execute> requires source attribute or text content');
25
+ }
26
+ if (session.debug) {
27
+ console.error(`[EXECUTE] Interpreting dynamic code:\n${diracCode}\n`);
28
+ }
29
+ // Strip markdown code blocks if present
30
+ diracCode = diracCode.trim();
31
+ if (diracCode.startsWith('```')) {
32
+ // Remove markdown code fences
33
+ diracCode = diracCode.replace(/^```(?:xml|html)?\n?/m, '').replace(/\n?```$/m, '').trim();
34
+ }
35
+ try {
36
+ // Parse the dynamic Dirac code
37
+ const parser = new DiracParser();
38
+ const dynamicAST = parser.parse(diracCode);
39
+ // Execute the dynamically generated code
40
+ await integrate(session, dynamicAST);
41
+ }
42
+ catch (error) {
43
+ throw new Error(`Execute error: ${error instanceof Error ? error.message : String(error)}`);
44
+ }
45
+ }