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/src/checker.ts ADDED
@@ -0,0 +1,1861 @@
1
+ // FreeLang v4 — TypeChecker (SPEC_06, 07, 08 구현)
2
+ // 정적 타입 검사 + Move/Copy 추적 + 스코프 관리
3
+
4
+ import { Program, Stmt, Expr, TypeAnnotation, Pattern, MatchArm, Param, ImportDecl, ExportDecl } from "./ast";
5
+
6
+ // ============================================================
7
+ // 내부 타입 표현
8
+ // ============================================================
9
+
10
+ export type Type =
11
+ | { kind: "i32" }
12
+ | { kind: "i64" }
13
+ | { kind: "f64" }
14
+ | { kind: "bool" }
15
+ | { kind: "string" }
16
+ | { kind: "void" }
17
+ | { kind: "array"; element: Type }
18
+ | { kind: "channel"; element: Type }
19
+ | { kind: "option"; element: Type }
20
+ | { kind: "result"; ok: Type; err: Type }
21
+ | { kind: "promise"; element: Type }
22
+ | { kind: "struct"; fields: Map<string, Type> }
23
+ | { kind: "fn"; params: Type[]; returnType: Type }
24
+ | { kind: "type_param"; name: string } // 제네릭 타입 파라미터 (T, K, V)
25
+ | { kind: "generic_ref"; name: string; typeArgs: Type[] } // 제네릭 타입 참조 (List<T>)
26
+ | { kind: "trait"; name: string; methods: Map<string, { params: Type[]; returnType: Type }> } // Trait
27
+ | { kind: "unknown" };
28
+
29
+ // ============================================================
30
+ // CheckError — SPEC_06 Q9: 14종 에러
31
+ // ============================================================
32
+
33
+ export type CheckError = {
34
+ message: string;
35
+ line: number;
36
+ col: number;
37
+ };
38
+
39
+ // ============================================================
40
+ // 변수 정보
41
+ // ============================================================
42
+
43
+ type VarInfo = {
44
+ type: Type;
45
+ mutable: boolean;
46
+ moved: boolean; // Move 타입이 이동됐는지 (SPEC_07)
47
+ line: number;
48
+ col: number;
49
+ };
50
+
51
+ // ============================================================
52
+ // 함수 정보
53
+ // ============================================================
54
+
55
+ type FnInfo = {
56
+ params: { name: string; type: Type }[];
57
+ returnType: Type;
58
+ };
59
+
60
+ // Generic 함수 정의 저장
61
+ type GenericFnDef = {
62
+ typeParams: string[];
63
+ params: { name: string; type: Type }[];
64
+ returnType: Type;
65
+ };
66
+
67
+ // Generic 구조체 정의 저장
68
+ type GenericStructDef = {
69
+ typeParams: string[];
70
+ fields: Map<string, Type>;
71
+ };
72
+
73
+ // ============================================================
74
+ // 스코프 (SPEC_08: 3종 — global, function, block)
75
+ // ============================================================
76
+
77
+ class Scope {
78
+ vars: Map<string, VarInfo> = new Map();
79
+ parent: Scope | null;
80
+
81
+ constructor(parent: Scope | null) {
82
+ this.parent = parent;
83
+ }
84
+
85
+ define(name: string, info: VarInfo): void {
86
+ this.vars.set(name, info);
87
+ }
88
+
89
+ lookup(name: string): VarInfo | null {
90
+ const v = this.vars.get(name);
91
+ if (v) return v;
92
+ if (this.parent) return this.parent.lookup(name);
93
+ return null;
94
+ }
95
+ }
96
+
97
+ // ============================================================
98
+ // Copy vs Move (SPEC_07 Q2)
99
+ // ============================================================
100
+
101
+ function isCopyType(t: Type): boolean {
102
+ switch (t.kind) {
103
+ case "i32":
104
+ case "i64":
105
+ case "f64":
106
+ case "bool":
107
+ case "string": // string은 immutable이므로 Copy (SPEC_06 Q8)
108
+ case "array": // array는 자동 복사본 전달 (SPEC_09: Copy-on-Pass)
109
+ return true;
110
+ case "option":
111
+ return isCopyType(t.element);
112
+ case "result":
113
+ return isCopyType(t.ok) && isCopyType(t.err);
114
+ case "promise":
115
+ return false; // Promise는 Move 타입
116
+ case "struct":
117
+ return true; // struct도 자동 복사본 전달 (SPEC_09: Copy-on-Pass)
118
+ case "channel":
119
+ case "fn":
120
+ return false; // Move 타입 (채널, 함수는 Move)
121
+ case "type_param":
122
+ return true; // Generic 타입 파라미터는 Copy로 취급 (Type Erasure)
123
+ case "generic_ref":
124
+ return true; // Generic 타입도 기본적으로 Copy
125
+ case "trait":
126
+ return true; // Trait은 기본적으로 Copy
127
+ default:
128
+ return true;
129
+ }
130
+ }
131
+
132
+ // ============================================================
133
+ // 타입 동등 비교 (구조적 — SPEC_06 Q7)
134
+ // ============================================================
135
+
136
+ function typesEqual(a: Type, b: Type): boolean {
137
+ // type_param은 항상 compatible (Type Erasure 전략)
138
+ if (a.kind === "type_param" || b.kind === "type_param") return true;
139
+
140
+ if (a.kind !== b.kind) return false;
141
+
142
+ switch (a.kind) {
143
+ case "i32": case "i64": case "f64": case "bool": case "string": case "void": case "unknown":
144
+ return true;
145
+ case "array":
146
+ return typesEqual(a.element, (b as any).element);
147
+ case "channel":
148
+ return typesEqual(a.element, (b as any).element);
149
+ case "option":
150
+ return typesEqual(a.element, (b as any).element);
151
+ case "result":
152
+ return typesEqual(a.ok, (b as any).ok) && typesEqual(a.err, (b as any).err);
153
+ case "promise":
154
+ return typesEqual(a.element, (b as any).element);
155
+ case "struct": {
156
+ const bStruct = b as { kind: "struct"; fields: Map<string, Type> };
157
+ if (a.fields.size !== bStruct.fields.size) return false;
158
+ for (const [k, v] of a.fields) {
159
+ const bv = bStruct.fields.get(k);
160
+ if (!bv || !typesEqual(v, bv)) return false;
161
+ }
162
+ return true;
163
+ }
164
+ case "fn": {
165
+ const bFn = b as { kind: "fn"; params: Type[]; returnType: Type };
166
+ if (a.params.length !== bFn.params.length) return false;
167
+ for (let i = 0; i < a.params.length; i++) {
168
+ if (!typesEqual(a.params[i], bFn.params[i])) return false;
169
+ }
170
+ return typesEqual(a.returnType, bFn.returnType);
171
+ }
172
+ case "generic_ref": {
173
+ const bGen = b as { kind: "generic_ref"; name: string; typeArgs: Type[] };
174
+ if (a.name !== bGen.name) return false;
175
+ if (a.typeArgs.length !== bGen.typeArgs.length) return false;
176
+ for (let i = 0; i < a.typeArgs.length; i++) {
177
+ if (!typesEqual(a.typeArgs[i], bGen.typeArgs[i])) return false;
178
+ }
179
+ return true;
180
+ }
181
+ case "trait": {
182
+ const bTrait = b as { kind: "trait"; name: string };
183
+ return a.name === bTrait.name;
184
+ }
185
+ default:
186
+ return false;
187
+ }
188
+ }
189
+
190
+ function typeToString(t: Type): string {
191
+ switch (t.kind) {
192
+ case "i32": case "i64": case "f64": case "bool": case "string": case "void":
193
+ return t.kind;
194
+ case "array": return `[${typeToString(t.element)}]`;
195
+ case "channel": return `channel<${typeToString(t.element)}>`;
196
+ case "option": return `Option<${typeToString(t.element)}>`;
197
+ case "result": return `Result<${typeToString(t.ok)}, ${typeToString(t.err)}>`;
198
+ case "promise": return `Promise<${typeToString(t.element)}>`;
199
+ case "struct": {
200
+ const fields = [...t.fields.entries()].map(([k, v]) => `${k}: ${typeToString(v)}`).join(", ");
201
+ return `{ ${fields} }`;
202
+ }
203
+ case "fn": {
204
+ const paramStr = t.params.map(typeToString).join(", ");
205
+ return `fn(${paramStr}) -> ${typeToString(t.returnType)}`;
206
+ }
207
+ case "type_param":
208
+ return t.name;
209
+ case "generic_ref": {
210
+ const typeArgStr = t.typeArgs.map(typeToString).join(", ");
211
+ return `${t.name}<${typeArgStr}>`;
212
+ }
213
+ case "trait":
214
+ return `trait ${t.name}`;
215
+ case "unknown": return "unknown";
216
+ }
217
+ }
218
+
219
+ // ============================================================
220
+ // 제네릭 타입 치환 — substituteType
221
+ // ============================================================
222
+
223
+ function substituteType(t: Type, bindings: Map<string, Type>): Type {
224
+ switch (t.kind) {
225
+ case "type_param": {
226
+ return bindings.get(t.name) ?? t;
227
+ }
228
+ case "array": {
229
+ return { kind: "array", element: substituteType(t.element, bindings) };
230
+ }
231
+ case "channel": {
232
+ return { kind: "channel", element: substituteType(t.element, bindings) };
233
+ }
234
+ case "option": {
235
+ return { kind: "option", element: substituteType(t.element, bindings) };
236
+ }
237
+ case "result": {
238
+ return { kind: "result", ok: substituteType(t.ok, bindings), err: substituteType(t.err, bindings) };
239
+ }
240
+ case "promise": {
241
+ return { kind: "promise", element: substituteType(t.element, bindings) };
242
+ }
243
+ case "struct": {
244
+ const fields = new Map<string, Type>();
245
+ for (const [k, v] of t.fields) {
246
+ fields.set(k, substituteType(v, bindings));
247
+ }
248
+ return { kind: "struct", fields };
249
+ }
250
+ case "fn": {
251
+ return {
252
+ kind: "fn",
253
+ params: t.params.map(p => substituteType(p, bindings)),
254
+ returnType: substituteType(t.returnType, bindings),
255
+ };
256
+ }
257
+ case "generic_ref": {
258
+ const typeArgs = t.typeArgs.map(ta => substituteType(ta, bindings));
259
+ return { kind: "generic_ref", name: t.name, typeArgs };
260
+ }
261
+ case "trait":
262
+ return t; // Trait types don't have substitutable parts yet
263
+ default:
264
+ return t;
265
+ }
266
+ }
267
+
268
+ function annotationToType(
269
+ a: TypeAnnotation,
270
+ structDefs: Map<string, Type> = new Map(),
271
+ typeEnv: Map<string, Type> = new Map(),
272
+ ): Type {
273
+ switch (a.kind) {
274
+ case "i32": return { kind: "i32" };
275
+ case "i64": return { kind: "i64" };
276
+ case "f64": return { kind: "f64" };
277
+ case "bool": return { kind: "bool" };
278
+ case "string": return { kind: "string" };
279
+ case "void": return { kind: "void" };
280
+ case "array": return { kind: "array", element: annotationToType(a.element, structDefs, typeEnv) };
281
+ case "channel": return { kind: "channel", element: annotationToType(a.element, structDefs, typeEnv) };
282
+ case "option": return { kind: "option", element: annotationToType(a.element, structDefs, typeEnv) };
283
+ case "result": return { kind: "result", ok: annotationToType(a.ok, structDefs, typeEnv), err: annotationToType(a.err, structDefs, typeEnv) };
284
+ case "promise": return { kind: "promise", element: annotationToType(a.element, structDefs, typeEnv) };
285
+ case "struct_ref": {
286
+ const structType = structDefs.get(a.name);
287
+ return structType || { kind: "unknown" };
288
+ }
289
+ case "fn": {
290
+ const params = a.params.map(p => annotationToType(p, structDefs, typeEnv));
291
+ const returnType = annotationToType(a.returnType, structDefs, typeEnv);
292
+ return { kind: "fn", params, returnType };
293
+ }
294
+ case "type_param": {
295
+ // typeEnv에서 치환된 타입 찾기, 없으면 type_param 유지
296
+ return typeEnv.get(a.name) ?? { kind: "type_param", name: a.name };
297
+ }
298
+ case "generic_ref": {
299
+ const typeArgs = a.typeArgs.map(arg => annotationToType(arg, structDefs, typeEnv));
300
+ return { kind: "generic_ref", name: a.name, typeArgs };
301
+ }
302
+ case "trait_ref":
303
+ return { kind: "unknown" }; // Trait reference not yet fully supported
304
+ case "self_type":
305
+ return { kind: "unknown" }; // Self type not yet fully supported
306
+ }
307
+ }
308
+
309
+ // ============================================================
310
+ // TypeChecker
311
+ // ============================================================
312
+
313
+ export class TypeChecker {
314
+ private errors: CheckError[] = [];
315
+ private functions: Map<string, FnInfo> = new Map();
316
+ private structs: Map<string, Type> = new Map(); // struct 정의 저장소
317
+ private traits: Map<string, Type> = new Map(); // trait 정의 저장소 (trait_decl)
318
+ private impls: Array<{ trait: string | null; forType: string; methods: Map<string, { params: Type[]; returnType: Type }> }> = []; // impl 정의 저장소
319
+ private genericFunctions: Map<string, GenericFnDef> = new Map();
320
+ private genericStructs: Map<string, GenericStructDef> = new Map();
321
+ private instantiatedFunctions: Map<string, FnInfo> = new Map(); // 인스턴스화된 함수
322
+ private instantiatedStructs: Map<string, Type> = new Map(); // 인스턴스화된 구조체
323
+ private scope: Scope;
324
+ private currentReturnType: Type | null = null;
325
+
326
+ constructor() {
327
+ this.scope = new Scope(null); // global scope
328
+ }
329
+
330
+ check(program: Program): CheckError[] {
331
+ // Builtin 함수 등록
332
+ this.registerBuiltinFunctions();
333
+
334
+ // Pass 1: trait과 struct 정의 등록
335
+ for (const stmt of program.stmts) {
336
+ if (stmt.kind === "struct_decl") {
337
+ this.registerStruct(stmt);
338
+ }
339
+ if (stmt.kind === "trait_decl") {
340
+ this.registerTrait(stmt);
341
+ }
342
+ }
343
+
344
+ // Pass 2: impl 정의 등록
345
+ for (const stmt of program.stmts) {
346
+ if (stmt.kind === "impl_decl") {
347
+ this.registerImpl(stmt);
348
+ }
349
+ }
350
+
351
+ // Pass 3: 함수 전방참조 등록 (SPEC_08 Q5)
352
+ for (const stmt of program.stmts) {
353
+ if (stmt.kind === "fn_decl") {
354
+ this.registerFunction(stmt);
355
+ }
356
+ }
357
+
358
+ // Pass 4: 본문 검사
359
+ for (const stmt of program.stmts) {
360
+ this.checkStmt(stmt);
361
+ }
362
+
363
+ return this.errors;
364
+ }
365
+
366
+ private registerFunction(stmt: Stmt & { kind: "fn_decl" }): void {
367
+ const params = stmt.params.map((p) => ({
368
+ name: p.name,
369
+ type: annotationToType(p.type, this.structs),
370
+ }));
371
+ let returnType = annotationToType(stmt.returnType, this.structs);
372
+
373
+ // async fn인 경우 반환 타입을 Promise<T>로 자동 변환
374
+ if (stmt.isAsync) {
375
+ returnType = { kind: "promise", element: returnType };
376
+ }
377
+
378
+ if (this.functions.has(stmt.name)) {
379
+ this.error(`function '${stmt.name}' already declared`, stmt.line, stmt.col);
380
+ return;
381
+ }
382
+
383
+ // Generic 함수인 경우 genericFunctions에 등록
384
+ if (stmt.typeParams.length > 0) {
385
+ this.genericFunctions.set(stmt.name, { typeParams: stmt.typeParams, params, returnType });
386
+ } else {
387
+ this.functions.set(stmt.name, { params, returnType });
388
+ }
389
+ }
390
+
391
+ private registerBuiltinFunctions(): void {
392
+ // Some<T>(value: T) -> Option<T>
393
+ // 실제로는 제네릭 함수이지만, 여기서는 unknown 파라미터로 처리
394
+ this.functions.set("Some", {
395
+ params: [{ name: "value", type: { kind: "unknown" } }],
396
+ returnType: { kind: "option", element: { kind: "unknown" } }
397
+ });
398
+
399
+ // None는 값이므로 함수로 등록하지 않음 (상수처럼 취급)
400
+ // Ok<T>(value: T) -> Result<T, E>
401
+ this.functions.set("Ok", {
402
+ params: [{ name: "value", type: { kind: "unknown" } }],
403
+ returnType: { kind: "result", ok: { kind: "unknown" }, err: { kind: "unknown" } }
404
+ });
405
+
406
+ // Err<E>(error: E) -> Result<T, E>
407
+ this.functions.set("Err", {
408
+ params: [{ name: "error", type: { kind: "unknown" } }],
409
+ returnType: { kind: "result", ok: { kind: "unknown" }, err: { kind: "unknown" } }
410
+ });
411
+ }
412
+
413
+ private registerStruct(stmt: Stmt & { kind: "struct_decl" }): void {
414
+ const fields = new Map<string, Type>();
415
+ for (const field of stmt.fields) {
416
+ const fieldType = annotationToType(field.type, this.structs);
417
+ fields.set(field.name, fieldType);
418
+ }
419
+
420
+ if (this.structs.has(stmt.name)) {
421
+ this.error(`struct '${stmt.name}' already declared`, stmt.line, stmt.col);
422
+ return;
423
+ }
424
+
425
+ // Generic 구조체인 경우 genericStructs에 등록
426
+ if (stmt.typeParams.length > 0) {
427
+ this.genericStructs.set(stmt.name, { typeParams: stmt.typeParams, fields });
428
+ } else {
429
+ this.structs.set(stmt.name, { kind: "struct", fields });
430
+ }
431
+ }
432
+
433
+ private registerTrait(stmt: Stmt & { kind: "trait_decl" }): void {
434
+ const methods = new Map<string, { params: Type[]; returnType: Type }>();
435
+
436
+ for (const method of stmt.methods) {
437
+ const params = method.params.map(p => annotationToType(p.type, this.structs));
438
+ const returnType = annotationToType(method.returnType, this.structs);
439
+ methods.set(method.name, { params, returnType });
440
+ }
441
+
442
+ if (this.traits.has(stmt.name)) {
443
+ this.error(`trait '${stmt.name}' already declared`, stmt.line, stmt.col);
444
+ return;
445
+ }
446
+
447
+ this.traits.set(stmt.name, {
448
+ kind: "trait",
449
+ name: stmt.name,
450
+ methods,
451
+ });
452
+ }
453
+
454
+ private registerImpl(stmt: Stmt & { kind: "impl_decl" }): void {
455
+ // forType을 문자열로 간단히 저장 (간소화)
456
+ let forTypeName = "unknown";
457
+ if (stmt.forType.kind === "struct_ref") {
458
+ forTypeName = stmt.forType.name;
459
+ }
460
+
461
+ const methods = new Map<string, { params: Type[]; returnType: Type }>();
462
+
463
+ for (const method of stmt.methods) {
464
+ const params = method.params.map(p => annotationToType(p.type, this.structs));
465
+ const returnType = annotationToType(method.returnType, this.structs);
466
+ methods.set(method.name, { params, returnType });
467
+ }
468
+
469
+ // impl 저장
470
+ this.impls.push({
471
+ trait: stmt.trait,
472
+ forType: forTypeName,
473
+ methods,
474
+ });
475
+ }
476
+
477
+ // ============================================================
478
+ // 문 검사
479
+ // ============================================================
480
+
481
+ private checkStmt(stmt: Stmt): void {
482
+ switch (stmt.kind) {
483
+ case "var_decl":
484
+ return this.checkVarDecl(stmt);
485
+ case "fn_decl":
486
+ return this.checkFnDecl(stmt);
487
+ case "struct_decl":
488
+ return; // struct는 Pass 1에서 이미 등록됨
489
+ case "trait_decl":
490
+ return; // trait은 Pass 1에서 이미 등록됨
491
+ case "impl_decl":
492
+ return; // impl은 Pass 2에서 이미 등록됨
493
+ case "if_stmt":
494
+ return this.checkIfStmt(stmt);
495
+ case "match_stmt":
496
+ return this.checkMatchStmt(stmt);
497
+ case "for_stmt":
498
+ return this.checkForStmt(stmt);
499
+ case "for_of_stmt":
500
+ return this.checkForOfStmt(stmt);
501
+ case "while_stmt":
502
+ return this.checkWhileStmt(stmt);
503
+ case "break_stmt":
504
+ return this.checkBreakStmt(stmt);
505
+ case "continue_stmt":
506
+ return this.checkContinueStmt(stmt);
507
+ case "spawn_stmt":
508
+ return this.checkSpawnStmt(stmt);
509
+ case "return_stmt":
510
+ return this.checkReturnStmt(stmt);
511
+ case "expr_stmt":
512
+ return this.checkExprStmt(stmt);
513
+ case "import_decl":
514
+ return this.checkImportDecl(stmt as ImportDecl);
515
+ case "export_decl":
516
+ return this.checkExportDecl(stmt as ExportDecl);
517
+ }
518
+ }
519
+
520
+ private checkVarDecl(stmt: Stmt & { kind: "var_decl" }): void {
521
+ const initType = this.checkExpr(stmt.init);
522
+
523
+ let declType: Type;
524
+ if (stmt.type) {
525
+ declType = annotationToType(stmt.type, this.structs);
526
+ if (!typesEqual(declType, initType) && initType.kind !== "unknown") {
527
+ this.error(
528
+ `type mismatch: declared ${typeToString(declType)}, got ${typeToString(initType)}`,
529
+ stmt.line, stmt.col,
530
+ );
531
+ }
532
+ } else {
533
+ // 타입 추론 (SPEC_06 Q3)
534
+ declType = initType;
535
+ }
536
+
537
+ // void 변수 금지 (SPEC_06 Q9)
538
+ if (declType.kind === "void") {
539
+ this.error("cannot declare variable of type void", stmt.line, stmt.col);
540
+ return;
541
+ }
542
+
543
+ // 스코프에 등록
544
+ if (this.scope.vars.has(stmt.name)) {
545
+ // 섀도잉 허용 (SPEC_08 Q4) — 같은 스코프에서도 재선언 가능
546
+ }
547
+
548
+ this.scope.define(stmt.name, {
549
+ type: declType,
550
+ mutable: stmt.mutable,
551
+ moved: false,
552
+ line: stmt.line,
553
+ col: stmt.col,
554
+ });
555
+ }
556
+
557
+ private checkFnDecl(stmt: Stmt & { kind: "fn_decl" }): void {
558
+ const fnInfo = this.functions.get(stmt.name);
559
+ if (!fnInfo) return;
560
+
561
+ // 새 스코프
562
+ const prevScope = this.scope;
563
+ this.scope = new Scope(prevScope);
564
+
565
+ const prevReturn = this.currentReturnType;
566
+ this.currentReturnType = fnInfo.returnType;
567
+
568
+ // 매개변수 등록
569
+ for (const p of fnInfo.params) {
570
+ this.scope.define(p.name, {
571
+ type: p.type,
572
+ mutable: false, // 매개변수는 immutable (SPEC_08)
573
+ moved: false,
574
+ line: stmt.line,
575
+ col: stmt.col,
576
+ });
577
+ }
578
+
579
+ // 본문 검사
580
+ for (const s of stmt.body) {
581
+ this.checkStmt(s);
582
+ }
583
+
584
+ this.currentReturnType = prevReturn;
585
+ this.scope = prevScope;
586
+ }
587
+
588
+ private checkIfStmt(stmt: Stmt & { kind: "if_stmt" }): void {
589
+ const condType = this.checkExpr(stmt.condition);
590
+ if (condType.kind !== "bool" && condType.kind !== "unknown") {
591
+ this.error(
592
+ `if condition must be bool, got ${typeToString(condType)}`,
593
+ stmt.line, stmt.col,
594
+ );
595
+ }
596
+
597
+ // then 블록
598
+ const prevScope = this.scope;
599
+ this.scope = new Scope(prevScope);
600
+ for (const s of stmt.then) this.checkStmt(s);
601
+ this.scope = prevScope;
602
+
603
+ // else 블록
604
+ if (stmt.else_) {
605
+ const prevScope2 = this.scope;
606
+ this.scope = new Scope(prevScope2);
607
+ for (const s of stmt.else_) this.checkStmt(s);
608
+ this.scope = prevScope2;
609
+ }
610
+ }
611
+
612
+ private checkMatchStmt(stmt: Stmt & { kind: "match_stmt" }): void {
613
+ const subjectType = this.checkExpr(stmt.subject);
614
+ for (const arm of stmt.arms) {
615
+ this.checkMatchArm(arm, subjectType);
616
+ }
617
+ }
618
+
619
+ private checkMatchArm(arm: MatchArm, subjectType: Type): void {
620
+ const prevScope = this.scope;
621
+ this.scope = new Scope(prevScope);
622
+
623
+ this.checkPattern(arm.pattern, subjectType);
624
+
625
+ // Guard 절 검증: bool 타입이어야 함
626
+ if (arm.guard) {
627
+ const guardType = this.checkExpr(arm.guard);
628
+ if (guardType.kind !== "bool" && guardType.kind !== "unknown") {
629
+ this.error(`guard condition must be bool, got ${typeToString(guardType)}`, 0, 0);
630
+ }
631
+ }
632
+
633
+ this.checkExpr(arm.body);
634
+
635
+ this.scope = prevScope;
636
+ }
637
+
638
+ private checkPattern(pattern: Pattern, expectedType: Type): void {
639
+ switch (pattern.kind) {
640
+ case "ident":
641
+ // 바인딩 — 새 변수 생성
642
+ this.scope.define(pattern.name, {
643
+ type: expectedType,
644
+ mutable: false,
645
+ moved: false,
646
+ line: 0, col: 0,
647
+ });
648
+ break;
649
+ case "wildcard":
650
+ break;
651
+ case "none":
652
+ if (expectedType.kind !== "option" && expectedType.kind !== "unknown") {
653
+ this.error(`None pattern on non-Option type ${typeToString(expectedType)}`, 0, 0);
654
+ }
655
+ break;
656
+ case "some":
657
+ if (expectedType.kind === "option") {
658
+ this.checkPattern(pattern.inner, expectedType.element);
659
+ } else if (expectedType.kind !== "unknown") {
660
+ this.error(`Some pattern on non-Option type ${typeToString(expectedType)}`, 0, 0);
661
+ }
662
+ break;
663
+ case "ok":
664
+ if (expectedType.kind === "result") {
665
+ this.checkPattern(pattern.inner, expectedType.ok);
666
+ } else if (expectedType.kind !== "unknown") {
667
+ this.error(`Ok pattern on non-Result type ${typeToString(expectedType)}`, 0, 0);
668
+ }
669
+ break;
670
+ case "err":
671
+ if (expectedType.kind === "result") {
672
+ this.checkPattern(pattern.inner, expectedType.err);
673
+ } else if (expectedType.kind !== "unknown") {
674
+ this.error(`Err pattern on non-Result type ${typeToString(expectedType)}`, 0, 0);
675
+ }
676
+ break;
677
+ case "literal":
678
+ // 리터럴 타입은 checkExpr에서 확인
679
+ this.checkExpr(pattern.value);
680
+ break;
681
+
682
+ case "struct":
683
+ // 구조체 분해 패턴: Point { x, y }
684
+ if (expectedType.kind !== "struct" && expectedType.kind !== "unknown") {
685
+ this.error(
686
+ `struct pattern on non-struct type ${typeToString(expectedType)}`,
687
+ 0,
688
+ 0
689
+ );
690
+ }
691
+
692
+ if (expectedType.kind === "struct") {
693
+ for (const field of pattern.fields) {
694
+ const fieldType = expectedType.fields.get(field.name);
695
+ if (!fieldType) {
696
+ this.error(`struct ${pattern.name} has no field ${field.name}`, 0, 0);
697
+ } else {
698
+ this.checkPattern(field.pattern, fieldType);
699
+ }
700
+ }
701
+ }
702
+ break;
703
+
704
+ case "array":
705
+ // 배열 분해 패턴: [a, b, c], [x, .., y]
706
+ if (expectedType.kind !== "array" && expectedType.kind !== "unknown") {
707
+ this.error(
708
+ `array pattern on non-array type ${typeToString(expectedType)}`,
709
+ 0,
710
+ 0
711
+ );
712
+ }
713
+
714
+ if (expectedType.kind === "array") {
715
+ const elementType = expectedType.element;
716
+ for (const element of pattern.elements) {
717
+ this.checkPattern(element, elementType);
718
+ }
719
+ }
720
+ break;
721
+
722
+ case "tuple":
723
+ // 튜플 분해 패턴: (a, b, c)
724
+ if (expectedType.kind !== "unknown") {
725
+ this.error(`tuple patterns not yet supported`, 0, 0);
726
+ }
727
+ break;
728
+ }
729
+ }
730
+
731
+ private checkForStmt(stmt: Stmt & { kind: "for_stmt" }): void {
732
+ const iterType = this.checkExpr(stmt.iterable);
733
+
734
+ // iterable은 array여야 함
735
+ let elemType: Type = { kind: "unknown" };
736
+ if (iterType.kind === "array") {
737
+ elemType = iterType.element;
738
+ } else if (iterType.kind !== "unknown") {
739
+ this.error(`for...in requires array, got ${typeToString(iterType)}`, stmt.line, stmt.col);
740
+ }
741
+
742
+ // 루프 스코프
743
+ const prevScope = this.scope;
744
+ this.scope = new Scope(prevScope);
745
+
746
+ // 루프 변수 (immutable — SPEC_08 Q6)
747
+ this.scope.define(stmt.variable, {
748
+ type: elemType,
749
+ mutable: false,
750
+ moved: false,
751
+ line: stmt.line,
752
+ col: stmt.col,
753
+ });
754
+
755
+ for (const s of stmt.body) this.checkStmt(s);
756
+ this.scope = prevScope;
757
+ }
758
+
759
+ private checkForOfStmt(stmt: Stmt & { kind: "for_of_stmt" }): void {
760
+ const iterType = this.checkExpr(stmt.iterable);
761
+
762
+ // iterable은 array 또는 string이어야 함
763
+ let elemType: Type = { kind: "unknown" };
764
+ if (iterType.kind === "array") {
765
+ elemType = iterType.element;
766
+ } else if (iterType.kind === "string") {
767
+ // 문자열을 순회하면 각 요소는 string (한 글자)
768
+ elemType = { kind: "string" };
769
+ } else if (iterType.kind !== "unknown") {
770
+ this.error(`for...of requires array or string, got ${typeToString(iterType)}`, stmt.line, stmt.col);
771
+ }
772
+
773
+ // 루프 스코프
774
+ const prevScope = this.scope;
775
+ this.scope = new Scope(prevScope);
776
+
777
+ // 루프 변수 (immutable — SPEC_08 Q6)
778
+ this.scope.define(stmt.variable, {
779
+ type: elemType,
780
+ mutable: false,
781
+ moved: false,
782
+ line: stmt.line,
783
+ col: stmt.col,
784
+ });
785
+
786
+ for (const s of stmt.body) this.checkStmt(s);
787
+ this.scope = prevScope;
788
+ }
789
+
790
+ private checkWhileStmt(stmt: Stmt & { kind: "while_stmt" }): void {
791
+ const condType = this.checkExpr(stmt.condition);
792
+
793
+ // while 조건은 bool이어야 함
794
+ if (condType.kind !== "bool" && condType.kind !== "unknown") {
795
+ this.error(`while condition must be bool, got ${typeToString(condType)}`, stmt.line, stmt.col);
796
+ }
797
+
798
+ // 루프 스코프
799
+ const prevScope = this.scope;
800
+ this.scope = new Scope(prevScope);
801
+
802
+ for (const s of stmt.body) this.checkStmt(s);
803
+ this.scope = prevScope;
804
+ }
805
+
806
+ private checkBreakStmt(stmt: Stmt & { kind: "break_stmt" }): void {
807
+ // break는 루프 내에서만 사용 가능 (현재는 미지원)
808
+ // 나중에 구현할 수 있음
809
+ }
810
+
811
+ private checkContinueStmt(stmt: Stmt & { kind: "continue_stmt" }): void {
812
+ // continue는 루프 내에서만 사용 가능 (현재는 미지원)
813
+ // 나중에 구현할 수 있음
814
+ }
815
+
816
+ private checkSpawnStmt(stmt: Stmt & { kind: "spawn_stmt" }): void {
817
+ // spawn은 새로운 스코프 (외부 변수 접근 가능)
818
+ const prevScope = this.scope;
819
+ this.scope = new Scope(prevScope); // 부모 스코프 유지
820
+
821
+ for (const s of stmt.body) this.checkStmt(s);
822
+ this.scope = prevScope;
823
+ }
824
+
825
+ private checkReturnStmt(stmt: Stmt & { kind: "return_stmt" }): void {
826
+ if (!this.currentReturnType) {
827
+ this.error("return outside function", stmt.line, stmt.col);
828
+ return;
829
+ }
830
+
831
+ if (stmt.value) {
832
+ const valType = this.checkExpr(stmt.value);
833
+ if (!typesEqual(this.currentReturnType, valType) && valType.kind !== "unknown") {
834
+ this.error(
835
+ `return type mismatch: expected ${typeToString(this.currentReturnType)}, got ${typeToString(valType)}`,
836
+ stmt.line, stmt.col,
837
+ );
838
+ }
839
+ } else {
840
+ // return; (void)
841
+ if (this.currentReturnType.kind !== "void") {
842
+ this.error(
843
+ `return without value in non-void function (expected ${typeToString(this.currentReturnType)})`,
844
+ stmt.line, stmt.col,
845
+ );
846
+ }
847
+ }
848
+ }
849
+
850
+ private checkExprStmt(stmt: Stmt & { kind: "expr_stmt" }): void {
851
+ this.checkExpr(stmt.expr);
852
+ }
853
+
854
+ private checkImportDecl(decl: ImportDecl): void {
855
+ // 기본 import 검증: 현재는 모듈 로드 없이 항목만 추적
856
+ for (const item of decl.items) {
857
+ const name = item.alias || item.name;
858
+ // import된 항목을 스코프에 등록 (타입은 unknown)
859
+ this.scope.define(name, {
860
+ type: { kind: "unknown" },
861
+ mutable: false,
862
+ moved: false,
863
+ line: decl.line,
864
+ col: decl.col,
865
+ });
866
+ }
867
+ }
868
+
869
+ private checkExportDecl(decl: ExportDecl): void {
870
+ // 기본 export 검증: 현재는 항목 추적만 수행
871
+ if (typeof decl.target !== "string") {
872
+ // export fn/struct: 이미 checkStmt에서 처리됨
873
+ return;
874
+ }
875
+ // export { ... }의 경우 항목 이름들이 실제로 정의되어 있는지는
876
+ // 향후 더 정교한 검증이 필요
877
+ }
878
+
879
+ // ============================================================
880
+ // 식 검사 — 타입 반환
881
+ // ============================================================
882
+
883
+ checkExpr(expr: Expr): Type {
884
+ switch (expr.kind) {
885
+ case "int_lit": return { kind: "i32" };
886
+ case "float_lit": return { kind: "f64" };
887
+ case "string_lit": return { kind: "string" };
888
+ case "bool_lit": return { kind: "bool" };
889
+
890
+ case "ident":
891
+ return this.checkIdent(expr);
892
+
893
+ case "binary":
894
+ return this.checkBinary(expr);
895
+
896
+ case "unary":
897
+ return this.checkUnary(expr);
898
+
899
+ case "await":
900
+ return this.checkAwait(expr);
901
+
902
+ case "call":
903
+ return this.checkCall(expr);
904
+
905
+ case "index":
906
+ return this.checkIndex(expr);
907
+
908
+ case "field_access":
909
+ return this.checkFieldAccess(expr);
910
+
911
+ case "assign":
912
+ return this.checkAssign(expr);
913
+
914
+ case "try":
915
+ return this.checkTry(expr);
916
+
917
+ case "if_expr":
918
+ return this.checkIfExpr(expr);
919
+
920
+ case "match_expr":
921
+ return this.checkMatchExpr(expr);
922
+
923
+ case "array_lit":
924
+ return this.checkArrayLit(expr);
925
+
926
+ case "struct_lit":
927
+ return this.checkStructLit(expr);
928
+
929
+ case "fn_lit":
930
+ return this.checkFnLit(expr);
931
+
932
+ case "block_expr":
933
+ return this.checkBlockExpr(expr);
934
+
935
+ case "chan_new":
936
+ return this.checkChanNew(expr);
937
+
938
+ case "chan_send":
939
+ return this.checkChanSend(expr);
940
+
941
+ case "chan_recv":
942
+ return this.checkChanRecv(expr);
943
+
944
+ default:
945
+ return { kind: "unknown" };
946
+ }
947
+ }
948
+
949
+ private checkIdent(expr: Expr & { kind: "ident" }): Type {
950
+ const info = this.scope.lookup(expr.name);
951
+ if (!info) {
952
+ // 내장 함수 확인
953
+ if (this.isBuiltin(expr.name)) return { kind: "unknown" };
954
+ // 함수 이름 확인
955
+ if (this.functions.has(expr.name)) return { kind: "unknown" };
956
+
957
+ this.error(`undefined variable: '${expr.name}'`, expr.line, expr.col);
958
+ return { kind: "unknown" };
959
+ }
960
+
961
+ // Move 검사 (SPEC_07 Q4)
962
+ if (info.moved) {
963
+ this.error(`use of moved value: '${expr.name}'`, expr.line, expr.col);
964
+ return info.type;
965
+ }
966
+
967
+ return info.type;
968
+ }
969
+
970
+ private checkBinary(expr: Expr & { kind: "binary" }): Type {
971
+ const left = this.checkExpr(expr.left);
972
+ const right = this.checkExpr(expr.right);
973
+
974
+ // 비교 연산자 → bool (타입 강제 변환 지원)
975
+ if (["==", "!=", "<", ">", "<=", ">="].includes(expr.op)) {
976
+ if (!typesEqual(left, right) && left.kind !== "unknown" && right.kind !== "unknown") {
977
+ // numeric 타입은 강제 변환 허용
978
+ const coerced = this.coerceNumeric(left, right);
979
+ if (!coerced) {
980
+ this.error(
981
+ `cannot compare ${typeToString(left)} and ${typeToString(right)}`,
982
+ expr.line, expr.col,
983
+ );
984
+ }
985
+ }
986
+ return { kind: "bool" };
987
+ }
988
+
989
+ // 논리 연산자 → bool
990
+ if (expr.op === "&&" || expr.op === "||") {
991
+ if (left.kind !== "bool" && left.kind !== "unknown") {
992
+ this.error(`'${expr.op}' requires bool, got ${typeToString(left)}`, expr.line, expr.col);
993
+ }
994
+ if (right.kind !== "bool" && right.kind !== "unknown") {
995
+ this.error(`'${expr.op}' requires bool, got ${typeToString(right)}`, expr.line, expr.col);
996
+ }
997
+ return { kind: "bool" };
998
+ }
999
+
1000
+ // 산술 연산자: + 는 문자열 연결도 가능
1001
+ if (expr.op === "+") {
1002
+ if (left.kind === "string" && right.kind === "string") return { kind: "string" };
1003
+ }
1004
+
1005
+ // 산술: i32, i64, f64 (타입 강제 변환 지원)
1006
+ if (["+", "-", "*", "/", "%"].includes(expr.op)) {
1007
+ const numericTypes = ["i32", "i64", "f64"];
1008
+
1009
+ // 타입이 같거나 numeric 타입 강제 변환 가능
1010
+ if (!typesEqual(left, right) && left.kind !== "unknown" && right.kind !== "unknown") {
1011
+ const coerced = this.coerceNumeric(left, right);
1012
+ if (!coerced) {
1013
+ this.error(
1014
+ `type mismatch in '${expr.op}': ${typeToString(left)} and ${typeToString(right)}`,
1015
+ expr.line, expr.col,
1016
+ );
1017
+ return { kind: "unknown" };
1018
+ }
1019
+ return coerced; // 강제 변환된 타입 반환
1020
+ }
1021
+
1022
+ // 한쪽이 unknown이면 다른 쪽 반환
1023
+ if (left.kind === "unknown") return right;
1024
+ if (right.kind === "unknown") return left;
1025
+
1026
+ return left;
1027
+ }
1028
+
1029
+ return { kind: "unknown" };
1030
+ }
1031
+
1032
+ private checkUnary(expr: Expr & { kind: "unary" }): Type {
1033
+ const operand = this.checkExpr(expr.operand);
1034
+
1035
+ if (expr.op === "-") {
1036
+ if (!["i32", "i64", "f64", "unknown"].includes(operand.kind)) {
1037
+ this.error(`unary '-' requires numeric type, got ${typeToString(operand)}`, expr.line, expr.col);
1038
+ }
1039
+ return operand;
1040
+ }
1041
+
1042
+ if (expr.op === "!") {
1043
+ if (operand.kind !== "bool" && operand.kind !== "unknown") {
1044
+ this.error(`unary '!' requires bool, got ${typeToString(operand)}`, expr.line, expr.col);
1045
+ }
1046
+ return { kind: "bool" };
1047
+ }
1048
+
1049
+ return { kind: "unknown" };
1050
+ }
1051
+
1052
+ private checkAwait(expr: Expr & { kind: "await" }): Type {
1053
+ const operandType = this.checkExpr(expr.expr);
1054
+
1055
+ // await는 Promise 타입에만 사용 가능
1056
+ if (operandType.kind !== "promise" && operandType.kind !== "unknown") {
1057
+ this.error(
1058
+ `await requires Promise type, got ${typeToString(operandType)}`,
1059
+ expr.line, expr.col,
1060
+ );
1061
+ return { kind: "unknown" };
1062
+ }
1063
+
1064
+ // Promise<T>이면 T를 반환
1065
+ if (operandType.kind === "promise") {
1066
+ return operandType.element;
1067
+ }
1068
+
1069
+ return { kind: "unknown" };
1070
+ }
1071
+
1072
+ private checkCall(expr: Expr & { kind: "call" }): Type {
1073
+ // 내장 함수 처리
1074
+ if (expr.callee.kind === "ident") {
1075
+ const name = expr.callee.name;
1076
+
1077
+ // 내장 함수 타입 (SPEC_10)
1078
+ const builtinType = this.getBuiltinReturnType(name, expr.args);
1079
+ if (builtinType) {
1080
+ for (const arg of expr.args) this.checkExpr(arg);
1081
+ return builtinType;
1082
+ }
1083
+
1084
+ // 사용자 함수
1085
+ const fn = this.functions.get(name);
1086
+ if (fn) {
1087
+ if (expr.args.length !== fn.params.length) {
1088
+ this.error(
1089
+ `'${name}' expects ${fn.params.length} arguments, got ${expr.args.length}`,
1090
+ expr.line, expr.col,
1091
+ );
1092
+ }
1093
+
1094
+ for (let i = 0; i < expr.args.length; i++) {
1095
+ const argType = this.checkExpr(expr.args[i]);
1096
+ if (i < fn.params.length) {
1097
+ if (!typesEqual(argType, fn.params[i].type) && argType.kind !== "unknown") {
1098
+ this.error(
1099
+ `argument ${i + 1} type mismatch: expected ${typeToString(fn.params[i].type)}, got ${typeToString(argType)}`,
1100
+ expr.line, expr.col,
1101
+ );
1102
+ }
1103
+
1104
+ // Move semantics: 인자 전달 시 Move (SPEC_07 Q4)
1105
+ if (!isCopyType(argType) && expr.args[i].kind === "ident") {
1106
+ const varInfo = this.scope.lookup((expr.args[i] as any).name);
1107
+ if (varInfo) varInfo.moved = true;
1108
+ }
1109
+ }
1110
+ }
1111
+
1112
+ return fn.returnType;
1113
+ }
1114
+ }
1115
+
1116
+ // 함수 타입 변수 호출 (fn 타입 값)
1117
+ const calleeType = this.checkExpr(expr.callee);
1118
+ if (calleeType.kind === "fn") {
1119
+ // 함수 타입 인자 개수 검사
1120
+ if (expr.args.length !== calleeType.params.length) {
1121
+ this.error(
1122
+ `function expects ${calleeType.params.length} arguments, got ${expr.args.length}`,
1123
+ expr.line, expr.col,
1124
+ );
1125
+ }
1126
+
1127
+ // 각 인자의 타입 검사
1128
+ for (let i = 0; i < expr.args.length; i++) {
1129
+ const argType = this.checkExpr(expr.args[i]);
1130
+ if (i < calleeType.params.length) {
1131
+ if (!typesEqual(argType, calleeType.params[i]) && argType.kind !== "unknown") {
1132
+ this.error(
1133
+ `argument ${i + 1} type mismatch: expected ${typeToString(calleeType.params[i])}, got ${typeToString(argType)}`,
1134
+ expr.line, expr.col,
1135
+ );
1136
+ }
1137
+
1138
+ // Move semantics for function arguments
1139
+ if (!isCopyType(argType) && expr.args[i].kind === "ident") {
1140
+ const varInfo = this.scope.lookup((expr.args[i] as any).name);
1141
+ if (varInfo) varInfo.moved = true;
1142
+ }
1143
+ }
1144
+ }
1145
+
1146
+ return calleeType.returnType;
1147
+ }
1148
+
1149
+ // 메서드 호출 (field_access + call)
1150
+ if (expr.callee.kind === "field_access") {
1151
+ for (const arg of expr.args) this.checkExpr(arg);
1152
+ return { kind: "unknown" }; // 메서드 반환 타입은 정적으로 모름
1153
+ }
1154
+
1155
+ // 알 수 없는 함수
1156
+ for (const arg of expr.args) this.checkExpr(arg);
1157
+ return { kind: "unknown" };
1158
+ }
1159
+
1160
+ private checkIndex(expr: Expr & { kind: "index" }): Type {
1161
+ const objType = this.checkExpr(expr.object);
1162
+ const idxType = this.checkExpr(expr.index);
1163
+
1164
+ if (idxType.kind !== "i32" && idxType.kind !== "unknown") {
1165
+ this.error(`array index must be i32, got ${typeToString(idxType)}`, expr.line, expr.col);
1166
+ }
1167
+
1168
+ if (objType.kind === "array") return objType.element;
1169
+ if (objType.kind === "string") return { kind: "string" }; // char_at 대체
1170
+ if (objType.kind !== "unknown") {
1171
+ this.error(`cannot index into ${typeToString(objType)}`, expr.line, expr.col);
1172
+ }
1173
+
1174
+ return { kind: "unknown" };
1175
+ }
1176
+
1177
+ private checkFieldAccess(expr: Expr & { kind: "field_access" }): Type {
1178
+ const objType = this.checkExpr(expr.object);
1179
+
1180
+ if (objType.kind === "struct") {
1181
+ // 필드 접근
1182
+ const fieldType = objType.fields.get(expr.field);
1183
+ if (fieldType) {
1184
+ return fieldType;
1185
+ }
1186
+
1187
+ // 메서드 호출인지 확인
1188
+ const structName = this.findStructName(objType);
1189
+ if (structName) {
1190
+ const impl = this.findImplMethod(structName, expr.field);
1191
+ if (impl) {
1192
+ // 메서드를 함수 타입으로 반환
1193
+ return { kind: "fn", params: impl.params, returnType: impl.returnType };
1194
+ }
1195
+ }
1196
+
1197
+ this.error(`struct has no field or method '${expr.field}'`, expr.line, expr.col);
1198
+ return { kind: "unknown" };
1199
+ }
1200
+
1201
+ // 메서드 스타일 호출 (ch.recv 등) — unknown 반환
1202
+ if (objType.kind !== "unknown") {
1203
+ // 채널 메서드
1204
+ if (objType.kind === "channel") {
1205
+ if (expr.field === "recv" || expr.field === "send") return { kind: "unknown" };
1206
+ }
1207
+ }
1208
+
1209
+ return { kind: "unknown" };
1210
+ }
1211
+
1212
+ private findStructName(objType: Type): string | null {
1213
+ // 구조체 이름 찾기 (간소화)
1214
+ for (const [name, sType] of this.structs) {
1215
+ if (typesEqual(sType, objType)) {
1216
+ return name;
1217
+ }
1218
+ }
1219
+ return null;
1220
+ }
1221
+
1222
+ private findImplMethod(structName: string, methodName: string): { params: Type[]; returnType: Type } | null {
1223
+ // impl에서 메서드 찾기
1224
+ for (const impl of this.impls) {
1225
+ if (impl.forType === structName) {
1226
+ const method = impl.methods.get(methodName);
1227
+ if (method) {
1228
+ return method;
1229
+ }
1230
+ }
1231
+ }
1232
+ return null;
1233
+ }
1234
+
1235
+ private checkAssign(expr: Expr & { kind: "assign" }): Type {
1236
+ const valType = this.checkExpr(expr.value);
1237
+
1238
+ if (expr.target.kind === "ident") {
1239
+ const info = this.scope.lookup(expr.target.name);
1240
+ if (!info) {
1241
+ this.error(`undefined variable: '${expr.target.name}'`, expr.line, expr.col);
1242
+ return { kind: "void" };
1243
+ }
1244
+ if (!info.mutable) {
1245
+ this.error(`cannot assign to immutable variable '${expr.target.name}'`, expr.line, expr.col);
1246
+ return { kind: "void" };
1247
+ }
1248
+ if (!typesEqual(info.type, valType) && valType.kind !== "unknown") {
1249
+ this.error(
1250
+ `assignment type mismatch: ${typeToString(info.type)} = ${typeToString(valType)}`,
1251
+ expr.line, expr.col,
1252
+ );
1253
+ }
1254
+
1255
+ // 재할당으로 Move 복구 (SPEC_07 Q7)
1256
+ info.moved = false;
1257
+ }
1258
+
1259
+ if (expr.target.kind === "index") {
1260
+ this.checkExpr(expr.target);
1261
+ }
1262
+
1263
+ if (expr.target.kind === "field_access") {
1264
+ const objType = this.checkExpr(expr.target.object);
1265
+
1266
+ if (objType.kind === "struct") {
1267
+ const fieldType = objType.fields.get(expr.target.field);
1268
+ if (!fieldType) {
1269
+ this.error(`struct has no field '${expr.target.field}'`, expr.line, expr.col);
1270
+ return { kind: "void" };
1271
+ }
1272
+
1273
+ if (!typesEqual(fieldType, valType) && valType.kind !== "unknown") {
1274
+ this.error(
1275
+ `field assignment type mismatch: expected ${typeToString(fieldType)}, got ${typeToString(valType)}`,
1276
+ expr.line, expr.col,
1277
+ );
1278
+ }
1279
+ } else if (objType.kind !== "unknown") {
1280
+ this.error(`cannot assign to field of ${typeToString(objType)}`, expr.line, expr.col);
1281
+ }
1282
+ }
1283
+
1284
+ return { kind: "void" };
1285
+ }
1286
+
1287
+ private checkTry(expr: Expr & { kind: "try" }): Type {
1288
+ const operandType = this.checkExpr(expr.operand);
1289
+
1290
+ // ? 는 Result 또는 Option에만 사용 (SPEC_09 Q6)
1291
+ if (operandType.kind === "result") return operandType.ok;
1292
+ if (operandType.kind === "option") return operandType.element;
1293
+ if (operandType.kind !== "unknown") {
1294
+ this.error(`'?' requires Result or Option, got ${typeToString(operandType)}`, expr.line, expr.col);
1295
+ }
1296
+
1297
+ return { kind: "unknown" };
1298
+ }
1299
+
1300
+ private checkIfExpr(expr: Expr & { kind: "if_expr" }): Type {
1301
+ const condType = this.checkExpr(expr.condition);
1302
+ if (condType.kind !== "bool" && condType.kind !== "unknown") {
1303
+ this.error(`if condition must be bool, got ${typeToString(condType)}`, expr.line, expr.col);
1304
+ }
1305
+
1306
+ // then/else 마지막 식의 타입이 일치해야 함 (SPEC_06)
1307
+ let thenType: Type = { kind: "void" };
1308
+ for (const e of expr.then) thenType = this.checkExpr(e);
1309
+
1310
+ let elseType: Type = { kind: "void" };
1311
+ for (const e of expr.else_) elseType = this.checkExpr(e);
1312
+
1313
+ if (!typesEqual(thenType, elseType) && thenType.kind !== "unknown" && elseType.kind !== "unknown") {
1314
+ this.error(
1315
+ `if expression branches have different types: ${typeToString(thenType)} vs ${typeToString(elseType)}`,
1316
+ expr.line, expr.col,
1317
+ );
1318
+ }
1319
+
1320
+ return thenType;
1321
+ }
1322
+
1323
+ private checkMatchExpr(expr: Expr & { kind: "match_expr" }): Type {
1324
+ const subjectType = this.checkExpr(expr.subject);
1325
+ let resultType: Type = { kind: "unknown" };
1326
+
1327
+ for (const arm of expr.arms) {
1328
+ const prevScope = this.scope;
1329
+ this.scope = new Scope(prevScope);
1330
+ this.checkPattern(arm.pattern, subjectType);
1331
+ const armType = this.checkExpr(arm.body);
1332
+ this.scope = prevScope;
1333
+
1334
+ if (resultType.kind === "unknown") {
1335
+ resultType = armType;
1336
+ } else if (!typesEqual(resultType, armType) && armType.kind !== "unknown") {
1337
+ this.error(
1338
+ `match arms have different types: ${typeToString(resultType)} vs ${typeToString(armType)}`,
1339
+ expr.line, expr.col,
1340
+ );
1341
+ }
1342
+ }
1343
+
1344
+ return resultType;
1345
+ }
1346
+
1347
+ private checkArrayLit(expr: Expr & { kind: "array_lit" }): Type {
1348
+ if (expr.elements.length === 0) return { kind: "array", element: { kind: "unknown" } };
1349
+
1350
+ const firstType = this.checkExpr(expr.elements[0]);
1351
+ for (let i = 1; i < expr.elements.length; i++) {
1352
+ const elemType = this.checkExpr(expr.elements[i]);
1353
+ if (!typesEqual(firstType, elemType) && elemType.kind !== "unknown") {
1354
+ this.error(
1355
+ `array element type mismatch: expected ${typeToString(firstType)}, got ${typeToString(elemType)}`,
1356
+ expr.line, expr.col,
1357
+ );
1358
+ }
1359
+ }
1360
+
1361
+ return { kind: "array", element: firstType };
1362
+ }
1363
+
1364
+ private checkStructLit(expr: Expr & { kind: "struct_lit" }): Type {
1365
+ // struct 정의 확인
1366
+ const structDef = this.structs.get(expr.structName);
1367
+ if (!structDef || structDef.kind !== "struct") {
1368
+ this.error(`undefined struct: '${expr.structName}'`, expr.line, expr.col);
1369
+ return { kind: "unknown" };
1370
+ }
1371
+
1372
+ // 필드 타입 확인
1373
+ const fields = new Map<string, Type>();
1374
+ for (const f of expr.fields) {
1375
+ const fType = this.checkExpr(f.value);
1376
+ const expectedType = structDef.fields.get(f.name);
1377
+
1378
+ if (!expectedType) {
1379
+ this.error(`struct '${expr.structName}' has no field '${f.name}'`, expr.line, expr.col);
1380
+ fields.set(f.name, fType);
1381
+ continue;
1382
+ }
1383
+
1384
+ if (!typesEqual(fType, expectedType) && fType.kind !== "unknown") {
1385
+ this.error(
1386
+ `struct field '${f.name}' type mismatch: expected ${typeToString(expectedType)}, got ${typeToString(fType)}`,
1387
+ expr.line, expr.col,
1388
+ );
1389
+ }
1390
+ fields.set(f.name, expectedType);
1391
+ }
1392
+
1393
+ // 모든 필드가 제공되었는지 확인
1394
+ for (const [fieldName, fieldType] of structDef.fields.entries()) {
1395
+ if (!fields.has(fieldName)) {
1396
+ this.error(`struct '${expr.structName}' is missing field '${fieldName}'`, expr.line, expr.col);
1397
+ }
1398
+ }
1399
+
1400
+ return structDef;
1401
+ }
1402
+
1403
+ private checkFnLit(expr: Expr & { kind: "fn_lit" }): Type {
1404
+ // 함수 리터럴의 매개변수 타입 확인
1405
+ const paramTypes: Type[] = [];
1406
+ for (const param of expr.params) {
1407
+ if (param.type) {
1408
+ const paramType = annotationToType(param.type, this.structs);
1409
+ paramTypes.push(paramType);
1410
+ } else {
1411
+ // 타입 어노테이션 없으면 unknown (타입 추론 미지원)
1412
+ paramTypes.push({ kind: "unknown" });
1413
+ }
1414
+ }
1415
+
1416
+ // 반환 타입 확인
1417
+ let returnType: Type;
1418
+ if (expr.returnType) {
1419
+ returnType = annotationToType(expr.returnType, this.structs);
1420
+ } else {
1421
+ // 함수 본체에서 반환 타입 추론
1422
+ const bodyType = this.checkExpr(expr.body);
1423
+ returnType = bodyType;
1424
+ }
1425
+
1426
+ // 함수 본체 타입 검사 (새로운 스코프에서)
1427
+ const prevScope = this.scope;
1428
+ this.scope = new Scope(prevScope);
1429
+
1430
+ // 매개변수를 스코프에 등록
1431
+ for (let i = 0; i < expr.params.length; i++) {
1432
+ const param = expr.params[i];
1433
+ const paramType = paramTypes[i];
1434
+ this.scope.define(param.name, {
1435
+ type: paramType,
1436
+ mutable: false,
1437
+ moved: false,
1438
+ line: expr.line,
1439
+ col: expr.col,
1440
+ });
1441
+ }
1442
+
1443
+ // 함수 본체 타입 검사
1444
+ const actualBodyType = this.checkExpr(expr.body);
1445
+
1446
+ // 반환 타입과 일치 검사
1447
+ if (expr.returnType && !typesEqual(actualBodyType, returnType) && actualBodyType.kind !== "unknown") {
1448
+ this.error(
1449
+ `function body type mismatch: expected ${typeToString(returnType)}, got ${typeToString(actualBodyType)}`,
1450
+ expr.line, expr.col,
1451
+ );
1452
+ }
1453
+
1454
+ this.scope = prevScope;
1455
+
1456
+ // 함수 타입 반환
1457
+ return { kind: "fn", params: paramTypes, returnType };
1458
+ }
1459
+
1460
+ private checkBlockExpr(expr: Expr & { kind: "block_expr" }): Type {
1461
+ const prevScope = this.scope;
1462
+ this.scope = new Scope(prevScope);
1463
+
1464
+ for (const s of expr.stmts) this.checkStmt(s);
1465
+ let result: Type = { kind: "void" };
1466
+ if (expr.expr) result = this.checkExpr(expr.expr);
1467
+
1468
+ this.scope = prevScope;
1469
+ return result;
1470
+ }
1471
+
1472
+ // ============================================================
1473
+ // 타입 강제 변환 (Type Coercion)
1474
+ // ============================================================
1475
+
1476
+ private coerceNumeric(left: Type, right: Type): Type | null {
1477
+ // 양쪽이 numeric 타입이면 강제 변환 가능
1478
+ const numericTypes = ["i32", "i64", "f64"];
1479
+ if (!numericTypes.includes(left.kind) || !numericTypes.includes(right.kind)) {
1480
+ return null;
1481
+ }
1482
+
1483
+ // 타입 승격 규칙: f64 > i64 > i32
1484
+ const hierarchy = { i32: 0, i64: 1, f64: 2 };
1485
+ const leftRank = hierarchy[left.kind as keyof typeof hierarchy];
1486
+ const rightRank = hierarchy[right.kind as keyof typeof hierarchy];
1487
+
1488
+ if (leftRank >= rightRank) return left;
1489
+ else return right;
1490
+ }
1491
+
1492
+ // ============================================================
1493
+ // 내장 함수 (SPEC_10)
1494
+ // ============================================================
1495
+
1496
+ private isBuiltin(name: string): boolean {
1497
+ return [
1498
+ "println", "print", "read_line", "read_file", "write_file",
1499
+ "i32", "i64", "f64", "str",
1500
+ "push", "pop", "slice", "clone", "length",
1501
+ "char_at", "char_code", "chr", "contains", "split", "trim", "to_upper", "to_lower",
1502
+ "abs", "min", "max", "pow", "sqrt",
1503
+ "range", "channel", "panic", "typeof", "assert",
1504
+ // Phase 7: 20 Core Libraries
1505
+ "md5", "sha256", "sha512", "base64_encode", "base64_decode", "hmac",
1506
+ "json_parse", "json_stringify", "json_validate", "json_pretty",
1507
+ "starts_with", "ends_with", "replace",
1508
+ "reverse", "sort", "unique",
1509
+ "gcd", "lcm",
1510
+ "uuid", "timestamp",
1511
+ "send", "recv",
1512
+ // Environment (1)
1513
+ "env",
1514
+ // Phase 2: HTTP Client
1515
+ "http_get", "http_post", "http_post_json", "fetch",
1516
+ // HTTP Server & External Commands (gogs-server 지원)
1517
+ "http_server_create", "http_server_accept", "http_server_respond", "exec_command",
1518
+ ].includes(name);
1519
+ }
1520
+
1521
+ private getBuiltinReturnType(name: string, args: Expr[]): Type | null {
1522
+ switch (name) {
1523
+ // Basic I/O
1524
+ case "println": case "print": return { kind: "void" };
1525
+ case "read_line": return { kind: "string" };
1526
+ case "read_file": return { kind: "result", ok: { kind: "string" }, err: { kind: "string" } };
1527
+ case "write_file": return { kind: "result", ok: { kind: "void" }, err: { kind: "string" } };
1528
+
1529
+ // Type conversions
1530
+ case "i32": return { kind: "result", ok: { kind: "i32" }, err: { kind: "string" } };
1531
+ case "i64": return { kind: "result", ok: { kind: "i64" }, err: { kind: "string" } };
1532
+ case "f64": return { kind: "result", ok: { kind: "f64" }, err: { kind: "string" } };
1533
+ case "str": return { kind: "string" };
1534
+
1535
+ // Array operations
1536
+ case "length": return { kind: "i32" };
1537
+ case "push": return { kind: "void" };
1538
+ case "pop": return { kind: "unknown" };
1539
+ case "clone": return { kind: "unknown" };
1540
+ case "slice": return { kind: "unknown" };
1541
+ case "reverse": case "sort": case "unique":
1542
+ return { kind: "unknown" }; // 배열 타입 복제
1543
+
1544
+ // String operations
1545
+ case "char_at": case "trim": case "to_upper": case "to_lower": case "chr":
1546
+ return { kind: "string" };
1547
+ case "char_code":
1548
+ return { kind: "i32" };
1549
+ case "contains": case "starts_with": case "ends_with":
1550
+ return { kind: "bool" };
1551
+ case "split": return { kind: "array", element: { kind: "string" } };
1552
+ case "replace": return { kind: "string" };
1553
+
1554
+ // Range & channel
1555
+ case "range": return { kind: "array", element: { kind: "i32" } };
1556
+ case "channel": return { kind: "unknown" };
1557
+ case "send": case "recv": return { kind: "unknown" };
1558
+
1559
+ // Control
1560
+ case "panic": return { kind: "void" };
1561
+ case "typeof": return { kind: "string" };
1562
+ case "assert": return { kind: "void" };
1563
+
1564
+ // Math
1565
+ case "abs": case "min": case "max": case "pow": case "sqrt":
1566
+ return null; // 인자 타입에 의존
1567
+ case "gcd": case "lcm": return { kind: "i32" };
1568
+ // Bitwise Operations (A-1)
1569
+ case "bitand": case "bitor": case "bitxor": case "shl": case "shr":
1570
+ return { kind: "i32" };
1571
+
1572
+ // Cryptography & Encoding (Phase 7)
1573
+ case "md5": case "sha256": case "sha512": case "hmac":
1574
+ return { kind: "string" };
1575
+ case "base64_encode": return { kind: "string" };
1576
+ case "base64_decode":
1577
+ return { kind: "result", ok: { kind: "string" }, err: { kind: "string" } };
1578
+
1579
+ // JSON (Phase 7)
1580
+ case "json_parse":
1581
+ return { kind: "result", ok: { kind: "unknown" }, err: { kind: "string" } };
1582
+ case "json_stringify": return { kind: "string" };
1583
+ case "json_validate": return { kind: "bool" };
1584
+ case "json_pretty": return { kind: "string" };
1585
+
1586
+ // Utils (Phase 7)
1587
+ case "uuid": return { kind: "string" };
1588
+ case "timestamp": return { kind: "f64" };
1589
+
1590
+ // Environment
1591
+ case "env": return { kind: "string" };
1592
+
1593
+ // HTTP Client (Phase 2)
1594
+ case "http_get": case "http_post": case "http_post_json": case "fetch":
1595
+ return { kind: "result", ok: { kind: "string" }, err: { kind: "string" } };
1596
+
1597
+ // Database (Phase 3)
1598
+ case "sqlite_open":
1599
+ case "pg_connect":
1600
+ case "mysql_connect":
1601
+ return { kind: "unknown" }; // Returns database handle
1602
+ case "sqlite_query":
1603
+ case "pg_query":
1604
+ case "mysql_query":
1605
+ return { kind: "result", ok: { kind: "array", element: { kind: "unknown" } }, err: { kind: "string" } };
1606
+ case "sqlite_execute":
1607
+ case "pg_execute":
1608
+ case "mysql_execute":
1609
+ return { kind: "result", ok: { kind: "unknown" }, err: { kind: "string" } };
1610
+ case "sqlite_close":
1611
+ case "sqlite_begin": case "sqlite_commit": case "sqlite_rollback":
1612
+ case "pg_close":
1613
+ case "pg_begin": case "pg_commit": case "pg_rollback":
1614
+ case "mysql_close":
1615
+ case "mysql_begin": case "mysql_commit": case "mysql_rollback":
1616
+ return { kind: "void" };
1617
+
1618
+ // v4.3 Extensions - Math (7) - B-1
1619
+ case "floor": case "ceil": case "round":
1620
+ return { kind: "i32" };
1621
+ case "random":
1622
+ return { kind: "f64" };
1623
+ case "sin": case "cos":
1624
+ return { kind: "f64" };
1625
+ case "log":
1626
+ return { kind: "result", ok: { kind: "f64" }, err: { kind: "string" } };
1627
+
1628
+ // v4.3 Extensions - String (3) - B-2
1629
+ case "index_of":
1630
+ return { kind: "option", element: { kind: "i32" } };
1631
+ case "pad_left": case "pad_right":
1632
+ return { kind: "string" };
1633
+
1634
+ // v4.3 Extensions - Regex (3) - B-3
1635
+ case "regex_match":
1636
+ return { kind: "option", element: { kind: "string" } };
1637
+ case "regex_find_all":
1638
+ return { kind: "array", element: { kind: "string" } };
1639
+ case "regex_replace":
1640
+ return { kind: "result", ok: { kind: "string" }, err: { kind: "string" } };
1641
+
1642
+ // v4.3 Extensions - CSV (2) - B-4
1643
+ case "csv_parse":
1644
+ return { kind: "array", element: { kind: "array", element: { kind: "string" } } };
1645
+ case "csv_stringify":
1646
+ return { kind: "string" };
1647
+
1648
+ // v4.3 Extensions - DateTime (3) - B-5
1649
+ case "now":
1650
+ return { kind: "f64" };
1651
+ case "format_date":
1652
+ return { kind: "string" };
1653
+ case "parse_date":
1654
+ return { kind: "result", ok: { kind: "f64" }, err: { kind: "string" } };
1655
+
1656
+ // v4.3 Extensions - YAML (2)
1657
+ case "yaml_parse":
1658
+ return { kind: "result", ok: { kind: "unknown" }, err: { kind: "string" } };
1659
+ case "yaml_stringify":
1660
+ return { kind: "string" };
1661
+
1662
+ // HTTP Server & External Commands
1663
+ case "http_server_create":
1664
+ return { kind: "result", ok: { kind: "i32" }, err: { kind: "string" } };
1665
+ case "http_server_accept":
1666
+ return { kind: "result", ok: { kind: "unknown" }, err: { kind: "string" } };
1667
+ case "http_server_respond":
1668
+ return { kind: "void" };
1669
+ case "exec_command":
1670
+ return { kind: "result", ok: { kind: "string" }, err: { kind: "string" } };
1671
+
1672
+ default: return null;
1673
+ }
1674
+ }
1675
+
1676
+ // ============================================================
1677
+ // 채널/Actor 관련 검사
1678
+ // ============================================================
1679
+
1680
+ private checkChanNew(expr: Expr & { kind: "chan_new" }): Type {
1681
+ const elementType = annotationToType(expr.element, this.structs);
1682
+ return { kind: "channel", element: elementType };
1683
+ }
1684
+
1685
+ private checkChanSend(expr: Expr & { kind: "chan_send" }): Type {
1686
+ const chanType = this.checkExpr(expr.chan);
1687
+ const valueType = this.checkExpr(expr.value);
1688
+
1689
+ if (chanType.kind !== "channel" && chanType.kind !== "unknown") {
1690
+ this.error(
1691
+ `cannot send on non-channel type ${typeToString(chanType)}`,
1692
+ expr.line, expr.col,
1693
+ );
1694
+ return { kind: "void" };
1695
+ }
1696
+
1697
+ if (chanType.kind === "channel") {
1698
+ if (!typesEqual(chanType.element, valueType) && valueType.kind !== "unknown") {
1699
+ this.error(
1700
+ `channel element type mismatch: expected ${typeToString(chanType.element)}, got ${typeToString(valueType)}`,
1701
+ expr.line, expr.col,
1702
+ );
1703
+ }
1704
+ }
1705
+
1706
+ return { kind: "void" };
1707
+ }
1708
+
1709
+ private checkChanRecv(expr: Expr & { kind: "chan_recv" }): Type {
1710
+ const chanType = this.checkExpr(expr.chan);
1711
+
1712
+ if (chanType.kind !== "channel" && chanType.kind !== "unknown") {
1713
+ this.error(
1714
+ `cannot receive on non-channel type ${typeToString(chanType)}`,
1715
+ expr.line, expr.col,
1716
+ );
1717
+ return { kind: "unknown" };
1718
+ }
1719
+
1720
+ if (chanType.kind === "channel") {
1721
+ return chanType.element;
1722
+ }
1723
+
1724
+ return { kind: "unknown" };
1725
+ }
1726
+
1727
+ // ============================================================
1728
+ // 에러 헬퍼
1729
+ // ============================================================
1730
+
1731
+ private error(message: string, line: number, col: number): void {
1732
+ this.errors.push({ message, line, col });
1733
+ }
1734
+
1735
+ // ============================================================
1736
+ // 제네릭 관련 public 메서드
1737
+ // ============================================================
1738
+
1739
+ getInstantiatedFunctions(): Map<string, FnInfo> {
1740
+ return this.instantiatedFunctions;
1741
+ }
1742
+
1743
+ getInstantiatedStructs(): Map<string, Type> {
1744
+ return this.instantiatedStructs;
1745
+ }
1746
+
1747
+ getGenericFunctions(): Map<string, GenericFnDef> {
1748
+ return this.genericFunctions;
1749
+ }
1750
+
1751
+ getGenericStructs(): Map<string, GenericStructDef> {
1752
+ return this.genericStructs;
1753
+ }
1754
+
1755
+ getStructs(): Map<string, Type> {
1756
+ return this.structs;
1757
+ }
1758
+
1759
+ // ============================================================
1760
+ // Name mangling for generic instantiations
1761
+ // ============================================================
1762
+
1763
+ private typeToMangledName(t: Type): string {
1764
+ switch (t.kind) {
1765
+ case "i32": return "i32";
1766
+ case "i64": return "i64";
1767
+ case "f64": return "f64";
1768
+ case "bool": return "bool";
1769
+ case "string": return "str";
1770
+ case "void": return "void";
1771
+ case "array":
1772
+ return `arr_${this.typeToMangledName(t.element)}`;
1773
+ case "channel":
1774
+ return `chan_${this.typeToMangledName(t.element)}`;
1775
+ case "option":
1776
+ return `opt_${this.typeToMangledName(t.element)}`;
1777
+ case "result":
1778
+ return `res_${this.typeToMangledName(t.ok)}_${this.typeToMangledName(t.err)}`;
1779
+ case "struct":
1780
+ return `struct_${[...t.fields.keys()].join("_")}`;
1781
+ case "fn": {
1782
+ const paramNames = t.params.map(p => this.typeToMangledName(p)).join("_");
1783
+ const retName = this.typeToMangledName(t.returnType);
1784
+ return `fn_${paramNames}_${retName}`;
1785
+ }
1786
+ case "type_param":
1787
+ return t.name;
1788
+ case "generic_ref":
1789
+ return `gen_${t.name}`;
1790
+ default:
1791
+ return "unknown";
1792
+ }
1793
+ }
1794
+
1795
+ private mangleFunctionName(baseName: string, fnType: FnInfo): string {
1796
+ const argNames = fnType.params
1797
+ .map(p => this.typeToMangledName(p.type))
1798
+ .join("_");
1799
+ const retName = this.typeToMangledName(fnType.returnType);
1800
+ if (argNames) {
1801
+ return `${baseName}_${argNames}_${retName}`;
1802
+ } else {
1803
+ return `${baseName}_${retName}`;
1804
+ }
1805
+ }
1806
+
1807
+ // ============================================================
1808
+ // 제네릭 함수 인스턴시화
1809
+ // ============================================================
1810
+
1811
+ instantiateFunction(name: string, typeArgs: Type[]): FnInfo | null {
1812
+ const genericFn = this.genericFunctions.get(name);
1813
+ if (!genericFn) return null;
1814
+
1815
+ if (typeArgs.length !== genericFn.typeParams.length) {
1816
+ return null;
1817
+ }
1818
+
1819
+ // 타입 바인딩 생성
1820
+ const bindings = new Map<string, Type>();
1821
+ for (let i = 0; i < genericFn.typeParams.length; i++) {
1822
+ bindings.set(genericFn.typeParams[i], typeArgs[i]);
1823
+ }
1824
+
1825
+ // 파라미터와 반환 타입 치환
1826
+ const params = genericFn.params.map(p => ({
1827
+ name: p.name,
1828
+ type: substituteType(p.type, bindings),
1829
+ }));
1830
+ const returnType = substituteType(genericFn.returnType, bindings);
1831
+
1832
+ return { params, returnType };
1833
+ }
1834
+
1835
+ // ============================================================
1836
+ // 제네릭 구조체 인스턴시화
1837
+ // ============================================================
1838
+
1839
+ instantiateStruct(name: string, typeArgs: Type[]): Type {
1840
+ const genericStruct = this.genericStructs.get(name);
1841
+ if (!genericStruct) return { kind: "unknown" };
1842
+
1843
+ if (typeArgs.length !== genericStruct.typeParams.length) {
1844
+ return { kind: "unknown" };
1845
+ }
1846
+
1847
+ // 타입 바인딩 생성
1848
+ const bindings = new Map<string, Type>();
1849
+ for (let i = 0; i < genericStruct.typeParams.length; i++) {
1850
+ bindings.set(genericStruct.typeParams[i], typeArgs[i]);
1851
+ }
1852
+
1853
+ // 필드 타입 치환
1854
+ const fields = new Map<string, Type>();
1855
+ for (const [fieldName, fieldType] of genericStruct.fields) {
1856
+ fields.set(fieldName, substituteType(fieldType, bindings));
1857
+ }
1858
+
1859
+ return { kind: "struct", fields };
1860
+ }
1861
+ }