freelang-v4 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/README.md +548 -0
  2. package/dist/ast.d.ts +367 -0
  3. package/dist/ast.js +4 -0
  4. package/dist/ast.js.map +1 -0
  5. package/dist/async-basic.test.d.ts +1 -0
  6. package/dist/async-basic.test.js +88 -0
  7. package/dist/async-basic.test.js.map +1 -0
  8. package/dist/async-jest.test.d.ts +1 -0
  9. package/dist/async-jest.test.js +99 -0
  10. package/dist/async-jest.test.js.map +1 -0
  11. package/dist/channel-jest.test.d.ts +1 -0
  12. package/dist/channel-jest.test.js +148 -0
  13. package/dist/channel-jest.test.js.map +1 -0
  14. package/dist/checker-jest.test.d.ts +1 -0
  15. package/dist/checker-jest.test.js +160 -0
  16. package/dist/checker-jest.test.js.map +1 -0
  17. package/dist/checker.d.ts +149 -0
  18. package/dist/checker.js +1565 -0
  19. package/dist/checker.js.map +1 -0
  20. package/dist/checker.test.d.ts +1 -0
  21. package/dist/checker.test.js +217 -0
  22. package/dist/checker.test.js.map +1 -0
  23. package/dist/compiler-jest.test.d.ts +1 -0
  24. package/dist/compiler-jest.test.js +233 -0
  25. package/dist/compiler-jest.test.js.map +1 -0
  26. package/dist/compiler.d.ts +127 -0
  27. package/dist/compiler.js +1588 -0
  28. package/dist/compiler.js.map +1 -0
  29. package/dist/compiler.test.d.ts +1 -0
  30. package/dist/compiler.test.js +313 -0
  31. package/dist/compiler.test.js.map +1 -0
  32. package/dist/db-100m-full.d.ts +5 -0
  33. package/dist/db-100m-full.js +78 -0
  34. package/dist/db-100m-full.js.map +1 -0
  35. package/dist/db-100m-no-index.d.ts +12 -0
  36. package/dist/db-100m-no-index.js +119 -0
  37. package/dist/db-100m-no-index.js.map +1 -0
  38. package/dist/db-100m-real.d.ts +5 -0
  39. package/dist/db-100m-real.js +131 -0
  40. package/dist/db-100m-real.js.map +1 -0
  41. package/dist/db-100m-streaming.d.ts +15 -0
  42. package/dist/db-100m-streaming.js +164 -0
  43. package/dist/db-100m-streaming.js.map +1 -0
  44. package/dist/db-100m-test.d.ts +5 -0
  45. package/dist/db-100m-test.js +111 -0
  46. package/dist/db-100m-test.js.map +1 -0
  47. package/dist/db-jest.test.d.ts +1 -0
  48. package/dist/db-jest.test.js +182 -0
  49. package/dist/db-jest.test.js.map +1 -0
  50. package/dist/db-runtime.d.ts +24 -0
  51. package/dist/db-runtime.js +204 -0
  52. package/dist/db-runtime.js.map +1 -0
  53. package/dist/db.d.ts +249 -0
  54. package/dist/db.js +593 -0
  55. package/dist/db.js.map +1 -0
  56. package/dist/file-io-jest.test.d.ts +1 -0
  57. package/dist/file-io-jest.test.js +225 -0
  58. package/dist/file-io-jest.test.js.map +1 -0
  59. package/dist/for-of-jest.test.d.ts +1 -0
  60. package/dist/for-of-jest.test.js +230 -0
  61. package/dist/for-of-jest.test.js.map +1 -0
  62. package/dist/for-of.test.d.ts +1 -0
  63. package/dist/for-of.test.js +305 -0
  64. package/dist/for-of.test.js.map +1 -0
  65. package/dist/function-literal-jest.test.d.ts +1 -0
  66. package/dist/function-literal-jest.test.js +180 -0
  67. package/dist/function-literal-jest.test.js.map +1 -0
  68. package/dist/function-literal.test.d.ts +1 -0
  69. package/dist/function-literal.test.js +245 -0
  70. package/dist/function-literal.test.js.map +1 -0
  71. package/dist/generics-jest.test.d.ts +1 -0
  72. package/dist/generics-jest.test.js +93 -0
  73. package/dist/generics-jest.test.js.map +1 -0
  74. package/dist/ir-gen.d.ts +15 -0
  75. package/dist/ir-gen.js +400 -0
  76. package/dist/ir-gen.js.map +1 -0
  77. package/dist/ir.d.ts +114 -0
  78. package/dist/ir.js +5 -0
  79. package/dist/ir.js.map +1 -0
  80. package/dist/lexer.d.ts +110 -0
  81. package/dist/lexer.js +467 -0
  82. package/dist/lexer.js.map +1 -0
  83. package/dist/lexer.test.d.ts +1 -0
  84. package/dist/lexer.test.js +426 -0
  85. package/dist/lexer.test.js.map +1 -0
  86. package/dist/main.d.ts +2 -0
  87. package/dist/main.js +241 -0
  88. package/dist/main.js.map +1 -0
  89. package/dist/module-jest.test.d.ts +1 -0
  90. package/dist/module-jest.test.js +123 -0
  91. package/dist/module-jest.test.js.map +1 -0
  92. package/dist/parser.d.ts +56 -0
  93. package/dist/parser.js +1060 -0
  94. package/dist/parser.js.map +1 -0
  95. package/dist/parser.test.d.ts +1 -0
  96. package/dist/parser.test.js +461 -0
  97. package/dist/parser.test.js.map +1 -0
  98. package/dist/pattern-matching-jest.test.d.ts +1 -0
  99. package/dist/pattern-matching-jest.test.js +158 -0
  100. package/dist/pattern-matching-jest.test.js.map +1 -0
  101. package/dist/pkg/init.d.ts +1 -0
  102. package/dist/pkg/init.js +118 -0
  103. package/dist/pkg/init.js.map +1 -0
  104. package/dist/pkg/install.d.ts +1 -0
  105. package/dist/pkg/install.js +77 -0
  106. package/dist/pkg/install.js.map +1 -0
  107. package/dist/pkg/registry.d.ts +23 -0
  108. package/dist/pkg/registry.js +106 -0
  109. package/dist/pkg/registry.js.map +1 -0
  110. package/dist/pkg/run.d.ts +1 -0
  111. package/dist/pkg/run.js +76 -0
  112. package/dist/pkg/run.js.map +1 -0
  113. package/dist/pkg/toml.d.ts +5 -0
  114. package/dist/pkg/toml.js +117 -0
  115. package/dist/pkg/toml.js.map +1 -0
  116. package/dist/repl.d.ts +15 -0
  117. package/dist/repl.js +197 -0
  118. package/dist/repl.js.map +1 -0
  119. package/dist/runtime/bytecode.d.ts +92 -0
  120. package/dist/runtime/bytecode.js +253 -0
  121. package/dist/runtime/bytecode.js.map +1 -0
  122. package/dist/runtime/value.d.ts +102 -0
  123. package/dist/runtime/value.js +302 -0
  124. package/dist/runtime/value.js.map +1 -0
  125. package/dist/runtime/vm.d.ts +65 -0
  126. package/dist/runtime/vm.js +293 -0
  127. package/dist/runtime/vm.js.map +1 -0
  128. package/dist/struct-instance-jest.test.d.ts +1 -0
  129. package/dist/struct-instance-jest.test.js +209 -0
  130. package/dist/struct-instance-jest.test.js.map +1 -0
  131. package/dist/struct-instance.test.d.ts +1 -0
  132. package/dist/struct-instance.test.js +291 -0
  133. package/dist/struct-instance.test.js.map +1 -0
  134. package/dist/struct-jest.test.d.ts +1 -0
  135. package/dist/struct-jest.test.js +176 -0
  136. package/dist/struct-jest.test.js.map +1 -0
  137. package/dist/struct.test.d.ts +1 -0
  138. package/dist/struct.test.js +231 -0
  139. package/dist/struct.test.js.map +1 -0
  140. package/dist/trait-jest.test.d.ts +1 -0
  141. package/dist/trait-jest.test.js +120 -0
  142. package/dist/trait-jest.test.js.map +1 -0
  143. package/dist/vm-jest.test.d.ts +1 -0
  144. package/dist/vm-jest.test.js +569 -0
  145. package/dist/vm-jest.test.js.map +1 -0
  146. package/dist/vm.d.ts +81 -0
  147. package/dist/vm.js +1956 -0
  148. package/dist/vm.js.map +1 -0
  149. package/dist/vm.test.d.ts +1 -0
  150. package/dist/vm.test.js +337 -0
  151. package/dist/vm.test.js.map +1 -0
  152. package/dist/web-repl/sandbox.d.ts +11 -0
  153. package/dist/web-repl/sandbox.js +76 -0
  154. package/dist/web-repl/sandbox.js.map +1 -0
  155. package/dist/web-repl/server.d.ts +1 -0
  156. package/dist/web-repl/server.js +111 -0
  157. package/dist/web-repl/server.js.map +1 -0
  158. package/dist/while-loop-jest.test.d.ts +1 -0
  159. package/dist/while-loop-jest.test.js +201 -0
  160. package/dist/while-loop-jest.test.js.map +1 -0
  161. package/dist/while-loop.test.d.ts +1 -0
  162. package/dist/while-loop.test.js +262 -0
  163. package/dist/while-loop.test.js.map +1 -0
  164. package/docs/EXPERIENCE.md +787 -0
  165. package/docs/README.md +175 -0
  166. package/docs/V1_V2_V3_ANALYSIS.md +107 -0
  167. package/docs/_config.yml +36 -0
  168. package/docs/api-reference.md +459 -0
  169. package/docs/architecture.md +470 -0
  170. package/docs/benchmarks.md +295 -0
  171. package/docs/comparison.md +454 -0
  172. package/docs/index.md +335 -0
  173. package/docs/language-completeness.md +228 -0
  174. package/docs/learning-guide.md +651 -0
  175. package/package.json +65 -0
  176. package/src/api/deploy_key.fl +294 -0
  177. package/src/api/issue.fl +302 -0
  178. package/src/api/org.fl +356 -0
  179. package/src/api/repo.fl +394 -0
  180. package/src/api/team.fl +299 -0
  181. package/src/api/user.fl +385 -0
  182. package/src/api/webhook.fl +273 -0
  183. package/src/ast.ts +158 -0
  184. package/src/async-basic.test.ts +94 -0
  185. package/src/async-jest.test.ts +107 -0
  186. package/src/channel-jest.test.ts +158 -0
  187. package/src/checker-jest.test.ts +189 -0
  188. package/src/checker.test.ts +279 -0
  189. package/src/checker.ts +1861 -0
  190. package/src/commands/analyze.fl +227 -0
  191. package/src/commands/auth.fl +315 -0
  192. package/src/commands/batch.fl +349 -0
  193. package/src/commands/config.fl +199 -0
  194. package/src/commands/deploy_key.fl +352 -0
  195. package/src/commands/issue.fl +275 -0
  196. package/src/commands/main.fl +492 -0
  197. package/src/commands/org.fl +425 -0
  198. package/src/commands/repo.fl +581 -0
  199. package/src/commands/team.fl +244 -0
  200. package/src/commands/user.fl +423 -0
  201. package/src/commands/webhook.fl +400 -0
  202. package/src/compiler-jest.test.ts +275 -0
  203. package/src/compiler.test.ts +375 -0
  204. package/src/compiler.ts +1770 -0
  205. package/src/config.fl +175 -0
  206. package/src/core/batch.fl +355 -0
  207. package/src/core/cache.fl +284 -0
  208. package/src/core/ensure.fl +324 -0
  209. package/src/db-100m-full.ts +96 -0
  210. package/src/db-100m-no-index.ts +133 -0
  211. package/src/db-100m-real.ts +152 -0
  212. package/src/db-100m-streaming.ts +154 -0
  213. package/src/db-100m-test.ts +136 -0
  214. package/src/db-jest.test.ts +161 -0
  215. package/src/db-runtime.ts +242 -0
  216. package/src/db.ts +676 -0
  217. package/src/errors.fl +134 -0
  218. package/src/for-of-jest.test.ts +246 -0
  219. package/src/for-of.test.ts +308 -0
  220. package/src/function-literal-jest.test.ts +193 -0
  221. package/src/function-literal.test.ts +248 -0
  222. package/src/generics-jest.test.ts +104 -0
  223. package/src/http/client.fl +327 -0
  224. package/src/ir-gen.ts +459 -0
  225. package/src/ir.ts +80 -0
  226. package/src/lexer.test.ts +499 -0
  227. package/src/lexer.ts +522 -0
  228. package/src/main.ts +223 -0
  229. package/src/models.fl +162 -0
  230. package/src/module-jest.test.ts +145 -0
  231. package/src/parser.test.ts +542 -0
  232. package/src/parser.ts +1211 -0
  233. package/src/pattern-matching-jest.test.ts +170 -0
  234. package/src/pkg/init.ts +91 -0
  235. package/src/pkg/install.ts +56 -0
  236. package/src/pkg/registry.ts +103 -0
  237. package/src/pkg/run.ts +49 -0
  238. package/src/pkg/toml.ts +129 -0
  239. package/src/repl.ts +190 -0
  240. package/src/runtime/bytecode.ts +291 -0
  241. package/src/runtime/value.ts +322 -0
  242. package/src/runtime/vm.ts +354 -0
  243. package/src/self-host/bootstrap.fl +68 -0
  244. package/src/self-host/interpreter.fl +361 -0
  245. package/src/self-host/lexer-simple.fl +22 -0
  246. package/src/self-host/lexer.fl +305 -0
  247. package/src/self-host/parser.fl +580 -0
  248. package/src/struct-instance-jest.test.ts +221 -0
  249. package/src/struct-instance.test.ts +293 -0
  250. package/src/struct-jest.test.ts +187 -0
  251. package/src/struct.test.ts +234 -0
  252. package/src/trait-jest.test.ts +136 -0
  253. package/src/vm-jest.test.ts +754 -0
  254. package/src/vm.ts +1976 -0
  255. package/src/web-repl/public/index.html +50 -0
  256. package/src/web-repl/public/main.js +105 -0
  257. package/src/web-repl/public/style.css +225 -0
  258. package/src/web-repl/sandbox.ts +88 -0
  259. package/src/web-repl/server.ts +97 -0
  260. package/src/while-loop-jest.test.ts +218 -0
  261. package/src/while-loop.test.ts +267 -0
