@o-lang/olang 1.0.15 → 1.0.16
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/package.json +1 -1
- package/src/parser.js +72 -93
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
// src/parser.js
|
|
2
|
-
|
|
3
2
|
function parse(code, fileName = null) {
|
|
4
3
|
if (fileName && !fileName.endsWith(".ol")) {
|
|
5
4
|
throw new Error(`Expected .ol workflow, got: ${fileName}`);
|
|
6
5
|
}
|
|
7
|
-
|
|
8
6
|
const rawLines = code.split(/\r?\n/);
|
|
9
|
-
|
|
10
7
|
const lines = rawLines
|
|
11
8
|
.map(l => l.trim())
|
|
12
9
|
.filter(l => l && !l.startsWith('#') && !l.startsWith('//'));
|
|
@@ -17,37 +14,41 @@ function parse(code, fileName = null) {
|
|
|
17
14
|
steps: [],
|
|
18
15
|
returnValues: [],
|
|
19
16
|
allowedResolvers: [],
|
|
20
|
-
|
|
21
17
|
resolverPolicy: {
|
|
22
18
|
declared: [],
|
|
23
19
|
autoInjected: [],
|
|
24
20
|
used: [],
|
|
25
21
|
warnings: []
|
|
26
22
|
},
|
|
27
|
-
|
|
28
23
|
__warnings: [],
|
|
29
24
|
__requiresMath: false
|
|
30
25
|
};
|
|
31
26
|
|
|
32
27
|
let i = 0;
|
|
33
|
-
|
|
34
28
|
while (i < lines.length) {
|
|
35
29
|
let line = lines[i];
|
|
36
30
|
|
|
37
|
-
|
|
31
|
+
// ✅ FIXED: Match both "Allow resolvers" and "Allowed resolvers"
|
|
32
|
+
const allowMatch = line.match(/^Allow(?:ed)?\s+resolvers\s*:\s*$/i);
|
|
38
33
|
if (allowMatch) {
|
|
39
34
|
i++;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
// Read all subsequent non-empty lines until a new top-level section (starts with capital letter)
|
|
36
|
+
while (i < lines.length) {
|
|
37
|
+
const nextLine = lines[i].trim();
|
|
38
|
+
// Stop if line is empty or appears to be a new top-level keyword (e.g., Step, Workflow, Return)
|
|
39
|
+
if (nextLine === '' || /^[A-Z][a-z]/.test(nextLine)) {
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
if (nextLine) {
|
|
43
|
+
workflow.allowedResolvers.push(nextLine);
|
|
44
|
+
workflow.resolverPolicy.declared.push(nextLine);
|
|
45
45
|
}
|
|
46
46
|
i++;
|
|
47
47
|
}
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
// ---- Math step patterns (standalone) ----
|
|
51
52
|
let mathAdd = line.match(/^Add\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i);
|
|
52
53
|
if (mathAdd) {
|
|
53
54
|
workflow.__requiresMath = true;
|
|
@@ -65,7 +66,7 @@ function parse(code, fileName = null) {
|
|
|
65
66
|
workflow.__requiresMath = true;
|
|
66
67
|
workflow.steps.push({
|
|
67
68
|
type: 'calculate',
|
|
68
|
-
expression: `subtract({${
|
|
69
|
+
expression: `subtract({${mathAdd[2]}}, {${mathAdd[1]}})`,
|
|
69
70
|
saveAs: mathSub[3].trim()
|
|
70
71
|
});
|
|
71
72
|
i++;
|
|
@@ -96,6 +97,7 @@ function parse(code, fileName = null) {
|
|
|
96
97
|
continue;
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
// ---- Workflow declaration ----
|
|
99
101
|
const wfMatch = line.match(/^Workflow\s+"([^"]+)"(?:\s+with\s+(.+))?/i);
|
|
100
102
|
if (wfMatch) {
|
|
101
103
|
workflow.name = wfMatch[1];
|
|
@@ -106,96 +108,84 @@ function parse(code, fileName = null) {
|
|
|
106
108
|
continue;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
i++;
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// ---------------------------
|
|
131
|
-
// Steps (updated: auto-detect math + saveAs)
|
|
132
|
-
// ---------------------------
|
|
133
|
-
const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
134
|
-
if (stepMatch) {
|
|
135
|
-
const stepNum = parseInt(stepMatch[1], 10);
|
|
136
|
-
const raw = stepMatch[2].trim();
|
|
137
|
-
|
|
138
|
-
// --- Detect math inside Step ---
|
|
139
|
-
let mathDetected = null;
|
|
140
|
-
let expr = '';
|
|
141
|
-
let saveVar = null;
|
|
142
|
-
|
|
143
|
-
const mathOps = [
|
|
144
|
-
{ re: /^Add\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'add' },
|
|
145
|
-
{ re: /^Subtract\s+\{(.+?)\}\s+from\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'subtract' },
|
|
146
|
-
{ re: /^Multiply\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'multiply' },
|
|
147
|
-
{ re: /^Divide\s+\{(.+?)\}\s+by\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'divide' }
|
|
148
|
-
];
|
|
149
|
-
|
|
150
|
-
for (const op of mathOps) {
|
|
151
|
-
const m = raw.match(op.re);
|
|
152
|
-
if (m) {
|
|
153
|
-
mathDetected = op.fn;
|
|
154
|
-
saveVar = m[3].trim();
|
|
155
|
-
if (op.fn === 'subtract') expr = `subtract({${m[2]}}, {${m[1]}})`;
|
|
156
|
-
else expr = `${op.fn}({${m[1]}}, {${m[2]}})`;
|
|
157
|
-
break;
|
|
111
|
+
// ---- Return statement (updated: auto-detect math) ----
|
|
112
|
+
const returnMatch = line.match(/^Return\s+(.+)$/i);
|
|
113
|
+
if (returnMatch) {
|
|
114
|
+
const returns = returnMatch[1].split(',').map(v => v.trim());
|
|
115
|
+
workflow.returnValues = returns;
|
|
116
|
+
// Check if any return vars come from math steps
|
|
117
|
+
for (const retVar of returns) {
|
|
118
|
+
const producedByMath = workflow.steps.some(
|
|
119
|
+
s => s.saveAs === retVar && s.type === 'calculate'
|
|
120
|
+
);
|
|
121
|
+
if (producedByMath) workflow.__requiresMath = true;
|
|
122
|
+
}
|
|
123
|
+
i++;
|
|
124
|
+
continue;
|
|
158
125
|
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (mathDetected) workflow.__requiresMath = true;
|
|
162
|
-
|
|
163
|
-
workflow.steps.push({
|
|
164
|
-
type: mathDetected ? 'calculate' : 'action',
|
|
165
|
-
stepNumber: stepNum,
|
|
166
|
-
actionRaw: mathDetected ? null : raw,
|
|
167
|
-
expression: mathDetected ? expr : undefined,
|
|
168
|
-
saveAs: saveVar,
|
|
169
|
-
constraints: {}
|
|
170
|
-
});
|
|
171
126
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
127
|
+
// ---- Step parsing (with inline math detection) ----
|
|
128
|
+
const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
129
|
+
if (stepMatch) {
|
|
130
|
+
const stepNum = parseInt(stepMatch[1], 10);
|
|
131
|
+
const raw = stepMatch[2].trim();
|
|
132
|
+
|
|
133
|
+
let mathDetected = null;
|
|
134
|
+
let expr = '';
|
|
135
|
+
let saveVar = null;
|
|
136
|
+
const mathOps = [
|
|
137
|
+
{ re: /^Add\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'add' },
|
|
138
|
+
{ re: /^Subtract\s+\{(.+?)\}\s+from\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'subtract' },
|
|
139
|
+
{ re: /^Multiply\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'multiply' },
|
|
140
|
+
{ re: /^Divide\s+\{(.+?)\}\s+by\s+\{(.+?)\}\s+Save as\s+(.+)$/i, fn: 'divide' }
|
|
141
|
+
];
|
|
142
|
+
for (const op of mathOps) {
|
|
143
|
+
const m = raw.match(op.re);
|
|
144
|
+
if (m) {
|
|
145
|
+
mathDetected = op.fn;
|
|
146
|
+
saveVar = m[3].trim();
|
|
147
|
+
if (op.fn === 'subtract') {
|
|
148
|
+
expr = `subtract({${m[2]}}, {${m[1]}})`;
|
|
149
|
+
} else {
|
|
150
|
+
expr = `${op.fn}({${m[1]}}, {${m[2]}})`;
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (mathDetected) workflow.__requiresMath = true;
|
|
156
|
+
workflow.steps.push({
|
|
157
|
+
type: mathDetected ? 'calculate' : 'action',
|
|
158
|
+
stepNumber: stepNum,
|
|
159
|
+
actionRaw: mathDetected ? null : raw,
|
|
160
|
+
expression: mathDetected ? expr : undefined,
|
|
161
|
+
saveAs: saveVar,
|
|
162
|
+
constraints: {}
|
|
163
|
+
});
|
|
164
|
+
i++;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
175
167
|
|
|
168
|
+
// ---- Save as (for legacy or multi-line steps) ----
|
|
176
169
|
const saveMatch = line.match(/^Save as\s+(.+)$/i);
|
|
177
170
|
if (saveMatch && workflow.steps.length > 0) {
|
|
178
171
|
const lastStep = workflow.steps[workflow.steps.length - 1];
|
|
179
172
|
lastStep.saveAs = saveMatch[1].trim();
|
|
180
|
-
|
|
181
173
|
if (lastStep.saveAs.match(/[A-Z][A-Za-z0-9_]*/)) {
|
|
182
174
|
workflow.__requiresMath = true;
|
|
183
175
|
}
|
|
184
|
-
|
|
185
176
|
i++;
|
|
186
177
|
continue;
|
|
187
178
|
}
|
|
188
179
|
|
|
180
|
+
// ---- Constraint parsing ----
|
|
189
181
|
const constraintMatch = line.match(/^Constraint:\s*(.+)$/i);
|
|
190
182
|
if (constraintMatch && workflow.steps.length > 0) {
|
|
191
183
|
const lastStep = workflow.steps[workflow.steps.length - 1];
|
|
192
184
|
if (!lastStep.constraints) lastStep.constraints = {};
|
|
193
|
-
|
|
194
185
|
const eq = constraintMatch[1].match(/^([^=]+)=\s*(.+)$/);
|
|
195
186
|
if (eq) {
|
|
196
187
|
let key = eq[1].trim();
|
|
197
188
|
let value = eq[2].trim();
|
|
198
|
-
|
|
199
189
|
if (value.startsWith('[') && value.endsWith(']')) {
|
|
200
190
|
value = value.slice(1, -1).split(',').map(v =>
|
|
201
191
|
v.trim().replace(/^"/, '').replace(/"$/, '')
|
|
@@ -205,7 +195,6 @@ if (stepMatch) {
|
|
|
205
195
|
} else if (value.startsWith('"') && value.endsWith('"')) {
|
|
206
196
|
value = value.slice(1, -1);
|
|
207
197
|
}
|
|
208
|
-
|
|
209
198
|
lastStep.constraints[key] = value;
|
|
210
199
|
}
|
|
211
200
|
i++;
|
|
@@ -220,7 +209,6 @@ if (stepMatch) {
|
|
|
220
209
|
// ============================
|
|
221
210
|
if (workflow.__requiresMath) {
|
|
222
211
|
workflow.resolverPolicy.used.push('builtInMathResolver');
|
|
223
|
-
|
|
224
212
|
if (!workflow.resolverPolicy.declared.includes('builtInMathResolver')) {
|
|
225
213
|
workflow.resolverPolicy.autoInjected.push('builtInMathResolver');
|
|
226
214
|
workflow.allowedResolvers.unshift('builtInMathResolver');
|
|
@@ -237,7 +225,6 @@ if (stepMatch) {
|
|
|
237
225
|
}
|
|
238
226
|
|
|
239
227
|
workflow.resolverPolicy.warnings = workflow.__warnings.slice();
|
|
240
|
-
|
|
241
228
|
return workflow;
|
|
242
229
|
}
|
|
243
230
|
|
|
@@ -247,7 +234,6 @@ if (stepMatch) {
|
|
|
247
234
|
function parseBlock(lines) {
|
|
248
235
|
const steps = [];
|
|
249
236
|
let current = null;
|
|
250
|
-
|
|
251
237
|
for (const line of lines) {
|
|
252
238
|
const stepMatch = line.match(/^Step\s+(\d+)\s*:\s*(.+)$/i);
|
|
253
239
|
if (stepMatch) {
|
|
@@ -261,37 +247,30 @@ function parseBlock(lines) {
|
|
|
261
247
|
steps.push(current);
|
|
262
248
|
continue;
|
|
263
249
|
}
|
|
264
|
-
|
|
265
250
|
const saveMatch = line.match(/^Save as\s+(.+)$/i);
|
|
266
251
|
if (saveMatch && current) current.saveAs = saveMatch[1].trim();
|
|
267
|
-
|
|
268
252
|
const debriefMatch = line.match(/^Debrief\s+(\w+)\s+with\s+"(.+)"$/i);
|
|
269
253
|
if (debriefMatch) {
|
|
270
254
|
steps.push({ type: 'debrief', agent: debriefMatch[1], message: debriefMatch[2] });
|
|
271
255
|
}
|
|
272
|
-
|
|
273
256
|
const evolveMatch = line.match(/^Evolve\s+(\w+)\s+using\s+feedback:\s+"(.+)"$/i);
|
|
274
257
|
if (evolveMatch) {
|
|
275
258
|
steps.push({ type: 'evolve', agent: evolveMatch[1], feedback: evolveMatch[2] });
|
|
276
259
|
}
|
|
277
|
-
|
|
278
260
|
const promptMatch = line.match(/^Prompt user to\s+"(.+)"$/i);
|
|
279
261
|
if (promptMatch) {
|
|
280
262
|
steps.push({ type: 'prompt', question: promptMatch[1], saveAs: null });
|
|
281
263
|
}
|
|
282
|
-
|
|
283
264
|
const useMatch = line.match(/^Use\s+(.+)$/i);
|
|
284
265
|
if (useMatch) {
|
|
285
266
|
steps.push({ type: 'use', tool: useMatch[1].trim(), saveAs: null, constraints: {} });
|
|
286
267
|
}
|
|
287
|
-
|
|
288
268
|
const askMatch = line.match(/^Ask\s+(.+)$/i);
|
|
289
269
|
if (askMatch) {
|
|
290
270
|
steps.push({ type: 'ask', target: askMatch[1].trim(), saveAs: null, constraints: {} });
|
|
291
271
|
}
|
|
292
272
|
}
|
|
293
|
-
|
|
294
273
|
return steps;
|
|
295
274
|
}
|
|
296
275
|
|
|
297
|
-
module.exports = { parse };
|
|
276
|
+
module.exports = { parse };
|