@o-lang/olang 1.0.4 → 1.0.5
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/cli.js +34 -21
- package/package.json +1 -1
- package/src/parser.js +56 -0
- package/src/runtime.js +30 -2
package/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const { Command } = require('commander');
|
|
3
|
-
const { parse } = require('
|
|
4
|
-
const { execute } = require('
|
|
3
|
+
const { parse } = require('../src/parser'); // adjusted path if bin vs root
|
|
4
|
+
const { execute } = require('../src/runtime');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
|
|
@@ -21,6 +21,8 @@ function ensureOlExtension(filename) {
|
|
|
21
21
|
* Default mock resolver (for demo use)
|
|
22
22
|
*/
|
|
23
23
|
async function defaultMockResolver(action, context) {
|
|
24
|
+
if (!action || typeof action !== 'string') return `[Unhandled: ${String(action)}]`;
|
|
25
|
+
|
|
24
26
|
if (action.startsWith('Search for ')) {
|
|
25
27
|
return {
|
|
26
28
|
title: "HR Policy 2025",
|
|
@@ -47,31 +49,41 @@ async function defaultMockResolver(action, context) {
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
|
-
* Built-in Math Resolver
|
|
51
|
-
* Supports
|
|
52
|
+
* Built-in Math Resolver (so action style math strings are handled too)
|
|
53
|
+
* Supports action strings like: add(1,2), subtract(5,2), multiply(2,3), divide(6,3), sum([1,2,3])
|
|
54
|
+
* Note: runtime handles calculate steps; this resolver helps when parser emits actions with math strings.
|
|
52
55
|
*/
|
|
53
56
|
async function builtInMathResolver(action, context) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
if (!action || typeof action !== 'string') return null;
|
|
58
|
+
|
|
59
|
+
// Replace contextual placeholders {var}
|
|
60
|
+
const a = action.replace(/\{([^\}]+)\}/g, (_, k) => {
|
|
61
|
+
const v = context[k.trim()];
|
|
62
|
+
return v !== undefined ? v : `{${k}}`;
|
|
58
63
|
});
|
|
59
64
|
|
|
60
|
-
|
|
65
|
+
// simple function matches
|
|
66
|
+
let m;
|
|
67
|
+
m = a.match(/^add\(([^,]+),\s*([^)]+)\)$/i);
|
|
68
|
+
if (m) return parseFloat(m[1]) + parseFloat(m[2]);
|
|
61
69
|
|
|
62
|
-
|
|
63
|
-
if (
|
|
70
|
+
m = a.match(/^subtract\(([^,]+),\s*([^)]+)\)$/i);
|
|
71
|
+
if (m) return parseFloat(m[1]) - parseFloat(m[2]);
|
|
64
72
|
|
|
65
|
-
|
|
66
|
-
if (
|
|
73
|
+
m = a.match(/^multiply\(([^,]+),\s*([^)]+)\)$/i);
|
|
74
|
+
if (m) return parseFloat(m[1]) * parseFloat(m[2]);
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
if (
|
|
76
|
+
m = a.match(/^divide\(([^,]+),\s*([^)]+)\)$/i);
|
|
77
|
+
if (m) return parseFloat(m[1]) / parseFloat(m[2]);
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
if (
|
|
79
|
+
m = a.match(/^sum\(\s*\[([^\]]+)\]\s*\)$/i);
|
|
80
|
+
if (m) {
|
|
81
|
+
const arr = m[1].split(',').map(s => parseFloat(s.trim()));
|
|
82
|
+
return arr.reduce((s, v) => s + v, 0);
|
|
83
|
+
}
|
|
73
84
|
|
|
74
|
-
|
|
85
|
+
// not a math action
|
|
86
|
+
return null;
|
|
75
87
|
}
|
|
76
88
|
|
|
77
89
|
/**
|
|
@@ -119,8 +131,7 @@ function loadSingleResolver(specifier) {
|
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
/**
|
|
122
|
-
*
|
|
123
|
-
* Built-in math resolver is added first, then user resolvers, then default mock
|
|
134
|
+
* loadResolverChain: include built-in math resolver first, then user resolvers, then default mock resolver
|
|
124
135
|
*/
|
|
125
136
|
function loadResolverChain(specifiers) {
|
|
126
137
|
const userResolvers = specifiers?.map(loadSingleResolver) || [];
|
|
@@ -155,7 +166,9 @@ program
|
|
|
155
166
|
'Input parameters',
|
|
156
167
|
(val, acc = {}) => {
|
|
157
168
|
const [k, v] = val.split('=');
|
|
158
|
-
|
|
169
|
+
// try to parse numbers, preserve strings otherwise
|
|
170
|
+
const parsed = v === undefined ? '' : (v === 'true' ? true : (v === 'false' ? false : (isNaN(v) ? v : parseFloat(v))));
|
|
171
|
+
acc[k] = parsed;
|
|
159
172
|
return acc;
|
|
160
173
|
},
|
|
161
174
|
{}
|
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -22,6 +22,62 @@ function parse(code, fileName = null) {
|
|
|
22
22
|
while (i < lines.length) {
|
|
23
23
|
let line = lines[i];
|
|
24
24
|
|
|
25
|
+
// ============================
|
|
26
|
+
// NEW: Detect math operations
|
|
27
|
+
// ============================
|
|
28
|
+
|
|
29
|
+
// Add X and Y
|
|
30
|
+
let mathAdd = line.match(/^Add\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i);
|
|
31
|
+
if (mathAdd) {
|
|
32
|
+
workflow.steps.push({
|
|
33
|
+
type: 'calculate',
|
|
34
|
+
expression: `add({${mathAdd[1]}}, {${mathAdd[2]}})`,
|
|
35
|
+
saveAs: mathAdd[3].trim()
|
|
36
|
+
});
|
|
37
|
+
i++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Subtract A from B => B - A
|
|
42
|
+
let mathSub = line.match(/^Subtract\s+\{(.+?)\}\s+from\s+\{(.+?)\}\s+Save as\s+(.+)$/i);
|
|
43
|
+
if (mathSub) {
|
|
44
|
+
workflow.steps.push({
|
|
45
|
+
type: 'calculate',
|
|
46
|
+
expression: `subtract({${mathSub[2]}}, {${mathSub[1]}})`,
|
|
47
|
+
saveAs: mathSub[3].trim()
|
|
48
|
+
});
|
|
49
|
+
i++;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Multiply X and Y
|
|
54
|
+
let mathMul = line.match(/^Multiply\s+\{(.+?)\}\s+and\s+\{(.+?)\}\s+Save as\s+(.+)$/i);
|
|
55
|
+
if (mathMul) {
|
|
56
|
+
workflow.steps.push({
|
|
57
|
+
type: 'calculate',
|
|
58
|
+
expression: `multiply({${mathMul[1]}}, {${mathMul[2]}})`,
|
|
59
|
+
saveAs: mathMul[3].trim()
|
|
60
|
+
});
|
|
61
|
+
i++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Divide A by B
|
|
66
|
+
let mathDiv = line.match(/^Divide\s+\{(.+?)\}\s+by\s+\{(.+?)\}\s+Save as\s+(.+)$/i);
|
|
67
|
+
if (mathDiv) {
|
|
68
|
+
workflow.steps.push({
|
|
69
|
+
type: 'calculate',
|
|
70
|
+
expression: `divide({${mathDiv[1]}}, {${mathDiv[2]}})`,
|
|
71
|
+
saveAs: mathDiv[3].trim()
|
|
72
|
+
});
|
|
73
|
+
i++;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ====================== END NEW MATH RULES ======================
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
25
81
|
// Workflow
|
|
26
82
|
const wfMatch = line.match(/^Workflow\s+"([^"]+)"(?:\s+with\s+(.+))?/i);
|
|
27
83
|
if (wfMatch) {
|
package/src/runtime.js
CHANGED
|
@@ -74,10 +74,12 @@ class RuntimeAPI {
|
|
|
74
74
|
// Replace context variables in curly braces
|
|
75
75
|
expr = expr.replace(/\{([^\}]+)\}/g, (_, path) => {
|
|
76
76
|
const value = this.getNested(this.context, path.trim());
|
|
77
|
+
// if value is string, wrap in quotes for functions that accept strings
|
|
78
|
+
if (typeof value === 'string') return `"${value.replace(/"/g, '\\"')}"`;
|
|
77
79
|
return value !== undefined ? value : 0;
|
|
78
80
|
});
|
|
79
81
|
|
|
80
|
-
//
|
|
82
|
+
// expose safe functions only
|
|
81
83
|
const funcNames = Object.keys(this.mathFunctions);
|
|
82
84
|
const safeFunc = {};
|
|
83
85
|
funcNames.forEach(fn => {
|
|
@@ -100,7 +102,7 @@ class RuntimeAPI {
|
|
|
100
102
|
async executeStep(step, agentResolver) {
|
|
101
103
|
switch (step.type) {
|
|
102
104
|
case 'calculate': {
|
|
103
|
-
//
|
|
105
|
+
// step.expression or actionRaw can contain math expression strings
|
|
104
106
|
const expr = step.expression || step.actionRaw;
|
|
105
107
|
const result = this.evaluateMath(expr);
|
|
106
108
|
if (step.saveAs) this.context[step.saveAs] = result;
|
|
@@ -112,6 +114,32 @@ class RuntimeAPI {
|
|
|
112
114
|
const value = this.getNested(this.context, path.trim());
|
|
113
115
|
return value !== undefined ? String(value) : `{${path}}`;
|
|
114
116
|
});
|
|
117
|
+
|
|
118
|
+
// Provide fallback math recognition for action lines too (e.g., add(1,2))
|
|
119
|
+
// Try simple math function calls in action form
|
|
120
|
+
const mathCall = action.match(/^(add|subtract|multiply|divide|sum|avg|min|max|round|floor|ceil|abs)\((.*)\)$/i);
|
|
121
|
+
if (mathCall) {
|
|
122
|
+
const fn = mathCall[1].toLowerCase();
|
|
123
|
+
const argsRaw = mathCall[2];
|
|
124
|
+
// simple split by comma (doesn't handle nested arrays/funcs) — fine for workflow math
|
|
125
|
+
const args = argsRaw.split(',').map(s => s.trim()).map(s => {
|
|
126
|
+
// if it's a quoted string
|
|
127
|
+
if (/^".*"$/.test(s) || /^'.*'$/.test(s)) return s.slice(1, -1);
|
|
128
|
+
// try number
|
|
129
|
+
if (!isNaN(s)) return parseFloat(s);
|
|
130
|
+
// variable lookup
|
|
131
|
+
const lookup = s.replace(/^\{|\}$/g, '').trim();
|
|
132
|
+
return this.getNested(this.context, lookup);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (this.mathFunctions[fn]) {
|
|
136
|
+
const value = this.mathFunctions[fn](...args);
|
|
137
|
+
if (step.saveAs) this.context[step.saveAs] = value;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// fallback to agent resolver
|
|
115
143
|
const res = await agentResolver(action, this.context);
|
|
116
144
|
if (step.saveAs) this.context[step.saveAs] = res;
|
|
117
145
|
break;
|