lumos-language 1.0.0
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/index.js +153 -0
- package/package.json +13 -0
package/index.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const readline = require("readline");
|
|
4
|
+
|
|
5
|
+
class BreakException {}
|
|
6
|
+
class ContinueException {}
|
|
7
|
+
|
|
8
|
+
const vars = {};
|
|
9
|
+
const functions = {};
|
|
10
|
+
|
|
11
|
+
function evaluateExpression(expr) {
|
|
12
|
+
try {
|
|
13
|
+
return Function(...Object.keys(vars), `return (${expr})`)(...Object.values(vars));
|
|
14
|
+
} catch (e) {
|
|
15
|
+
throw new Error("Invalid expression: " + expr);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function preprocessCommand(command) {
|
|
20
|
+
command = command.replace(/\r\n/g, "\n");
|
|
21
|
+
|
|
22
|
+
const timesRegex = /^(\d+)\.times\s+do\s+\|([a-zA-Z_][a-zA-Z0-9_]*)\|([\s\S]+?)end$/;
|
|
23
|
+
const timesMatch = command.match(timesRegex);
|
|
24
|
+
if (timesMatch) {
|
|
25
|
+
const count = parseInt(timesMatch[1]);
|
|
26
|
+
const varName = timesMatch[2];
|
|
27
|
+
const body = timesMatch[3].trim();
|
|
28
|
+
let result = "";
|
|
29
|
+
for (let i = 0; i < count; i++) {
|
|
30
|
+
vars[varName] = i;
|
|
31
|
+
result += interpret(body) + "\n";
|
|
32
|
+
}
|
|
33
|
+
return result.trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ifElseRegex = /^if\s*\((.+?)\)\s*\{([\s\S]+?)\}(?:\s*elsif\s*\((.+?)\)\s*\{([\s\S]+?)\})?(?:\s*else\s*\{([\s\S]+?)\})?$/;
|
|
37
|
+
const ifElseMatch = command.match(ifElseRegex);
|
|
38
|
+
if (ifElseMatch) {
|
|
39
|
+
const [, cond1, body1, cond2, body2, body3] = ifElseMatch;
|
|
40
|
+
if (evaluateExpression(cond1)) return interpret(body1.trim());
|
|
41
|
+
if (cond2 && evaluateExpression(cond2)) return interpret(body2.trim());
|
|
42
|
+
if (body3) return interpret(body3.trim());
|
|
43
|
+
return "If condition was false.";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function interpret(command) {
|
|
50
|
+
const preprocessed = preprocessCommand(command);
|
|
51
|
+
if (preprocessed !== null) return preprocessed;
|
|
52
|
+
|
|
53
|
+
command = command.trim();
|
|
54
|
+
|
|
55
|
+
if (/^let\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/.test(command)) {
|
|
56
|
+
const [, name, value] = command.match(/^let\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/);
|
|
57
|
+
vars[name] = evaluateExpression(value);
|
|
58
|
+
return `${name} = ${vars[name]}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (/^def\s+([a-zA-Z_][a-zA-Z0-9_]*)\(([^)]*)\)\s*\{([\s\S]*)\}$/.test(command)) {
|
|
62
|
+
const [, name, args, body] = command.match(/^def\s+([a-zA-Z_][a-zA-Z0-9_]*)\(([^)]*)\)\s*\{([\s\S]*)\}$/);
|
|
63
|
+
functions[name] = {
|
|
64
|
+
args: args.split(",").map((s) => s.trim()).filter((s) => s),
|
|
65
|
+
body: body.trim(),
|
|
66
|
+
};
|
|
67
|
+
return `Function ${name} defined.`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (/^([a-zA-Z_][a-zA-Z0-9_]*)\((.*)\)$/.test(command)) {
|
|
71
|
+
const [, name, argstr] = command.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\((.*)\)$/);
|
|
72
|
+
if (!functions[name]) throw new Error("Undefined function: " + name);
|
|
73
|
+
const func = functions[name];
|
|
74
|
+
const argValues = argstr.split(",").map((s) => evaluateExpression(s.trim()));
|
|
75
|
+
const localVars = {};
|
|
76
|
+
func.args.forEach((arg, i) => (localVars[arg] = argValues[i]));
|
|
77
|
+
|
|
78
|
+
const prevVars = Object.assign({}, vars);
|
|
79
|
+
Object.assign(vars, localVars);
|
|
80
|
+
|
|
81
|
+
let result;
|
|
82
|
+
try {
|
|
83
|
+
result = interpret(func.body);
|
|
84
|
+
} finally {
|
|
85
|
+
Object.assign(vars, prevVars);
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let loopMatch = command.match(/^for\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+?)\s+to\s+(.+?)\s*\{([\s\S]+)\}$/);
|
|
91
|
+
if (loopMatch) {
|
|
92
|
+
const vname = loopMatch[1];
|
|
93
|
+
const start = evaluateExpression(loopMatch[2]);
|
|
94
|
+
const end = evaluateExpression(loopMatch[3]);
|
|
95
|
+
const body = loopMatch[4];
|
|
96
|
+
|
|
97
|
+
for (let i = start; i <= end; i++) {
|
|
98
|
+
vars[vname] = i;
|
|
99
|
+
try {
|
|
100
|
+
interpret(body.trim());
|
|
101
|
+
} catch (e) {
|
|
102
|
+
if (e instanceof BreakException) break;
|
|
103
|
+
if (e instanceof ContinueException) continue;
|
|
104
|
+
throw e;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return `Looped ${vname} from ${start} to ${end}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let whileMatch = command.match(/^while\s*\((.+?)\)\s*\{([\s\S]+)\}$/);
|
|
111
|
+
if (whileMatch) {
|
|
112
|
+
const condition = whileMatch[1];
|
|
113
|
+
const body = whileMatch[2];
|
|
114
|
+
|
|
115
|
+
while (evaluateExpression(condition)) {
|
|
116
|
+
try {
|
|
117
|
+
interpret(body.trim());
|
|
118
|
+
} catch (e) {
|
|
119
|
+
if (e instanceof BreakException) break;
|
|
120
|
+
if (e instanceof ContinueException) continue;
|
|
121
|
+
throw e;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return "While loop executed.";
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (command === "break") throw new BreakException();
|
|
128
|
+
if (command === "continue") throw new ContinueException();
|
|
129
|
+
|
|
130
|
+
return evaluateExpression(command);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// CLI 入力受付
|
|
134
|
+
const rl = readline.createInterface({
|
|
135
|
+
input: process.stdin,
|
|
136
|
+
output: process.stdout,
|
|
137
|
+
prompt: "Lumos> ",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
console.log("Welcome to Lumos CLI!");
|
|
141
|
+
rl.prompt();
|
|
142
|
+
|
|
143
|
+
rl.on("line", (line) => {
|
|
144
|
+
const command = line.trim();
|
|
145
|
+
try {
|
|
146
|
+
const result = interpret(command);
|
|
147
|
+
console.log("> " + result);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.error("! " + err.message);
|
|
150
|
+
}
|
|
151
|
+
rl.prompt();
|
|
152
|
+
});
|
|
153
|
+
|
package/package.json
ADDED