package/dist/parser.js ADDED
@@ -0,0 +1,1060 @@
1
+ "use strict";
2
+ // FreeLang v4 — Parser (SPEC_05 구현)
3
+ // RD(문) + Pratt(식) 하이브리드
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.Parser = void 0;
6
+ const lexer_1 = require("./lexer");
7
+ // ============================================================
8
+ // Binding Power (SPEC_05 Q4)
9
+ // ============================================================
10
+ const BP_ASSIGN = 10;
11
+ const BP_OR = 20;
12
+ const BP_AND = 30;
13
+ const BP_EQUALITY = 40;
14
+ const BP_COMPARISON = 50;
15
+ const BP_ADDITIVE = 60;
16
+ const BP_MULTIPLICATIVE = 70;
17
+ const BP_UNARY = 90;
18
+ const BP_POSTFIX = 100;
19
+ function infixBP(type) {
20
+ switch (type) {
21
+ // EQ는 Pratt에서 처리하지 않음 → ExprStmt에서 할당으로 처리
22
+ case lexer_1.TokenType.OR: return BP_OR;
23
+ case lexer_1.TokenType.AND: return BP_AND;
24
+ case lexer_1.TokenType.EQEQ:
25
+ case lexer_1.TokenType.NEQ: return BP_EQUALITY;
26
+ case lexer_1.TokenType.LT:
27
+ case lexer_1.TokenType.GT:
28
+ case lexer_1.TokenType.LTEQ:
29
+ case lexer_1.TokenType.GTEQ: return BP_COMPARISON;
30
+ case lexer_1.TokenType.PLUS:
31
+ case lexer_1.TokenType.MINUS: return BP_ADDITIVE;
32
+ case lexer_1.TokenType.STAR:
33
+ case lexer_1.TokenType.SLASH:
34
+ case lexer_1.TokenType.PERCENT: return BP_MULTIPLICATIVE;
35
+ case lexer_1.TokenType.LARROW: return BP_ASSIGN; // 채널 송신
36
+ case lexer_1.TokenType.LPAREN:
37
+ case lexer_1.TokenType.LBRACKET:
38
+ case lexer_1.TokenType.DOT:
39
+ case lexer_1.TokenType.QUESTION: return BP_POSTFIX;
40
+ default: return 0;
41
+ }
42
+ }
43
+ // ============================================================
44
+ // Parser
45
+ // ============================================================
46
+ class Parser {
47
+ constructor(tokens) {
48
+ this.pos = 0;
49
+ this.errors = [];
50
+ this.tokens = tokens;
51
+ }
52
+ parse() {
53
+ const stmts = [];
54
+ while (!this.isAtEnd()) {
55
+ try {
56
+ stmts.push(this.parseStmt());
57
+ }
58
+ catch (e) {
59
+ // 에러 복구: 다음 문 시작까지 건너뜀
60
+ this.synchronize();
61
+ }
62
+ }
63
+ return { program: { stmts }, errors: this.errors };
64
+ }
65
+ // ============================================================
66
+ // 문 파싱 — Recursive Descent (SPEC_05 Q1)
67
+ // ============================================================
68
+ parseStmt() {
69
+ const tok = this.peek();
70
+ switch (tok.type) {
71
+ case lexer_1.TokenType.IMPORT:
72
+ return this.parseImportStmt();
73
+ case lexer_1.TokenType.EXPORT:
74
+ return this.parseExportStmt();
75
+ case lexer_1.TokenType.VAR:
76
+ case lexer_1.TokenType.LET:
77
+ case lexer_1.TokenType.CONST:
78
+ return this.parseVarDecl();
79
+ case lexer_1.TokenType.ASYNC:
80
+ case lexer_1.TokenType.FN:
81
+ return this.parseFnDecl();
82
+ case lexer_1.TokenType.STRUCT:
83
+ return this.parseStructDecl();
84
+ case lexer_1.TokenType.TRAIT:
85
+ return this.parseTraitDecl();
86
+ case lexer_1.TokenType.IMPL:
87
+ return this.parseImplDecl();
88
+ case lexer_1.TokenType.IF:
89
+ return this.parseIfStmt();
90
+ case lexer_1.TokenType.MATCH:
91
+ return this.parseMatchStmt();
92
+ case lexer_1.TokenType.FOR:
93
+ return this.parseForStmt();
94
+ case lexer_1.TokenType.WHILE:
95
+ return this.parseWhileStmt();
96
+ case lexer_1.TokenType.BREAK:
97
+ return this.parseBreakStmt();
98
+ case lexer_1.TokenType.CONTINUE:
99
+ return this.parseContinueStmt();
100
+ case lexer_1.TokenType.SPAWN:
101
+ return this.parseSpawnStmt();
102
+ case lexer_1.TokenType.RETURN:
103
+ return this.parseReturnStmt();
104
+ default:
105
+ return this.parseExprStmt();
106
+ }
107
+ }
108
+ // var/let/const 선언
109
+ parseVarDecl() {
110
+ const kw = this.advance(); // var/let/const
111
+ const mutable = kw.type === lexer_1.TokenType.VAR;
112
+ const name = this.expectIdent("variable name");
113
+ let type = null;
114
+ if (this.check(lexer_1.TokenType.COLON)) {
115
+ this.advance(); // :
116
+ type = this.parseType();
117
+ }
118
+ this.expect(lexer_1.TokenType.EQ, "expected '=' in variable declaration");
119
+ const init = this.parseExpr(0);
120
+ this.match(lexer_1.TokenType.SEMICOLON); // optional semicolon
121
+ return { kind: "var_decl", name, mutable, type, init, line: kw.line, col: kw.col };
122
+ }
123
+ // fn 선언 (async fn 지원)
124
+ parseFnDecl() {
125
+ let isAsync = false;
126
+ let kw = this.peek();
127
+ // async 키워드 확인
128
+ if (this.match(lexer_1.TokenType.ASYNC)) {
129
+ isAsync = true;
130
+ }
131
+ kw = this.advance(); // fn
132
+ const name = this.expectIdent("function name");
133
+ // Generic 타입 파라미터: fn foo<T, K>(...) [STEP B-1]
134
+ const typeParams = [];
135
+ if (this.match(lexer_1.TokenType.LT)) {
136
+ do {
137
+ typeParams.push(this.expectIdent("type parameter"));
138
+ } while (this.match(lexer_1.TokenType.COMMA));
139
+ this.expect(lexer_1.TokenType.GT, "expected '>' after type parameters");
140
+ }
141
+ this.expect(lexer_1.TokenType.LPAREN, "expected '(' after function name");
142
+ const params = [];
143
+ if (!this.check(lexer_1.TokenType.RPAREN)) {
144
+ do {
145
+ const pName = this.expectIdent("parameter name");
146
+ this.expect(lexer_1.TokenType.COLON, "expected ':' after parameter name");
147
+ const pType = this.parseType();
148
+ params.push({ name: pName, type: pType });
149
+ } while (this.match(lexer_1.TokenType.COMMA));
150
+ }
151
+ this.expect(lexer_1.TokenType.RPAREN, "expected ')' after parameters");
152
+ // 반환 타입 (필수 — SPEC_06: 함수 시그니처 명시)
153
+ this.expect(lexer_1.TokenType.RARROW, "expected '->' for return type");
154
+ const returnType = this.parseType();
155
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' for function body");
156
+ const body = this.parseBlock();
157
+ return { kind: "fn_decl", name, isAsync, typeParams, params, returnType, body, line: kw.line, col: kw.col };
158
+ }
159
+ // struct 선언
160
+ parseStructDecl() {
161
+ const kw = this.advance(); // struct
162
+ const name = this.expectIdent("struct name");
163
+ // Generic 타입 파라미터: struct Box<T> [STEP B-2]
164
+ const typeParams = [];
165
+ if (this.match(lexer_1.TokenType.LT)) {
166
+ do {
167
+ typeParams.push(this.expectIdent("type parameter"));
168
+ } while (this.match(lexer_1.TokenType.COMMA));
169
+ this.expect(lexer_1.TokenType.GT, "expected '>' after type parameters");
170
+ }
171
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after struct name");
172
+ const fields = [];
173
+ if (!this.check(lexer_1.TokenType.RBRACE)) {
174
+ do {
175
+ if (this.check(lexer_1.TokenType.RBRACE))
176
+ break; // Allow trailing comma
177
+ const fieldName = this.expectIdent("field name");
178
+ this.expect(lexer_1.TokenType.COLON, "expected ':' after field name");
179
+ const fieldType = this.parseType();
180
+ fields.push({ name: fieldName, type: fieldType });
181
+ } while (this.match(lexer_1.TokenType.COMMA));
182
+ }
183
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}' to close struct");
184
+ return { kind: "struct_decl", name, typeParams, fields, line: kw.line, col: kw.col };
185
+ }
186
+ // trait 선언
187
+ parseTraitDecl() {
188
+ const kw = this.advance(); // trait
189
+ const name = this.expectIdent("trait name");
190
+ // Generic 타입 파라미터
191
+ const typeParams = [];
192
+ if (this.match(lexer_1.TokenType.LT)) {
193
+ do {
194
+ typeParams.push(this.expectIdent("type parameter"));
195
+ } while (this.match(lexer_1.TokenType.COMMA));
196
+ this.expect(lexer_1.TokenType.GT, "expected '>' after type parameters");
197
+ }
198
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after trait name");
199
+ const methods = [];
200
+ if (!this.check(lexer_1.TokenType.RBRACE)) {
201
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.isAtEnd()) {
202
+ const mLine = this.peek().line;
203
+ const mCol = this.peek().col;
204
+ this.expect(lexer_1.TokenType.FN, "expected 'fn' in trait method");
205
+ const methodName = this.expectIdent("method name");
206
+ this.expect(lexer_1.TokenType.LPAREN, "expected '(' after method name");
207
+ const params = [];
208
+ if (!this.check(lexer_1.TokenType.RPAREN)) {
209
+ do {
210
+ const pName = this.expectIdent("parameter name");
211
+ // self는 타입 명시 없음
212
+ if (pName === "self") {
213
+ if (this.check(lexer_1.TokenType.COMMA)) {
214
+ this.advance(); // consume comma
215
+ params.push({ name: pName, type: { kind: "self_type" } });
216
+ continue;
217
+ }
218
+ else if (this.check(lexer_1.TokenType.RPAREN)) {
219
+ params.push({ name: pName, type: { kind: "self_type" } });
220
+ break;
221
+ }
222
+ }
223
+ // 일반 파라미터
224
+ this.expect(lexer_1.TokenType.COLON, "expected ':' after parameter name");
225
+ const pType = this.parseType();
226
+ params.push({ name: pName, type: pType });
227
+ } while (this.match(lexer_1.TokenType.COMMA));
228
+ }
229
+ this.expect(lexer_1.TokenType.RPAREN, "expected ')' after method parameters");
230
+ this.expect(lexer_1.TokenType.RARROW, "expected '->' for return type");
231
+ const returnType = this.parseType();
232
+ this.match(lexer_1.TokenType.SEMICOLON);
233
+ methods.push({ name: methodName, params, returnType, line: mLine, col: mCol });
234
+ }
235
+ }
236
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}' to close trait");
237
+ return { kind: "trait_decl", name, typeParams, methods, line: kw.line, col: kw.col };
238
+ }
239
+ // impl 선언
240
+ parseImplDecl() {
241
+ const kw = this.advance(); // impl
242
+ // Generic 타입 파라미터
243
+ const typeParams = [];
244
+ if (this.match(lexer_1.TokenType.LT)) {
245
+ do {
246
+ typeParams.push(this.expectIdent("type parameter"));
247
+ } while (this.match(lexer_1.TokenType.COMMA));
248
+ this.expect(lexer_1.TokenType.GT, "expected '>' after type parameters");
249
+ }
250
+ // Trait name: impl [Drawable] for Circle
251
+ let trait = null;
252
+ let forType;
253
+ // Check if it's "impl Trait for Type" or just "impl Type"
254
+ const savedPos = this.pos;
255
+ if (this.peek().type === lexer_1.TokenType.IDENT) {
256
+ const firstIdent = this.peek().lexeme;
257
+ this.advance(); // consume identifier
258
+ if (this.match(lexer_1.TokenType.FOR)) {
259
+ // It was "impl Trait for Type"
260
+ trait = firstIdent;
261
+ forType = this.parseType();
262
+ }
263
+ else {
264
+ // It was "impl Type" (inherent impl) — reset and parse as type
265
+ this.pos = savedPos;
266
+ forType = this.parseType();
267
+ }
268
+ }
269
+ else {
270
+ forType = this.parseType();
271
+ }
272
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' for impl body");
273
+ const methods = [];
274
+ if (!this.check(lexer_1.TokenType.RBRACE)) {
275
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.isAtEnd()) {
276
+ const mLine = this.peek().line;
277
+ const mCol = this.peek().col;
278
+ this.expect(lexer_1.TokenType.FN, "expected 'fn' in impl method");
279
+ const methodName = this.expectIdent("method name");
280
+ this.expect(lexer_1.TokenType.LPAREN, "expected '(' after method name");
281
+ const params = [];
282
+ if (!this.check(lexer_1.TokenType.RPAREN)) {
283
+ do {
284
+ const pName = this.expectIdent("parameter name");
285
+ // self는 타입 명시 없음
286
+ if (pName === "self") {
287
+ if (this.check(lexer_1.TokenType.COMMA)) {
288
+ this.advance(); // consume comma
289
+ params.push({ name: pName, type: { kind: "self_type" } });
290
+ continue;
291
+ }
292
+ else if (this.check(lexer_1.TokenType.RPAREN)) {
293
+ params.push({ name: pName, type: { kind: "self_type" } });
294
+ break;
295
+ }
296
+ }
297
+ // 일반 파라미터
298
+ this.expect(lexer_1.TokenType.COLON, "expected ':' after parameter name");
299
+ const pType = this.parseType();
300
+ params.push({ name: pName, type: pType });
301
+ } while (this.match(lexer_1.TokenType.COMMA));
302
+ }
303
+ this.expect(lexer_1.TokenType.RPAREN, "expected ')' after method parameters");
304
+ this.expect(lexer_1.TokenType.RARROW, "expected '->' for return type");
305
+ const returnType = this.parseType();
306
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' for method body");
307
+ const body = this.parseBlock();
308
+ methods.push({ name: methodName, params, returnType, body, line: mLine, col: mCol });
309
+ }
310
+ }
311
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}' to close impl");
312
+ return { kind: "impl_decl", trait, forType, typeParams, methods, line: kw.line, col: kw.col };
313
+ }
314
+ // if 문 (문 위치)
315
+ parseIfStmt() {
316
+ const kw = this.advance(); // if
317
+ const condition = this.parseExpr(0);
318
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after if condition");
319
+ const then = this.parseBlock();
320
+ let else_ = null;
321
+ if (this.match(lexer_1.TokenType.ELSE)) {
322
+ if (this.check(lexer_1.TokenType.IF)) {
323
+ // else if 체인
324
+ else_ = [this.parseIfStmt()];
325
+ }
326
+ else {
327
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after else");
328
+ else_ = this.parseBlock();
329
+ }
330
+ }
331
+ return { kind: "if_stmt", condition, then, else_, line: kw.line, col: kw.col };
332
+ }
333
+ // match 문
334
+ parseMatchStmt() {
335
+ const kw = this.advance(); // match
336
+ const subject = this.parseExpr(0);
337
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after match expression");
338
+ const arms = this.parseMatchArms();
339
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}' to close match");
340
+ return { kind: "match_stmt", subject, arms, line: kw.line, col: kw.col };
341
+ }
342
+ // for 문
343
+ parseForStmt() {
344
+ const kw = this.advance(); // for
345
+ const variable = this.expectIdent("loop variable");
346
+ // for...in or for...of
347
+ const loopType = this.peek().type;
348
+ if (loopType === lexer_1.TokenType.IN) {
349
+ this.advance(); // in
350
+ const iterable = this.parseExpr(0);
351
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after for...in");
352
+ const body = this.parseBlock();
353
+ return { kind: "for_stmt", variable, iterable, body, line: kw.line, col: kw.col };
354
+ }
355
+ else if (loopType === lexer_1.TokenType.OF) {
356
+ this.advance(); // of
357
+ const iterable = this.parseExpr(0);
358
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after for...of");
359
+ const body = this.parseBlock();
360
+ return { kind: "for_of_stmt", variable, iterable, body, line: kw.line, col: kw.col };
361
+ }
362
+ else {
363
+ this.error("expected 'in' or 'of' after loop variable", this.peek());
364
+ return { kind: "for_stmt", variable, iterable: { kind: "ident", name: "", line: kw.line, col: kw.col }, body: [], line: kw.line, col: kw.col };
365
+ }
366
+ }
367
+ // while 문
368
+ parseWhileStmt() {
369
+ const kw = this.advance(); // while
370
+ const condition = this.parseExpr(0);
371
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after while condition");
372
+ const body = this.parseBlock();
373
+ return { kind: "while_stmt", condition, body, line: kw.line, col: kw.col };
374
+ }
375
+ // break 문
376
+ parseBreakStmt() {
377
+ const kw = this.advance(); // break
378
+ this.match(lexer_1.TokenType.SEMICOLON); // optional semicolon
379
+ return { kind: "break_stmt", line: kw.line, col: kw.col };
380
+ }
381
+ // continue 문
382
+ parseContinueStmt() {
383
+ const kw = this.advance(); // continue
384
+ this.match(lexer_1.TokenType.SEMICOLON); // optional semicolon
385
+ return { kind: "continue_stmt", line: kw.line, col: kw.col };
386
+ }
387
+ // spawn 문
388
+ parseSpawnStmt() {
389
+ const kw = this.advance(); // spawn
390
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after spawn");
391
+ const body = this.parseBlock();
392
+ return { kind: "spawn_stmt", body, line: kw.line, col: kw.col };
393
+ }
394
+ // import 문 파싱
395
+ parseImportStmt() {
396
+ const kw = this.advance(); // import
397
+ const items = [];
398
+ let default_ = false;
399
+ if (this.check(lexer_1.TokenType.LBRACE)) {
400
+ this.advance(); // {
401
+ if (!this.check(lexer_1.TokenType.RBRACE)) {
402
+ do {
403
+ const name = this.expectIdent("imported name");
404
+ let alias;
405
+ if (this.match(lexer_1.TokenType.AS)) {
406
+ alias = this.expectIdent("alias");
407
+ }
408
+ items.push({ name, alias });
409
+ } while (this.match(lexer_1.TokenType.COMMA));
410
+ }
411
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}' after import items");
412
+ }
413
+ else {
414
+ const name = this.expectIdent("module name");
415
+ items.push({ name });
416
+ default_ = true;
417
+ }
418
+ this.expect(lexer_1.TokenType.FROM, "expected 'from' in import statement");
419
+ const sourceToken = this.peek();
420
+ if (sourceToken.type !== lexer_1.TokenType.STRING_LIT) {
421
+ this.error("expected string literal for module source", sourceToken);
422
+ throw new Error("expected module path");
423
+ }
424
+ const source = this.advance().lexeme;
425
+ this.match(lexer_1.TokenType.SEMICOLON);
426
+ return { kind: "import_decl", source, items, default: default_, line: kw.line, col: kw.col };
427
+ }
428
+ // export 문 파싱
429
+ parseExportStmt() {
430
+ const kw = this.advance(); // export
431
+ if (this.check(lexer_1.TokenType.FN) || this.check(lexer_1.TokenType.STRUCT)) {
432
+ const target = this.parseStmt();
433
+ return { kind: "export_decl", target, line: kw.line, col: kw.col };
434
+ }
435
+ if (this.check(lexer_1.TokenType.LBRACE)) {
436
+ this.advance(); // {
437
+ const names = [];
438
+ if (!this.check(lexer_1.TokenType.RBRACE)) {
439
+ do {
440
+ names.push(this.expectIdent("export name"));
441
+ } while (this.match(lexer_1.TokenType.COMMA));
442
+ }
443
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}' after export names");
444
+ this.match(lexer_1.TokenType.SEMICOLON);
445
+ return { kind: "export_decl", target: names, line: kw.line, col: kw.col };
446
+ }
447
+ this.error("expected 'fn', 'struct', or '{' after 'export'", kw);
448
+ throw new Error("invalid export syntax");
449
+ }
450
+ // return 문
451
+ parseReturnStmt() {
452
+ const kw = this.advance(); // return
453
+ // return 뒤에 식이 있는지 확인
454
+ let value = null;
455
+ if (!this.check(lexer_1.TokenType.RBRACE) && !this.isAtEnd() && !this.isStmtStart()) {
456
+ value = this.parseExpr(0);
457
+ }
458
+ this.match(lexer_1.TokenType.SEMICOLON); // optional semicolon
459
+ return { kind: "return_stmt", value, line: kw.line, col: kw.col };
460
+ }
461
+ // 식 문 (ExprStmt)
462
+ parseExprStmt() {
463
+ const tok = this.peek();
464
+ const expr = this.parseExpr(0);
465
+ // 할당 처리: expr = value
466
+ if (this.check(lexer_1.TokenType.EQ)) {
467
+ const eq = this.advance();
468
+ const value = this.parseExpr(0);
469
+ this.match(lexer_1.TokenType.SEMICOLON); // optional semicolon
470
+ return {
471
+ kind: "expr_stmt",
472
+ expr: { kind: "assign", target: expr, value, line: eq.line, col: eq.col },
473
+ line: tok.line,
474
+ col: tok.col,
475
+ };
476
+ }
477
+ this.match(lexer_1.TokenType.SEMICOLON); // optional semicolon
478
+ return { kind: "expr_stmt", expr, line: tok.line, col: tok.col };
479
+ }
480
+ // ============================================================
481
+ // 블록 파싱 ({ 이미 소비됨, } 소비함 )
482
+ // ============================================================
483
+ parseBlock() {
484
+ const stmts = [];
485
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.isAtEnd()) {
486
+ try {
487
+ stmts.push(this.parseStmt());
488
+ }
489
+ catch {
490
+ this.synchronize();
491
+ }
492
+ }
493
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}'");
494
+ return stmts;
495
+ }
496
+ // ============================================================
497
+ // 식 파싱 — Pratt Parser (SPEC_05 Q3, Q4)
498
+ // ============================================================
499
+ parseExpr(minBP) {
500
+ let left = this.nud();
501
+ while (!this.isAtEnd()) {
502
+ const tok = this.peek();
503
+ // 구조체 리터럴: ident { field: value, ... }
504
+ // Lookahead: { 다음이 ident: 패턴인지 확인 (while/if 블록과 구분)
505
+ if (tok.type === lexer_1.TokenType.LBRACE && left.kind === "ident") {
506
+ // Lookahead: 다음 토큰이 ident이고, 그 다음이 :인지 확인
507
+ if (this.pos + 1 < this.tokens.length &&
508
+ this.tokens[this.pos + 1].type === lexer_1.TokenType.IDENT &&
509
+ this.pos + 2 < this.tokens.length &&
510
+ this.tokens[this.pos + 2].type === lexer_1.TokenType.COLON) {
511
+ this.advance(); // {
512
+ const fields = [];
513
+ if (!this.check(lexer_1.TokenType.RBRACE)) {
514
+ do {
515
+ const name = this.expectIdent("field name");
516
+ this.expect(lexer_1.TokenType.COLON, "expected ':' after field name");
517
+ const value = this.parseExpr(0);
518
+ fields.push({ name, value });
519
+ } while (this.match(lexer_1.TokenType.COMMA));
520
+ }
521
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}'");
522
+ left = { kind: "struct_lit", structName: left.name, fields, line: left.line, col: left.col };
523
+ continue;
524
+ }
525
+ }
526
+ const bp = infixBP(tok.type);
527
+ if (bp <= minBP)
528
+ break;
529
+ left = this.led(left, bp);
530
+ }
531
+ return left;
532
+ }
533
+ // nud — prefix 위치
534
+ nud() {
535
+ const tok = this.peek();
536
+ // 정수 리터럴
537
+ if (tok.type === lexer_1.TokenType.INT_LIT) {
538
+ this.advance();
539
+ const raw = tok.lexeme.replace(/_/g, "");
540
+ return { kind: "int_lit", value: parseInt(raw, 10), line: tok.line, col: tok.col };
541
+ }
542
+ // 부동소수점 리터럴
543
+ if (tok.type === lexer_1.TokenType.FLOAT_LIT) {
544
+ this.advance();
545
+ const raw = tok.lexeme.replace(/_/g, "");
546
+ return { kind: "float_lit", value: parseFloat(raw), line: tok.line, col: tok.col };
547
+ }
548
+ // 문자열 리터럴
549
+ if (tok.type === lexer_1.TokenType.STRING_LIT) {
550
+ this.advance();
551
+ return { kind: "string_lit", value: tok.lexeme, line: tok.line, col: tok.col };
552
+ }
553
+ // 불리언 리터럴
554
+ if (tok.type === lexer_1.TokenType.TRUE) {
555
+ this.advance();
556
+ return { kind: "bool_lit", value: true, line: tok.line, col: tok.col };
557
+ }
558
+ if (tok.type === lexer_1.TokenType.FALSE) {
559
+ this.advance();
560
+ return { kind: "bool_lit", value: false, line: tok.line, col: tok.col };
561
+ }
562
+ // 식별자 또는 channel<T>
563
+ if (tok.type === lexer_1.TokenType.IDENT || tok.type === lexer_1.TokenType.TYPE_CHANNEL) {
564
+ const identTok = this.advance();
565
+ const name = identTok.lexeme;
566
+ // channel<T> 특별 처리
567
+ if (name === "channel" && this.check(lexer_1.TokenType.LT)) {
568
+ this.advance(); // <
569
+ const element = this.parseType();
570
+ this.expect(lexer_1.TokenType.GT, "expected '>' after channel type");
571
+ this.expect(lexer_1.TokenType.LPAREN, "expected '()' after channel<T>");
572
+ this.expect(lexer_1.TokenType.RPAREN, "expected ')'");
573
+ return { kind: "chan_new", element, line: identTok.line, col: identTok.col };
574
+ }
575
+ return { kind: "ident", name, line: identTok.line, col: identTok.col };
576
+ }
577
+ // 단항 연산자: - !
578
+ if (tok.type === lexer_1.TokenType.MINUS || tok.type === lexer_1.TokenType.NOT) {
579
+ this.advance();
580
+ const operand = this.parseExpr(BP_UNARY);
581
+ return { kind: "unary", op: tok.lexeme, operand, line: tok.line, col: tok.col };
582
+ }
583
+ // await 연산자
584
+ if (tok.type === lexer_1.TokenType.AWAIT) {
585
+ this.advance();
586
+ const expr = this.parseExpr(BP_UNARY);
587
+ return { kind: "await", expr, line: tok.line, col: tok.col };
588
+ }
589
+ // 괄호 그룹: ( expr )
590
+ if (tok.type === lexer_1.TokenType.LPAREN) {
591
+ this.advance();
592
+ const expr = this.parseExpr(0);
593
+ this.expect(lexer_1.TokenType.RPAREN, "expected ')'");
594
+ return expr;
595
+ }
596
+ // 배열 리터럴: [ elem, ... ]
597
+ if (tok.type === lexer_1.TokenType.LBRACKET) {
598
+ return this.parseArrayLit();
599
+ }
600
+ // if 식 (식 위치)
601
+ if (tok.type === lexer_1.TokenType.IF) {
602
+ return this.parseIfExpr();
603
+ }
604
+ // match 식 (식 위치)
605
+ if (tok.type === lexer_1.TokenType.MATCH) {
606
+ return this.parseMatchExpr();
607
+ }
608
+ // 함수 리터럴 (람다): fn(x: i32) -> i32 { x + 1 }
609
+ if (tok.type === lexer_1.TokenType.FN) {
610
+ return this.parseFnLit();
611
+ }
612
+ // 채널 수신: <- chan
613
+ if (tok.type === lexer_1.TokenType.LARROW) {
614
+ this.advance();
615
+ const chan = this.parseExpr(BP_UNARY);
616
+ return { kind: "chan_recv", chan, line: tok.line, col: tok.col };
617
+ }
618
+ this.error(`unexpected token: ${tok.lexeme}`, tok);
619
+ this.advance();
620
+ return { kind: "ident", name: "__error__", line: tok.line, col: tok.col };
621
+ }
622
+ // led — infix/postfix 위치
623
+ led(left, bp) {
624
+ const tok = this.peek();
625
+ // 함수 호출: expr(args) 형태만 지원 (generic call은 제한)
626
+ // 제네릭 호출은 <T> 타입 인자를 파싱하기 위해 복잡한 lookahead가 필요하므로,
627
+ // 여기서는 skip하고 나중에 추가 개선
628
+ // 이항 연산자
629
+ if (tok.type === lexer_1.TokenType.PLUS || tok.type === lexer_1.TokenType.MINUS ||
630
+ tok.type === lexer_1.TokenType.STAR || tok.type === lexer_1.TokenType.SLASH ||
631
+ tok.type === lexer_1.TokenType.PERCENT ||
632
+ tok.type === lexer_1.TokenType.EQEQ || tok.type === lexer_1.TokenType.NEQ ||
633
+ tok.type === lexer_1.TokenType.LT || tok.type === lexer_1.TokenType.GT ||
634
+ tok.type === lexer_1.TokenType.LTEQ || tok.type === lexer_1.TokenType.GTEQ ||
635
+ tok.type === lexer_1.TokenType.AND || tok.type === lexer_1.TokenType.OR) {
636
+ this.advance();
637
+ const right = this.parseExpr(bp); // left-associative
638
+ return { kind: "binary", op: tok.lexeme, left, right, line: tok.line, col: tok.col };
639
+ }
640
+ if (tok.type === lexer_1.TokenType.LPAREN) {
641
+ this.advance();
642
+ const args = [];
643
+ if (!this.check(lexer_1.TokenType.RPAREN)) {
644
+ do {
645
+ args.push(this.parseExpr(0));
646
+ } while (this.match(lexer_1.TokenType.COMMA));
647
+ }
648
+ this.expect(lexer_1.TokenType.RPAREN, "expected ')' after arguments");
649
+ return { kind: "call", callee: left, args, line: tok.line, col: tok.col };
650
+ }
651
+ // 인덱스: expr[index]
652
+ if (tok.type === lexer_1.TokenType.LBRACKET) {
653
+ this.advance();
654
+ const index = this.parseExpr(0);
655
+ this.expect(lexer_1.TokenType.RBRACKET, "expected ']'");
656
+ return { kind: "index", object: left, index, line: tok.line, col: tok.col };
657
+ }
658
+ // 필드 접근: expr.field
659
+ if (tok.type === lexer_1.TokenType.DOT) {
660
+ this.advance();
661
+ const field = this.expectIdent("field name");
662
+ return { kind: "field_access", object: left, field, line: tok.line, col: tok.col };
663
+ }
664
+ // try 연산자: expr?
665
+ if (tok.type === lexer_1.TokenType.QUESTION) {
666
+ this.advance();
667
+ return { kind: "try", operand: left, line: tok.line, col: tok.col };
668
+ }
669
+ // 채널 송신: chan <- value
670
+ if (tok.type === lexer_1.TokenType.LARROW) {
671
+ this.advance();
672
+ const value = this.parseExpr(BP_ASSIGN);
673
+ return { kind: "chan_send", chan: left, value, line: tok.line, col: tok.col };
674
+ }
675
+ // 할당은 ExprStmt에서 처리하므로 여기선 패스
676
+ // EQ가 여기 오면 식 끝으로 처리
677
+ this.error(`unexpected operator: ${tok.lexeme}`, tok);
678
+ this.advance();
679
+ return left;
680
+ }
681
+ // ============================================================
682
+ // 복합 식 파싱
683
+ // ============================================================
684
+ // 배열 리터럴: [a, b, c]
685
+ parseArrayLit() {
686
+ const tok = this.advance(); // [
687
+ const elements = [];
688
+ if (!this.check(lexer_1.TokenType.RBRACKET)) {
689
+ do {
690
+ elements.push(this.parseExpr(0));
691
+ } while (this.match(lexer_1.TokenType.COMMA));
692
+ }
693
+ this.expect(lexer_1.TokenType.RBRACKET, "expected ']'");
694
+ return { kind: "array_lit", elements, line: tok.line, col: tok.col };
695
+ }
696
+ // 구조체 리터럴: { name: expr, ... }
697
+ // if 식 (식 위치, else 필수 — SPEC_06)
698
+ parseIfExpr() {
699
+ const tok = this.advance(); // if
700
+ const condition = this.parseExpr(0);
701
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after if condition");
702
+ const then = this.parseBlockExprs();
703
+ this.expect(lexer_1.TokenType.ELSE, "if expression requires else branch");
704
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after else");
705
+ const else_ = this.parseBlockExprs();
706
+ return { kind: "if_expr", condition, then, else_, line: tok.line, col: tok.col };
707
+ }
708
+ // match 식 (식 위치)
709
+ parseMatchExpr() {
710
+ const tok = this.advance(); // match
711
+ const subject = this.parseExpr(0);
712
+ this.expect(lexer_1.TokenType.LBRACE, "expected '{' after match expression");
713
+ const arms = this.parseMatchArms();
714
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}' to close match");
715
+ return { kind: "match_expr", subject, arms, line: tok.line, col: tok.col };
716
+ }
717
+ // 블록 내 식 목록 (if/match 식의 body) → } 소비
718
+ parseBlockExprs() {
719
+ const exprs = [];
720
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.isAtEnd()) {
721
+ exprs.push(this.parseExpr(0));
722
+ }
723
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}'");
724
+ return exprs;
725
+ }
726
+ // ============================================================
727
+ // match arms
728
+ // ============================================================
729
+ parseMatchArms() {
730
+ const arms = [];
731
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.isAtEnd()) {
732
+ const pattern = this.parsePattern();
733
+ // Guard 절 파싱: if 조건
734
+ let guard = undefined;
735
+ if (this.match(lexer_1.TokenType.IF)) {
736
+ guard = this.parseExpr(0);
737
+ }
738
+ this.expect(lexer_1.TokenType.ARROW, "expected '=>' after pattern");
739
+ let body;
740
+ if (this.check(lexer_1.TokenType.LBRACE)) {
741
+ // 블록 body
742
+ const bTok = this.advance(); // {
743
+ const stmts = [];
744
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.isAtEnd()) {
745
+ stmts.push(this.parseStmt());
746
+ }
747
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}'");
748
+ // 블록의 마지막 문이 ExprStmt면 그 식이 반환값
749
+ const lastExpr = stmts.length > 0 && stmts[stmts.length - 1].kind === "expr_stmt"
750
+ ? stmts[stmts.length - 1].expr
751
+ : null;
752
+ body = { kind: "block_expr", stmts: stmts.slice(0, lastExpr ? -1 : stmts.length), expr: lastExpr, line: bTok.line, col: bTok.col };
753
+ }
754
+ else {
755
+ body = this.parseExpr(0);
756
+ }
757
+ this.match(lexer_1.TokenType.COMMA); // trailing comma optional
758
+ arms.push({ pattern, guard, body });
759
+ }
760
+ return arms;
761
+ }
762
+ // 함수 리터럴: fn(x: i32, y: i32) -> i32 { x + y }
763
+ parseFnLit() {
764
+ const kw = this.advance(); // fn
765
+ this.expect(lexer_1.TokenType.LPAREN, "expected '(' after fn");
766
+ const params = [];
767
+ if (!this.check(lexer_1.TokenType.RPAREN)) {
768
+ do {
769
+ const name = this.expectIdent("parameter name");
770
+ let type = undefined;
771
+ if (this.match(lexer_1.TokenType.COLON)) {
772
+ type = this.parseType();
773
+ }
774
+ params.push({ name, type });
775
+ } while (this.match(lexer_1.TokenType.COMMA));
776
+ }
777
+ this.expect(lexer_1.TokenType.RPAREN, "expected ')' after parameters");
778
+ let returnType = undefined;
779
+ if (this.match(lexer_1.TokenType.RARROW)) {
780
+ returnType = this.parseType();
781
+ }
782
+ // body
783
+ let body;
784
+ if (this.check(lexer_1.TokenType.LBRACE)) {
785
+ const bTok = this.advance(); // {
786
+ const stmts = [];
787
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.isAtEnd()) {
788
+ stmts.push(this.parseStmt());
789
+ }
790
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}'");
791
+ const lastExpr = stmts.length > 0 && stmts[stmts.length - 1].kind === "expr_stmt"
792
+ ? stmts[stmts.length - 1].expr
793
+ : null;
794
+ body = { kind: "block_expr", stmts: stmts.slice(0, lastExpr ? -1 : stmts.length), expr: lastExpr, line: bTok.line, col: bTok.col };
795
+ }
796
+ else {
797
+ body = this.parseExpr(0);
798
+ }
799
+ return { kind: "fn_lit", params, returnType, body, line: kw.line, col: kw.col };
800
+ }
801
+ // ============================================================
802
+ // 패턴 파싱 (SPEC_05 Q8)
803
+ // ============================================================
804
+ parsePattern() {
805
+ const tok = this.peek();
806
+ // _ (wildcard)
807
+ if (tok.type === lexer_1.TokenType.IDENT && tok.lexeme === "_") {
808
+ this.advance();
809
+ return { kind: "wildcard" };
810
+ }
811
+ // Ok(p), Err(p), Some(p), None
812
+ if (tok.type === lexer_1.TokenType.IDENT) {
813
+ if (tok.lexeme === "Ok" || tok.lexeme === "Err" || tok.lexeme === "Some") {
814
+ this.advance();
815
+ this.expect(lexer_1.TokenType.LPAREN, `expected '(' after ${tok.lexeme}`);
816
+ const inner = this.parsePattern();
817
+ this.expect(lexer_1.TokenType.RPAREN, "expected ')'");
818
+ if (tok.lexeme === "Ok")
819
+ return { kind: "ok", inner };
820
+ if (tok.lexeme === "Err")
821
+ return { kind: "err", inner };
822
+ return { kind: "some", inner };
823
+ }
824
+ if (tok.lexeme === "None") {
825
+ this.advance();
826
+ return { kind: "none" };
827
+ }
828
+ // 구조 분해: Point { x, y }, Point { x as px, y as py }, Point { name, .. }
829
+ const ident = tok.lexeme;
830
+ this.advance();
831
+ if (this.check(lexer_1.TokenType.LBRACE)) {
832
+ this.advance(); // {
833
+ const fields = [];
834
+ let rest = false;
835
+ // 구조체 필드 파싱 (빈 중괄호도 허용)
836
+ if (!this.check(lexer_1.TokenType.RBRACE)) {
837
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.isAtEnd()) {
838
+ if (this.match(lexer_1.TokenType.DOTDOT)) {
839
+ rest = true;
840
+ break;
841
+ }
842
+ const fieldName = this.expectIdent("field name");
843
+ let fieldPattern;
844
+ let alias = undefined;
845
+ if (this.match(lexer_1.TokenType.AS)) {
846
+ alias = this.expectIdent("alias name");
847
+ fieldPattern = { kind: "ident", name: alias };
848
+ }
849
+ else {
850
+ fieldPattern = { kind: "ident", name: fieldName };
851
+ }
852
+ fields.push({ name: fieldName, pattern: fieldPattern, alias });
853
+ if (!this.match(lexer_1.TokenType.COMMA))
854
+ break;
855
+ }
856
+ }
857
+ this.expect(lexer_1.TokenType.RBRACE, "expected '}'");
858
+ return { kind: "struct", name: ident, fields, rest };
859
+ }
860
+ // 일반 식별자 바인딩
861
+ return { kind: "ident", name: ident };
862
+ }
863
+ // 배열 분해: [a, b, c], [x, .., y]
864
+ if (tok.type === lexer_1.TokenType.LBRACKET) {
865
+ this.advance(); // [
866
+ const elements = [];
867
+ let rest = false;
868
+ let restIndex = undefined;
869
+ while (!this.check(lexer_1.TokenType.RBRACKET) && !this.isAtEnd()) {
870
+ if (this.check(lexer_1.TokenType.DOTDOT)) {
871
+ this.advance(); // ..
872
+ rest = true;
873
+ restIndex = elements.length;
874
+ if (this.match(lexer_1.TokenType.COMMA)) {
875
+ // [x, .., y] 형태: 나머지 후 계속
876
+ while (!this.check(lexer_1.TokenType.RBRACKET) && !this.isAtEnd()) {
877
+ elements.push(this.parsePattern());
878
+ if (!this.match(lexer_1.TokenType.COMMA))
879
+ break;
880
+ }
881
+ }
882
+ break;
883
+ }
884
+ elements.push(this.parsePattern());
885
+ if (!this.match(lexer_1.TokenType.COMMA))
886
+ break;
887
+ }
888
+ this.expect(lexer_1.TokenType.RBRACKET, "expected ']'");
889
+ return { kind: "array", elements, rest, restIndex };
890
+ }
891
+ // 리터럴 패턴
892
+ if (tok.type === lexer_1.TokenType.INT_LIT || tok.type === lexer_1.TokenType.FLOAT_LIT ||
893
+ tok.type === lexer_1.TokenType.STRING_LIT || tok.type === lexer_1.TokenType.TRUE ||
894
+ tok.type === lexer_1.TokenType.FALSE) {
895
+ const expr = this.nud();
896
+ return { kind: "literal", value: expr };
897
+ }
898
+ // 단항 마이너스 (음수 리터럴 패턴)
899
+ if (tok.type === lexer_1.TokenType.MINUS) {
900
+ const expr = this.nud(); // unary minus
901
+ return { kind: "literal", value: expr };
902
+ }
903
+ this.error("expected pattern", tok);
904
+ this.advance();
905
+ return { kind: "wildcard" };
906
+ }
907
+ // ============================================================
908
+ // 타입 파싱
909
+ // ============================================================
910
+ parseType() {
911
+ const tok = this.peek();
912
+ // fn(T1, T2) -> R 함수 타입
913
+ if (tok.type === lexer_1.TokenType.FN) {
914
+ this.advance(); // fn
915
+ this.expect(lexer_1.TokenType.LPAREN, "expected '(' after fn");
916
+ const params = [];
917
+ if (!this.check(lexer_1.TokenType.RPAREN)) {
918
+ do {
919
+ params.push(this.parseType());
920
+ } while (this.match(lexer_1.TokenType.COMMA));
921
+ }
922
+ this.expect(lexer_1.TokenType.RPAREN, "expected ')' after fn params");
923
+ this.expect(lexer_1.TokenType.RARROW, "expected '->' in fn type");
924
+ const returnType = this.parseType();
925
+ return { kind: "fn", params, returnType };
926
+ }
927
+ switch (tok.type) {
928
+ case lexer_1.TokenType.TYPE_I32:
929
+ this.advance();
930
+ return { kind: "i32" };
931
+ case lexer_1.TokenType.TYPE_I64:
932
+ this.advance();
933
+ return { kind: "i64" };
934
+ case lexer_1.TokenType.TYPE_F64:
935
+ this.advance();
936
+ return { kind: "f64" };
937
+ case lexer_1.TokenType.TYPE_BOOL:
938
+ this.advance();
939
+ return { kind: "bool" };
940
+ case lexer_1.TokenType.TYPE_STRING:
941
+ this.advance();
942
+ return { kind: "string" };
943
+ case lexer_1.TokenType.TYPE_VOID:
944
+ this.advance();
945
+ return { kind: "void" };
946
+ default:
947
+ break;
948
+ }
949
+ // [T] → 배열
950
+ if (tok.type === lexer_1.TokenType.LBRACKET) {
951
+ this.advance();
952
+ const element = this.parseType();
953
+ this.expect(lexer_1.TokenType.RBRACKET, "expected ']' for array type");
954
+ return { kind: "array", element };
955
+ }
956
+ // channel<T>
957
+ if (tok.type === lexer_1.TokenType.TYPE_CHANNEL) {
958
+ this.advance();
959
+ this.expect(lexer_1.TokenType.LT, "expected '<' after channel");
960
+ const element = this.parseType();
961
+ this.expect(lexer_1.TokenType.GT, "expected '>' for channel type");
962
+ return { kind: "channel", element };
963
+ }
964
+ // IDENT: 타입 파라미터(T, K, V) 또는 Generic/Struct 타입 [STEP B-3]
965
+ if (tok.type === lexer_1.TokenType.IDENT) {
966
+ const name = tok.lexeme;
967
+ this.advance();
968
+ // 단일 대문자 → type_param (T, K, V)
969
+ if (name.length === 1 && name >= 'A' && name <= 'Z') {
970
+ return { kind: "type_param", name };
971
+ }
972
+ // Option<T>, Result<T,E>, Generic 타입, 커스텀 struct
973
+ if (this.check(lexer_1.TokenType.LT)) {
974
+ this.advance(); // LT 소비
975
+ const typeArgs = [];
976
+ do {
977
+ typeArgs.push(this.parseType());
978
+ } while (this.match(lexer_1.TokenType.COMMA));
979
+ this.expect(lexer_1.TokenType.GT, "expected '>' after type arguments");
980
+ // 내장 제네릭 타입 매핑
981
+ if (name === "Option")
982
+ return { kind: "option", element: typeArgs[0] };
983
+ if (name === "Result")
984
+ return { kind: "result", ok: typeArgs[0], err: typeArgs[1] };
985
+ if (name === "Promise")
986
+ return { kind: "promise", element: typeArgs[0] };
987
+ return { kind: "generic_ref", name, typeArgs };
988
+ }
989
+ return { kind: "struct_ref", name };
990
+ }
991
+ this.error(`expected type, got ${tok.lexeme}`, tok);
992
+ this.advance();
993
+ return { kind: "i32" }; // fallback
994
+ }
995
+ // ============================================================
996
+ // 유틸리티
997
+ // ============================================================
998
+ peek() {
999
+ return this.tokens[this.pos];
1000
+ }
1001
+ advance() {
1002
+ const tok = this.tokens[this.pos];
1003
+ if (!this.isAtEnd())
1004
+ this.pos++;
1005
+ return tok;
1006
+ }
1007
+ check(type) {
1008
+ return this.peek().type === type;
1009
+ }
1010
+ match(type) {
1011
+ if (this.check(type)) {
1012
+ this.advance();
1013
+ return true;
1014
+ }
1015
+ return false;
1016
+ }
1017
+ expect(type, message) {
1018
+ if (this.check(type)) {
1019
+ return this.advance();
1020
+ }
1021
+ const tok = this.peek();
1022
+ this.error(`${message} (got ${tok.type}: "${tok.lexeme}")`, tok);
1023
+ throw new Error(message);
1024
+ }
1025
+ expectIdent(context) {
1026
+ const tok = this.peek();
1027
+ if (tok.type === lexer_1.TokenType.IDENT) {
1028
+ this.advance();
1029
+ return tok.lexeme;
1030
+ }
1031
+ this.error(`expected ${context} (got ${tok.type}: "${tok.lexeme}")`, tok);
1032
+ throw new Error(`expected ${context}`);
1033
+ }
1034
+ isAtEnd() {
1035
+ return this.peek().type === lexer_1.TokenType.EOF;
1036
+ }
1037
+ isStmtStart() {
1038
+ const t = this.peek().type;
1039
+ return t === lexer_1.TokenType.VAR || t === lexer_1.TokenType.LET || t === lexer_1.TokenType.CONST ||
1040
+ t === lexer_1.TokenType.FN || t === lexer_1.TokenType.STRUCT || t === lexer_1.TokenType.IF || t === lexer_1.TokenType.MATCH ||
1041
+ t === lexer_1.TokenType.FOR || t === lexer_1.TokenType.WHILE || t === lexer_1.TokenType.BREAK || t === lexer_1.TokenType.CONTINUE ||
1042
+ t === lexer_1.TokenType.SPAWN || t === lexer_1.TokenType.RETURN || t === lexer_1.TokenType.IMPORT || t === lexer_1.TokenType.EXPORT;
1043
+ }
1044
+ error(message, tok) {
1045
+ this.errors.push({ message, line: tok.line, col: tok.col });
1046
+ }
1047
+ synchronize() {
1048
+ while (!this.isAtEnd()) {
1049
+ if (this.isStmtStart())
1050
+ return;
1051
+ if (this.check(lexer_1.TokenType.RBRACE)) {
1052
+ this.advance();
1053
+ return;
1054
+ }
1055
+ this.advance();
1056
+ }
1057
+ }
1058
+ }
1059
+ exports.Parser = Parser;
1060
+ //# sourceMappingURL=parser.js.map