cobolx-2 1.2.3

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 (284) hide show
  1. package/.github/pull_request_template.md +9 -0
  2. package/.github/workflows/ci.yml +18 -0
  3. package/.vscode/launch.json +19 -0
  4. package/.vscode/tasks.json +14 -0
  5. package/CHANGELOG.md +16 -0
  6. package/CONTRIBUTING.md +23 -0
  7. package/CargoX.lock +2 -0
  8. package/LICENSE +21 -0
  9. package/README.md +164 -0
  10. package/api-demo/CargoX.lock +1 -0
  11. package/api-demo/README.md +3 -0
  12. package/api-demo/benchmarks/arith.cbx +6 -0
  13. package/api-demo/cobolx.toml +6 -0
  14. package/api-demo/docs-output/index.html +1 -0
  15. package/api-demo/generated/LEGACYDEMO.cbx +5 -0
  16. package/api-demo/generated/client-types.ts +2 -0
  17. package/api-demo/generated/deploy.json +12 -0
  18. package/api-demo/generated/flowchart.mmd +4 -0
  19. package/api-demo/migrations/20260411215238_init.sql +1 -0
  20. package/api-demo/src/main.cbx +5 -0
  21. package/api-demo/tests/smoke.cbx +6 -0
  22. package/benchmarks/arithmetic.cbx +6 -0
  23. package/cargox/package.json +11 -0
  24. package/cargox/src/index.d.ts +4 -0
  25. package/cargox/src/index.js +5 -0
  26. package/cargox/src/index.js.map +1 -0
  27. package/cargox/src/index.ts +4 -0
  28. package/cargox/src/lockfile/index.d.ts +1 -0
  29. package/cargox/src/lockfile/index.js +9 -0
  30. package/cargox/src/lockfile/index.js.map +1 -0
  31. package/cargox/src/lockfile/index.ts +9 -0
  32. package/cargox/src/manifest.d.ts +10 -0
  33. package/cargox/src/manifest.js +49 -0
  34. package/cargox/src/manifest.js.map +1 -0
  35. package/cargox/src/manifest.ts +58 -0
  36. package/cargox/src/registry/index.d.ts +4 -0
  37. package/cargox/src/registry/index.js +13 -0
  38. package/cargox/src/registry/index.js.map +1 -0
  39. package/cargox/src/registry/index.ts +13 -0
  40. package/cargox/src/resolver/index.d.ts +5 -0
  41. package/cargox/src/resolver/index.js +4 -0
  42. package/cargox/src/resolver/index.js.map +1 -0
  43. package/cargox/src/resolver/index.ts +8 -0
  44. package/cargox/tsconfig.json +7 -0
  45. package/cli/cobolx-cli/package.json +23 -0
  46. package/cli/cobolx-cli/src/commands/add.ts +18 -0
  47. package/cli/cobolx-cli/src/commands/bench.ts +47 -0
  48. package/cli/cobolx-cli/src/commands/build.ts +74 -0
  49. package/cli/cobolx-cli/src/commands/check.ts +24 -0
  50. package/cli/cobolx-cli/src/commands/debug.ts +15 -0
  51. package/cli/cobolx-cli/src/commands/debug_rewind.ts +16 -0
  52. package/cli/cobolx-cli/src/commands/deploy.ts +18 -0
  53. package/cli/cobolx-cli/src/commands/dev.ts +24 -0
  54. package/cli/cobolx-cli/src/commands/doc.ts +19 -0
  55. package/cli/cobolx-cli/src/commands/fmt.ts +14 -0
  56. package/cli/cobolx-cli/src/commands/fuzz.ts +24 -0
  57. package/cli/cobolx-cli/src/commands/generate.ts +38 -0
  58. package/cli/cobolx-cli/src/commands/install.ts +25 -0
  59. package/cli/cobolx-cli/src/commands/legacy.ts +17 -0
  60. package/cli/cobolx-cli/src/commands/lint.ts +17 -0
  61. package/cli/cobolx-cli/src/commands/migrate.ts +27 -0
  62. package/cli/cobolx-cli/src/commands/new.ts +20 -0
  63. package/cli/cobolx-cli/src/commands/profile.ts +8 -0
  64. package/cli/cobolx-cli/src/commands/publish.ts +16 -0
  65. package/cli/cobolx-cli/src/commands/repl.ts +30 -0
  66. package/cli/cobolx-cli/src/commands/run.ts +22 -0
  67. package/cli/cobolx-cli/src/commands/task.ts +27 -0
  68. package/cli/cobolx-cli/src/commands/test.ts +44 -0
  69. package/cli/cobolx-cli/src/commands/update.ts +30 -0
  70. package/cli/cobolx-cli/src/commands/visualize.ts +25 -0
  71. package/cli/cobolx-cli/src/index.ts +101 -0
  72. package/cli/cobolx-cli/src/project.ts +74 -0
  73. package/cli/cobolx-cli/tsconfig.json +7 -0
  74. package/cobolx.toml +7 -0
  75. package/compiler/package.json +14 -0
  76. package/compiler/src/ast/types.d.ts +87 -0
  77. package/compiler/src/ast/types.js +2 -0
  78. package/compiler/src/ast/types.js.map +1 -0
  79. package/compiler/src/ast/types.ts +329 -0
  80. package/compiler/src/backend/custom.d.ts +8 -0
  81. package/compiler/src/backend/custom.js +12 -0
  82. package/compiler/src/backend/custom.js.map +1 -0
  83. package/compiler/src/backend/custom.ts +21 -0
  84. package/compiler/src/borrow_checker/checker.d.ts +3 -0
  85. package/compiler/src/borrow_checker/checker.js +82 -0
  86. package/compiler/src/borrow_checker/checker.js.map +1 -0
  87. package/compiler/src/borrow_checker/checker.ts +100 -0
  88. package/compiler/src/codegen/javascript.d.ts +2 -0
  89. package/compiler/src/codegen/javascript.js +89 -0
  90. package/compiler/src/codegen/javascript.js.map +1 -0
  91. package/compiler/src/codegen/javascript.ts +175 -0
  92. package/compiler/src/const_eval/evaluator.ts +58 -0
  93. package/compiler/src/diagnostics.d.ts +11 -0
  94. package/compiler/src/diagnostics.js +14 -0
  95. package/compiler/src/diagnostics.js.map +1 -0
  96. package/compiler/src/diagnostics.ts +20 -0
  97. package/compiler/src/hir/lower.d.ts +7 -0
  98. package/compiler/src/hir/lower.js +44 -0
  99. package/compiler/src/hir/lower.js.map +1 -0
  100. package/compiler/src/hir/lower.ts +60 -0
  101. package/compiler/src/hir/types.d.ts +21 -0
  102. package/compiler/src/hir/types.js +2 -0
  103. package/compiler/src/hir/types.js.map +1 -0
  104. package/compiler/src/hir/types.ts +26 -0
  105. package/compiler/src/index.d.ts +22 -0
  106. package/compiler/src/index.js +84 -0
  107. package/compiler/src/index.js.map +1 -0
  108. package/compiler/src/index.ts +122 -0
  109. package/compiler/src/lexer/lexer.d.ts +21 -0
  110. package/compiler/src/lexer/lexer.js +207 -0
  111. package/compiler/src/lexer/lexer.js.map +1 -0
  112. package/compiler/src/lexer/lexer.ts +274 -0
  113. package/compiler/src/lexer/tokens.d.ts +8 -0
  114. package/compiler/src/lexer/tokens.js +18 -0
  115. package/compiler/src/lexer/tokens.js.map +1 -0
  116. package/compiler/src/lexer/tokens.ts +126 -0
  117. package/compiler/src/macros/expand.ts +75 -0
  118. package/compiler/src/main.ts +4 -0
  119. package/compiler/src/mir/lower.d.ts +3 -0
  120. package/compiler/src/mir/lower.js +10 -0
  121. package/compiler/src/mir/lower.js.map +1 -0
  122. package/compiler/src/mir/lower.ts +12 -0
  123. package/compiler/src/mir/types.d.ts +13 -0
  124. package/compiler/src/mir/types.js +2 -0
  125. package/compiler/src/mir/types.js.map +1 -0
  126. package/compiler/src/mir/types.ts +16 -0
  127. package/compiler/src/optimizer/constantFold.d.ts +2 -0
  128. package/compiler/src/optimizer/constantFold.js +61 -0
  129. package/compiler/src/optimizer/constantFold.js.map +1 -0
  130. package/compiler/src/optimizer/constantFold.ts +109 -0
  131. package/compiler/src/parser/parser.d.ts +33 -0
  132. package/compiler/src/parser/parser.js +323 -0
  133. package/compiler/src/parser/parser.js.map +1 -0
  134. package/compiler/src/parser/parser.ts +710 -0
  135. package/compiler/src/plugins/api.ts +8 -0
  136. package/compiler/src/plugins/loader.ts +21 -0
  137. package/compiler/src/semantic/analyzer.d.ts +12 -0
  138. package/compiler/src/semantic/analyzer.js +144 -0
  139. package/compiler/src/semantic/analyzer.js.map +1 -0
  140. package/compiler/src/semantic/analyzer.ts +277 -0
  141. package/compiler/src/type_system/checker.d.ts +7 -0
  142. package/compiler/src/type_system/checker.js +84 -0
  143. package/compiler/src/type_system/checker.js.map +1 -0
  144. package/compiler/src/type_system/checker.ts +108 -0
  145. package/compiler/tsconfig.json +7 -0
  146. package/debugger/package.json +11 -0
  147. package/debugger/src/index.d.ts +1 -0
  148. package/debugger/src/index.js +9 -0
  149. package/debugger/src/index.js.map +1 -0
  150. package/debugger/src/index.ts +9 -0
  151. package/debugger/tsconfig.json +7 -0
  152. package/docs/CHANGELOG.md +11 -0
  153. package/docs/CONTRIBUTING.md +15 -0
  154. package/docs/LICENSE +21 -0
  155. package/docs/architecture.md +29 -0
  156. package/docs/cli.md +58 -0
  157. package/docs/language-spec.md +49 -0
  158. package/docs/packages.md +19 -0
  159. package/docs/platform-systems.md +31 -0
  160. package/docs/release-validation.md +22 -0
  161. package/docs/runtime.md +10 -0
  162. package/docs/tooling.md +17 -0
  163. package/docs/vscode-extension.md +40 -0
  164. package/enterprise-demo/CargoX.lock +2 -0
  165. package/enterprise-demo/README.md +3 -0
  166. package/enterprise-demo/benchmarks/arith.cbx +6 -0
  167. package/enterprise-demo/cobolx.toml +7 -0
  168. package/enterprise-demo/src/main.cbx +5 -0
  169. package/enterprise-demo/tests/smoke.cbx +6 -0
  170. package/examples/README.md +20 -0
  171. package/examples/actors-and-flags.md +8 -0
  172. package/examples/api-server/README.md +9 -0
  173. package/examples/api-server/cobolx.toml +6 -0
  174. package/examples/api-server/src/main.cbx +8 -0
  175. package/examples/debug-replay.md +7 -0
  176. package/examples/debugging-demo/README.md +3 -0
  177. package/examples/debugging-demo/cobolx.toml +6 -0
  178. package/examples/debugging-demo/src/main.cbx +7 -0
  179. package/examples/distributed-service.md +12 -0
  180. package/examples/distributed-system/README.md +3 -0
  181. package/examples/distributed-system/cobolx.toml +6 -0
  182. package/examples/distributed-system/generated/deploy.json +12 -0
  183. package/examples/distributed-system/src/main.cbx +6 -0
  184. package/examples/event-driven.md +5 -0
  185. package/examples/event-system/README.md +3 -0
  186. package/examples/event-system/cobolx.toml +6 -0
  187. package/examples/event-system/src/main.cbx +11 -0
  188. package/examples/functions.cbx +10 -0
  189. package/examples/functions.mjs +12 -0
  190. package/examples/hello.cbx +5 -0
  191. package/examples/hello.mjs +7 -0
  192. package/examples/legacy-sample.cob +5 -0
  193. package/examples/parallel-processing/README.md +3 -0
  194. package/examples/parallel-processing/cobolx.toml +6 -0
  195. package/examples/parallel-processing/src/main.cbx +6 -0
  196. package/examples/parallel-processing.md +8 -0
  197. package/examples/platform-features.cbx +32 -0
  198. package/examples/platform-features.mjs +35 -0
  199. package/examples/service.cbx +10 -0
  200. package/examples/workflow-engine/README.md +3 -0
  201. package/examples/workflow-engine/cobolx.toml +6 -0
  202. package/examples/workflow-engine/generated/flowchart.mmd +8 -0
  203. package/examples/workflow-engine/src/main.cbx +13 -0
  204. package/examples/workflow-engine.md +5 -0
  205. package/formatter/package.json +14 -0
  206. package/formatter/src/index.d.ts +1 -0
  207. package/formatter/src/index.js +59 -0
  208. package/formatter/src/index.js.map +1 -0
  209. package/formatter/src/index.ts +103 -0
  210. package/formatter/tsconfig.json +7 -0
  211. package/generated/LEGACYDEMO.cbx +5 -0
  212. package/install.ps1 +4 -0
  213. package/install.sh +5 -0
  214. package/linter/package.json +14 -0
  215. package/linter/src/index.d.ts +2 -0
  216. package/linter/src/index.js +18 -0
  217. package/linter/src/index.js.map +1 -0
  218. package/linter/src/index.ts +19 -0
  219. package/linter/tsconfig.json +7 -0
  220. package/lsp/server/package.json +16 -0
  221. package/lsp/server/src/index.ts +168 -0
  222. package/lsp/server/tsconfig.json +7 -0
  223. package/package.json +30 -0
  224. package/profiler/package.json +11 -0
  225. package/profiler/src/index.d.ts +5 -0
  226. package/profiler/src/index.js +11 -0
  227. package/profiler/src/index.js.map +1 -0
  228. package/profiler/src/index.ts +11 -0
  229. package/profiler/tsconfig.json +7 -0
  230. package/release.json +10 -0
  231. package/runtime/package.json +14 -0
  232. package/runtime/src/actors/index.ts +27 -0
  233. package/runtime/src/async/futures.ts +11 -0
  234. package/runtime/src/code_as_data/index.ts +5 -0
  235. package/runtime/src/config/index.ts +17 -0
  236. package/runtime/src/distributed/index.ts +13 -0
  237. package/runtime/src/events/index.ts +21 -0
  238. package/runtime/src/feature_flags/index.ts +9 -0
  239. package/runtime/src/gc_or_arc/index.ts +1 -0
  240. package/runtime/src/healing/index.ts +26 -0
  241. package/runtime/src/index.ts +20 -0
  242. package/runtime/src/intents/index.ts +9 -0
  243. package/runtime/src/iterators/index.ts +23 -0
  244. package/runtime/src/memory/arc.ts +22 -0
  245. package/runtime/src/observability/index.ts +23 -0
  246. package/runtime/src/repl/context.ts +7 -0
  247. package/runtime/src/scheduler/taskScheduler.ts +11 -0
  248. package/runtime/src/security/capabilities.ts +15 -0
  249. package/runtime/src/security/secrets.ts +13 -0
  250. package/runtime/src/state/versioned.ts +31 -0
  251. package/runtime/src/std_hooks/audit.ts +23 -0
  252. package/runtime/src/time_travel/index.ts +54 -0
  253. package/runtime/src/workflows/index.ts +35 -0
  254. package/runtime/tsconfig.json +7 -0
  255. package/sample-payroll/README.md +3 -0
  256. package/sample-payroll/cobolx.toml +3 -0
  257. package/sample-payroll/src/main.cbx +5 -0
  258. package/stdlib/business/index.js +15 -0
  259. package/stdlib/core/README.md +8 -0
  260. package/stdlib/core/runtime.js +191 -0
  261. package/stdlib/crypto/index.js +5 -0
  262. package/stdlib/datetime/index.js +3 -0
  263. package/stdlib/fs/index.js +9 -0
  264. package/stdlib/http/index.js +7 -0
  265. package/stdlib/io/index.js +1 -0
  266. package/stdlib/json/index.js +7 -0
  267. package/stdlib/net/index.js +4 -0
  268. package/tests/macros.cbx +10 -0
  269. package/tests/pattern_match.cbx +16 -0
  270. package/tests/smoke.cbx +7 -0
  271. package/tests/validate-release.mjs +54 -0
  272. package/tsconfig.base.json +24 -0
  273. package/vscode-extension/LICENSE +21 -0
  274. package/vscode-extension/README.md +47 -0
  275. package/vscode-extension/cobolx-1.2.0.vsix +0 -0
  276. package/vscode-extension/icon.png +0 -0
  277. package/vscode-extension/icon.svg +15 -0
  278. package/vscode-extension/language-configuration.json +17 -0
  279. package/vscode-extension/package.json +148 -0
  280. package/vscode-extension/snippets/cobolx.code-snippets +49 -0
  281. package/vscode-extension/src/extension.ts +283 -0
  282. package/vscode-extension/syntaxes/cobolx.tmLanguage.json +75 -0
  283. package/vscode-extension/tests/test.cbl +3 -0
  284. package/vscode-extension/tsconfig.json +7 -0
