@creationix/rex 0.3.1 → 0.4.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/package.json +1 -1
- package/rex-cli.js +615 -269
- package/rex-cli.ts +32 -11
- package/rex-repl.js +582 -256
- package/rex-repl.ts +4 -3
- package/rex.js +310 -180
- package/rex.ohm +50 -22
- package/rex.ohm-bundle.cjs +1 -1
- package/rex.ohm-bundle.d.ts +26 -8
- package/rex.ohm-bundle.js +1 -1
- package/rex.ts +342 -201
- package/rexc-interpreter.ts +262 -85
package/rex-cli.ts
CHANGED
|
@@ -3,9 +3,10 @@ import { evaluateSource } from "./rexc-interpreter.ts";
|
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
|
|
6
|
+
type SourceSegment = { type: "expr"; value: string } | { type: "file"; path: string };
|
|
7
|
+
|
|
6
8
|
type CliOptions = {
|
|
7
|
-
|
|
8
|
-
file?: string;
|
|
9
|
+
sources: SourceSegment[];
|
|
9
10
|
out?: string;
|
|
10
11
|
compile: boolean;
|
|
11
12
|
ir: boolean;
|
|
@@ -17,6 +18,7 @@ type CliOptions = {
|
|
|
17
18
|
|
|
18
19
|
function parseArgs(argv: string[]): CliOptions {
|
|
19
20
|
const options: CliOptions = {
|
|
21
|
+
sources: [],
|
|
20
22
|
compile: false,
|
|
21
23
|
ir: false,
|
|
22
24
|
minifyNames: false,
|
|
@@ -58,14 +60,14 @@ function parseArgs(argv: string[]): CliOptions {
|
|
|
58
60
|
if (arg === "--expr" || arg === "-e") {
|
|
59
61
|
const value = argv[index + 1];
|
|
60
62
|
if (!value) throw new Error("Missing value for --expr");
|
|
61
|
-
options.expr
|
|
63
|
+
options.sources.push({ type: "expr", value });
|
|
62
64
|
index += 1;
|
|
63
65
|
continue;
|
|
64
66
|
}
|
|
65
67
|
if (arg === "--file" || arg === "-f") {
|
|
66
68
|
const value = argv[index + 1];
|
|
67
69
|
if (!value) throw new Error("Missing value for --file");
|
|
68
|
-
options.file
|
|
70
|
+
options.sources.push({ type: "file", path: value });
|
|
69
71
|
index += 1;
|
|
70
72
|
continue;
|
|
71
73
|
}
|
|
@@ -78,8 +80,7 @@ function parseArgs(argv: string[]): CliOptions {
|
|
|
78
80
|
}
|
|
79
81
|
// Positional argument = file path
|
|
80
82
|
if (!arg.startsWith("-")) {
|
|
81
|
-
|
|
82
|
-
options.file = arg;
|
|
83
|
+
options.sources.push({ type: "file", path: arg });
|
|
83
84
|
continue;
|
|
84
85
|
}
|
|
85
86
|
throw new Error(`Unknown option: ${arg}`);
|
|
@@ -98,11 +99,18 @@ function usage() {
|
|
|
98
99
|
" cat input.rex | rex Evaluate from stdin",
|
|
99
100
|
" rex -c input.rex Compile to rexc bytecode",
|
|
100
101
|
"",
|
|
102
|
+
" Sources are concatenated in order, so flags and files can be mixed:",
|
|
103
|
+
" rex -e 'max = 200' primes.rex Set max before running script",
|
|
104
|
+
" rex primes.rex -e '42' Run script, then evaluate 42",
|
|
105
|
+
"",
|
|
101
106
|
"Input:",
|
|
102
107
|
" <file> Evaluate/compile a Rex source file",
|
|
103
108
|
" -e, --expr <source> Evaluate/compile an inline expression",
|
|
104
109
|
" -f, --file <path> Evaluate/compile source from a file",
|
|
105
110
|
"",
|
|
111
|
+
" Multiple -e and -f flags (and positional files) can be combined.",
|
|
112
|
+
" They are concatenated in the order they appear on the command line.",
|
|
113
|
+
"",
|
|
106
114
|
"Output mode:",
|
|
107
115
|
" (default) Evaluate and output result as JSON",
|
|
108
116
|
" -c, --compile Compile to rexc bytecode",
|
|
@@ -128,9 +136,14 @@ async function readStdin(): Promise<string> {
|
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
async function resolveSource(options: CliOptions): Promise<string> {
|
|
131
|
-
if (options.
|
|
132
|
-
|
|
133
|
-
|
|
139
|
+
if (options.sources.length > 0) {
|
|
140
|
+
const parts: string[] = [];
|
|
141
|
+
for (const seg of options.sources) {
|
|
142
|
+
if (seg.type === "expr") parts.push(seg.value);
|
|
143
|
+
else parts.push(await readFile(seg.path, "utf8"));
|
|
144
|
+
}
|
|
145
|
+
return parts.join("\n");
|
|
146
|
+
}
|
|
134
147
|
if (!process.stdin.isTTY) {
|
|
135
148
|
const piped = await readStdin();
|
|
136
149
|
if (piped.trim().length > 0) return piped;
|
|
@@ -138,6 +151,13 @@ async function resolveSource(options: CliOptions): Promise<string> {
|
|
|
138
151
|
throw new Error("No input provided. Use a file path, --expr, or pipe source via stdin.");
|
|
139
152
|
}
|
|
140
153
|
|
|
154
|
+
function findFirstFilePath(sources: SourceSegment[]): string | undefined {
|
|
155
|
+
for (const seg of sources) {
|
|
156
|
+
if (seg.type === "file") return seg.path;
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
|
|
141
161
|
async function loadDomainConfigFromFolder(folderPath: string): Promise<unknown | undefined> {
|
|
142
162
|
const configPath = resolve(folderPath, ".config.rex");
|
|
143
163
|
try {
|
|
@@ -149,7 +169,8 @@ async function loadDomainConfigFromFolder(folderPath: string): Promise<unknown |
|
|
|
149
169
|
}
|
|
150
170
|
|
|
151
171
|
async function resolveDomainConfig(options: CliOptions): Promise<unknown | undefined> {
|
|
152
|
-
const
|
|
172
|
+
const filePath = findFirstFilePath(options.sources);
|
|
173
|
+
const baseFolder = filePath ? dirname(resolve(filePath)) : process.cwd();
|
|
153
174
|
return loadDomainConfigFromFolder(baseFolder);
|
|
154
175
|
}
|
|
155
176
|
|
|
@@ -161,7 +182,7 @@ async function main() {
|
|
|
161
182
|
}
|
|
162
183
|
|
|
163
184
|
// No source provided on a TTY → launch interactive REPL
|
|
164
|
-
const hasSource = options.
|
|
185
|
+
const hasSource = options.sources.length > 0 || !process.stdin.isTTY;
|
|
165
186
|
if (!hasSource && !options.compile && !options.ir) {
|
|
166
187
|
const { startRepl } = await import("./rex-repl.ts");
|
|
167
188
|
await startRepl();
|