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
@@ -0,0 +1,322 @@
1
+ /**
2
+ * FreeLang Runtime Value System
3
+ * 모든 런타임 값을 나타내는 tagged union 타입
4
+ */
5
+
6
+ /**
7
+ * 런타임에서 사용되는 모든 값의 타입
8
+ * Discriminated Union 패턴 사용으로 타입 안전성 보장
9
+ */
10
+ export type Value =
11
+ | { type: 'null' }
12
+ | { type: 'bool'; value: boolean }
13
+ | { type: 'int'; value: number }
14
+ | { type: 'float'; value: number }
15
+ | { type: 'string'; value: string }
16
+ | { type: 'array'; value: Value[] }
17
+ | { type: 'object'; value: Map<string, Value> }
18
+ | { type: 'function'; params: string[]; body: any }
19
+ | { type: 'builtin'; name: string; arity: number };
20
+
21
+ /**
22
+ * Value 타입의 유틸리티 함수들
23
+ */
24
+ export class ValueHandler {
25
+ /**
26
+ * Value를 문자열로 변환
27
+ */
28
+ static toString(v: Value): string {
29
+ switch (v.type) {
30
+ case 'null':
31
+ return 'null';
32
+ case 'bool':
33
+ return v.value ? 'true' : 'false';
34
+ case 'int':
35
+ return String(v.value);
36
+ case 'float':
37
+ return String(v.value);
38
+ case 'string':
39
+ return v.value;
40
+ case 'array':
41
+ return `[${v.value.map(item => this.toString(item)).join(', ')}]`;
42
+ case 'object':
43
+ const entries = Array.from(v.value.entries());
44
+ const pairs = entries.map(([k, val]) => `${k}: ${this.toString(val)}`);
45
+ return `{${pairs.join(', ')}}`;
46
+ case 'function':
47
+ return `<function(${v.params.join(', ')})>`;
48
+ case 'builtin':
49
+ return `<builtin: ${v.name}/${v.arity}>`;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Value를 숫자로 변환 (JavaScript 의미론)
55
+ */
56
+ static toNumber(v: Value): number {
57
+ switch (v.type) {
58
+ case 'null':
59
+ return 0;
60
+ case 'bool':
61
+ return v.value ? 1 : 0;
62
+ case 'int':
63
+ case 'float':
64
+ return v.value;
65
+ case 'string':
66
+ const n = parseFloat(v.value);
67
+ return isNaN(n) ? 0 : n;
68
+ case 'array':
69
+ return v.value.length; // 배열의 길이
70
+ case 'object':
71
+ return v.value.size; // 객체의 크기
72
+ default:
73
+ return 0;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Value를 불린으로 변환 (JavaScript truthy/falsy 의미론)
79
+ */
80
+ static toBoolean(v: Value): boolean {
81
+ switch (v.type) {
82
+ case 'null':
83
+ return false;
84
+ case 'bool':
85
+ return v.value;
86
+ case 'int':
87
+ return v.value !== 0;
88
+ case 'float':
89
+ return v.value !== 0.0;
90
+ case 'string':
91
+ return v.value.length > 0;
92
+ case 'array':
93
+ return v.value.length > 0;
94
+ case 'object':
95
+ return v.value.size > 0;
96
+ case 'function':
97
+ case 'builtin':
98
+ return true;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Value의 타입을 문자열로 반환
104
+ */
105
+ static typeOf(v: Value): string {
106
+ switch (v.type) {
107
+ case 'null':
108
+ return 'null';
109
+ case 'bool':
110
+ return 'bool';
111
+ case 'int':
112
+ return 'int';
113
+ case 'float':
114
+ return 'float';
115
+ case 'string':
116
+ return 'string';
117
+ case 'array':
118
+ return 'array';
119
+ case 'object':
120
+ return 'object';
121
+ case 'function':
122
+ return 'function';
123
+ case 'builtin':
124
+ return 'builtin';
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Value의 길이를 반환 (string, array, object 타입)
130
+ */
131
+ static getLength(v: Value): number {
132
+ switch (v.type) {
133
+ case 'string':
134
+ return v.value.length;
135
+ case 'array':
136
+ return v.value.length;
137
+ case 'object':
138
+ return v.value.size;
139
+ default:
140
+ return 0;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * 두 Value가 같은지 비교 (==)
146
+ */
147
+ static equals(a: Value, b: Value): boolean {
148
+ // 같은 타입끼리만 비교 (엄격한 비교)
149
+ if (a.type !== b.type) {
150
+ return false;
151
+ }
152
+
153
+ switch (a.type) {
154
+ case 'null':
155
+ return true;
156
+ case 'bool':
157
+ return a.value === (b as typeof a).value;
158
+ case 'int':
159
+ case 'float':
160
+ return a.value === (b as typeof a).value;
161
+ case 'string':
162
+ return a.value === (b as typeof a).value;
163
+ case 'array': {
164
+ const bArr = (b as typeof a).value;
165
+ if (a.value.length !== bArr.length) return false;
166
+ return a.value.every((v, i) => this.equals(v, bArr[i]));
167
+ }
168
+ case 'object': {
169
+ const bObj = (b as typeof a).value;
170
+ if (a.value.size !== bObj.size) return false;
171
+ for (const [k, v] of a.value) {
172
+ const bVal = bObj.get(k);
173
+ if (!bVal || !this.equals(v, bVal)) return false;
174
+ }
175
+ return true;
176
+ }
177
+ case 'function':
178
+ return false; // 함수는 참조로 비교 (추후 개선 가능)
179
+ case 'builtin':
180
+ return a.name === (b as typeof a).name;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * 숫자 비교 (<)
186
+ */
187
+ static lessThan(a: Value, b: Value): boolean {
188
+ const aNum = this.toNumber(a);
189
+ const bNum = this.toNumber(b);
190
+ return aNum < bNum;
191
+ }
192
+
193
+ /**
194
+ * Value를 깊은 복사 (Deep Clone)
195
+ */
196
+ static clone(v: Value): Value {
197
+ switch (v.type) {
198
+ case 'null':
199
+ case 'bool':
200
+ case 'int':
201
+ case 'float':
202
+ case 'string':
203
+ return v;
204
+ case 'array':
205
+ return {
206
+ type: 'array',
207
+ value: v.value.map(item => this.clone(item)),
208
+ };
209
+ case 'object': {
210
+ const newMap = new Map<string, Value>();
211
+ for (const [k, val] of v.value) {
212
+ newMap.set(k, this.clone(val));
213
+ }
214
+ return {
215
+ type: 'object',
216
+ value: newMap,
217
+ };
218
+ }
219
+ case 'function':
220
+ return v; // 함수는 참조로 복사
221
+ case 'builtin':
222
+ return v;
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Value를 JSON 호환 형식으로 변환
228
+ */
229
+ static toJSON(v: Value): any {
230
+ switch (v.type) {
231
+ case 'null':
232
+ return null;
233
+ case 'bool':
234
+ return v.value;
235
+ case 'int':
236
+ case 'float':
237
+ return v.value;
238
+ case 'string':
239
+ return v.value;
240
+ case 'array':
241
+ return v.value.map(item => this.toJSON(item));
242
+ case 'object': {
243
+ const obj: any = {};
244
+ for (const [k, val] of v.value) {
245
+ obj[k] = this.toJSON(val);
246
+ }
247
+ return obj;
248
+ }
249
+ case 'function':
250
+ return '[Function]';
251
+ case 'builtin':
252
+ return '[Builtin]';
253
+ }
254
+ }
255
+
256
+ /**
257
+ * JSON에서 Value로 변환
258
+ */
259
+ static fromJSON(obj: any): Value {
260
+ if (obj === null) {
261
+ return { type: 'null' };
262
+ }
263
+ if (typeof obj === 'boolean') {
264
+ return { type: 'bool', value: obj };
265
+ }
266
+ if (typeof obj === 'number') {
267
+ return Number.isInteger(obj)
268
+ ? { type: 'int', value: obj }
269
+ : { type: 'float', value: obj };
270
+ }
271
+ if (typeof obj === 'string') {
272
+ return { type: 'string', value: obj };
273
+ }
274
+ if (Array.isArray(obj)) {
275
+ return {
276
+ type: 'array',
277
+ value: obj.map(item => this.fromJSON(item)),
278
+ };
279
+ }
280
+ if (typeof obj === 'object') {
281
+ const map = new Map<string, Value>();
282
+ for (const [k, v] of Object.entries(obj)) {
283
+ map.set(k, this.fromJSON(v));
284
+ }
285
+ return { type: 'object', value: map };
286
+ }
287
+ return { type: 'null' };
288
+ }
289
+ }
290
+
291
+ /**
292
+ * 자주 사용되는 Value 상수들
293
+ */
294
+ export const NULL: Value = { type: 'null' };
295
+ export const TRUE: Value = { type: 'bool', value: true };
296
+ export const FALSE: Value = { type: 'bool', value: false };
297
+
298
+ /**
299
+ * Value를 생성하는 헬퍼 함수들
300
+ */
301
+ export const Value = {
302
+ null: (): Value => ({ type: 'null' }),
303
+ bool: (b: boolean): Value => ({ type: 'bool', value: b }),
304
+ int: (n: number): Value => ({ type: 'int', value: Math.floor(n) }),
305
+ float: (n: number): Value => ({ type: 'float', value: n }),
306
+ string: (s: string): Value => ({ type: 'string', value: s }),
307
+ array: (arr: Value[]): Value => ({ type: 'array', value: arr }),
308
+ object: (obj: Map<string, Value>): Value => ({
309
+ type: 'object',
310
+ value: obj,
311
+ }),
312
+ function: (params: string[], body: any): Value => ({
313
+ type: 'function',
314
+ params,
315
+ body,
316
+ }),
317
+ builtin: (name: string, arity: number): Value => ({
318
+ type: 'builtin',
319
+ name,
320
+ arity,
321
+ }),
322
+ };
@@ -0,0 +1,354 @@
1
+ /**
2
+ * FreeLang Virtual Machine
3
+ * 스택 기반의 간단한 인터프리터
4
+ */
5
+
6
+ import { Value, ValueHandler } from './value';
7
+ import { Instruction, OpcodeType } from './bytecode';
8
+
9
+ /**
10
+ * 함수 호출 프레임
11
+ */
12
+ interface Frame {
13
+ name: string;
14
+ returnAddr: number;
15
+ localVariables: Map<number, Value>;
16
+ }
17
+
18
+ /**
19
+ * 런타임 에러
20
+ */
21
+ export class RuntimeError extends Error {
22
+ constructor(
23
+ message: string,
24
+ public line: number = 0,
25
+ public column: number = 0
26
+ ) {
27
+ super(message);
28
+ this.name = 'RuntimeError';
29
+ }
30
+ }
31
+
32
+ /**
33
+ * FreeLang Virtual Machine
34
+ */
35
+ export class VirtualMachine {
36
+ private stack: Value[] = [];
37
+ private memory: Map<number, Value> = new Map(); // 힙 메모리
38
+ private globals: Map<string, Value> = new Map(); // 전역 변수
39
+ private locals: Map<number, Value> = new Map(); // 지역 변수
40
+ private callStack: Frame[] = [];
41
+ private pc: number = 0; // 프로그램 카운터
42
+ private instructions: Instruction[] = [];
43
+ private builtins: Map<string, Function> = new Map();
44
+ private nextPtr: number = 0; // 메모리 포인터
45
+
46
+ constructor() {
47
+ this.registerBuiltins();
48
+ }
49
+
50
+ /**
51
+ * 표준 내장 함수 등록
52
+ */
53
+ private registerBuiltins(): void {
54
+ // 나중에 stdlib 모듈에서 자동으로 등록됨
55
+ }
56
+
57
+ /**
58
+ * Bytecode 실행
59
+ */
60
+ public execute(instructions: Instruction[]): Value {
61
+ this.instructions = instructions;
62
+ this.pc = 0;
63
+ this.stack = [];
64
+
65
+ try {
66
+ while (this.pc < instructions.length) {
67
+ const instr = instructions[this.pc];
68
+ this.executeInstruction(instr);
69
+ this.pc++;
70
+ }
71
+ return this.stack.pop() || { type: 'null' };
72
+ } catch (error) {
73
+ if (error instanceof RuntimeError) {
74
+ throw error;
75
+ }
76
+ throw new RuntimeError(
77
+ `VM Error: ${error instanceof Error ? error.message : String(error)}`,
78
+ instructions[this.pc]?.line || 0,
79
+ instructions[this.pc]?.column || 0
80
+ );
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 개별 명령어 실행
86
+ */
87
+ private executeInstruction(instr: Instruction): void {
88
+ switch (instr.op) {
89
+ case OpcodeType.PUSH:
90
+ this.stack.push(instr.arg);
91
+ break;
92
+
93
+ case OpcodeType.POP:
94
+ if (this.stack.length === 0) {
95
+ throw new RuntimeError('Stack underflow');
96
+ }
97
+ this.stack.pop();
98
+ break;
99
+
100
+ case OpcodeType.DUP:
101
+ if (this.stack.length === 0) {
102
+ throw new RuntimeError('Stack underflow');
103
+ }
104
+ const top = this.stack[this.stack.length - 1];
105
+ this.stack.push(ValueHandler.clone(top));
106
+ break;
107
+
108
+ // 산술 연산
109
+ case OpcodeType.ADD:
110
+ this.binaryOp((a, b) => a + b, instr);
111
+ break;
112
+
113
+ case OpcodeType.SUB:
114
+ this.binaryOp((a, b) => a - b, instr);
115
+ break;
116
+
117
+ case OpcodeType.MUL:
118
+ this.binaryOp((a, b) => a * b, instr);
119
+ break;
120
+
121
+ case OpcodeType.DIV:
122
+ this.binaryOp((a, b) => {
123
+ if (b === 0) throw new RuntimeError('Division by zero');
124
+ return a / b;
125
+ }, instr);
126
+ break;
127
+
128
+ case OpcodeType.MOD:
129
+ this.binaryOp((a, b) => {
130
+ if (b === 0) throw new RuntimeError('Division by zero');
131
+ return a % b;
132
+ }, instr);
133
+ break;
134
+
135
+ // 비교 연산
136
+ case OpcodeType.EQ: {
137
+ const b = this.popValue(instr);
138
+ const a = this.popValue(instr);
139
+ const result = ValueHandler.equals(a, b);
140
+ this.stack.push({ type: 'bool', value: result });
141
+ break;
142
+ }
143
+
144
+ case OpcodeType.NE: {
145
+ const b = this.popValue(instr);
146
+ const a = this.popValue(instr);
147
+ const result = !ValueHandler.equals(a, b);
148
+ this.stack.push({ type: 'bool', value: result });
149
+ break;
150
+ }
151
+
152
+ case OpcodeType.LT: {
153
+ const b = this.popValue(instr);
154
+ const a = this.popValue(instr);
155
+ const result = ValueHandler.lessThan(a, b);
156
+ this.stack.push({ type: 'bool', value: result });
157
+ break;
158
+ }
159
+
160
+ case OpcodeType.LTE: {
161
+ const b = this.popValue(instr);
162
+ const a = this.popValue(instr);
163
+ const result =
164
+ ValueHandler.lessThan(a, b) || ValueHandler.equals(a, b);
165
+ this.stack.push({ type: 'bool', value: result });
166
+ break;
167
+ }
168
+
169
+ case OpcodeType.GT: {
170
+ const b = this.popValue(instr);
171
+ const a = this.popValue(instr);
172
+ const result = ValueHandler.lessThan(b, a);
173
+ this.stack.push({ type: 'bool', value: result });
174
+ break;
175
+ }
176
+
177
+ case OpcodeType.GTE: {
178
+ const b = this.popValue(instr);
179
+ const a = this.popValue(instr);
180
+ const result =
181
+ ValueHandler.lessThan(b, a) || ValueHandler.equals(a, b);
182
+ this.stack.push({ type: 'bool', value: result });
183
+ break;
184
+ }
185
+
186
+ // 논리 연산
187
+ case OpcodeType.AND: {
188
+ const b = this.popValue(instr);
189
+ const a = this.popValue(instr);
190
+ const result =
191
+ ValueHandler.toBoolean(a) && ValueHandler.toBoolean(b);
192
+ this.stack.push({ type: 'bool', value: result });
193
+ break;
194
+ }
195
+
196
+ case OpcodeType.OR: {
197
+ const b = this.popValue(instr);
198
+ const a = this.popValue(instr);
199
+ const result =
200
+ ValueHandler.toBoolean(a) || ValueHandler.toBoolean(b);
201
+ this.stack.push({ type: 'bool', value: result });
202
+ break;
203
+ }
204
+
205
+ case OpcodeType.NOT: {
206
+ const a = this.popValue(instr);
207
+ const result = !ValueHandler.toBoolean(a);
208
+ this.stack.push({ type: 'bool', value: result });
209
+ break;
210
+ }
211
+
212
+ // 제어 흐름
213
+ case OpcodeType.JUMP:
214
+ this.pc = instr.arg - 1; // -1은 pc++이 되기 때문
215
+ break;
216
+
217
+ case OpcodeType.JUMP_IF_TRUE: {
218
+ const cond = this.popValue(instr);
219
+ if (ValueHandler.toBoolean(cond)) {
220
+ this.pc = instr.arg - 1;
221
+ }
222
+ break;
223
+ }
224
+
225
+ case OpcodeType.JUMP_IF_FALSE: {
226
+ const cond = this.popValue(instr);
227
+ if (!ValueHandler.toBoolean(cond)) {
228
+ this.pc = instr.arg - 1;
229
+ }
230
+ break;
231
+ }
232
+
233
+ // 변수 접근
234
+ case OpcodeType.LOAD_LOCAL: {
235
+ const index = instr.arg;
236
+ const value = this.locals.get(index) || { type: 'null' };
237
+ this.stack.push(value);
238
+ break;
239
+ }
240
+
241
+ case OpcodeType.STORE_LOCAL: {
242
+ const index = instr.arg;
243
+ const value = this.popValue(instr);
244
+ this.locals.set(index, value);
245
+ break;
246
+ }
247
+
248
+ case OpcodeType.LOAD_GLOBAL: {
249
+ const name = instr.arg;
250
+ const value = this.globals.get(name) || { type: 'null' };
251
+ this.stack.push(value);
252
+ break;
253
+ }
254
+
255
+ case OpcodeType.STORE_GLOBAL: {
256
+ const name = instr.arg;
257
+ const value = this.popValue(instr);
258
+ this.globals.set(name, value);
259
+ break;
260
+ }
261
+
262
+ case OpcodeType.PRINT: {
263
+ const value = this.popValue(instr);
264
+ process.stdout.write(ValueHandler.toString(value));
265
+ break;
266
+ }
267
+
268
+ case OpcodeType.HALT:
269
+ this.pc = this.instructions.length; // 프로그램 종료
270
+ break;
271
+
272
+ case OpcodeType.NOP:
273
+ // No operation
274
+ break;
275
+
276
+ default:
277
+ throw new RuntimeError(`Unknown opcode: ${instr.op}`);
278
+ }
279
+ }
280
+
281
+ /**
282
+ * 이진 연산 수행
283
+ */
284
+ private binaryOp(
285
+ op: (a: number, b: number) => number,
286
+ instr: Instruction
287
+ ): void {
288
+ const b = this.popNumber(instr);
289
+ const a = this.popNumber(instr);
290
+ const result = op(a, b);
291
+
292
+ // 결과 타입 결정 (정수인지 실수인지)
293
+ const isInt = Number.isInteger(result);
294
+ this.stack.push(
295
+ isInt ? { type: 'int', value: result } : { type: 'float', value: result }
296
+ );
297
+ }
298
+
299
+ /**
300
+ * 스택에서 Value 팝
301
+ */
302
+ private popValue(instr: Instruction): Value {
303
+ if (this.stack.length === 0) {
304
+ throw new RuntimeError('Stack underflow', instr.line, instr.column);
305
+ }
306
+ return this.stack.pop()!;
307
+ }
308
+
309
+ /**
310
+ * 스택에서 숫자 팝
311
+ */
312
+ private popNumber(instr: Instruction): number {
313
+ const value = this.popValue(instr);
314
+ if (value.type !== 'int' && value.type !== 'float') {
315
+ throw new RuntimeError(
316
+ `Expected number, got ${value.type}`,
317
+ instr.line,
318
+ instr.column
319
+ );
320
+ }
321
+ return value.value;
322
+ }
323
+
324
+ /**
325
+ * 내장 함수 등록
326
+ */
327
+ public registerBuiltin(name: string, fn: Function): void {
328
+ this.builtins.set(name, fn);
329
+ }
330
+
331
+ /**
332
+ * 내장 함수 호출
333
+ */
334
+ public callBuiltin(name: string, args: Value[]): Value {
335
+ const fn = this.builtins.get(name);
336
+ if (!fn) {
337
+ throw new RuntimeError(`Unknown builtin function: ${name}`);
338
+ }
339
+ return fn(this, args) as Value;
340
+ }
341
+
342
+ /**
343
+ * 스택 상태 확인 (디버깅용)
344
+ */
345
+ public getStackTrace(): string {
346
+ const trace: string[] = [];
347
+ trace.push(`PC: ${this.pc}`);
348
+ trace.push(`Stack (${this.stack.length} items):`);
349
+ this.stack.forEach((v, i) => {
350
+ trace.push(` [${i}] ${ValueHandler.toString(v)}`);
351
+ });
352
+ return trace.join('\n');
353
+ }
354
+ }