@@ -0,0 +1,710 @@
1
+ import type {
2
+ ArrayLiteralNode,
3
+ AssertStatementNode,
4
+ BinaryExpressionNode,
5
+ BooleanLiteralNode,
6
+ CallExpressionNode,
7
+ ConstDeclarationNode,
8
+ DisplayStatementNode,
9
+ EnumConstructorExpressionNode,
10
+ EnumDeclarationNode,
11
+ EnumVariantNode,
12
+ ExportDeclarationNode,
13
+ ExpressionNode,
14
+ ExpressionStatementNode,
15
+ FunctionDeclarationNode,
16
+ FunctionSignatureNode,
17
+ GenericParam,
18
+ IdentifierNode,
19
+ IdentifierPatternNode,
20
+ IfStatementNode,
21
+ ImplDeclarationNode,
22
+ ImportDeclarationNode,
23
+ InputStatementNode,
24
+ LetStatementNode,
25
+ LiteralPatternNode,
26
+ MacroDeclarationNode,
27
+ MacroInvocationNode,
28
+ MatchArmNode,
29
+ MatchStatementNode,
30
+ MemberExpressionNode,
31
+ ModuleDeclarationNode,
32
+ NumberLiteralNode,
33
+ PatternNode,
34
+ PluginUseNode,
35
+ ProgramNode,
36
+ ReturnStatementNode,
37
+ SetStatementNode,
38
+ SpawnStatementNode,
39
+ StatementNode,
40
+ StringLiteralNode,
41
+ TestDeclarationNode,
42
+ TraitDeclarationNode,
43
+ TryExpressionNode,
44
+ TypedName,
45
+ UnaryExpressionNode,
46
+ UnsafeBlockNode,
47
+ VariantPatternNode,
48
+ WildcardPatternNode
49
+ } from "../ast/types.js";
50
+ import type { SourceRange } from "../ast/types.js";
51
+ import { CobolxError } from "../diagnostics.js";
52
+ import type { Token, TokenType } from "../lexer/tokens.js";
53
+
54
+ export class Parser {
55
+ private index = 0;
56
+ private pendingDocs: string[] = [];
57
+
58
+ constructor(private readonly tokens: Token[]) {}
59
+
60
+ parseProgram(): ProgramNode {
61
+ this.skipTrivia();
62
+ const programToken = this.consume("PROGRAM", "Expected PROGRAM declaration");
63
+ const name = this.consume("IDENTIFIER", "Expected program name");
64
+ this.skipTrivia();
65
+
66
+ const imports: ImportDeclarationNode[] = [];
67
+ const exports: ExportDeclarationNode[] = [];
68
+ const plugins: PluginUseNode[] = [];
69
+ const consts: ConstDeclarationNode[] = [];
70
+ const enums: EnumDeclarationNode[] = [];
71
+ const traits: TraitDeclarationNode[] = [];
72
+ const impls: ImplDeclarationNode[] = [];
73
+ const macros: MacroDeclarationNode[] = [];
74
+ const modules: ModuleDeclarationNode[] = [];
75
+ const functions: FunctionDeclarationNode[] = [];
76
+ const tests: TestDeclarationNode[] = [];
77
+
78
+ while (!this.check("BEGIN") && !this.check("EOF")) {
79
+ const docs = this.takeDocs();
80
+ if (this.match("IMPORT")) imports.push(this.parseImport(docs));
81
+ else if (this.match("EXPORT")) exports.push(this.parseExport(docs));
82
+ else if (this.match("PLUGIN")) plugins.push(this.parsePluginUse(docs));
83
+ else if (this.match("CONST")) consts.push(this.parseConstDeclaration(docs));
84
+ else if (this.match("ENUM")) enums.push(this.parseEnumDeclaration(docs));
85
+ else if (this.match("TRAIT")) traits.push(this.parseTraitDeclaration(docs));
86
+ else if (this.match("IMPL")) impls.push(this.parseImplDeclaration(docs));
87
+ else if (this.match("MACRO")) macros.push(this.parseMacroDeclaration(docs));
88
+ else if (this.match("MODULE")) modules.push(this.parseModuleDeclaration(docs));
89
+ else if (this.match("ASYNC", "FUNCTION")) functions.push(this.parseFunctionFromCurrent(docs));
90
+ else if (this.match("TEST")) tests.push(this.parseTestDeclaration(docs));
91
+ else break;
92
+ this.skipTrivia();
93
+ }
94
+
95
+ this.consume("BEGIN", "Expected BEGIN");
96
+ this.skipTrivia();
97
+ const body = this.parseStatementsUntil(["END"]);
98
+ const endToken = this.consume("END", "Expected END");
99
+ this.skipTrivia();
100
+ this.consume("EOF", "Expected end of file");
101
+
102
+ return {
103
+ kind: "Program",
104
+ name: name.lexeme,
105
+ imports,
106
+ exports,
107
+ plugins,
108
+ consts,
109
+ enums,
110
+ traits,
111
+ impls,
112
+ macros,
113
+ modules,
114
+ functions,
115
+ tests,
116
+ body,
117
+ range: this.mergeRanges(programToken.range, endToken.range)
118
+ };
119
+ }
120
+
121
+ private parseImport(docs?: string[]): ImportDeclarationNode {
122
+ const start = this.previous().range;
123
+ const importedName = this.consume("IDENTIFIER", "Expected import name");
124
+ let alias: string | undefined;
125
+ if (this.match("AS")) alias = this.consume("IDENTIFIER", "Expected alias").lexeme;
126
+ this.consume("FROM", "Expected FROM in import");
127
+ const modulePath = this.consume("STRING", "Expected string path after FROM");
128
+ return { kind: "ImportDeclaration", importedName: importedName.lexeme, alias, modulePath: modulePath.lexeme, docs, range: this.mergeRanges(start, modulePath.range) };
129
+ }
130
+
131
+ private parseExport(docs?: string[]): ExportDeclarationNode {
132
+ const start = this.previous().range;
133
+ const name = this.consume("IDENTIFIER", "Expected export name");
134
+ return { kind: "ExportDeclaration", name: name.lexeme, docs, range: this.mergeRanges(start, name.range) };
135
+ }
136
+
137
+ private parsePluginUse(docs?: string[]): PluginUseNode {
138
+ const start = this.previous().range;
139
+ const pluginPath = this.consume("STRING", "Expected plugin path");
140
+ return { kind: "PluginUse", pluginPath: pluginPath.lexeme, docs, range: this.mergeRanges(start, pluginPath.range) };
141
+ }
142
+
143
+ private parseConstDeclaration(docs?: string[]): ConstDeclarationNode {
144
+ const start = this.previous().range;
145
+ const name = this.consume("IDENTIFIER", "Expected const name");
146
+ this.consume("ASSIGN", "Expected = in const declaration");
147
+ const expression = this.parseExpression();
148
+ return { kind: "ConstDeclaration", name: name.lexeme, expression, docs, range: this.mergeRanges(start, expression.range) };
149
+ }
150
+
151
+ private parseEnumDeclaration(docs?: string[]): EnumDeclarationNode {
152
+ const start = this.previous().range;
153
+ const name = this.consume("IDENTIFIER", "Expected enum name");
154
+ if (this.match("COLON")) this.skipTrivia();
155
+ const variants: EnumVariantNode[] = [];
156
+ while (!this.check("END") && !this.check("EOF")) {
157
+ if (this.match("NEWLINE")) continue;
158
+ const variantName = this.consume("IDENTIFIER", "Expected enum variant name");
159
+ const fields: TypedName[] = [];
160
+ if (this.match("LPAREN")) {
161
+ if (!this.check("RPAREN")) {
162
+ do fields.push(this.parseTypedName());
163
+ while (this.match("COMMA"));
164
+ }
165
+ this.consume("RPAREN", "Expected ) after variant fields");
166
+ }
167
+ variants.push({ kind: "EnumVariant", name: variantName.lexeme, fields, range: this.mergeRanges(variantName.range, this.previous().range) });
168
+ this.skipTrivia();
169
+ }
170
+ const endToken = this.consume("END", "Expected END after enum");
171
+ return { kind: "EnumDeclaration", name: name.lexeme, variants, docs, range: this.mergeRanges(start, endToken.range) };
172
+ }
173
+
174
+ private parseTraitDeclaration(docs?: string[]): TraitDeclarationNode {
175
+ const start = this.previous().range;
176
+ const name = this.consume("IDENTIFIER", "Expected trait name");
177
+ const composedTraits: string[] = [];
178
+ if (this.match("COLON")) {
179
+ do composedTraits.push(this.consume("IDENTIFIER", "Expected composed trait name").lexeme);
180
+ while (this.match("COMMA"));
181
+ }
182
+ this.skipTrivia();
183
+ const methods: FunctionDeclarationNode[] = [];
184
+ while (!this.check("END-TRAIT") && !this.check("EOF")) {
185
+ if (this.match("NEWLINE")) continue;
186
+ const docsForMethod = this.takeDocs();
187
+ this.match("ASYNC");
188
+ this.consume("FUNCTION", "Expected FUNCTION inside trait");
189
+ methods.push(this.parseFunctionFromCurrent(docsForMethod));
190
+ this.skipTrivia();
191
+ }
192
+ const endToken = this.consume("END-TRAIT", "Expected END-TRAIT");
193
+ return { kind: "TraitDeclaration", name: name.lexeme, composedTraits, methods, docs, range: this.mergeRanges(start, endToken.range) };
194
+ }
195
+
196
+ private parseImplDeclaration(docs?: string[]): ImplDeclarationNode {
197
+ const start = this.previous().range;
198
+ const first = this.consume("IDENTIFIER", "Expected trait or type name");
199
+ let traitName: string | undefined;
200
+ let targetType = first.lexeme;
201
+ if (this.match("FOR")) {
202
+ traitName = first.lexeme;
203
+ targetType = this.consume("IDENTIFIER", "Expected impl target type").lexeme;
204
+ }
205
+ if (this.match("COLON")) this.skipTrivia();
206
+ const methods: FunctionDeclarationNode[] = [];
207
+ while (!this.check("END-IMPL") && !this.check("EOF")) {
208
+ if (this.match("NEWLINE")) continue;
209
+ const docsForMethod = this.takeDocs();
210
+ this.match("ASYNC");
211
+ this.consume("FUNCTION", "Expected FUNCTION inside impl");
212
+ methods.push(this.parseFunctionFromCurrent(docsForMethod));
213
+ this.skipTrivia();
214
+ }
215
+ const endToken = this.consume("END-IMPL", "Expected END-IMPL");
216
+ return { kind: "ImplDeclaration", traitName, targetType, methods, docs, range: this.mergeRanges(start, endToken.range) };
217
+ }
218
+
219
+ private parseMacroDeclaration(docs?: string[]): MacroDeclarationNode {
220
+ const start = this.previous().range;
221
+ const name = this.consume("IDENTIFIER", "Expected macro name");
222
+ const params = this.parseIdentifierList();
223
+ this.skipTrivia();
224
+ this.consume("BEGIN", "Expected BEGIN in macro");
225
+ this.skipTrivia();
226
+ const body = this.parseStatementsUntil(["END-MACRO"]);
227
+ const endToken = this.consume("END-MACRO", "Expected END-MACRO");
228
+ return { kind: "MacroDeclaration", name: name.lexeme, params, body, docs, range: this.mergeRanges(start, endToken.range) };
229
+ }
230
+
231
+ private parseModuleDeclaration(docs?: string[]): ModuleDeclarationNode {
232
+ const start = this.previous().range;
233
+ const name = this.consume("IDENTIFIER", "Expected module name");
234
+ this.skipTrivia();
235
+ this.consume("BEGIN", "Expected BEGIN in module");
236
+ this.skipTrivia();
237
+
238
+ const exports: ExportDeclarationNode[] = [];
239
+ const consts: ConstDeclarationNode[] = [];
240
+ const enums: EnumDeclarationNode[] = [];
241
+ const traits: TraitDeclarationNode[] = [];
242
+ const impls: ImplDeclarationNode[] = [];
243
+ const macros: MacroDeclarationNode[] = [];
244
+ const functions: FunctionDeclarationNode[] = [];
245
+
246
+ while (!this.check("END-MODULE") && !this.check("EOF")) {
247
+ const docsForMember = this.takeDocs();
248
+ if (this.match("EXPORT")) exports.push(this.parseExport(docsForMember));
249
+ else if (this.match("CONST")) consts.push(this.parseConstDeclaration(docsForMember));
250
+ else if (this.match("ENUM")) enums.push(this.parseEnumDeclaration(docsForMember));
251
+ else if (this.match("TRAIT")) traits.push(this.parseTraitDeclaration(docsForMember));
252
+ else if (this.match("IMPL")) impls.push(this.parseImplDeclaration(docsForMember));
253
+ else if (this.match("MACRO")) macros.push(this.parseMacroDeclaration(docsForMember));
254
+ else if (this.match("ASYNC", "FUNCTION")) functions.push(this.parseFunctionFromCurrent(docsForMember));
255
+ else this.skipTrivia();
256
+ this.skipTrivia();
257
+ }
258
+
259
+ const endToken = this.consume("END-MODULE", "Expected END-MODULE");
260
+ return { kind: "ModuleDeclaration", name: name.lexeme, exports, consts, enums, traits, impls, macros, functions, docs, range: this.mergeRanges(start, endToken.range) };
261
+ }
262
+
263
+ private parseFunctionFromCurrent(docs?: string[]): FunctionDeclarationNode {
264
+ const functionToken = this.previous();
265
+ const isAsync = functionToken.type === "ASYNC";
266
+ if (functionToken.type !== "FUNCTION") this.consume("FUNCTION", "Expected FUNCTION");
267
+ const name = this.consume("IDENTIFIER", "Expected function name");
268
+ const genericParams = this.parseGenericParams();
269
+ const params = this.parseTypedNameList();
270
+ let returnType: string | undefined;
271
+ let returnLifetime: string | undefined;
272
+ if (this.match("ARROW")) {
273
+ const typeRef = this.parseTypeReference();
274
+ returnType = typeRef.typeName;
275
+ returnLifetime = typeRef.lifetime;
276
+ }
277
+ this.skipTrivia();
278
+ this.consume("BEGIN", "Expected BEGIN after function signature");
279
+ this.skipTrivia();
280
+ const body = this.parseStatementsUntil(["END-FUNCTION"]);
281
+ const endToken = this.consume("END-FUNCTION", "Expected END-FUNCTION");
282
+ const signature: FunctionSignatureNode = {
283
+ kind: "FunctionSignature",
284
+ name: name.lexeme,
285
+ genericParams,
286
+ params,
287
+ returnType,
288
+ returnLifetime,
289
+ isAsync,
290
+ range: this.mergeRanges(name.range, endToken.range)
291
+ };
292
+ return { kind: "FunctionDeclaration", signature, body, docs, range: this.mergeRanges(functionToken.range, endToken.range) };
293
+ }
294
+
295
+ private parseTestDeclaration(docs?: string[]): TestDeclarationNode {
296
+ const start = this.previous().range;
297
+ const name = this.consume("STRING", "Expected test name");
298
+ this.skipTrivia();
299
+ this.consume("BEGIN", "Expected BEGIN in test");
300
+ this.skipTrivia();
301
+ const body = this.parseStatementsUntil(["END-TEST"]);
302
+ const endToken = this.consume("END-TEST", "Expected END-TEST");
303
+ return { kind: "TestDeclaration", name: name.lexeme, body, docs, range: this.mergeRanges(start, endToken.range) };
304
+ }
305
+
306
+ private parseStatementsUntil(stopTokens: TokenType[]): StatementNode[] {
307
+ const statements: StatementNode[] = [];
308
+ while (!this.checkAny(stopTokens) && !this.check("EOF")) {
309
+ this.skipTrivia();
310
+ if (this.checkAny(stopTokens) || this.check("EOF")) break;
311
+ statements.push(this.parseStatement());
312
+ this.skipTrivia();
313
+ }
314
+ return statements;
315
+ }
316
+
317
+ private parseStatement(): StatementNode {
318
+ if (this.match("LET")) return this.parseLetStatement();
319
+ if (this.match("SET")) return this.parseSetStatement();
320
+ if (this.match("DISPLAY")) return this.parseDisplayStatement();
321
+ if (this.match("INPUT")) return this.parseInputStatement();
322
+ if (this.match("IF")) return this.parseIfStatement();
323
+ if (this.match("MATCH")) return this.parseMatchStatement();
324
+ if (this.match("RETURN")) return this.parseReturnStatement();
325
+ if (this.match("UNSAFE")) return this.parseUnsafeBlock();
326
+ if (this.match("ASSERT")) return this.parseAssertStatement();
327
+ if (this.match("SPAWN")) return this.parseSpawnStatement();
328
+ const expression = this.parseExpression();
329
+ return { kind: "ExpressionStatement", expression, range: expression.range } satisfies ExpressionStatementNode;
330
+ }
331
+
332
+ private parseLetStatement(): LetStatementNode {
333
+ const start = this.previous().range;
334
+ const isMutable = this.match("MUT");
335
+ const binding = this.parseTypedName();
336
+ this.consume("ASSIGN", "Expected = in let statement");
337
+ const expression = this.parseExpression();
338
+ return { kind: "LetStatement", binding, expression, isMutable, range: this.mergeRanges(start, expression.range) };
339
+ }
340
+
341
+ private parseSetStatement(): SetStatementNode {
342
+ const start = this.previous().range;
343
+ const name = this.consume("IDENTIFIER", "Expected variable name after SET");
344
+ this.consume("ASSIGN", "Expected = in SET statement");
345
+ const expression = this.parseExpression();
346
+ return { kind: "SetStatement", name: name.lexeme, expression, range: this.mergeRanges(start, expression.range) };
347
+ }
348
+
349
+ private parseDisplayStatement(): DisplayStatementNode {
350
+ const start = this.previous().range;
351
+ const expression = this.parseExpression();
352
+ return { kind: "DisplayStatement", expression, range: this.mergeRanges(start, expression.range) };
353
+ }
354
+
355
+ private parseInputStatement(): InputStatementNode {
356
+ const start = this.previous().range;
357
+ const name = this.consume("IDENTIFIER", "Expected variable name after INPUT");
358
+ let prompt: ExpressionNode | undefined;
359
+ if (!this.check("NEWLINE") && !this.check("EOF")) prompt = this.parseExpression();
360
+ return { kind: "InputStatement", name: name.lexeme, prompt, range: this.mergeRanges(start, prompt?.range ?? name.range) };
361
+ }
362
+
363
+ private parseIfStatement(): IfStatementNode {
364
+ const start = this.previous().range;
365
+ const condition = this.parseExpression();
366
+ this.consume("THEN", "Expected THEN after IF condition");
367
+ this.skipTrivia();
368
+ const thenBranch = this.parseStatementsUntil(["ELSE", "END-IF"]);
369
+ let elseBranch: StatementNode[] = [];
370
+ if (this.match("ELSE")) {
371
+ this.skipTrivia();
372
+ elseBranch = this.parseStatementsUntil(["END-IF"]);
373
+ }
374
+ const endToken = this.consume("END-IF", "Expected END-IF");
375
+ return { kind: "IfStatement", condition, thenBranch, elseBranch, range: this.mergeRanges(start, endToken.range) };
376
+ }
377
+
378
+ private parseMatchStatement(): MatchStatementNode {
379
+ const start = this.previous().range;
380
+ const expression = this.parseExpression();
381
+ this.consume("COLON", "Expected : after MATCH expression");
382
+ this.skipTrivia();
383
+ const arms: MatchArmNode[] = [];
384
+ while (!this.check("END-MATCH") && !this.check("EOF")) {
385
+ if (this.match("NEWLINE")) continue;
386
+ const pattern = this.parsePattern();
387
+ this.consume("COLON", "Expected : after match pattern");
388
+ this.skipTrivia();
389
+ const body: StatementNode[] = [];
390
+ while (!this.check("END-MATCH") && !this.check("EOF") && !this.isPatternArmBoundary()) {
391
+ body.push(this.parseStatement());
392
+ this.skipTrivia();
393
+ }
394
+ arms.push({ kind: "MatchArm", pattern, body, range: this.mergeRanges(pattern.range, body.at(-1)?.range ?? pattern.range) });
395
+ this.skipTrivia();
396
+ if (!this.check("END-MATCH") && this.isPatternArmBoundary()) continue;
397
+ }
398
+ const endToken = this.consume("END-MATCH", "Expected END-MATCH");
399
+ return { kind: "MatchStatement", expression, arms, range: this.mergeRanges(start, endToken.range) };
400
+ }
401
+
402
+ private parseReturnStatement(): ReturnStatementNode {
403
+ const start = this.previous().range;
404
+ const expression = this.parseExpression();
405
+ return { kind: "ReturnStatement", expression, range: this.mergeRanges(start, expression.range) };
406
+ }
407
+
408
+ private parseUnsafeBlock(): UnsafeBlockNode {
409
+ const start = this.previous().range;
410
+ this.skipTrivia();
411
+ this.consume("BEGIN", "Expected BEGIN after UNSAFE");
412
+ this.skipTrivia();
413
+ const body = this.parseStatementsUntil(["END-UNSAFE"]);
414
+ const endToken = this.consume("END-UNSAFE", "Expected END-UNSAFE");
415
+ return { kind: "UnsafeBlock", body, range: this.mergeRanges(start, endToken.range) };
416
+ }
417
+
418
+ private parseAssertStatement(): AssertStatementNode {
419
+ const start = this.previous().range;
420
+ const expression = this.parseExpression();
421
+ return { kind: "AssertStatement", expression, range: this.mergeRanges(start, expression.range) };
422
+ }
423
+
424
+ private parseSpawnStatement(): SpawnStatementNode {
425
+ const start = this.previous().range;
426
+ const expression = this.parseExpression();
427
+ return { kind: "SpawnStatement", expression, range: this.mergeRanges(start, expression.range) };
428
+ }
429
+
430
+ private parsePattern(): PatternNode {
431
+ if (this.match("IDENTIFIER")) {
432
+ const token = this.previous();
433
+ if (token.lexeme === "_") return { kind: "WildcardPattern", range: token.range } satisfies WildcardPatternNode;
434
+ if (this.match("LPAREN")) {
435
+ const bindings: string[] = [];
436
+ if (!this.check("RPAREN")) {
437
+ do bindings.push(this.consume("IDENTIFIER", "Expected pattern binding").lexeme);
438
+ while (this.match("COMMA"));
439
+ }
440
+ const close = this.consume("RPAREN", "Expected ) after pattern");
441
+ return { kind: "VariantPattern", variantName: token.lexeme, bindings, range: this.mergeRanges(token.range, close.range) } satisfies VariantPatternNode;
442
+ }
443
+ return { kind: "IdentifierPattern", name: token.lexeme, range: token.range } satisfies IdentifierPatternNode;
444
+ }
445
+ if (this.match("NUMBER", "STRING", "TRUE", "FALSE")) {
446
+ const expression = this.literalFromToken(this.previous());
447
+ return { kind: "LiteralPattern", expression, range: expression.range } satisfies LiteralPatternNode;
448
+ }
449
+ throw new CobolxError({ message: "Expected match pattern", range: this.peek().range, severity: "error" });
450
+ }
451
+
452
+ private parseExpression(): ExpressionNode {
453
+ return this.parseComparison();
454
+ }
455
+
456
+ private parseComparison(): ExpressionNode {
457
+ let expression = this.parseTerm();
458
+ while (this.match("LESS", "LESS_EQUAL", "GREATER", "GREATER_EQUAL", "NOT_EQUAL", "ASSIGN")) {
459
+ const operator = this.previous();
460
+ const mappedOperator = operator.type === "ASSIGN" ? "==" : operator.lexeme;
461
+ const right = this.parseTerm();
462
+ expression = this.binaryNode(expression, mappedOperator, right);
463
+ }
464
+ return expression;
465
+ }
466
+
467
+ private parseTerm(): ExpressionNode {
468
+ let expression = this.parseFactor();
469
+ while (this.match("PLUS", "MINUS")) {
470
+ const operator = this.previous();
471
+ const right = this.parseFactor();
472
+ expression = this.binaryNode(expression, operator.lexeme, right);
473
+ }
474
+ return expression;
475
+ }
476
+
477
+ private parseFactor(): ExpressionNode {
478
+ let expression = this.parseUnary();
479
+ while (this.match("STAR", "SLASH", "PERCENT")) {
480
+ const operator = this.previous();
481
+ const right = this.parseUnary();
482
+ expression = this.binaryNode(expression, operator.lexeme, right);
483
+ }
484
+ return expression;
485
+ }
486
+
487
+ private parseUnary(): ExpressionNode {
488
+ if (this.match("MINUS", "BANG", "AMPERSAND")) {
489
+ const operator = this.previous();
490
+ const operand = this.parseUnary();
491
+ return { kind: "UnaryExpression", operator: operator.lexeme, operand, range: this.mergeRanges(operator.range, operand.range) } satisfies UnaryExpressionNode;
492
+ }
493
+ return this.parsePostfix();
494
+ }
495
+
496
+ private parsePostfix(): ExpressionNode {
497
+ let expression = this.parsePrimary();
498
+ while (true) {
499
+ if (this.match("BANG")) {
500
+ if (expression.kind !== "Identifier") throw new CobolxError({ message: "Macros must be invoked by name", range: expression.range, severity: "error" });
501
+ const args = this.parseCallArguments();
502
+ expression = { kind: "MacroInvocation", name: expression.name, args, range: this.mergeRanges(expression.range, this.previous().range) } satisfies MacroInvocationNode;
503
+ continue;
504
+ }
505
+ if (this.check("LPAREN")) {
506
+ const start = expression.range;
507
+ const args = this.parseCallArguments();
508
+ expression = { kind: "CallExpression", callee: expression, args, range: this.mergeRanges(start, this.previous().range) } satisfies CallExpressionNode;
509
+ continue;
510
+ }
511
+ if (this.match("DOT")) {
512
+ const property = this.consume("IDENTIFIER", "Expected property name after .");
513
+ expression = { kind: "MemberExpression", object: expression, property: property.lexeme, range: this.mergeRanges(expression.range, property.range) } satisfies MemberExpressionNode;
514
+ continue;
515
+ }
516
+ if (this.match("QUESTION")) {
517
+ expression = { kind: "TryExpression", expression, range: this.mergeRanges(expression.range, this.previous().range) } satisfies TryExpressionNode;
518
+ continue;
519
+ }
520
+ break;
521
+ }
522
+ return expression;
523
+ }
524
+
525
+ private parsePrimary(): ExpressionNode {
526
+ if (this.match("NUMBER", "STRING", "TRUE", "FALSE")) return this.literalFromToken(this.previous());
527
+ if (this.match("IDENTIFIER")) {
528
+ const token = this.previous();
529
+ if (this.check("LPAREN") && /^[A-Z]/.test(token.lexeme)) {
530
+ const args = this.parseCallArguments();
531
+ return { kind: "EnumConstructorExpression", variantName: token.lexeme, fields: args, range: this.mergeRanges(token.range, this.previous().range) } satisfies EnumConstructorExpressionNode;
532
+ }
533
+ return { kind: "Identifier", name: token.lexeme, range: token.range } satisfies IdentifierNode;
534
+ }
535
+ if (this.match("LBRACKET")) {
536
+ const start = this.previous().range;
537
+ const items: ExpressionNode[] = [];
538
+ if (!this.check("RBRACKET")) {
539
+ do items.push(this.parseExpression());
540
+ while (this.match("COMMA"));
541
+ }
542
+ const close = this.consume("RBRACKET", "Expected ] after array literal");
543
+ return { kind: "ArrayLiteral", items, range: this.mergeRanges(start, close.range) } satisfies ArrayLiteralNode;
544
+ }
545
+ if (this.match("LPAREN")) {
546
+ const expression = this.parseExpression();
547
+ this.consume("RPAREN", "Expected ) after expression");
548
+ return expression;
549
+ }
550
+ throw new CobolxError({ message: `Unexpected token ${this.peek().type}`, range: this.peek().range, severity: "error" });
551
+ }
552
+
553
+ private literalFromToken(token: Token): ExpressionNode {
554
+ if (token.type === "NUMBER") return { kind: "NumberLiteral", value: Number(token.lexeme), range: token.range } satisfies NumberLiteralNode;
555
+ if (token.type === "STRING") return { kind: "StringLiteral", value: token.lexeme, range: token.range } satisfies StringLiteralNode;
556
+ return { kind: "BooleanLiteral", value: token.type === "TRUE", range: token.range } satisfies BooleanLiteralNode;
557
+ }
558
+
559
+ private parseTypedName(): TypedName {
560
+ let lifetime: string | undefined;
561
+ if (this.match("AMPERSAND")) lifetime = this.consume("IDENTIFIER", "Expected lifetime").lexeme;
562
+ const name = this.consume("IDENTIFIER", "Expected name");
563
+ let typeName: string | undefined;
564
+ if (this.match("COLON")) {
565
+ const type = this.parseTypeReference();
566
+ typeName = type.typeName;
567
+ lifetime = lifetime ?? type.lifetime;
568
+ }
569
+ return { name: name.lexeme, typeName, lifetime };
570
+ }
571
+
572
+ private parseTypeReference(): { typeName: string; lifetime?: string } {
573
+ let lifetime: string | undefined;
574
+ if (this.match("AMPERSAND")) lifetime = this.consume("IDENTIFIER", "Expected lifetime").lexeme;
575
+ const typeName = this.consume("IDENTIFIER", "Expected type name").lexeme;
576
+ return { typeName, lifetime };
577
+ }
578
+
579
+ private parseGenericParams(): GenericParam[] {
580
+ const params: GenericParam[] = [];
581
+ if (!this.match("LESS")) return params;
582
+ do {
583
+ let lifetime: string | undefined;
584
+ if (this.match("AMPERSAND")) lifetime = this.consume("IDENTIFIER", "Expected lifetime").lexeme;
585
+ const name = this.consume("IDENTIFIER", "Expected generic parameter").lexeme;
586
+ let bound: string | undefined;
587
+ if (this.match("COLON")) bound = this.consume("IDENTIFIER", "Expected generic bound").lexeme;
588
+ params.push({ name, bound, lifetime });
589
+ } while (this.match("COMMA"));
590
+ this.consume("GREATER", "Expected > after generic parameters");
591
+ return params;
592
+ }
593
+
594
+ private parseTypedNameList(): TypedName[] {
595
+ this.consume("LPAREN", "Expected (");
596
+ const params: TypedName[] = [];
597
+ if (!this.check("RPAREN")) {
598
+ do params.push(this.parseTypedName());
599
+ while (this.match("COMMA"));
600
+ }
601
+ this.consume("RPAREN", "Expected )");
602
+ return params;
603
+ }
604
+
605
+ private parseIdentifierList(): string[] {
606
+ this.consume("LPAREN", "Expected (");
607
+ const ids: string[] = [];
608
+ if (!this.check("RPAREN")) {
609
+ do ids.push(this.consume("IDENTIFIER", "Expected identifier").lexeme);
610
+ while (this.match("COMMA"));
611
+ }
612
+ this.consume("RPAREN", "Expected )");
613
+ return ids;
614
+ }
615
+
616
+ private parseCallArguments(): ExpressionNode[] {
617
+ this.consume("LPAREN", "Expected (");
618
+ const args: ExpressionNode[] = [];
619
+ if (!this.check("RPAREN")) {
620
+ do args.push(this.parseExpression());
621
+ while (this.match("COMMA"));
622
+ }
623
+ this.consume("RPAREN", "Expected )");
624
+ return args;
625
+ }
626
+
627
+ private looksLikePatternStart(): boolean {
628
+ return this.check("IDENTIFIER") || this.check("NUMBER") || this.check("STRING") || this.check("TRUE") || this.check("FALSE");
629
+ }
630
+
631
+ private isPatternArmBoundary(): boolean {
632
+ if (!this.looksLikePatternStart()) return false;
633
+ if (this.peek().type !== "IDENTIFIER") return this.tokens[this.index + 1]?.type === "COLON";
634
+ const next = this.tokens[this.index + 1];
635
+ if (!next) return false;
636
+ if (next.type === "COLON") return true;
637
+ if (next.type !== "LPAREN") return false;
638
+ let cursor = this.index + 2;
639
+ let depth = 1;
640
+ while (cursor < this.tokens.length && depth > 0) {
641
+ const token = this.tokens[cursor];
642
+ if (token.type === "LPAREN") depth += 1;
643
+ if (token.type === "RPAREN") depth -= 1;
644
+ cursor += 1;
645
+ }
646
+ return this.tokens[cursor]?.type === "COLON";
647
+ }
648
+
649
+ private skipTrivia(): void {
650
+ while (true) {
651
+ if (this.match("NEWLINE")) continue;
652
+ if (this.match("DOC_COMMENT")) {
653
+ this.pendingDocs.push(this.previous().lexeme);
654
+ continue;
655
+ }
656
+ break;
657
+ }
658
+ }
659
+
660
+ private takeDocs(): string[] | undefined {
661
+ if (this.pendingDocs.length === 0) return undefined;
662
+ const docs = [...this.pendingDocs];
663
+ this.pendingDocs = [];
664
+ return docs;
665
+ }
666
+
667
+ private binaryNode(left: ExpressionNode, operator: string, right: ExpressionNode): BinaryExpressionNode {
668
+ return { kind: "BinaryExpression", operator, left, right, range: this.mergeRanges(left.range, right.range) };
669
+ }
670
+
671
+ private match(...types: TokenType[]): boolean {
672
+ for (const type of types) {
673
+ if (this.check(type)) {
674
+ this.advance();
675
+ return true;
676
+ }
677
+ }
678
+ return false;
679
+ }
680
+
681
+ private consume(type: TokenType, message: string): Token {
682
+ if (this.check(type)) return this.advance();
683
+ throw new CobolxError({ message, range: this.peek().range, severity: "error" });
684
+ }
685
+
686
+ private check(type: TokenType): boolean {
687
+ return this.peek().type === type;
688
+ }
689
+
690
+ private checkAny(types: TokenType[]): boolean {
691
+ return types.some((type) => this.check(type));
692
+ }
693
+
694
+ private advance(): Token {
695
+ if (this.index < this.tokens.length) this.index += 1;
696
+ return this.previous();
697
+ }
698
+
699
+ private peek(): Token {
700
+ return this.tokens[this.index];
701
+ }
702
+
703
+ private previous(): Token {
704
+ return this.tokens[this.index - 1];
705
+ }
706
+
707
+ private mergeRanges(start: SourceRange, end: SourceRange): SourceRange {
708
+ return { start: start.start, end: end.end };
709
+ }
710
+ }