exprify 1.0.4 → 1.0.7

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/bin/cli.mjs ADDED
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from 'node:fs';
4
+ import { createInterface } from 'node:readline';
5
+ import { stdin, stdout, stderr, exit, argv, version } from 'node:process';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { dirname, resolve } from 'node:path';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
11
+ let pkg;
12
+ try {
13
+ pkg = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf-8'));
14
+ } catch {
15
+ pkg = { version: 'unknown' };
16
+ }
17
+
18
+ let Exprify;
19
+ try {
20
+ const mod = await import('../src/core/exprify.js');
21
+ Exprify = mod.default || mod.Exprify;
22
+ } catch {
23
+ stderr.write('Error: Could not load Exprify module\n');
24
+ exit(1);
25
+ }
26
+
27
+ const expr = new Exprify();
28
+
29
+ const USAGE = `Usage: exprify [options] [expression...]
30
+
31
+ Options:
32
+ --help Show this help message
33
+ --version Show version number
34
+ --parse <expr> Parse expression and show token/AST structure
35
+ --tokens <expr> Tokenize expression and show tokens
36
+
37
+ If no expression is provided and stdin is a TTY, starts interactive REPL.
38
+ If stdin is piped, reads expression from stdin.
39
+
40
+ Examples:
41
+ exprify "2 + 2"
42
+ exprify "sqrt(16)" "5 * 3"
43
+ exprify --parse "x ^ 2 + 2 * x + 1"
44
+ echo "2 + 2" | exprify
45
+ `;
46
+
47
+ const COLORS = {
48
+ reset: '\x1b[0m',
49
+ red: '\x1b[31m',
50
+ green: '\x1b[32m',
51
+ yellow: '\x1b[33m',
52
+ cyan: '\x1b[36m',
53
+ bold: '\x1b[1m',
54
+ dim: '\x1b[2m',
55
+ };
56
+
57
+ function formatResult(value) {
58
+ if (value === null) return 'null';
59
+ if (value === undefined) return 'undefined';
60
+ if (typeof value === 'object' || Array.isArray(value)) {
61
+ return JSON.stringify(value, null, 2);
62
+ }
63
+ return String(value);
64
+ }
65
+
66
+ function printError(msg) {
67
+ stderr.write(COLORS.red + 'Error: ' + COLORS.reset + msg + '\n');
68
+ }
69
+
70
+ function evaluateAndPrint(expression, mode) {
71
+ try {
72
+ if (mode === 'parse') {
73
+ const result = expr.parse(expression);
74
+ console.log(JSON.stringify(result, null, 2));
75
+ } else if (mode === 'tokens') {
76
+ const result = expr.tokenize(expression);
77
+ console.log(JSON.stringify(result, null, 2));
78
+ } else {
79
+ const result = expr.evaluate(expression);
80
+ console.log(formatResult(result));
81
+ }
82
+ } catch (err) {
83
+ printError(err.message);
84
+ exit(1);
85
+ }
86
+ }
87
+
88
+ const args = argv.slice(2);
89
+
90
+ if (args.length === 0) {
91
+ if (stdin.isTTY) {
92
+ startREPL();
93
+ } else {
94
+ let input = '';
95
+ stdin.setEncoding('utf-8');
96
+ stdin.on('data', (chunk) => {
97
+ input += chunk;
98
+ });
99
+ stdin.on('end', () => {
100
+ const exprStr = input.trim();
101
+ if (exprStr) evaluateAndPrint(exprStr);
102
+ });
103
+ }
104
+ exit(0);
105
+ }
106
+
107
+ const flag = args[0];
108
+
109
+ if (flag === '--help' || flag === '-h') {
110
+ console.log(USAGE);
111
+ exit(0);
112
+ }
113
+
114
+ if (flag === '--version' || flag === '-v') {
115
+ console.log(pkg.version);
116
+ exit(0);
117
+ }
118
+
119
+ if (flag === '--parse' || flag === '--tokens') {
120
+ if (args.length < 2) {
121
+ printError('Missing expression argument');
122
+ console.log(USAGE);
123
+ exit(2);
124
+ }
125
+ const mode = flag.slice(2);
126
+ for (let i = 1; i < args.length; i++) {
127
+ evaluateAndPrint(args[i], mode);
128
+ }
129
+ exit(0);
130
+ }
131
+
132
+ for (const arg of args) {
133
+ evaluateAndPrint(arg);
134
+ }
135
+
136
+ function startREPL() {
137
+ console.log(`Exprify v${pkg.version} - interactive REPL`);
138
+ console.log('Type an expression or .help for commands\n');
139
+
140
+ const rl = createInterface({
141
+ input: stdin,
142
+ output: stdout,
143
+ prompt: COLORS.cyan + '» ' + COLORS.reset,
144
+ completer: (line) => {
145
+ const completions = [
146
+ 'help',
147
+ '.help',
148
+ '.exit',
149
+ 'pi',
150
+ 'e',
151
+ 'i',
152
+ 'PHI',
153
+ 'TAU',
154
+ 'INFINITY',
155
+ 'NaN',
156
+ 'sin',
157
+ 'cos',
158
+ 'tan',
159
+ 'sqrt',
160
+ 'abs',
161
+ 'log',
162
+ 'exp',
163
+ 'map',
164
+ 'filter',
165
+ 'sum',
166
+ 'prod',
167
+ 'mean',
168
+ 'max',
169
+ 'min',
170
+ 'if',
171
+ 'parse',
172
+ 'leafCount',
173
+ 'random',
174
+ 'simplify',
175
+ 'expand',
176
+ 'factor',
177
+ 'solve',
178
+ 'derivative',
179
+ 'integral',
180
+ 'sigma',
181
+ 'limit',
182
+ 'substitute',
183
+ 'det',
184
+ 'transpose',
185
+ 'inverse',
186
+ 'trace',
187
+ 'rank',
188
+ ];
189
+ const hits = completions.filter((c) => c.startsWith(line));
190
+ return [hits.length ? hits : completions, line];
191
+ },
192
+ });
193
+
194
+ rl.on('line', (line) => {
195
+ const input = line.trim();
196
+
197
+ if (!input) {
198
+ rl.prompt();
199
+ return;
200
+ }
201
+
202
+ if (input === '.exit' || input === 'exit' || input === 'quit') {
203
+ rl.close();
204
+ return;
205
+ }
206
+
207
+ if (input === '.help' || input === 'help') {
208
+ console.log(`\n ${COLORS.bold}Commands:${COLORS.reset}`);
209
+ console.log(' .exit Exit the REPL');
210
+ console.log(' .help Show this message');
211
+ console.log(' <expr> Evaluate an expression');
212
+ console.log(' Ctrl+C Cancel / exit');
213
+ console.log('');
214
+ rl.prompt();
215
+ return;
216
+ }
217
+
218
+ try {
219
+ const result = expr.evaluate(input);
220
+ console.log(COLORS.green + formatResult(result) + COLORS.reset);
221
+ } catch (err) {
222
+ console.log(COLORS.red + 'Error: ' + COLORS.reset + err.message);
223
+ }
224
+
225
+ rl.prompt();
226
+ });
227
+
228
+ rl.on('close', () => {
229
+ console.log('');
230
+ exit(0);
231
+ });
232
+
233
+ rl.prompt();
234
+ }