freelang-v4 4.3.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.
Files changed (261) hide show
  1. package/README.md +548 -0
  2. package/dist/ast.d.ts +367 -0
  3. package/dist/ast.js +4 -0
  4. package/dist/ast.js.map +1 -0
  5. package/dist/async-basic.test.d.ts +1 -0
  6. package/dist/async-basic.test.js +88 -0
  7. package/dist/async-basic.test.js.map +1 -0
  8. package/dist/async-jest.test.d.ts +1 -0
  9. package/dist/async-jest.test.js +99 -0
  10. package/dist/async-jest.test.js.map +1 -0
  11. package/dist/channel-jest.test.d.ts +1 -0
  12. package/dist/channel-jest.test.js +148 -0
  13. package/dist/channel-jest.test.js.map +1 -0
  14. package/dist/checker-jest.test.d.ts +1 -0
  15. package/dist/checker-jest.test.js +160 -0
  16. package/dist/checker-jest.test.js.map +1 -0
  17. package/dist/checker.d.ts +149 -0
  18. package/dist/checker.js +1565 -0
  19. package/dist/checker.js.map +1 -0
  20. package/dist/checker.test.d.ts +1 -0
  21. package/dist/checker.test.js +217 -0
  22. package/dist/checker.test.js.map +1 -0
  23. package/dist/compiler-jest.test.d.ts +1 -0
  24. package/dist/compiler-jest.test.js +233 -0
  25. package/dist/compiler-jest.test.js.map +1 -0
  26. package/dist/compiler.d.ts +127 -0
  27. package/dist/compiler.js +1588 -0
  28. package/dist/compiler.js.map +1 -0
  29. package/dist/compiler.test.d.ts +1 -0
  30. package/dist/compiler.test.js +313 -0
  31. package/dist/compiler.test.js.map +1 -0
  32. package/dist/db-100m-full.d.ts +5 -0
  33. package/dist/db-100m-full.js +78 -0
  34. package/dist/db-100m-full.js.map +1 -0
  35. package/dist/db-100m-no-index.d.ts +12 -0
  36. package/dist/db-100m-no-index.js +119 -0
  37. package/dist/db-100m-no-index.js.map +1 -0
  38. package/dist/db-100m-real.d.ts +5 -0
  39. package/dist/db-100m-real.js +131 -0
  40. package/dist/db-100m-real.js.map +1 -0
  41. package/dist/db-100m-streaming.d.ts +15 -0
  42. package/dist/db-100m-streaming.js +164 -0
  43. package/dist/db-100m-streaming.js.map +1 -0
  44. package/dist/db-100m-test.d.ts +5 -0
  45. package/dist/db-100m-test.js +111 -0
  46. package/dist/db-100m-test.js.map +1 -0
  47. package/dist/db-jest.test.d.ts +1 -0
  48. package/dist/db-jest.test.js +182 -0
  49. package/dist/db-jest.test.js.map +1 -0
  50. package/dist/db-runtime.d.ts +24 -0
  51. package/dist/db-runtime.js +204 -0
  52. package/dist/db-runtime.js.map +1 -0
  53. package/dist/db.d.ts +249 -0
  54. package/dist/db.js +593 -0
  55. package/dist/db.js.map +1 -0
  56. package/dist/file-io-jest.test.d.ts +1 -0
  57. package/dist/file-io-jest.test.js +225 -0
  58. package/dist/file-io-jest.test.js.map +1 -0
  59. package/dist/for-of-jest.test.d.ts +1 -0
  60. package/dist/for-of-jest.test.js +230 -0
  61. package/dist/for-of-jest.test.js.map +1 -0
  62. package/dist/for-of.test.d.ts +1 -0
  63. package/dist/for-of.test.js +305 -0
  64. package/dist/for-of.test.js.map +1 -0
  65. package/dist/function-literal-jest.test.d.ts +1 -0
  66. package/dist/function-literal-jest.test.js +180 -0
  67. package/dist/function-literal-jest.test.js.map +1 -0
  68. package/dist/function-literal.test.d.ts +1 -0
  69. package/dist/function-literal.test.js +245 -0
  70. package/dist/function-literal.test.js.map +1 -0
  71. package/dist/generics-jest.test.d.ts +1 -0
  72. package/dist/generics-jest.test.js +93 -0
  73. package/dist/generics-jest.test.js.map +1 -0
  74. package/dist/ir-gen.d.ts +15 -0
  75. package/dist/ir-gen.js +400 -0
  76. package/dist/ir-gen.js.map +1 -0
  77. package/dist/ir.d.ts +114 -0
  78. package/dist/ir.js +5 -0
  79. package/dist/ir.js.map +1 -0
  80. package/dist/lexer.d.ts +110 -0
  81. package/dist/lexer.js +467 -0
  82. package/dist/lexer.js.map +1 -0
  83. package/dist/lexer.test.d.ts +1 -0
  84. package/dist/lexer.test.js +426 -0
  85. package/dist/lexer.test.js.map +1 -0
  86. package/dist/main.d.ts +2 -0
  87. package/dist/main.js +241 -0
  88. package/dist/main.js.map +1 -0
  89. package/dist/module-jest.test.d.ts +1 -0
  90. package/dist/module-jest.test.js +123 -0
  91. package/dist/module-jest.test.js.map +1 -0
  92. package/dist/parser.d.ts +56 -0
  93. package/dist/parser.js +1060 -0
  94. package/dist/parser.js.map +1 -0
  95. package/dist/parser.test.d.ts +1 -0
  96. package/dist/parser.test.js +461 -0
  97. package/dist/parser.test.js.map +1 -0
  98. package/dist/pattern-matching-jest.test.d.ts +1 -0
  99. package/dist/pattern-matching-jest.test.js +158 -0
  100. package/dist/pattern-matching-jest.test.js.map +1 -0
  101. package/dist/pkg/init.d.ts +1 -0
  102. package/dist/pkg/init.js +118 -0
  103. package/dist/pkg/init.js.map +1 -0
  104. package/dist/pkg/install.d.ts +1 -0
  105. package/dist/pkg/install.js +77 -0
  106. package/dist/pkg/install.js.map +1 -0
  107. package/dist/pkg/registry.d.ts +23 -0
  108. package/dist/pkg/registry.js +106 -0
  109. package/dist/pkg/registry.js.map +1 -0
  110. package/dist/pkg/run.d.ts +1 -0
  111. package/dist/pkg/run.js +76 -0
  112. package/dist/pkg/run.js.map +1 -0
  113. package/dist/pkg/toml.d.ts +5 -0
  114. package/dist/pkg/toml.js +117 -0
  115. package/dist/pkg/toml.js.map +1 -0
  116. package/dist/repl.d.ts +15 -0
  117. package/dist/repl.js +197 -0
  118. package/dist/repl.js.map +1 -0
  119. package/dist/runtime/bytecode.d.ts +92 -0
  120. package/dist/runtime/bytecode.js +253 -0
  121. package/dist/runtime/bytecode.js.map +1 -0
  122. package/dist/runtime/value.d.ts +102 -0
  123. package/dist/runtime/value.js +302 -0
  124. package/dist/runtime/value.js.map +1 -0
  125. package/dist/runtime/vm.d.ts +65 -0
  126. package/dist/runtime/vm.js +293 -0
  127. package/dist/runtime/vm.js.map +1 -0
  128. package/dist/struct-instance-jest.test.d.ts +1 -0
  129. package/dist/struct-instance-jest.test.js +209 -0
  130. package/dist/struct-instance-jest.test.js.map +1 -0
  131. package/dist/struct-instance.test.d.ts +1 -0
  132. package/dist/struct-instance.test.js +291 -0
  133. package/dist/struct-instance.test.js.map +1 -0
  134. package/dist/struct-jest.test.d.ts +1 -0
  135. package/dist/struct-jest.test.js +176 -0
  136. package/dist/struct-jest.test.js.map +1 -0
  137. package/dist/struct.test.d.ts +1 -0
  138. package/dist/struct.test.js +231 -0
  139. package/dist/struct.test.js.map +1 -0
  140. package/dist/trait-jest.test.d.ts +1 -0
  141. package/dist/trait-jest.test.js +120 -0
  142. package/dist/trait-jest.test.js.map +1 -0
  143. package/dist/vm-jest.test.d.ts +1 -0
  144. package/dist/vm-jest.test.js +569 -0
  145. package/dist/vm-jest.test.js.map +1 -0
  146. package/dist/vm.d.ts +81 -0
  147. package/dist/vm.js +1956 -0
  148. package/dist/vm.js.map +1 -0
  149. package/dist/vm.test.d.ts +1 -0
  150. package/dist/vm.test.js +337 -0
  151. package/dist/vm.test.js.map +1 -0
  152. package/dist/web-repl/sandbox.d.ts +11 -0
  153. package/dist/web-repl/sandbox.js +76 -0
  154. package/dist/web-repl/sandbox.js.map +1 -0
  155. package/dist/web-repl/server.d.ts +1 -0
  156. package/dist/web-repl/server.js +111 -0
  157. package/dist/web-repl/server.js.map +1 -0
  158. package/dist/while-loop-jest.test.d.ts +1 -0
  159. package/dist/while-loop-jest.test.js +201 -0
  160. package/dist/while-loop-jest.test.js.map +1 -0
  161. package/dist/while-loop.test.d.ts +1 -0
  162. package/dist/while-loop.test.js +262 -0
  163. package/dist/while-loop.test.js.map +1 -0
  164. package/docs/EXPERIENCE.md +787 -0
  165. package/docs/README.md +175 -0
  166. package/docs/V1_V2_V3_ANALYSIS.md +107 -0
  167. package/docs/_config.yml +36 -0
  168. package/docs/api-reference.md +459 -0
  169. package/docs/architecture.md +470 -0
  170. package/docs/benchmarks.md +295 -0
  171. package/docs/comparison.md +454 -0
  172. package/docs/index.md +335 -0
  173. package/docs/language-completeness.md +228 -0
  174. package/docs/learning-guide.md +651 -0
  175. package/package.json +65 -0
  176. package/src/api/deploy_key.fl +294 -0
  177. package/src/api/issue.fl +302 -0
  178. package/src/api/org.fl +356 -0
  179. package/src/api/repo.fl +394 -0
  180. package/src/api/team.fl +299 -0
  181. package/src/api/user.fl +385 -0
  182. package/src/api/webhook.fl +273 -0
  183. package/src/ast.ts +158 -0
  184. package/src/async-basic.test.ts +94 -0
  185. package/src/async-jest.test.ts +107 -0
  186. package/src/channel-jest.test.ts +158 -0
  187. package/src/checker-jest.test.ts +189 -0
  188. package/src/checker.test.ts +279 -0
  189. package/src/checker.ts +1861 -0
  190. package/src/commands/analyze.fl +227 -0
  191. package/src/commands/auth.fl +315 -0
  192. package/src/commands/batch.fl +349 -0
  193. package/src/commands/config.fl +199 -0
  194. package/src/commands/deploy_key.fl +352 -0
  195. package/src/commands/issue.fl +275 -0
  196. package/src/commands/main.fl +492 -0
  197. package/src/commands/org.fl +425 -0
  198. package/src/commands/repo.fl +581 -0
  199. package/src/commands/team.fl +244 -0
  200. package/src/commands/user.fl +423 -0
  201. package/src/commands/webhook.fl +400 -0
  202. package/src/compiler-jest.test.ts +275 -0
  203. package/src/compiler.test.ts +375 -0
  204. package/src/compiler.ts +1770 -0
  205. package/src/config.fl +175 -0
  206. package/src/core/batch.fl +355 -0
  207. package/src/core/cache.fl +284 -0
  208. package/src/core/ensure.fl +324 -0
  209. package/src/db-100m-full.ts +96 -0
  210. package/src/db-100m-no-index.ts +133 -0
  211. package/src/db-100m-real.ts +152 -0
  212. package/src/db-100m-streaming.ts +154 -0
  213. package/src/db-100m-test.ts +136 -0
  214. package/src/db-jest.test.ts +161 -0
  215. package/src/db-runtime.ts +242 -0
  216. package/src/db.ts +676 -0
  217. package/src/errors.fl +134 -0
  218. package/src/for-of-jest.test.ts +246 -0
  219. package/src/for-of.test.ts +308 -0
  220. package/src/function-literal-jest.test.ts +193 -0
  221. package/src/function-literal.test.ts +248 -0
  222. package/src/generics-jest.test.ts +104 -0
  223. package/src/http/client.fl +327 -0
  224. package/src/ir-gen.ts +459 -0
  225. package/src/ir.ts +80 -0
  226. package/src/lexer.test.ts +499 -0
  227. package/src/lexer.ts +522 -0
  228. package/src/main.ts +223 -0
  229. package/src/models.fl +162 -0
  230. package/src/module-jest.test.ts +145 -0
  231. package/src/parser.test.ts +542 -0
  232. package/src/parser.ts +1211 -0
  233. package/src/pattern-matching-jest.test.ts +170 -0
  234. package/src/pkg/init.ts +91 -0
  235. package/src/pkg/install.ts +56 -0
  236. package/src/pkg/registry.ts +103 -0
  237. package/src/pkg/run.ts +49 -0
  238. package/src/pkg/toml.ts +129 -0
  239. package/src/repl.ts +190 -0
  240. package/src/runtime/bytecode.ts +291 -0
  241. package/src/runtime/value.ts +322 -0
  242. package/src/runtime/vm.ts +354 -0
  243. package/src/self-host/bootstrap.fl +68 -0
  244. package/src/self-host/interpreter.fl +361 -0
  245. package/src/self-host/lexer-simple.fl +22 -0
  246. package/src/self-host/lexer.fl +305 -0
  247. package/src/self-host/parser.fl +580 -0
  248. package/src/struct-instance-jest.test.ts +221 -0
  249. package/src/struct-instance.test.ts +293 -0
  250. package/src/struct-jest.test.ts +187 -0
  251. package/src/struct.test.ts +234 -0
  252. package/src/trait-jest.test.ts +136 -0
  253. package/src/vm-jest.test.ts +754 -0
  254. package/src/vm.ts +1976 -0
  255. package/src/web-repl/public/index.html +50 -0
  256. package/src/web-repl/public/main.js +105 -0
  257. package/src/web-repl/public/style.css +225 -0
  258. package/src/web-repl/sandbox.ts +88 -0
  259. package/src/web-repl/server.ts +97 -0
  260. package/src/while-loop-jest.test.ts +218 -0
  261. package/src/while-loop.test.ts +267 -0
