dirac-lang 0.1.33 → 0.1.35
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/chunk-DRPHX3WX.js +166 -0
- package/dist/cli.js +45 -166
- package/dist/shell-6ANKVL77.js +303 -0
- package/package.json +1 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// src/runtime/braket-parser.ts
|
|
2
|
+
var BraKetParser = class {
|
|
3
|
+
lines = [];
|
|
4
|
+
currentLine = 0;
|
|
5
|
+
/**
|
|
6
|
+
* Parse bra-ket notation and compile to XML
|
|
7
|
+
*/
|
|
8
|
+
parse(source) {
|
|
9
|
+
this.lines = source.split("\n");
|
|
10
|
+
this.currentLine = 0;
|
|
11
|
+
const xml = ["<dirac>"];
|
|
12
|
+
this.parseBlock(xml, -1);
|
|
13
|
+
xml.push("</dirac>");
|
|
14
|
+
return xml.join("\n");
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse a block of lines at a given indentation level
|
|
18
|
+
*/
|
|
19
|
+
parseBlock(output, parentIndent) {
|
|
20
|
+
while (this.currentLine < this.lines.length) {
|
|
21
|
+
const line = this.parseLine(this.lines[this.currentLine]);
|
|
22
|
+
if (line.type === "empty") {
|
|
23
|
+
this.currentLine++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (line.indent <= parentIndent) {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
if (line.type === "bra") {
|
|
30
|
+
const attrs = line.attrs ? ` ${this.convertAttributes(line.attrs)}` : "";
|
|
31
|
+
output.push(`${" ".repeat(line.indent)}<subroutine name="${line.tag}"${attrs}>`);
|
|
32
|
+
this.currentLine++;
|
|
33
|
+
this.parseBlock(output, line.indent);
|
|
34
|
+
output.push(`${" ".repeat(line.indent)}</subroutine>`);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (line.type === "ket") {
|
|
38
|
+
const indent = " ".repeat(line.indent);
|
|
39
|
+
const attrs = line.attrs ? ` ${this.convertAttributes(line.attrs)}` : "";
|
|
40
|
+
const nextLine = this.currentLine + 1 < this.lines.length ? this.parseLine(this.lines[this.currentLine + 1]) : null;
|
|
41
|
+
if (nextLine && nextLine.indent > line.indent && nextLine.type !== "empty") {
|
|
42
|
+
output.push(`${indent}<${line.tag}${attrs}>`);
|
|
43
|
+
this.currentLine++;
|
|
44
|
+
this.parseBlock(output, line.indent);
|
|
45
|
+
output.push(`${indent}</${line.tag}>`);
|
|
46
|
+
} else {
|
|
47
|
+
if (line.text) {
|
|
48
|
+
const content = this.convertInlineKets(line.text);
|
|
49
|
+
output.push(`${indent}<${line.tag}${attrs}>${content}</${line.tag}>`);
|
|
50
|
+
} else {
|
|
51
|
+
output.push(`${indent}<${line.tag}${attrs}/>`);
|
|
52
|
+
}
|
|
53
|
+
this.currentLine++;
|
|
54
|
+
}
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (line.type === "text") {
|
|
58
|
+
const indent = " ".repeat(line.indent);
|
|
59
|
+
const content = this.convertInlineKets(line.text || "");
|
|
60
|
+
output.push(`${indent}${content}`);
|
|
61
|
+
this.currentLine++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse a single line into structured form
|
|
68
|
+
*/
|
|
69
|
+
parseLine(raw) {
|
|
70
|
+
const match = raw.match(/^(\s*)(.*)/);
|
|
71
|
+
const indent = match ? Math.floor(match[1].length / 2) : 0;
|
|
72
|
+
const content = match ? match[2] : "";
|
|
73
|
+
if (!content.trim()) {
|
|
74
|
+
return { indent, type: "empty", raw };
|
|
75
|
+
}
|
|
76
|
+
const braMatch = content.match(/^<([a-zA-Z_][a-zA-Z0-9_-]*)\s*([^|]*)\|$/);
|
|
77
|
+
if (braMatch) {
|
|
78
|
+
return {
|
|
79
|
+
indent,
|
|
80
|
+
type: "bra",
|
|
81
|
+
tag: braMatch[1],
|
|
82
|
+
attrs: braMatch[2].trim() || void 0,
|
|
83
|
+
raw
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const ketMatch = content.match(/^\|([a-zA-Z_][a-zA-Z0-9_-]*)\s*([^>]*?)>\s*(.*)/);
|
|
87
|
+
if (ketMatch) {
|
|
88
|
+
return {
|
|
89
|
+
indent,
|
|
90
|
+
type: "ket",
|
|
91
|
+
tag: ketMatch[1],
|
|
92
|
+
attrs: ketMatch[2].trim() || void 0,
|
|
93
|
+
text: ketMatch[3] || void 0,
|
|
94
|
+
raw
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
indent,
|
|
99
|
+
type: "text",
|
|
100
|
+
text: content,
|
|
101
|
+
raw
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Convert bra-ket attribute syntax to XML
|
|
106
|
+
* Examples:
|
|
107
|
+
* name=value → name="value"
|
|
108
|
+
* x=5 y=10 → x="5" y="10"
|
|
109
|
+
* select=@* → select="@*"
|
|
110
|
+
*/
|
|
111
|
+
convertAttributes(attrs) {
|
|
112
|
+
if (!attrs) return "";
|
|
113
|
+
const parts = [];
|
|
114
|
+
let current = "";
|
|
115
|
+
let inQuotes = false;
|
|
116
|
+
let quoteChar = "";
|
|
117
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
118
|
+
const char = attrs[i];
|
|
119
|
+
if ((char === '"' || char === "'") && (i === 0 || attrs[i - 1] !== "\\")) {
|
|
120
|
+
if (!inQuotes) {
|
|
121
|
+
inQuotes = true;
|
|
122
|
+
quoteChar = char;
|
|
123
|
+
current += char;
|
|
124
|
+
} else if (char === quoteChar) {
|
|
125
|
+
inQuotes = false;
|
|
126
|
+
current += char;
|
|
127
|
+
} else {
|
|
128
|
+
current += char;
|
|
129
|
+
}
|
|
130
|
+
} else if (char === " " && !inQuotes) {
|
|
131
|
+
if (current.trim()) {
|
|
132
|
+
parts.push(current.trim());
|
|
133
|
+
current = "";
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
current += char;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (current.trim()) {
|
|
140
|
+
parts.push(current.trim());
|
|
141
|
+
}
|
|
142
|
+
return parts.map((part) => {
|
|
143
|
+
const match = part.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)=(.+)$/);
|
|
144
|
+
if (!match) return part;
|
|
145
|
+
const [, name, value] = match;
|
|
146
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
147
|
+
return `${name}=${value}`;
|
|
148
|
+
}
|
|
149
|
+
return `${name}="${value}"`;
|
|
150
|
+
}).join(" ");
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Convert inline kets within text content
|
|
154
|
+
* Example: "Hello |variable name=x> world" → "Hello <variable name="x"/> world"
|
|
155
|
+
*/
|
|
156
|
+
convertInlineKets(text) {
|
|
157
|
+
return text.replace(/\|([a-zA-Z_][a-zA-Z0-9_-]*)\s*([^>]*?)>/g, (match, tag, attrs) => {
|
|
158
|
+
const attrStr = attrs.trim() ? ` ${this.convertAttributes(attrs.trim())}` : "";
|
|
159
|
+
return `<${tag}${attrStr}/>`;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export {
|
|
165
|
+
BraKetParser
|
|
166
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
BraKetParser
|
|
4
|
+
} from "./chunk-DRPHX3WX.js";
|
|
2
5
|
import {
|
|
3
6
|
execute
|
|
4
7
|
} from "./chunk-4LLFMVOW.js";
|
|
@@ -13,7 +16,7 @@ import "dotenv/config";
|
|
|
13
16
|
// package.json
|
|
14
17
|
var package_default = {
|
|
15
18
|
name: "dirac-lang",
|
|
16
|
-
version: "0.1.
|
|
19
|
+
version: "0.1.35",
|
|
17
20
|
description: "LLM-Augmented Declarative Execution",
|
|
18
21
|
type: "module",
|
|
19
22
|
main: "dist/index.js",
|
|
@@ -63,175 +66,14 @@ var package_default = {
|
|
|
63
66
|
import fs from "fs";
|
|
64
67
|
import yaml from "js-yaml";
|
|
65
68
|
import { resolve, extname } from "path";
|
|
66
|
-
|
|
67
|
-
// src/runtime/braket-parser.ts
|
|
68
|
-
var BraKetParser = class {
|
|
69
|
-
lines = [];
|
|
70
|
-
currentLine = 0;
|
|
71
|
-
/**
|
|
72
|
-
* Parse bra-ket notation and compile to XML
|
|
73
|
-
*/
|
|
74
|
-
parse(source) {
|
|
75
|
-
this.lines = source.split("\n");
|
|
76
|
-
this.currentLine = 0;
|
|
77
|
-
const xml = ["<dirac>"];
|
|
78
|
-
this.parseBlock(xml, -1);
|
|
79
|
-
xml.push("</dirac>");
|
|
80
|
-
return xml.join("\n");
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Parse a block of lines at a given indentation level
|
|
84
|
-
*/
|
|
85
|
-
parseBlock(output, parentIndent) {
|
|
86
|
-
while (this.currentLine < this.lines.length) {
|
|
87
|
-
const line = this.parseLine(this.lines[this.currentLine]);
|
|
88
|
-
if (line.type === "empty") {
|
|
89
|
-
this.currentLine++;
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (line.indent <= parentIndent) {
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
if (line.type === "bra") {
|
|
96
|
-
const attrs = line.attrs ? ` ${this.convertAttributes(line.attrs)}` : "";
|
|
97
|
-
output.push(`${" ".repeat(line.indent)}<subroutine name="${line.tag}"${attrs}>`);
|
|
98
|
-
this.currentLine++;
|
|
99
|
-
this.parseBlock(output, line.indent);
|
|
100
|
-
output.push(`${" ".repeat(line.indent)}</subroutine>`);
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
if (line.type === "ket") {
|
|
104
|
-
const indent = " ".repeat(line.indent);
|
|
105
|
-
const attrs = line.attrs ? ` ${this.convertAttributes(line.attrs)}` : "";
|
|
106
|
-
const nextLine = this.currentLine + 1 < this.lines.length ? this.parseLine(this.lines[this.currentLine + 1]) : null;
|
|
107
|
-
if (nextLine && nextLine.indent > line.indent && nextLine.type !== "empty") {
|
|
108
|
-
output.push(`${indent}<${line.tag}${attrs}>`);
|
|
109
|
-
this.currentLine++;
|
|
110
|
-
this.parseBlock(output, line.indent);
|
|
111
|
-
output.push(`${indent}</${line.tag}>`);
|
|
112
|
-
} else {
|
|
113
|
-
if (line.text) {
|
|
114
|
-
const content = this.convertInlineKets(line.text);
|
|
115
|
-
output.push(`${indent}<${line.tag}${attrs}>${content}</${line.tag}>`);
|
|
116
|
-
} else {
|
|
117
|
-
output.push(`${indent}<${line.tag}${attrs}/>`);
|
|
118
|
-
}
|
|
119
|
-
this.currentLine++;
|
|
120
|
-
}
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
if (line.type === "text") {
|
|
124
|
-
const indent = " ".repeat(line.indent);
|
|
125
|
-
const content = this.convertInlineKets(line.text || "");
|
|
126
|
-
output.push(`${indent}${content}`);
|
|
127
|
-
this.currentLine++;
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Parse a single line into structured form
|
|
134
|
-
*/
|
|
135
|
-
parseLine(raw) {
|
|
136
|
-
const match = raw.match(/^(\s*)(.*)/);
|
|
137
|
-
const indent = match ? Math.floor(match[1].length / 2) : 0;
|
|
138
|
-
const content = match ? match[2] : "";
|
|
139
|
-
if (!content.trim()) {
|
|
140
|
-
return { indent, type: "empty", raw };
|
|
141
|
-
}
|
|
142
|
-
const braMatch = content.match(/^<([a-zA-Z_][a-zA-Z0-9_-]*)\s*([^|]*)\|$/);
|
|
143
|
-
if (braMatch) {
|
|
144
|
-
return {
|
|
145
|
-
indent,
|
|
146
|
-
type: "bra",
|
|
147
|
-
tag: braMatch[1],
|
|
148
|
-
attrs: braMatch[2].trim() || void 0,
|
|
149
|
-
raw
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
const ketMatch = content.match(/^\|([a-zA-Z_][a-zA-Z0-9_-]*)\s*([^>]*?)>\s*(.*)/);
|
|
153
|
-
if (ketMatch) {
|
|
154
|
-
return {
|
|
155
|
-
indent,
|
|
156
|
-
type: "ket",
|
|
157
|
-
tag: ketMatch[1],
|
|
158
|
-
attrs: ketMatch[2].trim() || void 0,
|
|
159
|
-
text: ketMatch[3] || void 0,
|
|
160
|
-
raw
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
return {
|
|
164
|
-
indent,
|
|
165
|
-
type: "text",
|
|
166
|
-
text: content,
|
|
167
|
-
raw
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Convert bra-ket attribute syntax to XML
|
|
172
|
-
* Examples:
|
|
173
|
-
* name=value → name="value"
|
|
174
|
-
* x=5 y=10 → x="5" y="10"
|
|
175
|
-
* select=@* → select="@*"
|
|
176
|
-
*/
|
|
177
|
-
convertAttributes(attrs) {
|
|
178
|
-
if (!attrs) return "";
|
|
179
|
-
const parts = [];
|
|
180
|
-
let current = "";
|
|
181
|
-
let inQuotes = false;
|
|
182
|
-
let quoteChar = "";
|
|
183
|
-
for (let i = 0; i < attrs.length; i++) {
|
|
184
|
-
const char = attrs[i];
|
|
185
|
-
if ((char === '"' || char === "'") && (i === 0 || attrs[i - 1] !== "\\")) {
|
|
186
|
-
if (!inQuotes) {
|
|
187
|
-
inQuotes = true;
|
|
188
|
-
quoteChar = char;
|
|
189
|
-
current += char;
|
|
190
|
-
} else if (char === quoteChar) {
|
|
191
|
-
inQuotes = false;
|
|
192
|
-
current += char;
|
|
193
|
-
} else {
|
|
194
|
-
current += char;
|
|
195
|
-
}
|
|
196
|
-
} else if (char === " " && !inQuotes) {
|
|
197
|
-
if (current.trim()) {
|
|
198
|
-
parts.push(current.trim());
|
|
199
|
-
current = "";
|
|
200
|
-
}
|
|
201
|
-
} else {
|
|
202
|
-
current += char;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
if (current.trim()) {
|
|
206
|
-
parts.push(current.trim());
|
|
207
|
-
}
|
|
208
|
-
return parts.map((part) => {
|
|
209
|
-
const match = part.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)=(.+)$/);
|
|
210
|
-
if (!match) return part;
|
|
211
|
-
const [, name, value] = match;
|
|
212
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
213
|
-
return `${name}=${value}`;
|
|
214
|
-
}
|
|
215
|
-
return `${name}="${value}"`;
|
|
216
|
-
}).join(" ");
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Convert inline kets within text content
|
|
220
|
-
* Example: "Hello |variable name=x> world" → "Hello <variable name="x"/> world"
|
|
221
|
-
*/
|
|
222
|
-
convertInlineKets(text) {
|
|
223
|
-
return text.replace(/\|([a-zA-Z_][a-zA-Z0-9_-]*)\s*([^>]*?)>/g, (match, tag, attrs) => {
|
|
224
|
-
const attrStr = attrs.trim() ? ` ${this.convertAttributes(attrs.trim())}` : "";
|
|
225
|
-
return `<${tag}${attrStr}/>`;
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
// src/cli.ts
|
|
231
69
|
async function main() {
|
|
232
70
|
const args = process.argv.slice(2);
|
|
233
71
|
if (args.includes("--help") || args.includes("-h")) {
|
|
234
72
|
console.log("Usage: dirac <file.di|file.bk>");
|
|
73
|
+
console.log(" dirac shell [options]");
|
|
74
|
+
console.log("");
|
|
75
|
+
console.log("Commands:");
|
|
76
|
+
console.log(" shell Start interactive shell (REPL)");
|
|
235
77
|
console.log("");
|
|
236
78
|
console.log("File formats:");
|
|
237
79
|
console.log(" .di XML notation (verbose)");
|
|
@@ -245,14 +87,51 @@ async function main() {
|
|
|
245
87
|
console.log(" --model <name> Set default LLM model");
|
|
246
88
|
console.log(" --max-llm <n> Maximum LLM calls (default: 100)");
|
|
247
89
|
console.log(" --max-depth <n> Maximum recursion depth (default: 50)");
|
|
90
|
+
console.log(" -f, --config <path> Path to config.yml file");
|
|
248
91
|
process.exit(0);
|
|
249
92
|
}
|
|
250
93
|
if (args.includes("--version") || args.includes("-v")) {
|
|
251
94
|
console.log(package_default.version);
|
|
252
95
|
process.exit(0);
|
|
253
96
|
}
|
|
97
|
+
if (args[0] === "shell") {
|
|
98
|
+
const { DiracShell } = await import("./shell-6ANKVL77.js");
|
|
99
|
+
const shellConfig = { debug: false };
|
|
100
|
+
for (let i = 1; i < args.length; i++) {
|
|
101
|
+
const arg = args[i];
|
|
102
|
+
if (arg === "--debug") {
|
|
103
|
+
shellConfig.debug = true;
|
|
104
|
+
} else if ((arg === "-f" || arg === "--config") && i + 1 < args.length) {
|
|
105
|
+
const configPath = resolve(args[++i]);
|
|
106
|
+
if (fs.existsSync(configPath)) {
|
|
107
|
+
const configData = yaml.load(fs.readFileSync(configPath, "utf-8"));
|
|
108
|
+
Object.assign(shellConfig, {
|
|
109
|
+
llmProvider: configData.llmProvider,
|
|
110
|
+
llmModel: configData.llmModel,
|
|
111
|
+
customLLMUrl: configData.customLLMUrl
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!shellConfig.llmProvider) {
|
|
117
|
+
const defaultConfigPath = resolve(process.cwd(), "config.yml");
|
|
118
|
+
if (fs.existsSync(defaultConfigPath)) {
|
|
119
|
+
try {
|
|
120
|
+
const configData = yaml.load(fs.readFileSync(defaultConfigPath, "utf-8"));
|
|
121
|
+
shellConfig.llmProvider = shellConfig.llmProvider || configData.llmProvider;
|
|
122
|
+
shellConfig.llmModel = shellConfig.llmModel || configData.llmModel;
|
|
123
|
+
shellConfig.customLLMUrl = shellConfig.customLLMUrl || configData.customLLMUrl;
|
|
124
|
+
} catch (err) {
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const shell = new DiracShell(shellConfig);
|
|
129
|
+
shell.start();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
254
132
|
if (args.length === 0) {
|
|
255
133
|
console.error("Usage: dirac <file.di|file.bk>");
|
|
134
|
+
console.error(" dirac shell [options]");
|
|
256
135
|
console.error("Try dirac --help for more information.");
|
|
257
136
|
process.exit(1);
|
|
258
137
|
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
BraKetParser
|
|
4
|
+
} from "./chunk-DRPHX3WX.js";
|
|
5
|
+
import {
|
|
6
|
+
integrate
|
|
7
|
+
} from "./chunk-HPGONBNW.js";
|
|
8
|
+
import {
|
|
9
|
+
DiracParser
|
|
10
|
+
} from "./chunk-HRHAMPOB.js";
|
|
11
|
+
import "./chunk-4QLTSCDG.js";
|
|
12
|
+
import {
|
|
13
|
+
createSession
|
|
14
|
+
} from "./chunk-GLXVY235.js";
|
|
15
|
+
|
|
16
|
+
// src/shell.ts
|
|
17
|
+
import * as readline from "readline";
|
|
18
|
+
import * as fs from "fs";
|
|
19
|
+
import * as path from "path";
|
|
20
|
+
import * as os from "os";
|
|
21
|
+
import yaml from "js-yaml";
|
|
22
|
+
var HISTORY_FILE = path.join(os.homedir(), ".dirac_history");
|
|
23
|
+
var MAX_HISTORY = 1e3;
|
|
24
|
+
var DiracShell = class {
|
|
25
|
+
session;
|
|
26
|
+
braketParser;
|
|
27
|
+
xmlParser;
|
|
28
|
+
rl;
|
|
29
|
+
inputBuffer = [];
|
|
30
|
+
baseIndent = null;
|
|
31
|
+
currentIndent = 0;
|
|
32
|
+
config;
|
|
33
|
+
constructor(config = {}) {
|
|
34
|
+
this.config = config;
|
|
35
|
+
this.session = createSession(config);
|
|
36
|
+
this.braketParser = new BraKetParser();
|
|
37
|
+
this.xmlParser = new DiracParser();
|
|
38
|
+
this.rl = readline.createInterface({
|
|
39
|
+
input: process.stdin,
|
|
40
|
+
output: process.stdout,
|
|
41
|
+
prompt: "> ",
|
|
42
|
+
historySize: MAX_HISTORY
|
|
43
|
+
});
|
|
44
|
+
this.loadHistory();
|
|
45
|
+
this.setupHandlers();
|
|
46
|
+
}
|
|
47
|
+
loadHistory() {
|
|
48
|
+
try {
|
|
49
|
+
if (fs.existsSync(HISTORY_FILE)) {
|
|
50
|
+
const history = fs.readFileSync(HISTORY_FILE, "utf-8").split("\n").filter((line) => line.trim()).slice(-MAX_HISTORY);
|
|
51
|
+
this.rl.history = history.reverse();
|
|
52
|
+
}
|
|
53
|
+
} catch (err) {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
saveHistory() {
|
|
57
|
+
try {
|
|
58
|
+
const history = this.rl.history.slice().reverse().join("\n");
|
|
59
|
+
fs.writeFileSync(HISTORY_FILE, history, "utf-8");
|
|
60
|
+
} catch (err) {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
setupHandlers() {
|
|
64
|
+
this.rl.on("line", async (input) => {
|
|
65
|
+
await this.handleInput(input);
|
|
66
|
+
});
|
|
67
|
+
this.rl.on("close", () => {
|
|
68
|
+
this.saveHistory();
|
|
69
|
+
console.log("\nGoodbye!");
|
|
70
|
+
process.exit(0);
|
|
71
|
+
});
|
|
72
|
+
this.rl.on("SIGINT", () => {
|
|
73
|
+
if (this.inputBuffer.length > 0) {
|
|
74
|
+
this.inputBuffer = [];
|
|
75
|
+
this.baseIndent = null;
|
|
76
|
+
this.currentIndent = 0;
|
|
77
|
+
console.log("\n(Input cancelled)");
|
|
78
|
+
this.rl.setPrompt("> ");
|
|
79
|
+
this.rl.prompt();
|
|
80
|
+
} else {
|
|
81
|
+
this.rl.close();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async handleInput(input) {
|
|
86
|
+
if (!this.inputBuffer.length && input.trim().startsWith(":")) {
|
|
87
|
+
await this.handleCommand(input.trim());
|
|
88
|
+
this.rl.prompt();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const indent = this.getIndent(input);
|
|
92
|
+
if (this.inputBuffer.length === 0) {
|
|
93
|
+
this.inputBuffer.push(input);
|
|
94
|
+
this.baseIndent = indent;
|
|
95
|
+
if (this.needsContinuation(input)) {
|
|
96
|
+
this.currentIndent = (this.baseIndent || 0) + 2;
|
|
97
|
+
this.rl.setPrompt("... ");
|
|
98
|
+
console.log(` (Indent with ${this.currentIndent} spaces, or press Enter on empty line to execute)`);
|
|
99
|
+
this.rl.prompt();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
if (input.trim() === "") {
|
|
104
|
+
await this.executeBuffer();
|
|
105
|
+
this.currentIndent = 0;
|
|
106
|
+
this.rl.setPrompt("> ");
|
|
107
|
+
this.rl.prompt();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
let processedInput = input;
|
|
111
|
+
if (this.currentIndent > 0 && indent < this.currentIndent) {
|
|
112
|
+
processedInput = " ".repeat(this.currentIndent) + input;
|
|
113
|
+
}
|
|
114
|
+
this.inputBuffer.push(processedInput);
|
|
115
|
+
const trimmed = input.trim();
|
|
116
|
+
const currentLineIndent = this.getIndent(processedInput);
|
|
117
|
+
if (trimmed.match(/^<[a-zA-Z_][a-zA-Z0-9_-]*.*\|$/) || trimmed.match(/^\|[a-zA-Z_][a-zA-Z0-9_-]*\s*[^>]*?>$/) && !trimmed.match(/>\s*.+$/)) {
|
|
118
|
+
this.currentIndent = currentLineIndent + 2;
|
|
119
|
+
} else {
|
|
120
|
+
this.currentIndent = currentLineIndent;
|
|
121
|
+
}
|
|
122
|
+
this.rl.setPrompt("... ");
|
|
123
|
+
this.rl.prompt();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
await this.executeBuffer();
|
|
127
|
+
this.rl.prompt();
|
|
128
|
+
}
|
|
129
|
+
getIndent(line) {
|
|
130
|
+
const match = line.match(/^(\s*)/);
|
|
131
|
+
return match ? match[1].length : 0;
|
|
132
|
+
}
|
|
133
|
+
needsContinuation(line) {
|
|
134
|
+
const trimmed = line.trim();
|
|
135
|
+
if (trimmed.match(/^<[a-zA-Z_][a-zA-Z0-9_-]*.*\|$/)) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
const ketMatch = trimmed.match(/^\|([a-zA-Z_][a-zA-Z0-9_-]*)\s*([^>]*?)>\s*(.*)$/);
|
|
139
|
+
if (ketMatch && !ketMatch[3]) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
async executeBuffer() {
|
|
145
|
+
if (this.inputBuffer.length === 0) return;
|
|
146
|
+
const input = this.inputBuffer.join("\n");
|
|
147
|
+
this.inputBuffer = [];
|
|
148
|
+
this.baseIndent = null;
|
|
149
|
+
try {
|
|
150
|
+
this.session.output = [];
|
|
151
|
+
const xml = this.braketParser.parse(input);
|
|
152
|
+
if (this.config.debug) {
|
|
153
|
+
console.log("[Debug] Generated XML:\n", xml);
|
|
154
|
+
}
|
|
155
|
+
const ast = this.xmlParser.parse(xml);
|
|
156
|
+
await integrate(this.session, ast);
|
|
157
|
+
if (this.session.output.length > 0) {
|
|
158
|
+
console.log(this.session.output.join(""));
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
162
|
+
if (this.config.debug && error instanceof Error && error.stack) {
|
|
163
|
+
console.error(error.stack);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async handleCommand(cmd) {
|
|
168
|
+
const parts = cmd.slice(1).split(/\s+/);
|
|
169
|
+
const command = parts[0];
|
|
170
|
+
const args = parts.slice(1);
|
|
171
|
+
switch (command) {
|
|
172
|
+
case "help":
|
|
173
|
+
console.log(`
|
|
174
|
+
Dirac Shell - Interactive REPL
|
|
175
|
+
|
|
176
|
+
Commands:
|
|
177
|
+
:help Show this help
|
|
178
|
+
:vars List all variables
|
|
179
|
+
:subs List all subroutines
|
|
180
|
+
:clear Clear session (reset variables and subroutines)
|
|
181
|
+
:debug Toggle debug mode
|
|
182
|
+
:config Show current configuration
|
|
183
|
+
:exit Exit shell
|
|
184
|
+
|
|
185
|
+
Syntax:
|
|
186
|
+
|tag attrs>text Ket notation (most tags)
|
|
187
|
+
<name| Bra notation (subroutine definitions)
|
|
188
|
+
|output>content Indented children
|
|
189
|
+
|
|
190
|
+
Multi-line Input:
|
|
191
|
+
- Type a line that needs continuation (like <greeting| or |llm>)
|
|
192
|
+
- Shell switches to '...' prompt and shows expected indent level
|
|
193
|
+
- You can type spaces manually, or just type content (shell adds spaces)
|
|
194
|
+
- Press ENTER on an empty line to execute
|
|
195
|
+
- Or press Ctrl+C to cancel
|
|
196
|
+
|
|
197
|
+
Examples:
|
|
198
|
+
|output>Hello World
|
|
199
|
+
|defvar name=count value=5>
|
|
200
|
+
|llm>create a greeting subroutine
|
|
201
|
+
<greeting| name=String
|
|
202
|
+
|output>Hello |variable name=name>
|
|
203
|
+
|greeting name=World>
|
|
204
|
+
`);
|
|
205
|
+
break;
|
|
206
|
+
case "vars":
|
|
207
|
+
if (this.session.variables.length === 0) {
|
|
208
|
+
console.log("No variables defined");
|
|
209
|
+
} else {
|
|
210
|
+
console.log("Variables:");
|
|
211
|
+
for (const v of this.session.variables) {
|
|
212
|
+
if (v.visible) {
|
|
213
|
+
console.log(` ${v.name} = ${JSON.stringify(v.value)}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
case "subs":
|
|
219
|
+
if (this.session.subroutines.length === 0) {
|
|
220
|
+
console.log("No subroutines defined");
|
|
221
|
+
} else {
|
|
222
|
+
console.log("Subroutines:");
|
|
223
|
+
for (const s of this.session.subroutines) {
|
|
224
|
+
const params = s.parameters?.map((p) => p.name).join(", ") || "";
|
|
225
|
+
console.log(` ${s.name}(${params})`);
|
|
226
|
+
if (s.description) {
|
|
227
|
+
console.log(` ${s.description}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
case "clear":
|
|
233
|
+
this.session.variables = [];
|
|
234
|
+
this.session.subroutines = [];
|
|
235
|
+
this.session.varBoundary = 0;
|
|
236
|
+
this.session.subBoundary = 0;
|
|
237
|
+
console.log("Session cleared");
|
|
238
|
+
break;
|
|
239
|
+
case "debug":
|
|
240
|
+
this.config.debug = !this.config.debug;
|
|
241
|
+
this.session.debug = this.config.debug;
|
|
242
|
+
console.log(`Debug mode: ${this.config.debug ? "ON" : "OFF"}`);
|
|
243
|
+
break;
|
|
244
|
+
case "config":
|
|
245
|
+
console.log("Configuration:");
|
|
246
|
+
console.log(` LLM Provider: ${this.config.llmProvider || "none"}`);
|
|
247
|
+
console.log(` LLM Model: ${this.config.llmModel || "default"}`);
|
|
248
|
+
console.log(` Debug: ${this.config.debug ? "ON" : "OFF"}`);
|
|
249
|
+
if (this.config.customLLMUrl) {
|
|
250
|
+
console.log(` Custom LLM URL: ${this.config.customLLMUrl}`);
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
case "exit":
|
|
254
|
+
case "quit":
|
|
255
|
+
this.rl.close();
|
|
256
|
+
break;
|
|
257
|
+
default:
|
|
258
|
+
console.log(`Unknown command: ${command}. Type :help for available commands.`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
start() {
|
|
262
|
+
console.log("Dirac Shell v0.1.0");
|
|
263
|
+
console.log("Type :help for commands, :exit to quit\n");
|
|
264
|
+
if (this.config.llmProvider) {
|
|
265
|
+
console.log(`LLM: ${this.config.llmProvider} (${this.config.llmModel || "default"})
|
|
266
|
+
`);
|
|
267
|
+
} else {
|
|
268
|
+
console.log("Warning: No LLM provider configured. Set LLM_PROVIDER environment variable.\n");
|
|
269
|
+
}
|
|
270
|
+
this.rl.prompt();
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
async function main() {
|
|
274
|
+
let config = {
|
|
275
|
+
debug: process.env.DEBUG === "1"
|
|
276
|
+
};
|
|
277
|
+
const configPath = path.join(process.cwd(), "config.yml");
|
|
278
|
+
if (fs.existsSync(configPath)) {
|
|
279
|
+
try {
|
|
280
|
+
const configData = yaml.load(fs.readFileSync(configPath, "utf-8"));
|
|
281
|
+
config = {
|
|
282
|
+
...config,
|
|
283
|
+
llmProvider: configData.llmProvider || process.env.LLM_PROVIDER,
|
|
284
|
+
llmModel: configData.llmModel || process.env.LLM_MODEL,
|
|
285
|
+
customLLMUrl: configData.customLLMUrl || process.env.CUSTOM_LLM_URL
|
|
286
|
+
};
|
|
287
|
+
} catch (err) {
|
|
288
|
+
console.error("Warning: Could not load config.yml");
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
config.llmProvider = process.env.LLM_PROVIDER;
|
|
292
|
+
config.llmModel = process.env.LLM_MODEL;
|
|
293
|
+
config.customLLMUrl = process.env.CUSTOM_LLM_URL;
|
|
294
|
+
}
|
|
295
|
+
const shell = new DiracShell(config);
|
|
296
|
+
shell.start();
|
|
297
|
+
}
|
|
298
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
299
|
+
main();
|
|
300
|
+
}
|
|
301
|
+
export {
|
|
302
|
+
DiracShell
|
|
303
|
+
};
|