package/dist/vm.js ADDED
@@ -0,0 +1,1956 @@
1
+ "use strict";
2
+ // FreeLang v4 — Stack VM (SPEC_02 구현)
3
+ // fetch-decode-execute + Actor cooperative scheduling
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || (function () {
21
+ var ownKeys = function(o) {
22
+ ownKeys = Object.getOwnPropertyNames || function (o) {
23
+ var ar = [];
24
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
25
+ return ar;
26
+ };
27
+ return ownKeys(o);
28
+ };
29
+ return function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ })();
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.VM = void 0;
39
+ const compiler_1 = require("./compiler");
40
+ const crypto = __importStar(require("crypto"));
41
+ const fs = __importStar(require("fs"));
42
+ const db_1 = require("./db");
43
+ // ============================================================
44
+ // VM
45
+ // ============================================================
46
+ class VM {
47
+ constructor() {
48
+ this.actors = [];
49
+ this.channels = new Map();
50
+ this.nextChannelId = 0;
51
+ this.databases = new Map();
52
+ this.nextDbId = 0;
53
+ this.httpServers = new Map(); // {app, server, requestQueue, nextReqId}
54
+ this.nextServerId = 0;
55
+ this.nextReqId = 0;
56
+ this.globals = new Map();
57
+ this.output = [];
58
+ this.instructionCount = 0;
59
+ this.maxInstructions = 1_000_000;
60
+ this.runningCount = 0;
61
+ }
62
+ async run(chunk) {
63
+ this.chunk = chunk;
64
+ this.output = [];
65
+ this.instructionCount = 0;
66
+ // main actor
67
+ this.actors = [{
68
+ id: 0,
69
+ ip: 0,
70
+ stack: [],
71
+ frames: [{ returnAddr: -1, baseSlot: 0, locals: [] }],
72
+ state: "running",
73
+ waitingChan: null,
74
+ }];
75
+ this.runningCount = 1;
76
+ try {
77
+ await this.schedule();
78
+ return { output: this.output, error: null };
79
+ }
80
+ catch (e) {
81
+ return { output: this.output, error: e.message || String(e) };
82
+ }
83
+ }
84
+ // ============================================================
85
+ // Scheduler — round-robin (SPEC_02 Q7)
86
+ // ============================================================
87
+ async schedule() {
88
+ const SLICE = 1000;
89
+ let current = 0;
90
+ while (this.runningCount > 0) {
91
+ const actor = this.actors[current];
92
+ if (actor.state === "running") {
93
+ await this.runSlice(actor, SLICE);
94
+ }
95
+ else if (actor.state === "waiting" && actor.waitingChan !== null) {
96
+ const chan = this.channels.get(actor.waitingChan);
97
+ if (chan && chan.buffer.length > 0) {
98
+ const val = chan.buffer.shift();
99
+ actor.stack.push({ tag: "ok", val });
100
+ actor.state = "running";
101
+ actor.waitingChan = null;
102
+ }
103
+ }
104
+ current = (current + 1) % this.actors.length;
105
+ if (this.instructionCount > this.maxInstructions) {
106
+ throw new Error("execution limit exceeded (infinite loop?)");
107
+ }
108
+ }
109
+ }
110
+ // ============================================================
111
+ // Execute slice
112
+ // ============================================================
113
+ async runSlice(actor, maxOps) {
114
+ let ops = 0;
115
+ while (ops < maxOps && actor.state === "running") {
116
+ if (actor.ip >= this.chunk.code.length) {
117
+ actor.state = "done";
118
+ this.runningCount--;
119
+ return;
120
+ }
121
+ const op = this.chunk.code[actor.ip++];
122
+ this.instructionCount++;
123
+ ops++;
124
+ // currentFrame 캐시 (CALL/RETURN에서 업데이트)
125
+ let currentFrame = actor.frames[actor.frames.length - 1];
126
+ switch (op) {
127
+ // ---- 상수 로드 ----
128
+ case compiler_1.Op.PUSH_I32: {
129
+ const val = this.readI32(actor);
130
+ actor.stack.push({ tag: "i32", val });
131
+ break;
132
+ }
133
+ case compiler_1.Op.PUSH_F64: {
134
+ const val = this.readF64(actor);
135
+ actor.stack.push({ tag: "f64", val });
136
+ break;
137
+ }
138
+ case compiler_1.Op.PUSH_STR: {
139
+ const idx = this.readI32(actor);
140
+ actor.stack.push({ tag: "str", val: this.chunk.constants[idx] });
141
+ break;
142
+ }
143
+ case compiler_1.Op.PUSH_TRUE:
144
+ actor.stack.push({ tag: "bool", val: true });
145
+ break;
146
+ case compiler_1.Op.PUSH_FALSE:
147
+ actor.stack.push({ tag: "bool", val: false });
148
+ break;
149
+ case compiler_1.Op.PUSH_VOID:
150
+ actor.stack.push({ tag: "void" });
151
+ break;
152
+ case compiler_1.Op.PUSH_NONE:
153
+ actor.stack.push({ tag: "none" });
154
+ break;
155
+ case compiler_1.Op.POP:
156
+ actor.stack.pop();
157
+ break;
158
+ case compiler_1.Op.DUP:
159
+ actor.stack.push(actor.stack[actor.stack.length - 1]);
160
+ break;
161
+ // ---- 산술 (i32) ----
162
+ case compiler_1.Op.ADD_I32: {
163
+ const b = actor.stack.pop();
164
+ const a = actor.stack.pop();
165
+ if (a.tag === "str" && b.tag === "str") {
166
+ actor.stack.push({ tag: "str", val: a.val + b.val });
167
+ }
168
+ else {
169
+ actor.stack.push({ tag: a.tag, val: a.val + b.val });
170
+ }
171
+ break;
172
+ }
173
+ case compiler_1.Op.SUB_I32: {
174
+ const b = actor.stack.pop();
175
+ const a = actor.stack.pop();
176
+ actor.stack.push({ tag: a.tag, val: a.val - b.val });
177
+ break;
178
+ }
179
+ case compiler_1.Op.MUL_I32: {
180
+ const b = actor.stack.pop();
181
+ const a = actor.stack.pop();
182
+ actor.stack.push({ tag: a.tag, val: a.val * b.val });
183
+ break;
184
+ }
185
+ case compiler_1.Op.DIV_I32: {
186
+ const b = actor.stack.pop();
187
+ const a = actor.stack.pop();
188
+ if (b.val === 0)
189
+ throw new Error("panic: division by zero");
190
+ const result = a.tag === "i32"
191
+ ? Math.trunc(a.val / b.val)
192
+ : a.val / b.val;
193
+ actor.stack.push({ tag: a.tag, val: result });
194
+ break;
195
+ }
196
+ case compiler_1.Op.MOD_I32: {
197
+ const b = actor.stack.pop();
198
+ const a = actor.stack.pop();
199
+ if (b.val === 0)
200
+ throw new Error("panic: division by zero");
201
+ actor.stack.push({ tag: a.tag, val: a.val % b.val });
202
+ break;
203
+ }
204
+ case compiler_1.Op.NEG_I32: {
205
+ const a = actor.stack.pop();
206
+ actor.stack.push({ tag: a.tag, val: -a.val });
207
+ break;
208
+ }
209
+ // ---- f64 산술 ----
210
+ case compiler_1.Op.ADD_F64: {
211
+ const b = actor.stack.pop();
212
+ const a = actor.stack.pop();
213
+ if (a.tag === "str" && b.tag === "str") {
214
+ actor.stack.push({ tag: "str", val: a.val + b.val });
215
+ }
216
+ else {
217
+ actor.stack.push({ tag: "f64", val: a.val + b.val });
218
+ }
219
+ break;
220
+ }
221
+ case compiler_1.Op.SUB_F64: {
222
+ const b = actor.stack.pop();
223
+ const a = actor.stack.pop();
224
+ actor.stack.push({ tag: "f64", val: a.val - b.val });
225
+ break;
226
+ }
227
+ case compiler_1.Op.MUL_F64: {
228
+ const b = actor.stack.pop();
229
+ const a = actor.stack.pop();
230
+ actor.stack.push({ tag: "f64", val: a.val * b.val });
231
+ break;
232
+ }
233
+ case compiler_1.Op.DIV_F64: {
234
+ const b = actor.stack.pop();
235
+ const a = actor.stack.pop();
236
+ if (b.val === 0)
237
+ throw new Error("panic: division by zero");
238
+ actor.stack.push({ tag: "f64", val: a.val / b.val });
239
+ break;
240
+ }
241
+ case compiler_1.Op.MOD_F64: {
242
+ const b = actor.stack.pop();
243
+ const a = actor.stack.pop();
244
+ if (b.val === 0)
245
+ throw new Error("panic: division by zero");
246
+ actor.stack.push({ tag: "f64", val: a.val % b.val });
247
+ break;
248
+ }
249
+ case compiler_1.Op.NEG_F64: {
250
+ const a = actor.stack.pop();
251
+ actor.stack.push({ tag: "f64", val: -a.val });
252
+ break;
253
+ }
254
+ // ---- 비교 ----
255
+ case compiler_1.Op.EQ: {
256
+ const b = actor.stack.pop();
257
+ const a = actor.stack.pop();
258
+ actor.stack.push({ tag: "bool", val: this.valuesEqual(a, b) });
259
+ break;
260
+ }
261
+ case compiler_1.Op.NEQ: {
262
+ const b = actor.stack.pop();
263
+ const a = actor.stack.pop();
264
+ actor.stack.push({ tag: "bool", val: !this.valuesEqual(a, b) });
265
+ break;
266
+ }
267
+ case compiler_1.Op.LT: {
268
+ const b = actor.stack.pop();
269
+ const a = actor.stack.pop();
270
+ actor.stack.push({ tag: "bool", val: a.val < b.val });
271
+ break;
272
+ }
273
+ case compiler_1.Op.GT: {
274
+ const b = actor.stack.pop();
275
+ const a = actor.stack.pop();
276
+ actor.stack.push({ tag: "bool", val: a.val > b.val });
277
+ break;
278
+ }
279
+ case compiler_1.Op.LTEQ: {
280
+ const b = actor.stack.pop();
281
+ const a = actor.stack.pop();
282
+ actor.stack.push({ tag: "bool", val: a.val <= b.val });
283
+ break;
284
+ }
285
+ case compiler_1.Op.GTEQ: {
286
+ const b = actor.stack.pop();
287
+ const a = actor.stack.pop();
288
+ actor.stack.push({ tag: "bool", val: a.val >= b.val });
289
+ break;
290
+ }
291
+ // ---- 논리 ----
292
+ case compiler_1.Op.AND: {
293
+ const b = actor.stack.pop();
294
+ const a = actor.stack.pop();
295
+ actor.stack.push({ tag: "bool", val: a.val && b.val });
296
+ break;
297
+ }
298
+ case compiler_1.Op.OR: {
299
+ const b = actor.stack.pop();
300
+ const a = actor.stack.pop();
301
+ actor.stack.push({ tag: "bool", val: a.val || b.val });
302
+ break;
303
+ }
304
+ case compiler_1.Op.NOT: {
305
+ const a = actor.stack.pop();
306
+ actor.stack.push({ tag: "bool", val: !a.val });
307
+ break;
308
+ }
309
+ // ---- 변수 ----
310
+ case compiler_1.Op.LOAD_LOCAL: {
311
+ const slot = this.readI32(actor);
312
+ const frame = actor.frames[actor.frames.length - 1];
313
+ actor.stack.push(frame.locals[slot] ?? { tag: "void" });
314
+ break;
315
+ }
316
+ case compiler_1.Op.STORE_LOCAL: {
317
+ const slot = this.readI32(actor);
318
+ const val = actor.stack.pop();
319
+ const frame = actor.frames[actor.frames.length - 1];
320
+ while (frame.locals.length <= slot)
321
+ frame.locals.push({ tag: "void" });
322
+ frame.locals[slot] = val;
323
+ break;
324
+ }
325
+ case compiler_1.Op.LOAD_GLOBAL: {
326
+ const idx = this.readI32(actor);
327
+ const name = this.chunk.constants[idx];
328
+ actor.stack.push(this.globals.get(name) ?? { tag: "void" });
329
+ break;
330
+ }
331
+ case compiler_1.Op.STORE_GLOBAL: {
332
+ const idx = this.readI32(actor);
333
+ const name = this.chunk.constants[idx];
334
+ const val = actor.stack.pop();
335
+ this.globals.set(name, val);
336
+ break;
337
+ }
338
+ // ---- 제어 ----
339
+ case compiler_1.Op.JUMP: {
340
+ const target = this.readI32(actor);
341
+ actor.ip = target;
342
+ break;
343
+ }
344
+ case compiler_1.Op.JUMP_IF_FALSE: {
345
+ const target = this.readI32(actor);
346
+ const cond = actor.stack.pop();
347
+ if (cond.tag === "bool" && !cond.val) {
348
+ actor.ip = target;
349
+ }
350
+ break;
351
+ }
352
+ case compiler_1.Op.RETURN: {
353
+ const retVal = actor.stack.pop() ?? { tag: "void" };
354
+ const frame = actor.frames.pop();
355
+ if (actor.frames.length === 0) {
356
+ actor.state = "done";
357
+ this.runningCount--;
358
+ return;
359
+ }
360
+ actor.ip = frame.returnAddr;
361
+ // 스택 정리
362
+ actor.stack.length = frame.baseSlot;
363
+ actor.stack.push(retVal);
364
+ break;
365
+ }
366
+ case compiler_1.Op.HALT:
367
+ actor.state = "done";
368
+ this.runningCount--;
369
+ return;
370
+ // ---- 함수 호출 ----
371
+ case compiler_1.Op.CALL: {
372
+ const fnIdx = this.readI32(actor);
373
+ const argCount = this.chunk.code[actor.ip++];
374
+ const fn = this.chunk.functions[fnIdx];
375
+ if (!fn)
376
+ throw new Error(`panic: undefined function index ${fnIdx}`);
377
+ const args = new Array(argCount);
378
+ for (let i = argCount - 1; i >= 0; i--) {
379
+ args[i] = actor.stack.pop();
380
+ }
381
+ actor.frames.push({
382
+ returnAddr: actor.ip,
383
+ baseSlot: actor.stack.length,
384
+ locals: args,
385
+ });
386
+ actor.ip = fn.offset;
387
+ break;
388
+ }
389
+ case compiler_1.Op.CALL_BUILTIN: {
390
+ const nameIdx = this.readI32(actor);
391
+ const argCount = this.chunk.code[actor.ip++];
392
+ const name = this.chunk.constants[nameIdx];
393
+ const args = new Array(argCount);
394
+ for (let i = argCount - 1; i >= 0; i--) {
395
+ args[i] = actor.stack.pop();
396
+ }
397
+ const result = await this.callBuiltin(name, args);
398
+ actor.stack.push(result);
399
+ break;
400
+ }
401
+ // ---- 배열 ----
402
+ case compiler_1.Op.ARRAY_NEW: {
403
+ const count = this.readI32(actor);
404
+ const elements = new Array(count);
405
+ for (let i = count - 1; i >= 0; i--) {
406
+ elements[i] = actor.stack.pop();
407
+ }
408
+ actor.stack.push({ tag: "arr", val: elements });
409
+ break;
410
+ }
411
+ case compiler_1.Op.ARRAY_GET: {
412
+ const idx = actor.stack.pop();
413
+ const arr = actor.stack.pop();
414
+ if (arr.tag !== "arr")
415
+ throw new Error("panic: not an array");
416
+ const i = idx.val;
417
+ if (i < 0 || i >= arr.val.length)
418
+ throw new Error(`panic: index out of bounds: ${i}`);
419
+ actor.stack.push(arr.val[i]);
420
+ break;
421
+ }
422
+ case compiler_1.Op.ARRAY_SET: {
423
+ const val = actor.stack.pop();
424
+ const idx = actor.stack.pop();
425
+ const arr = actor.stack.pop();
426
+ if (arr.tag !== "arr")
427
+ throw new Error("panic: not an array");
428
+ arr.val[idx.val] = val;
429
+ break;
430
+ }
431
+ // ---- 구조체 ----
432
+ case compiler_1.Op.STRUCT_NEW: {
433
+ const count = this.readI32(actor);
434
+ const fields = new Map();
435
+ for (let i = 0; i < count; i++) {
436
+ const val = actor.stack.pop();
437
+ const key = actor.stack.pop();
438
+ fields.set(key.val, val);
439
+ }
440
+ actor.stack.push({ tag: "struct", fields });
441
+ break;
442
+ }
443
+ case compiler_1.Op.STRUCT_GET: {
444
+ const nameIdx = this.readI32(actor);
445
+ const fieldName = this.chunk.constants[nameIdx];
446
+ const obj = actor.stack.pop();
447
+ if (obj.tag !== "struct")
448
+ throw new Error("panic: not a struct");
449
+ actor.stack.push(obj.fields.get(fieldName) ?? { tag: "void" });
450
+ break;
451
+ }
452
+ // ---- Option/Result ----
453
+ case compiler_1.Op.WRAP_OK: {
454
+ const val = actor.stack.pop();
455
+ actor.stack.push({ tag: "ok", val });
456
+ break;
457
+ }
458
+ case compiler_1.Op.WRAP_ERR: {
459
+ const val = actor.stack.pop();
460
+ actor.stack.push({ tag: "err", val });
461
+ break;
462
+ }
463
+ case compiler_1.Op.WRAP_SOME: {
464
+ const val = actor.stack.pop();
465
+ actor.stack.push({ tag: "some", val });
466
+ break;
467
+ }
468
+ case compiler_1.Op.UNWRAP: {
469
+ const val = actor.stack.pop();
470
+ if (val.tag === "ok" || val.tag === "some") {
471
+ actor.stack.push(val.val);
472
+ }
473
+ else {
474
+ throw new Error(`panic: unwrap on ${val.tag}`);
475
+ }
476
+ break;
477
+ }
478
+ case compiler_1.Op.IS_OK: {
479
+ const val = actor.stack.pop();
480
+ actor.stack.push({ tag: "bool", val: val.tag === "ok" });
481
+ break;
482
+ }
483
+ case compiler_1.Op.IS_ERR: {
484
+ const val = actor.stack.pop();
485
+ actor.stack.push({ tag: "bool", val: val.tag === "err" });
486
+ break;
487
+ }
488
+ case compiler_1.Op.IS_SOME: {
489
+ const val = actor.stack.pop();
490
+ actor.stack.push({ tag: "bool", val: val.tag === "some" });
491
+ break;
492
+ }
493
+ case compiler_1.Op.IS_NONE: {
494
+ const val = actor.stack.pop();
495
+ actor.stack.push({ tag: "bool", val: val.tag === "none" });
496
+ break;
497
+ }
498
+ // ---- Actor/Channel ----
499
+ case compiler_1.Op.SPAWN: {
500
+ const bodyOffset = this.readI32(actor);
501
+ const newActor = {
502
+ id: this.actors.length,
503
+ ip: bodyOffset,
504
+ stack: [],
505
+ frames: [{ returnAddr: -1, baseSlot: 0, locals: [] }],
506
+ state: "running",
507
+ waitingChan: null,
508
+ };
509
+ this.actors.push(newActor);
510
+ this.runningCount++;
511
+ break;
512
+ }
513
+ case compiler_1.Op.CHAN_NEW: {
514
+ const id = this.nextChannelId++;
515
+ const chan = {
516
+ id,
517
+ buffer: [],
518
+ waitingRecv: [],
519
+ };
520
+ this.channels.set(id, chan);
521
+ actor.stack.push({ tag: "chan", id });
522
+ break;
523
+ }
524
+ case compiler_1.Op.CHAN_SEND: {
525
+ const val = actor.stack.pop();
526
+ const chanVal = actor.stack.pop();
527
+ if (chanVal.tag !== "chan")
528
+ throw new Error("panic: send on non-channel");
529
+ const chan = this.channels.get(chanVal.id);
530
+ chan.buffer.push(val);
531
+ // 대기 중인 actor 깨우기
532
+ if (chan.waitingRecv.length > 0) {
533
+ const waitId = chan.waitingRecv.shift();
534
+ const waitActor = this.actors[waitId];
535
+ if (waitActor) {
536
+ waitActor.state = "running";
537
+ waitActor.waitingChan = null;
538
+ }
539
+ }
540
+ break;
541
+ }
542
+ case compiler_1.Op.CHAN_RECV: {
543
+ const chanVal = actor.stack.pop();
544
+ if (chanVal.tag !== "chan")
545
+ throw new Error("panic: recv on non-channel");
546
+ const chan = this.channels.get(chanVal.id);
547
+ if (chan.buffer.length > 0) {
548
+ const val = chan.buffer.shift();
549
+ actor.stack.push({ tag: "ok", val });
550
+ }
551
+ else {
552
+ // 대기 상태로 전환
553
+ actor.state = "waiting";
554
+ actor.waitingChan = chanVal.id;
555
+ chan.waitingRecv.push(actor.id);
556
+ return;
557
+ }
558
+ break;
559
+ }
560
+ default:
561
+ throw new Error(`panic: unknown opcode 0x${op.toString(16)}`);
562
+ }
563
+ }
564
+ }
565
+ // ============================================================
566
+ // 내장 함수 (SPEC_10)
567
+ // ============================================================
568
+ async callBuiltin(name, args) {
569
+ // ============================================================
570
+ // DB 헬퍼 함수들
571
+ // ============================================================
572
+ const getDB = (arg) => {
573
+ if (arg.tag !== "db")
574
+ return null;
575
+ return this.databases.get(arg.id) ?? null;
576
+ };
577
+ const dbErr = (msg) => ({
578
+ tag: "err",
579
+ val: { tag: "str", val: msg },
580
+ });
581
+ const rowToValue = (row) => {
582
+ const fields = new Map();
583
+ for (const [key, val] of Object.entries(row)) {
584
+ fields.set(key, this.jsonToValue(val));
585
+ }
586
+ return { tag: "struct", fields };
587
+ };
588
+ switch (name) {
589
+ case "println": {
590
+ const text = args.map((a) => this.valueToString(a)).join(" ");
591
+ this.output.push(text);
592
+ return { tag: "void" };
593
+ }
594
+ case "print": {
595
+ const text = args.map((a) => this.valueToString(a)).join(" ");
596
+ // print는 줄바꿈 없이 마지막 출력에 이어붙임
597
+ if (this.output.length > 0) {
598
+ this.output[this.output.length - 1] += text;
599
+ }
600
+ else {
601
+ this.output.push(text);
602
+ }
603
+ return { tag: "void" };
604
+ }
605
+ case "str":
606
+ return { tag: "str", val: this.valueToString(args[0]) };
607
+ case "length":
608
+ if (args[0].tag === "arr")
609
+ return { tag: "i32", val: args[0].val.length };
610
+ if (args[0].tag === "str")
611
+ return { tag: "i32", val: args[0].val.length };
612
+ return { tag: "i32", val: 0 };
613
+ case "range": {
614
+ const start = args[0].val;
615
+ const end = args[1].val;
616
+ const arr = [];
617
+ for (let i = start; i < end; i++)
618
+ arr.push({ tag: "i32", val: i });
619
+ return { tag: "arr", val: arr };
620
+ }
621
+ case "push":
622
+ if (args[0].tag === "arr")
623
+ args[0].val.push(args[1]);
624
+ return { tag: "void" };
625
+ case "pop":
626
+ if (args[0].tag === "arr")
627
+ return args[0].val.pop() ?? { tag: "void" };
628
+ return { tag: "void" };
629
+ case "abs":
630
+ return { tag: args[0].tag, val: Math.abs(args[0].val) };
631
+ case "min":
632
+ return { tag: args[0].tag, val: Math.min(args[0].val, args[1].val) };
633
+ case "max":
634
+ return { tag: args[0].tag, val: Math.max(args[0].val, args[1].val) };
635
+ case "pow":
636
+ return { tag: "f64", val: Math.pow(args[0].val, args[1].val) };
637
+ case "sqrt":
638
+ return { tag: "f64", val: Math.sqrt(args[0].val) };
639
+ case "typeof":
640
+ return { tag: "str", val: args[0].tag };
641
+ case "assert":
642
+ if (args[0].tag === "bool" && !args[0].val) {
643
+ const msg = args.length > 1 ? this.valueToString(args[1]) : "assertion failed";
644
+ throw new Error(`panic: ${msg}`);
645
+ }
646
+ return { tag: "void" };
647
+ case "panic":
648
+ throw new Error(`panic: ${this.valueToString(args[0])}`);
649
+ case "bitand":
650
+ return { tag: "i32", val: args[0].val & args[1].val };
651
+ case "bitor":
652
+ return { tag: "i32", val: args[0].val | args[1].val };
653
+ case "bitxor":
654
+ return { tag: "i32", val: args[0].val ^ args[1].val };
655
+ case "shl":
656
+ return { tag: "i32", val: args[0].val << args[1].val };
657
+ case "shr":
658
+ return { tag: "i32", val: args[0].val >> args[1].val };
659
+ case "contains":
660
+ if (args[0].tag === "str") {
661
+ return { tag: "bool", val: args[0].val.includes(args[1].val) };
662
+ }
663
+ return { tag: "bool", val: false };
664
+ case "split":
665
+ if (args[0].tag === "str") {
666
+ const parts = args[0].val.split(args[1].val);
667
+ return { tag: "arr", val: parts.map((s) => ({ tag: "str", val: s })) };
668
+ }
669
+ return { tag: "arr", val: [] };
670
+ case "trim":
671
+ if (args[0].tag === "str")
672
+ return { tag: "str", val: args[0].val.trim() };
673
+ return args[0];
674
+ case "to_upper":
675
+ if (args[0].tag === "str")
676
+ return { tag: "str", val: args[0].val.toUpperCase() };
677
+ return args[0];
678
+ case "to_lower":
679
+ if (args[0].tag === "str")
680
+ return { tag: "str", val: args[0].val.toLowerCase() };
681
+ return args[0];
682
+ case "char_at":
683
+ if (args[0].tag === "str") {
684
+ const i = args[1].val;
685
+ return { tag: "str", val: args[0].val[i] ?? "" };
686
+ }
687
+ return { tag: "str", val: "" };
688
+ case "slice":
689
+ if (args[0].tag === "arr") {
690
+ const s = args[1].val;
691
+ const e = args[2].val;
692
+ return { tag: "arr", val: args[0].val.slice(s, e) };
693
+ }
694
+ if (args[0].tag === "str") {
695
+ const s = args[1].val;
696
+ const e = args[2].val;
697
+ return { tag: "str", val: args[0].val.slice(s, e) };
698
+ }
699
+ return args[0];
700
+ case "clone":
701
+ return this.deepClone(args[0]);
702
+ case "channel": {
703
+ const id = this.nextChannelId++;
704
+ const chan = { id, buffer: [], waitingRecv: [] };
705
+ this.channels.set(id, chan);
706
+ return { tag: "chan", id };
707
+ }
708
+ case "i32": {
709
+ const parsed = parseInt(this.valueToString(args[0]), 10);
710
+ if (isNaN(parsed))
711
+ return { tag: "err", val: { tag: "str", val: "Invalid number for i32" } };
712
+ return { tag: "ok", val: { tag: "i32", val: parsed } };
713
+ }
714
+ case "i64":
715
+ return { tag: "ok", val: { tag: "i32", val: parseInt(this.valueToString(args[0]), 10) || 0 } };
716
+ case "f64":
717
+ return { tag: "ok", val: { tag: "f64", val: parseFloat(this.valueToString(args[0])) || 0 } };
718
+ case "read_line": {
719
+ // synchronous readline from stdin (simplified - returns empty for now)
720
+ // In real implementation, would need async or proper stdin handling
721
+ return { tag: "str", val: "" };
722
+ }
723
+ case "read_file": {
724
+ const filepath = this.valueToString(args[0]);
725
+ try {
726
+ const content = fs.readFileSync(filepath, "utf-8");
727
+ return { tag: "ok", val: { tag: "str", val: content } };
728
+ }
729
+ catch (err) {
730
+ const errMsg = err instanceof Error ? err.message : "unknown error";
731
+ return { tag: "err", val: { tag: "str", val: errMsg } };
732
+ }
733
+ }
734
+ case "write_file": {
735
+ const filepath = this.valueToString(args[0]);
736
+ const content = this.valueToString(args[1]);
737
+ try {
738
+ fs.writeFileSync(filepath, content, "utf-8");
739
+ return { tag: "ok", val: { tag: "void" } };
740
+ }
741
+ catch (err) {
742
+ const errMsg = err instanceof Error ? err.message : "unknown error";
743
+ return { tag: "err", val: { tag: "str", val: errMsg } };
744
+ }
745
+ }
746
+ case "recv":
747
+ // method-style: obj.recv()
748
+ if (args[0] && args[0].tag === "chan") {
749
+ const chan = this.channels.get(args[0].id);
750
+ if (chan && chan.buffer.length > 0) {
751
+ return { tag: "ok", val: chan.buffer.shift() };
752
+ }
753
+ return { tag: "err", val: { tag: "str", val: "channel empty" } };
754
+ }
755
+ return { tag: "err", val: { tag: "str", val: "not a channel" } };
756
+ case "send":
757
+ if (args[0] && args[0].tag === "chan") {
758
+ const chan = this.channels.get(args[0].id);
759
+ if (chan)
760
+ chan.buffer.push(args[1]);
761
+ return { tag: "void" };
762
+ }
763
+ return { tag: "void" };
764
+ // ============================================================
765
+ // Phase 7: 20 Core Libraries
766
+ // ============================================================
767
+ // Cryptography & Encoding (6)
768
+ case "md5": {
769
+ const input = this.valueToString(args[0]);
770
+ const hash = crypto.createHash("md5").update(input).digest("hex");
771
+ return { tag: "str", val: hash };
772
+ }
773
+ case "sha256": {
774
+ const input = this.valueToString(args[0]);
775
+ const hash = crypto.createHash("sha256").update(input).digest("hex");
776
+ return { tag: "str", val: hash };
777
+ }
778
+ case "sha512": {
779
+ const input = this.valueToString(args[0]);
780
+ const hash = crypto.createHash("sha512").update(input).digest("hex");
781
+ return { tag: "str", val: hash };
782
+ }
783
+ case "base64_encode": {
784
+ const input = this.valueToString(args[0]);
785
+ const encoded = Buffer.from(input, "utf8").toString("base64");
786
+ return { tag: "str", val: encoded };
787
+ }
788
+ case "base64_decode": {
789
+ try {
790
+ const input = this.valueToString(args[0]);
791
+ const decoded = Buffer.from(input, "base64").toString("utf8");
792
+ return { tag: "ok", val: { tag: "str", val: decoded } };
793
+ }
794
+ catch (e) {
795
+ return { tag: "err", val: { tag: "str", val: "invalid base64" } };
796
+ }
797
+ }
798
+ case "hmac": {
799
+ const message = this.valueToString(args[0]);
800
+ const secret = this.valueToString(args[1]);
801
+ const hmac = crypto.createHmac("sha256", secret).update(message).digest("hex");
802
+ return { tag: "str", val: hmac };
803
+ }
804
+ // JSON (4)
805
+ case "json_parse": {
806
+ try {
807
+ const jsonStr = this.valueToString(args[0]);
808
+ const obj = JSON.parse(jsonStr);
809
+ const value = this.jsonToValue(obj);
810
+ return { tag: "ok", val: value };
811
+ }
812
+ catch (e) {
813
+ return { tag: "err", val: { tag: "str", val: `JSON parse error: ${String(e)}` } };
814
+ }
815
+ }
816
+ case "json_stringify": {
817
+ try {
818
+ const jsonStr = JSON.stringify(this.valueToJSON(args[0]), null, 0);
819
+ return { tag: "str", val: jsonStr };
820
+ }
821
+ catch (e) {
822
+ return { tag: "err", val: { tag: "str", val: `JSON stringify error: ${String(e)}` } };
823
+ }
824
+ }
825
+ case "json_validate": {
826
+ try {
827
+ const jsonStr = this.valueToString(args[0]);
828
+ JSON.parse(jsonStr);
829
+ return { tag: "bool", val: true };
830
+ }
831
+ catch {
832
+ return { tag: "bool", val: false };
833
+ }
834
+ }
835
+ case "json_pretty": {
836
+ try {
837
+ const jsonStr = this.valueToString(args[0]);
838
+ const obj = JSON.parse(jsonStr);
839
+ const pretty = JSON.stringify(obj, null, 2);
840
+ return { tag: "str", val: pretty };
841
+ }
842
+ catch (e) {
843
+ return { tag: "err", val: { tag: "str", val: `JSON pretty error: ${String(e)}` } };
844
+ }
845
+ }
846
+ // Advanced Strings (3)
847
+ case "starts_with": {
848
+ if (args[0].tag === "str" && args[1].tag === "str") {
849
+ const result = args[0].val.startsWith(args[1].val);
850
+ return { tag: "bool", val: result };
851
+ }
852
+ return { tag: "bool", val: false };
853
+ }
854
+ case "ends_with": {
855
+ if (args[0].tag === "str" && args[1].tag === "str") {
856
+ const result = args[0].val.endsWith(args[1].val);
857
+ return { tag: "bool", val: result };
858
+ }
859
+ return { tag: "bool", val: false };
860
+ }
861
+ case "replace": {
862
+ if (args[0].tag === "str" && args[1].tag === "str" && args[2].tag === "str") {
863
+ const result = args[0].val.replaceAll(args[1].val, args[2].val);
864
+ return { tag: "str", val: result };
865
+ }
866
+ return args[0];
867
+ }
868
+ // Advanced Arrays (3)
869
+ case "reverse": {
870
+ if (args[0].tag === "arr") {
871
+ const reversed = [...args[0].val].reverse();
872
+ return { tag: "arr", val: reversed };
873
+ }
874
+ return args[0];
875
+ }
876
+ case "sort": {
877
+ if (args[0].tag === "arr") {
878
+ const sorted = [...args[0].val].sort((a, b) => {
879
+ const aVal = a.val ?? 0;
880
+ const bVal = b.val ?? 0;
881
+ return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
882
+ });
883
+ return { tag: "arr", val: sorted };
884
+ }
885
+ return args[0];
886
+ }
887
+ case "unique": {
888
+ if (args[0].tag === "arr") {
889
+ const seen = new Set();
890
+ const unique = [];
891
+ for (const item of args[0].val) {
892
+ const key = JSON.stringify(this.valueToJSON(item));
893
+ if (!seen.has(key)) {
894
+ seen.add(key);
895
+ unique.push(item);
896
+ }
897
+ }
898
+ return { tag: "arr", val: unique };
899
+ }
900
+ return args[0];
901
+ }
902
+ // Math (2)
903
+ case "gcd": {
904
+ const a = Math.abs(args[0].val);
905
+ const b = Math.abs(args[1].val);
906
+ const gcd = (x, y) => (y === 0 ? x : gcd(y, x % y));
907
+ return { tag: "i32", val: gcd(a, b) };
908
+ }
909
+ case "lcm": {
910
+ const a = Math.abs(args[0].val);
911
+ const b = Math.abs(args[1].val);
912
+ const gcd = (x, y) => (y === 0 ? x : gcd(y, x % y));
913
+ return { tag: "i32", val: (a * b) / gcd(a, b) };
914
+ }
915
+ // Utils (2)
916
+ case "uuid": {
917
+ const uuid = crypto.randomUUID();
918
+ return { tag: "str", val: uuid };
919
+ }
920
+ case "timestamp": {
921
+ const now = Date.now();
922
+ return { tag: "f64", val: now };
923
+ }
924
+ // Environment (1)
925
+ case "env": {
926
+ const key = this.valueToString(args[0]);
927
+ const value = process.env[key] ?? "";
928
+ return { tag: "str", val: value };
929
+ }
930
+ // HTTP Client (5) — Phase 2
931
+ case "http_get": {
932
+ const url = this.valueToString(args[0]);
933
+ try {
934
+ const result = await this.httpGetAsync(url);
935
+ return result;
936
+ }
937
+ catch (e) {
938
+ return { tag: "err", val: { tag: "str", val: `HTTP error: ${String(e)}` } };
939
+ }
940
+ }
941
+ case "http_post": {
942
+ const url = this.valueToString(args[0]);
943
+ const body = this.valueToString(args[1]);
944
+ try {
945
+ const result = await this.httpPostAsync(url, body);
946
+ return result;
947
+ }
948
+ catch (e) {
949
+ return { tag: "err", val: { tag: "str", val: `HTTP error: ${String(e)}` } };
950
+ }
951
+ }
952
+ case "http_post_json": {
953
+ const url = this.valueToString(args[0]);
954
+ const jsonBody = this.valueToString(args[1]);
955
+ try {
956
+ const result = await this.httpPostJsonAsync(url, jsonBody);
957
+ return result;
958
+ }
959
+ catch (e) {
960
+ return { tag: "err", val: { tag: "str", val: `HTTP error: ${String(e)}` } };
961
+ }
962
+ }
963
+ case "fetch": {
964
+ const url = this.valueToString(args[0]);
965
+ const method = args.length > 1 ? this.valueToString(args[1]) : "GET";
966
+ const headers = args.length > 2 ? args[2] : null;
967
+ const body = args.length > 3 ? this.valueToString(args[3]) : null;
968
+ try {
969
+ const result = await this.fetchAsync(url, method, headers, body);
970
+ return result;
971
+ }
972
+ catch (e) {
973
+ return { tag: "err", val: { tag: "str", val: `HTTP error: ${String(e)}` } };
974
+ }
975
+ }
976
+ // Database (5)
977
+ case "sqlite_open": {
978
+ const path = this.valueToString(args[0]);
979
+ try {
980
+ const db = new db_1.SQLiteDB(path);
981
+ await db.init();
982
+ const dbId = this.nextDbId++;
983
+ this.databases.set(dbId, db);
984
+ return { tag: "db", id: dbId };
985
+ }
986
+ catch (e) {
987
+ return { tag: "err", val: { tag: "str", val: `Database error: ${e.message}` } };
988
+ }
989
+ }
990
+ case "sqlite_query": {
991
+ const db = getDB(args[0]);
992
+ if (!db)
993
+ return dbErr("first argument must be a database");
994
+ const sql = this.valueToString(args[1]);
995
+ const params = args.length > 2 && args[2].tag === "arr" ? args[2].val.map(v => v.val) : [];
996
+ try {
997
+ const rows = await db.query(sql, params);
998
+ const result = rows.map(rowToValue);
999
+ return { tag: "ok", val: { tag: "arr", val: result } };
1000
+ }
1001
+ catch (e) {
1002
+ return dbErr(e.message);
1003
+ }
1004
+ }
1005
+ case "sqlite_execute": {
1006
+ const db = getDB(args[0]);
1007
+ if (!db)
1008
+ return dbErr("first argument must be a database");
1009
+ const sql = this.valueToString(args[1]);
1010
+ const params = args.length > 2 && args[2].tag === "arr" ? args[2].val.map(v => v.val) : [];
1011
+ try {
1012
+ const result = await db.execute(sql, params);
1013
+ return { tag: "ok", val: { tag: "struct", fields: new Map([["changes", { tag: "i32", val: result.changes }]]) } };
1014
+ }
1015
+ catch (e) {
1016
+ return dbErr(e.message);
1017
+ }
1018
+ }
1019
+ case "sqlite_close": {
1020
+ const db = getDB(args[0]);
1021
+ if (!db)
1022
+ return dbErr("argument must be a database");
1023
+ try {
1024
+ await db.close();
1025
+ const dbId = args[0].id;
1026
+ this.databases.delete(dbId);
1027
+ return { tag: "void" };
1028
+ }
1029
+ catch (e) {
1030
+ return dbErr(e.message);
1031
+ }
1032
+ }
1033
+ // Transaction builtins
1034
+ case "sqlite_begin": {
1035
+ const db = getDB(args[0]);
1036
+ if (!db)
1037
+ return dbErr("argument must be a database");
1038
+ const isolation = args.length > 1 ? this.valueToString(args[1]) : "deferred";
1039
+ try {
1040
+ await db.begin(isolation);
1041
+ return { tag: "void" };
1042
+ }
1043
+ catch (e) {
1044
+ return dbErr(e.message);
1045
+ }
1046
+ }
1047
+ case "sqlite_commit": {
1048
+ const db = getDB(args[0]);
1049
+ if (!db)
1050
+ return dbErr("argument must be a database");
1051
+ try {
1052
+ await db.commit();
1053
+ return { tag: "void" };
1054
+ }
1055
+ catch (e) {
1056
+ return dbErr(e.message);
1057
+ }
1058
+ }
1059
+ case "sqlite_rollback": {
1060
+ const db = getDB(args[0]);
1061
+ if (!db)
1062
+ return dbErr("argument must be a database");
1063
+ try {
1064
+ await db.rollback();
1065
+ return { tag: "void" };
1066
+ }
1067
+ catch (e) {
1068
+ return dbErr(e.message);
1069
+ }
1070
+ }
1071
+ // PostgreSQL builtins
1072
+ case "pg_connect": {
1073
+ if (args.length < 5)
1074
+ return dbErr("pg_connect requires 5 arguments");
1075
+ const [host, port, user, password, database] = args.map(a => this.valueToString(a));
1076
+ try {
1077
+ const { PostgreSQLDB } = await Promise.resolve().then(() => __importStar(require("./db")));
1078
+ const db = new PostgreSQLDB({
1079
+ host,
1080
+ port: parseInt(port),
1081
+ user,
1082
+ password,
1083
+ database,
1084
+ });
1085
+ await db.connect();
1086
+ const dbId = this.nextDbId++;
1087
+ this.databases.set(dbId, db);
1088
+ return { tag: "db", id: dbId };
1089
+ }
1090
+ catch (e) {
1091
+ return dbErr(`pg_connect error: ${e.message}`);
1092
+ }
1093
+ }
1094
+ case "pg_query": {
1095
+ const db = getDB(args[0]);
1096
+ if (!db)
1097
+ return dbErr("first argument must be a database");
1098
+ const sql = this.valueToString(args[1]);
1099
+ const params = args.length > 2 && args[2].tag === "arr" ? args[2].val.map(v => v.val) : [];
1100
+ try {
1101
+ const rows = await db.query(sql, params);
1102
+ const result = rows.map(rowToValue);
1103
+ return { tag: "ok", val: { tag: "arr", val: result } };
1104
+ }
1105
+ catch (e) {
1106
+ return dbErr(e.message);
1107
+ }
1108
+ }
1109
+ case "pg_execute": {
1110
+ const db = getDB(args[0]);
1111
+ if (!db)
1112
+ return dbErr("first argument must be a database");
1113
+ const sql = this.valueToString(args[1]);
1114
+ const params = args.length > 2 && args[2].tag === "arr" ? args[2].val.map(v => v.val) : [];
1115
+ try {
1116
+ const result = await db.execute(sql, params);
1117
+ return { tag: "ok", val: { tag: "struct", fields: new Map([["changes", { tag: "i32", val: result.changes }]]) } };
1118
+ }
1119
+ catch (e) {
1120
+ return dbErr(e.message);
1121
+ }
1122
+ }
1123
+ case "pg_close": {
1124
+ const db = getDB(args[0]);
1125
+ if (!db)
1126
+ return dbErr("argument must be a database");
1127
+ try {
1128
+ await db.close();
1129
+ const dbId = args[0].id;
1130
+ this.databases.delete(dbId);
1131
+ return { tag: "void" };
1132
+ }
1133
+ catch (e) {
1134
+ return dbErr(e.message);
1135
+ }
1136
+ }
1137
+ case "pg_begin": {
1138
+ const db = getDB(args[0]);
1139
+ if (!db)
1140
+ return dbErr("argument must be a database");
1141
+ const isolation = args.length > 1 ? this.valueToString(args[1]) : "deferred";
1142
+ try {
1143
+ await db.begin(isolation);
1144
+ return { tag: "void" };
1145
+ }
1146
+ catch (e) {
1147
+ return dbErr(e.message);
1148
+ }
1149
+ }
1150
+ case "pg_commit": {
1151
+ const db = getDB(args[0]);
1152
+ if (!db)
1153
+ return dbErr("argument must be a database");
1154
+ try {
1155
+ await db.commit();
1156
+ return { tag: "void" };
1157
+ }
1158
+ catch (e) {
1159
+ return dbErr(e.message);
1160
+ }
1161
+ }
1162
+ case "pg_rollback": {
1163
+ const db = getDB(args[0]);
1164
+ if (!db)
1165
+ return dbErr("argument must be a database");
1166
+ try {
1167
+ await db.rollback();
1168
+ return { tag: "void" };
1169
+ }
1170
+ catch (e) {
1171
+ return dbErr(e.message);
1172
+ }
1173
+ }
1174
+ case "mysql_connect": {
1175
+ if (args.length < 5)
1176
+ return dbErr("mysql_connect requires 5 arguments");
1177
+ const [host, port, user, password, database] = args.map(a => this.valueToString(a));
1178
+ try {
1179
+ const { MySQLDB } = await Promise.resolve().then(() => __importStar(require("./db")));
1180
+ const db = new MySQLDB({
1181
+ host,
1182
+ port: parseInt(port),
1183
+ user,
1184
+ password,
1185
+ database,
1186
+ });
1187
+ await db.connect();
1188
+ const dbId = this.nextDbId++;
1189
+ this.databases.set(dbId, db);
1190
+ return { tag: "db", id: dbId };
1191
+ }
1192
+ catch (e) {
1193
+ return dbErr(`mysql_connect error: ${e.message}`);
1194
+ }
1195
+ }
1196
+ case "mysql_query": {
1197
+ const db = getDB(args[0]);
1198
+ if (!db)
1199
+ return dbErr("first argument must be a database");
1200
+ const sql = this.valueToString(args[1]);
1201
+ const params = args.length > 2 && args[2].tag === "arr" ? args[2].val.map(v => v.val) : [];
1202
+ try {
1203
+ const rows = await db.query(sql, params);
1204
+ const result = rows.map(rowToValue);
1205
+ return { tag: "ok", val: { tag: "arr", val: result } };
1206
+ }
1207
+ catch (e) {
1208
+ return dbErr(e.message);
1209
+ }
1210
+ }
1211
+ case "mysql_execute": {
1212
+ const db = getDB(args[0]);
1213
+ if (!db)
1214
+ return dbErr("first argument must be a database");
1215
+ const sql = this.valueToString(args[1]);
1216
+ const params = args.length > 2 && args[2].tag === "arr" ? args[2].val.map(v => v.val) : [];
1217
+ try {
1218
+ const result = await db.execute(sql, params);
1219
+ return { tag: "ok", val: { tag: "struct", fields: new Map([["changes", { tag: "i32", val: result.changes }]]) } };
1220
+ }
1221
+ catch (e) {
1222
+ return dbErr(e.message);
1223
+ }
1224
+ }
1225
+ case "mysql_close": {
1226
+ const db = getDB(args[0]);
1227
+ if (!db)
1228
+ return dbErr("argument must be a database");
1229
+ try {
1230
+ await db.close();
1231
+ const dbId = args[0].id;
1232
+ this.databases.delete(dbId);
1233
+ return { tag: "void" };
1234
+ }
1235
+ catch (e) {
1236
+ return dbErr(e.message);
1237
+ }
1238
+ }
1239
+ case "mysql_begin": {
1240
+ const db = getDB(args[0]);
1241
+ if (!db)
1242
+ return dbErr("argument must be a database");
1243
+ const isolation = args.length > 1 ? this.valueToString(args[1]) : "deferred";
1244
+ try {
1245
+ await db.begin(isolation);
1246
+ return { tag: "void" };
1247
+ }
1248
+ catch (e) {
1249
+ return dbErr(e.message);
1250
+ }
1251
+ }
1252
+ case "mysql_commit": {
1253
+ const db = getDB(args[0]);
1254
+ if (!db)
1255
+ return dbErr("argument must be a database");
1256
+ try {
1257
+ await db.commit();
1258
+ return { tag: "void" };
1259
+ }
1260
+ catch (e) {
1261
+ return dbErr(e.message);
1262
+ }
1263
+ }
1264
+ case "mysql_rollback": {
1265
+ const db = getDB(args[0]);
1266
+ if (!db)
1267
+ return dbErr("argument must be a database");
1268
+ try {
1269
+ await db.rollback();
1270
+ return { tag: "void" };
1271
+ }
1272
+ catch (e) {
1273
+ return dbErr(e.message);
1274
+ }
1275
+ }
1276
+ // Math Functions (7) — B-1
1277
+ case "floor": {
1278
+ const num = args[0].val ?? 0;
1279
+ return { tag: "i32", val: Math.floor(num) };
1280
+ }
1281
+ case "ceil": {
1282
+ const num = args[0].val ?? 0;
1283
+ return { tag: "i32", val: Math.ceil(num) };
1284
+ }
1285
+ case "round": {
1286
+ const num = args[0].val ?? 0;
1287
+ return { tag: "i32", val: Math.round(num) };
1288
+ }
1289
+ case "random": {
1290
+ return { tag: "f64", val: Math.random() };
1291
+ }
1292
+ case "sin": {
1293
+ const num = args[0].val ?? 0;
1294
+ return { tag: "f64", val: Math.sin(num) };
1295
+ }
1296
+ case "cos": {
1297
+ const num = args[0].val ?? 0;
1298
+ return { tag: "f64", val: Math.cos(num) };
1299
+ }
1300
+ case "log": {
1301
+ const num = args[0].val ?? 1;
1302
+ if (num <= 0)
1303
+ return { tag: "err", val: { tag: "str", val: "log: invalid argument" } };
1304
+ return { tag: "f64", val: Math.log(num) };
1305
+ }
1306
+ // String Functions (3) — B-2
1307
+ case "index_of": {
1308
+ if (args[0].tag === "str" && args[1].tag === "str") {
1309
+ const idx = args[0].val.indexOf(args[1].val);
1310
+ if (idx >= 0) {
1311
+ return { tag: "some", val: { tag: "i32", val: idx } };
1312
+ }
1313
+ return { tag: "none" };
1314
+ }
1315
+ return { tag: "none" };
1316
+ }
1317
+ case "pad_left": {
1318
+ if (args[0].tag === "str" && args[1].tag === "i32" && args[2].tag === "str") {
1319
+ const char = args[2].val.charAt(0) || " ";
1320
+ const padded = args[0].val.padStart(args[1].val, char);
1321
+ return { tag: "str", val: padded };
1322
+ }
1323
+ return args[0];
1324
+ }
1325
+ case "pad_right": {
1326
+ if (args[0].tag === "str" && args[1].tag === "i32" && args[2].tag === "str") {
1327
+ const char = args[2].val.charAt(0) || " ";
1328
+ const padded = args[0].val.padEnd(args[1].val, char);
1329
+ return { tag: "str", val: padded };
1330
+ }
1331
+ return args[0];
1332
+ }
1333
+ // Regex Functions (3) — B-3
1334
+ case "regex_match": {
1335
+ if (args[0].tag === "str" && args[1].tag === "str") {
1336
+ try {
1337
+ const regex = new RegExp(args[1].val);
1338
+ const match = args[0].val.match(regex);
1339
+ if (match) {
1340
+ return { tag: "some", val: { tag: "str", val: match[0] } };
1341
+ }
1342
+ return { tag: "none" };
1343
+ }
1344
+ catch (e) {
1345
+ return { tag: "err", val: { tag: "str", val: `regex error: ${String(e)}` } };
1346
+ }
1347
+ }
1348
+ return { tag: "none" };
1349
+ }
1350
+ case "regex_find_all": {
1351
+ if (args[0].tag === "str" && args[1].tag === "str") {
1352
+ try {
1353
+ const regex = new RegExp(args[1].val, "g");
1354
+ const matches = args[0].val.match(regex) || [];
1355
+ const arr = matches.map(m => ({ tag: "str", val: m }));
1356
+ return { tag: "arr", val: arr };
1357
+ }
1358
+ catch (e) {
1359
+ return { tag: "err", val: { tag: "str", val: `regex error: ${String(e)}` } };
1360
+ }
1361
+ }
1362
+ return { tag: "arr", val: [] };
1363
+ }
1364
+ case "regex_replace": {
1365
+ if (args[0].tag === "str" && args[1].tag === "str" && args[2].tag === "str") {
1366
+ try {
1367
+ const regex = new RegExp(args[1].val, "g");
1368
+ const replaced = args[0].val.replace(regex, args[2].val);
1369
+ return { tag: "str", val: replaced };
1370
+ }
1371
+ catch (e) {
1372
+ return { tag: "err", val: { tag: "str", val: `regex error: ${String(e)}` } };
1373
+ }
1374
+ }
1375
+ return args[0];
1376
+ }
1377
+ // CSV Functions (2) — B-4
1378
+ case "csv_parse": {
1379
+ if (args[0].tag === "str") {
1380
+ try {
1381
+ const lines = args[0].val.split("\n").filter(l => l.trim());
1382
+ const result = [];
1383
+ for (const line of lines) {
1384
+ const cells = this.parseCsvRow(line);
1385
+ const row = cells.map(c => ({ tag: "str", val: c }));
1386
+ result.push({ tag: "arr", val: row });
1387
+ }
1388
+ return { tag: "arr", val: result };
1389
+ }
1390
+ catch (e) {
1391
+ return { tag: "err", val: { tag: "str", val: `CSV parse error: ${String(e)}` } };
1392
+ }
1393
+ }
1394
+ return { tag: "arr", val: [] };
1395
+ }
1396
+ case "csv_stringify": {
1397
+ if (args[0].tag === "arr") {
1398
+ try {
1399
+ const rows = [];
1400
+ for (const row of args[0].val) {
1401
+ if (row.tag === "arr") {
1402
+ const cells = row.val.map(v => this.valueToString(v));
1403
+ const escaped = cells.map(c => {
1404
+ if (c.includes(",") || c.includes('"') || c.includes("\n")) {
1405
+ return `"${c.replace(/"/g, '""')}"`;
1406
+ }
1407
+ return c;
1408
+ });
1409
+ rows.push(escaped.join(","));
1410
+ }
1411
+ }
1412
+ return { tag: "str", val: rows.join("\n") };
1413
+ }
1414
+ catch (e) {
1415
+ return { tag: "err", val: { tag: "str", val: `CSV stringify error: ${String(e)}` } };
1416
+ }
1417
+ }
1418
+ return { tag: "str", val: "" };
1419
+ }
1420
+ // DateTime Functions (3) — B-5
1421
+ case "now": {
1422
+ return { tag: "f64", val: Date.now() };
1423
+ }
1424
+ case "format_date": {
1425
+ if (args[0].tag === "f64" && args[1].tag === "str") {
1426
+ const timestamp = args[0].val;
1427
+ const format = args[1].val;
1428
+ const date = new Date(timestamp);
1429
+ let result = format;
1430
+ result = result.replace(/YYYY/g, date.getFullYear().toString());
1431
+ result = result.replace(/MM/g, String(date.getMonth() + 1).padStart(2, "0"));
1432
+ result = result.replace(/DD/g, String(date.getDate()).padStart(2, "0"));
1433
+ result = result.replace(/HH/g, String(date.getHours()).padStart(2, "0"));
1434
+ result = result.replace(/mm/g, String(date.getMinutes()).padStart(2, "0"));
1435
+ result = result.replace(/ss/g, String(date.getSeconds()).padStart(2, "0"));
1436
+ return { tag: "str", val: result };
1437
+ }
1438
+ return { tag: "str", val: "" };
1439
+ }
1440
+ case "parse_date": {
1441
+ if (args[0].tag === "str" && args[1].tag === "str") {
1442
+ const dateStr = args[0].val;
1443
+ const format = args[1].val;
1444
+ try {
1445
+ // Simple date parsing - support YYYY-MM-DD HH:mm:ss
1446
+ const timestamp = new Date(dateStr).getTime();
1447
+ if (isNaN(timestamp)) {
1448
+ return { tag: "err", val: { tag: "str", val: "Invalid date format" } };
1449
+ }
1450
+ return { tag: "ok", val: { tag: "f64", val: timestamp } };
1451
+ }
1452
+ catch (e) {
1453
+ return { tag: "err", val: { tag: "str", val: `Date parse error: ${String(e)}` } };
1454
+ }
1455
+ }
1456
+ return { tag: "err", val: { tag: "str", val: "Invalid arguments" } };
1457
+ }
1458
+ // YAML Functions (2) — v4.3 Extension
1459
+ case "yaml_parse": {
1460
+ if (args[0].tag === "str") {
1461
+ try {
1462
+ const yaml = args[0].val;
1463
+ const obj = this.parseYAML(yaml);
1464
+ return { tag: "ok", val: this.jsonToValue(obj) };
1465
+ }
1466
+ catch (e) {
1467
+ return { tag: "err", val: { tag: "str", val: `YAML parse error: ${String(e)}` } };
1468
+ }
1469
+ }
1470
+ return { tag: "err", val: { tag: "str", val: "Invalid arguments" } };
1471
+ }
1472
+ case "yaml_stringify": {
1473
+ try {
1474
+ const yamlStr = this.valueToYAML(args[0], 0);
1475
+ return { tag: "str", val: yamlStr };
1476
+ }
1477
+ catch (e) {
1478
+ return { tag: "err", val: { tag: "str", val: `YAML stringify error: ${String(e)}` } };
1479
+ }
1480
+ }
1481
+ case "http_server_create": {
1482
+ const port = args[0].val;
1483
+ try {
1484
+ const result = await this.httpServerCreateAsync(port);
1485
+ return result;
1486
+ }
1487
+ catch (e) {
1488
+ return { tag: "err", val: { tag: "str", val: `HTTP server error: ${String(e)}` } };
1489
+ }
1490
+ }
1491
+ case "http_server_accept": {
1492
+ const serverId = args[0].val;
1493
+ try {
1494
+ const result = await this.httpServerAcceptAsync(serverId);
1495
+ return result;
1496
+ }
1497
+ catch (e) {
1498
+ return { tag: "err", val: { tag: "str", val: `HTTP accept error: ${String(e)}` } };
1499
+ }
1500
+ }
1501
+ case "http_server_respond": {
1502
+ const serverId = args[0].val;
1503
+ const reqId = args[1].val;
1504
+ const status = args[2].val;
1505
+ const headersJson = args[3].val;
1506
+ const body = this.valueToString(args[4]);
1507
+ try {
1508
+ await this.httpServerRespondAsync(serverId, reqId, status, headersJson, body);
1509
+ return { tag: "void" };
1510
+ }
1511
+ catch (e) {
1512
+ return { tag: "err", val: { tag: "str", val: `HTTP respond error: ${String(e)}` } };
1513
+ }
1514
+ }
1515
+ case "exec_command": {
1516
+ const cmd = args[0].val;
1517
+ let cmdArgs = [];
1518
+ if (args[1].tag === "arr") {
1519
+ cmdArgs = args[1].val.map((v) => this.valueToString(v));
1520
+ }
1521
+ try {
1522
+ const result = await this.execCommandAsync(cmd, cmdArgs);
1523
+ return result;
1524
+ }
1525
+ catch (e) {
1526
+ return { tag: "err", val: { tag: "str", val: `Command execution error: ${String(e)}` } };
1527
+ }
1528
+ }
1529
+ case "char_code": {
1530
+ // char_code("A") → 65 (문자의 ASCII 코드)
1531
+ const s = args[0].val;
1532
+ if (s.length === 0) {
1533
+ return { tag: "i32", val: 0 };
1534
+ }
1535
+ return { tag: "i32", val: s.charCodeAt(0) };
1536
+ }
1537
+ case "chr": {
1538
+ // chr(65) → "A" (ASCII 코드를 문자로)
1539
+ const n = args[0].val;
1540
+ return { tag: "str", val: String.fromCharCode(n) };
1541
+ }
1542
+ default:
1543
+ throw new Error(`panic: unknown builtin '${name}'`);
1544
+ }
1545
+ }
1546
+ // ============================================================
1547
+ // 유틸리티
1548
+ // ============================================================
1549
+ readI32(actor) {
1550
+ const b0 = this.chunk.code[actor.ip++];
1551
+ const b1 = this.chunk.code[actor.ip++];
1552
+ const b2 = this.chunk.code[actor.ip++];
1553
+ const b3 = this.chunk.code[actor.ip++];
1554
+ return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
1555
+ }
1556
+ readF64(actor) {
1557
+ const buf = new ArrayBuffer(8);
1558
+ const bytes = new Uint8Array(buf);
1559
+ for (let i = 0; i < 8; i++)
1560
+ bytes[i] = this.chunk.code[actor.ip++];
1561
+ return new Float64Array(buf)[0];
1562
+ }
1563
+ valueToString(v) {
1564
+ switch (v.tag) {
1565
+ case "i32":
1566
+ case "f64": return String(v.val);
1567
+ case "str": return v.val;
1568
+ case "bool": return v.val ? "true" : "false";
1569
+ case "void": return "void";
1570
+ case "none": return "None";
1571
+ case "arr": return `[${v.val.map((e) => this.valueToString(e)).join(", ")}]`;
1572
+ case "struct": {
1573
+ const entries = [...v.fields.entries()].map(([k, val]) => `${k}: ${this.valueToString(val)}`);
1574
+ return `{ ${entries.join(", ")} }`;
1575
+ }
1576
+ case "ok": return `Ok(${this.valueToString(v.val)})`;
1577
+ case "err": return `Err(${this.valueToString(v.val)})`;
1578
+ case "some": return `Some(${this.valueToString(v.val)})`;
1579
+ case "chan": return `channel(${v.id})`;
1580
+ case "db": return `database(${v.id})`;
1581
+ }
1582
+ }
1583
+ valuesEqual(a, b) {
1584
+ if (a.tag !== b.tag)
1585
+ return false;
1586
+ if (a.tag === "void" && b.tag === "void")
1587
+ return true;
1588
+ if (a.tag === "none" && b.tag === "none")
1589
+ return true;
1590
+ if ("val" in a && "val" in b)
1591
+ return a.val === b.val;
1592
+ return false;
1593
+ }
1594
+ deepClone(v) {
1595
+ switch (v.tag) {
1596
+ case "arr": return { tag: "arr", val: v.val.map((e) => this.deepClone(e)) };
1597
+ case "struct": {
1598
+ const fields = new Map();
1599
+ for (const [k, val] of v.fields)
1600
+ fields.set(k, this.deepClone(val));
1601
+ return { tag: "struct", fields };
1602
+ }
1603
+ case "ok": return { tag: "ok", val: this.deepClone(v.val) };
1604
+ case "err": return { tag: "err", val: this.deepClone(v.val) };
1605
+ case "some": return { tag: "some", val: this.deepClone(v.val) };
1606
+ default: return v; // Copy 타입은 그대로
1607
+ }
1608
+ }
1609
+ // ============================================================
1610
+ // JSON Conversion (Phase 7)
1611
+ // ============================================================
1612
+ jsonToValue(obj) {
1613
+ if (obj === null)
1614
+ return { tag: "none" };
1615
+ if (typeof obj === "boolean")
1616
+ return { tag: "bool", val: obj };
1617
+ if (typeof obj === "number")
1618
+ return { tag: "i32", val: Math.floor(obj) };
1619
+ if (typeof obj === "string")
1620
+ return { tag: "str", val: obj };
1621
+ if (Array.isArray(obj)) {
1622
+ return { tag: "arr", val: obj.map((v) => this.jsonToValue(v)) };
1623
+ }
1624
+ if (typeof obj === "object") {
1625
+ const fields = new Map();
1626
+ for (const [k, v] of Object.entries(obj)) {
1627
+ fields.set(k, this.jsonToValue(v));
1628
+ }
1629
+ return { tag: "struct", fields };
1630
+ }
1631
+ return { tag: "void" };
1632
+ }
1633
+ valueToJSON(v) {
1634
+ switch (v.tag) {
1635
+ case "i32":
1636
+ case "f64":
1637
+ return v.val;
1638
+ case "bool":
1639
+ return v.val;
1640
+ case "str":
1641
+ return v.val;
1642
+ case "arr":
1643
+ return v.val.map((item) => this.valueToJSON(item));
1644
+ case "struct":
1645
+ const obj = {};
1646
+ for (const [k, val] of v.fields.entries()) {
1647
+ obj[k] = this.valueToJSON(val);
1648
+ }
1649
+ return obj;
1650
+ case "ok":
1651
+ return this.valueToJSON(v.val);
1652
+ case "err":
1653
+ return { error: this.valueToJSON(v.val) };
1654
+ case "some":
1655
+ return this.valueToJSON(v.val);
1656
+ case "none":
1657
+ return null;
1658
+ default:
1659
+ return null;
1660
+ }
1661
+ }
1662
+ parseYAML(yaml) {
1663
+ const lines = yaml.split("\n").map((l) => l);
1664
+ const result = {};
1665
+ let currentObj = result;
1666
+ const stack = [result];
1667
+ let lastIndent = -1;
1668
+ for (const line of lines) {
1669
+ if (!line.trim() || line.trim().startsWith("#"))
1670
+ continue;
1671
+ const indent = line.length - line.trimLeft().length;
1672
+ const trimmed = line.trim();
1673
+ // Handle indent changes
1674
+ if (indent > lastIndent) {
1675
+ // Push to stack
1676
+ if (lastIndent >= 0 && typeof currentObj === "object") {
1677
+ stack.push(currentObj);
1678
+ }
1679
+ }
1680
+ else if (indent < lastIndent) {
1681
+ // Pop from stack
1682
+ while (stack.length > 1 && indent < lastIndent) {
1683
+ stack.pop();
1684
+ currentObj = stack[stack.length - 1];
1685
+ lastIndent -= 2;
1686
+ }
1687
+ }
1688
+ lastIndent = indent;
1689
+ // Parse key: value
1690
+ if (trimmed.includes(":")) {
1691
+ const colonIdx = trimmed.indexOf(":");
1692
+ const key = trimmed.substring(0, colonIdx).trim();
1693
+ const valueStr = trimmed.substring(colonIdx + 1).trim();
1694
+ if (valueStr === "") {
1695
+ // Nested object
1696
+ currentObj[key] = {};
1697
+ currentObj = currentObj[key];
1698
+ }
1699
+ else {
1700
+ // Scalar value
1701
+ currentObj[key] = this.parseYAMLValue(valueStr);
1702
+ }
1703
+ }
1704
+ }
1705
+ return result;
1706
+ }
1707
+ parseYAMLValue(valueStr) {
1708
+ if (valueStr === "true")
1709
+ return true;
1710
+ if (valueStr === "false")
1711
+ return false;
1712
+ if (valueStr === "null")
1713
+ return null;
1714
+ if (!isNaN(Number(valueStr)))
1715
+ return Number(valueStr);
1716
+ if (valueStr.startsWith('"') && valueStr.endsWith('"')) {
1717
+ return valueStr.slice(1, -1).replace(/\\"/g, '"');
1718
+ }
1719
+ if (valueStr.startsWith("'") && valueStr.endsWith("'")) {
1720
+ return valueStr.slice(1, -1);
1721
+ }
1722
+ return valueStr;
1723
+ }
1724
+ valueToYAML(v, indent = 0) {
1725
+ const indentStr = " ".repeat(indent);
1726
+ const nextIndentStr = " ".repeat(indent + 1);
1727
+ switch (v.tag) {
1728
+ case "i32":
1729
+ case "f64":
1730
+ case "bool":
1731
+ return String(v.val);
1732
+ case "str":
1733
+ return `"${v.val.replace(/"/g, '\\"')}"`;
1734
+ case "arr": {
1735
+ const items = v.val;
1736
+ if (items.length === 0)
1737
+ return "[]";
1738
+ return "[\n" + items.map((item) => nextIndentStr + this.valueToYAML(item, indent + 1)).join(",\n") + "\n" + indentStr + "]";
1739
+ }
1740
+ case "struct": {
1741
+ const fields = v.fields;
1742
+ const lines = [];
1743
+ for (const [key, val] of fields) {
1744
+ lines.push(`${key}: ${this.valueToYAML(val, indent + 1)}`);
1745
+ }
1746
+ return lines.join("\n" + nextIndentStr);
1747
+ }
1748
+ case "ok":
1749
+ return this.valueToYAML(v.val, indent);
1750
+ case "err":
1751
+ return `error: ${this.valueToYAML(v.val, indent)}`;
1752
+ case "some":
1753
+ return this.valueToYAML(v.val, indent);
1754
+ case "none":
1755
+ return "null";
1756
+ default:
1757
+ return "null";
1758
+ }
1759
+ }
1760
+ parseCsvRow(line) {
1761
+ const cells = [];
1762
+ let current = "";
1763
+ let inQuotes = false;
1764
+ for (let i = 0; i < line.length; i++) {
1765
+ const char = line[i];
1766
+ if (char === '"' && (i === 0 || line[i - 1] !== "\\")) {
1767
+ inQuotes = !inQuotes;
1768
+ }
1769
+ else if (char === "," && !inQuotes) {
1770
+ cells.push(current);
1771
+ current = "";
1772
+ }
1773
+ else {
1774
+ current += char;
1775
+ }
1776
+ }
1777
+ cells.push(current);
1778
+ return cells;
1779
+ }
1780
+ // ============================================================
1781
+ // HTTP Client Implementation (Phase 2) — fetch based (async)
1782
+ // ============================================================
1783
+ async httpGetAsync(url) {
1784
+ try {
1785
+ const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
1786
+ const body = await res.text();
1787
+ return { tag: "ok", val: { tag: "str", val: body } };
1788
+ }
1789
+ catch (e) {
1790
+ return { tag: "err", val: { tag: "str", val: e.message } };
1791
+ }
1792
+ }
1793
+ async httpPostAsync(url, body) {
1794
+ try {
1795
+ const res = await fetch(url, {
1796
+ method: "POST",
1797
+ body,
1798
+ signal: AbortSignal.timeout(5000),
1799
+ });
1800
+ const responseBody = await res.text();
1801
+ return { tag: "ok", val: { tag: "str", val: responseBody } };
1802
+ }
1803
+ catch (e) {
1804
+ return { tag: "err", val: { tag: "str", val: e.message } };
1805
+ }
1806
+ }
1807
+ async httpPostJsonAsync(url, jsonBody) {
1808
+ try {
1809
+ const res = await fetch(url, {
1810
+ method: "POST",
1811
+ headers: { "Content-Type": "application/json" },
1812
+ body: jsonBody,
1813
+ signal: AbortSignal.timeout(5000),
1814
+ });
1815
+ const responseBody = await res.text();
1816
+ return { tag: "ok", val: { tag: "str", val: responseBody } };
1817
+ }
1818
+ catch (e) {
1819
+ return { tag: "err", val: { tag: "str", val: e.message } };
1820
+ }
1821
+ }
1822
+ async fetchAsync(url, method, headers, body) {
1823
+ try {
1824
+ const options = { method, signal: AbortSignal.timeout(5000) };
1825
+ if (headers && headers.tag === "struct") {
1826
+ options.headers = {};
1827
+ for (const [k, v] of headers.fields) {
1828
+ options.headers[k] = this.valueToString(v);
1829
+ }
1830
+ }
1831
+ if (body) {
1832
+ options.body = body;
1833
+ }
1834
+ const res = await fetch(url, options);
1835
+ const responseBody = await res.text();
1836
+ return { tag: "ok", val: { tag: "str", val: responseBody } };
1837
+ }
1838
+ catch (e) {
1839
+ return { tag: "err", val: { tag: "str", val: e.message } };
1840
+ }
1841
+ }
1842
+ // ============================================================
1843
+ // HTTP Server Builtins
1844
+ // ============================================================
1845
+ async httpServerCreateAsync(port) {
1846
+ try {
1847
+ const express = (await Promise.resolve().then(() => __importStar(require("express")))).default;
1848
+ const app = express();
1849
+ app.use(express.json());
1850
+ app.use(express.urlencoded({ extended: true }));
1851
+ const requestQueue = [];
1852
+ const responseMap = new Map(); // reqId -> res
1853
+ let nextReqId = 0;
1854
+ app.all("*", (req, res) => {
1855
+ const reqId = String(nextReqId++);
1856
+ const reqObj = {
1857
+ reqId,
1858
+ method: req.method,
1859
+ path: req.path,
1860
+ headers: req.headers,
1861
+ body: req.body || {},
1862
+ query: req.query || {},
1863
+ };
1864
+ requestQueue.push(reqObj);
1865
+ responseMap.set(reqId, res);
1866
+ });
1867
+ const serverId = this.nextServerId++;
1868
+ this.httpServers.set(serverId, {
1869
+ app,
1870
+ server: null,
1871
+ requestQueue,
1872
+ responseMap,
1873
+ port,
1874
+ });
1875
+ // Start server
1876
+ const server = app.listen(port);
1877
+ const serverData = this.httpServers.get(serverId);
1878
+ serverData.server = server;
1879
+ return { tag: "ok", val: { tag: "i32", val: serverId } };
1880
+ }
1881
+ catch (e) {
1882
+ return { tag: "err", val: { tag: "str", val: e.message } };
1883
+ }
1884
+ }
1885
+ async httpServerAcceptAsync(serverId) {
1886
+ const serverData = this.httpServers.get(serverId);
1887
+ if (!serverData) {
1888
+ return { tag: "err", val: { tag: "str", val: "Server not found" } };
1889
+ }
1890
+ // Poll for requests
1891
+ while (serverData.requestQueue.length === 0) {
1892
+ await new Promise((resolve) => setTimeout(resolve, 10));
1893
+ }
1894
+ const req = serverData.requestQueue.shift();
1895
+ // Return as JSON string that can be parsed by FreeLang
1896
+ const reqJson = JSON.stringify({
1897
+ id: req.reqId,
1898
+ method: req.method,
1899
+ path: req.path,
1900
+ query: req.query,
1901
+ body: typeof req.body === "string" ? req.body : JSON.stringify(req.body),
1902
+ });
1903
+ return { tag: "ok", val: { tag: "str", val: reqJson } };
1904
+ }
1905
+ async httpServerRespondAsync(serverId, reqId, status, headersJson, body) {
1906
+ const serverData = this.httpServers.get(serverId);
1907
+ if (!serverData)
1908
+ throw new Error("Server not found");
1909
+ const res = serverData.responseMap.get(reqId);
1910
+ if (!res)
1911
+ throw new Error(`Request ${reqId} not found`);
1912
+ try {
1913
+ const headers = JSON.parse(headersJson);
1914
+ res.status(status);
1915
+ Object.entries(headers).forEach(([k, v]) => {
1916
+ res.set(k, v);
1917
+ });
1918
+ res.send(body);
1919
+ }
1920
+ finally {
1921
+ serverData.responseMap.delete(reqId);
1922
+ }
1923
+ }
1924
+ async execCommandAsync(cmd, args) {
1925
+ try {
1926
+ const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process")));
1927
+ const proc = spawn(cmd, args);
1928
+ let stdout = "";
1929
+ let stderr = "";
1930
+ return new Promise((resolve) => {
1931
+ proc.stdout?.on("data", (data) => {
1932
+ stdout += data.toString();
1933
+ });
1934
+ proc.stderr?.on("data", (data) => {
1935
+ stderr += data.toString();
1936
+ });
1937
+ proc.on("close", (code) => {
1938
+ if (code === 0) {
1939
+ resolve({ tag: "ok", val: { tag: "str", val: stdout } });
1940
+ }
1941
+ else {
1942
+ resolve({ tag: "err", val: { tag: "str", val: stderr || `Exit code ${code}` } });
1943
+ }
1944
+ });
1945
+ proc.on("error", (err) => {
1946
+ resolve({ tag: "err", val: { tag: "str", val: err.message } });
1947
+ });
1948
+ });
1949
+ }
1950
+ catch (e) {
1951
+ return { tag: "err", val: { tag: "str", val: e.message } };
1952
+ }
1953
+ }
1954
+ }
1955
+ exports.VM = VM;
1956
+ //# sourceMappingURL=vm.js.map