mt-lang 0.2.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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module IR
5
+ Program = Data.define(:module_name, :includes, :constants, :globals, :opaques, :structs, :unions, :enums, :variants, :static_asserts, :functions, :source_path) do
6
+ def initialize(module_name:, includes:, constants:, globals:, opaques:, structs:, unions:, enums:, variants:, static_asserts:, functions:, source_path: nil) = super
7
+ end
8
+ Include = Data.define(:header)
9
+ Constant = Data.define(:name, :linkage_name, :type, :value)
10
+ Global = Data.define(:name, :linkage_name, :type, :value)
11
+ OpaqueDecl = Data.define(:name, :linkage_name, :forward_declarable, :source_module) do
12
+ def initialize(name:, linkage_name:, forward_declarable:, source_module: nil) = super
13
+ end
14
+ StructDecl = Data.define(:name, :linkage_name, :fields, :packed, :alignment, :source_module) do
15
+ def initialize(name:, linkage_name:, fields:, packed:, alignment:, source_module: nil) = super
16
+ end
17
+ UnionDecl = Data.define(:name, :linkage_name, :fields, :source_module) do
18
+ def initialize(name:, linkage_name:, fields:, source_module: nil) = super
19
+ end
20
+ EnumDecl = Data.define(:name, :linkage_name, :backing_type, :members, :flags)
21
+ EnumMember = Data.define(:name, :linkage_name, :value)
22
+ Field = Data.define(:name, :type)
23
+ Function = Data.define(:name, :linkage_name, :params, :return_type, :body, :entry_point, :method_receiver_param) do
24
+ def initialize(name:, linkage_name:, params:, return_type:, body:, entry_point:, method_receiver_param: false) = super
25
+ end
26
+ Param = Data.define(:name, :linkage_name, :type, :pointer)
27
+
28
+ LocalDecl = Data.define(:name, :linkage_name, :type, :value, :line, :source_path) do
29
+ def initialize(name:, linkage_name:, type:, value:, line: nil, source_path: nil) = super
30
+ end
31
+ Assignment = Data.define(:target, :operator, :value)
32
+ BlockStmt = Data.define(:body)
33
+ IfStmt = Data.define(:condition, :then_body, :else_body)
34
+ SwitchCase = Data.define(:value, :body)
35
+ SwitchDefaultCase = Data.define(:body)
36
+ SwitchStmt = Data.define(:expression, :cases, :exhaustive) do
37
+ def initialize(expression:, cases:, exhaustive: false) = super
38
+ end
39
+ WhileStmt = Data.define(:condition, :body)
40
+ ForStmt = Data.define(:init, :condition, :post, :body)
41
+ BreakStmt = Data.define()
42
+ ContinueStmt = Data.define()
43
+ GotoStmt = Data.define(:label)
44
+ LabelStmt = Data.define(:name)
45
+ StaticAssert = Data.define(:condition, :message)
46
+ ReturnStmt = Data.define(:value, :line, :source_path) do
47
+ def initialize(value:, line: nil, source_path: nil) = super
48
+ end
49
+ ExpressionStmt = Data.define(:expression, :line, :source_path) do
50
+ def initialize(expression:, line: nil, source_path: nil) = super
51
+ end
52
+
53
+ Name = Data.define(:name, :type, :pointer)
54
+ Member = Data.define(:receiver, :member, :type)
55
+ Index = Data.define(:receiver, :index, :type)
56
+ CheckedIndex = Data.define(:receiver, :index, :receiver_type, :type)
57
+ CheckedSpanIndex = Data.define(:receiver, :index, :receiver_type, :type)
58
+ NullableIndex = Data.define(:receiver, :index, :receiver_type, :type)
59
+ NullableSpanIndex = Data.define(:receiver, :index, :receiver_type, :type)
60
+ Call = Data.define(:callee, :arguments, :type)
61
+ Unary = Data.define(:operator, :operand, :type)
62
+ Binary = Data.define(:operator, :left, :right, :type)
63
+ Conditional = Data.define(:condition, :then_expression, :else_expression, :type)
64
+ ReinterpretExpr = Data.define(:target_type, :source_type, :expression, :type)
65
+ SizeofExpr = Data.define(:target_type, :type)
66
+ AlignofExpr = Data.define(:target_type, :type)
67
+ OffsetofExpr = Data.define(:target_type, :field, :type)
68
+ IntegerLiteral = Data.define(:value, :type)
69
+ FloatLiteral = Data.define(:value, :type)
70
+ StringLiteral = Data.define(:value, :type, :cstring)
71
+ BooleanLiteral = Data.define(:value, :type)
72
+ NullLiteral = Data.define(:type)
73
+ ZeroInit = Data.define(:type)
74
+ AddressOf = Data.define(:expression, :type)
75
+ Cast = Data.define(:target_type, :expression, :type)
76
+ AggregateLiteral = Data.define(:type, :fields)
77
+ AggregateField = Data.define(:name, :value)
78
+ ArrayLiteral = Data.define(:type, :elements)
79
+ VariantDecl = Data.define(:name, :linkage_name, :arms, :source_module) do
80
+ def initialize(name:, linkage_name:, arms:, source_module: nil) = super
81
+ end
82
+ VariantArm = Data.define(:name, :linkage_name, :fields)
83
+ VariantLiteral = Data.define(:type, :arm_name, :fields)
84
+ end
85
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ KEYWORDS = {
5
+ "align_of" => :align_of,
6
+ "and" => :and,
7
+ "as" => :as,
8
+ "async" => :async,
9
+ "attribute" => :attribute,
10
+ "attribute_arg" => :attribute_arg,
11
+ "attribute_of" => :attribute_of,
12
+ "attributes_of" => :attributes_of,
13
+ "await" => :await,
14
+ "break" => :break,
15
+ "const" => :const,
16
+ "compiler_flag" => :compiler_flag,
17
+ "gather" => :gather,
18
+ "continue" => :continue,
19
+ "function" => :function,
20
+ "has_attribute" => :has_attribute,
21
+ "defer" => :defer,
22
+ "detach" => :detach,
23
+ "dyn" => :dyn,
24
+ "editable" => :editable,
25
+ "enum" => :enum,
26
+ "else" => :else,
27
+ "emit" => :emit,
28
+ "event" => :event,
29
+ "external" => :external,
30
+ "false" => :false,
31
+ "callable_of" => :callable_of,
32
+ "fields_of" => :fields_of,
33
+ "field_of" => :field_of,
34
+ "flags" => :flags,
35
+ "fn" => :fn,
36
+ "for" => :for,
37
+ "foreign" => :foreign,
38
+ "if" => :if,
39
+ "implements" => :implements,
40
+ "include" => :include,
41
+ "in" => :in,
42
+ "inline" => :inline,
43
+ "inout" => :inout,
44
+ "import" => :import,
45
+ "interface" => :interface,
46
+ "is" => :is,
47
+ "let" => :let,
48
+ "link" => :link,
49
+ "match" => :match,
50
+ "members_of" => :members_of,
51
+ "extending" => :extending,
52
+ "module" => :module,
53
+ "not" => :not,
54
+ "null" => :null,
55
+ "offset_of" => :offset_of,
56
+ "opaque" => :opaque,
57
+ "consuming" => :consuming,
58
+ "or" => :or,
59
+ "out" => :out,
60
+ "parallel" => :parallel,
61
+ "pass" => :pass,
62
+ "proc" => :proc,
63
+ "public" => :public,
64
+ "return" => :return,
65
+ "size_of" => :size_of,
66
+ "static" => :static,
67
+ "static_assert" => :static_assert,
68
+ "struct" => :struct,
69
+ "type" => :type,
70
+ "unsafe" => :unsafe,
71
+ "true" => :true,
72
+ "union" => :union,
73
+ "var" => :var,
74
+ "variant" => :variant,
75
+ "when" => :when,
76
+ "while" => :while,
77
+ }.freeze
78
+
79
+ BUILTIN_PRIMITIVE_NAMES = %w[
80
+ bool byte ubyte char short ushort int uint long ulong ptr_int ptr_uint float double void str cstr
81
+ vec2 vec3 vec4 ivec2 ivec3 ivec4 mat3 mat4 quat
82
+ ].freeze
83
+
84
+ RESERVED_VALUE_TYPE_NAMES = (BUILTIN_PRIMITIVE_NAMES + %w[
85
+ Option Result
86
+ ]).freeze
87
+
88
+ RESERVED_IMPORT_ALIAS_NAMES = %w[
89
+ Option Result
90
+ ].freeze
91
+
92
+ BUILTIN_TYPE_NAMES = (BUILTIN_PRIMITIVE_NAMES + %w[
93
+ ptr const_ptr own ref span array str_buffer atomic Task Option Result SoA
94
+ struct_handle field_handle callable_handle attribute_handle member_handle type
95
+ EventError Subscription
96
+ ]).freeze
97
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class Lexer
5
+ # Identifier lexing plus the raw-byte character-classification helpers.
6
+ module CharacterClasses
7
+ private
8
+
9
+ def lex_identifier(line, index, line_number, line_offset:)
10
+ start = index
11
+ length = line.length
12
+ index += 1
13
+ while index < length && (byte = line.getbyte(index)) && IDENT_PART_BYTE[byte]
14
+ index += 1
15
+ end
16
+
17
+ lexeme = line[start...index]
18
+ type = Token::KEYWORDS.fetch(lexeme, :identifier)
19
+ literal = case type
20
+ when :true then true
21
+ when :false then false
22
+ when :null then nil
23
+ else nil
24
+ end
25
+
26
+ @tokens << token(type, lexeme, literal, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index)
27
+ index
28
+ end
29
+
30
+ def leading_space_count(line)
31
+ index = 0
32
+ index += 1 while line.getbyte(index) == SPACE_BYTE
33
+ index
34
+ end
35
+
36
+ def identifier_start?(char)
37
+ byte = char && char.getbyte(0)
38
+ byte ? IDENT_START_BYTE[byte] : false
39
+ end
40
+
41
+ def identifier_part?(char)
42
+ byte = char && char.getbyte(0)
43
+ byte ? IDENT_PART_BYTE[byte] : false
44
+ end
45
+
46
+ def identifier_start_token(line, start_index)
47
+ return "" unless start_index < line.length && identifier_start?(line[start_index])
48
+
49
+ finish = start_index + 1
50
+ finish += 1 while finish < line.length && identifier_part?(line[finish])
51
+ line[start_index...finish]
52
+ end
53
+
54
+ def digit?(char)
55
+ byte = char && char.getbyte(0)
56
+ byte ? DIGIT_BYTE[byte] : false
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class Lexer
5
+ # Format string lexing (`f"...#{expr}..."`) and the interpolation
6
+ # scanning / format-spec splitting shared with format heredocs.
7
+ module FormatStrings
8
+ private
9
+
10
+ def parse_format_heredoc_parts(content, start_line:, start_column:)
11
+ parts = []
12
+ text = +""
13
+ index = 0
14
+ line = start_line
15
+ column = start_column
16
+ base_column = start_column
17
+
18
+ while index < content.length
19
+ char = content[index]
20
+ if char == "#" && content[index + 1] == "{"
21
+ parts << { kind: :text, value: text } unless text.empty?
22
+ text = +""
23
+
24
+ expr_start = index + 2
25
+ expr_line = line
26
+ expr_column = column + 2
27
+ expr_end = scan_format_interpolation_end(content, expr_start, expr_line, expr_column, recover: @recovery_errors)
28
+ raw_source = content[expr_start...expr_end]
29
+ if raw_source.strip.empty?
30
+ raise LexError.new("empty format interpolation", line: line, column:, path: @path)
31
+ end
32
+
33
+ source, format_spec = split_format_interpolation_source(raw_source)
34
+ parts << { kind: :expr, source:, format_spec:, line: expr_line, column: expr_column }
35
+
36
+ while index <= expr_end
37
+ line, column = advance_heredoc_position(char: content[index], line:, column:, base_column:)
38
+ index += 1
39
+ end
40
+ next
41
+ end
42
+
43
+ text << char
44
+ line, column = advance_heredoc_position(char:, line:, column:, base_column:)
45
+ index += 1
46
+ end
47
+
48
+ parts << { kind: :text, value: text } unless text.empty?
49
+ parts
50
+ end
51
+
52
+ def advance_heredoc_position(char:, line:, column:, base_column:)
53
+ if char == "\n"
54
+ [line + 1, base_column]
55
+ else
56
+ [line, column + 1]
57
+ end
58
+ end
59
+
60
+ def lex_format_string(line, index, line_number, line_offset:)
61
+ start = index
62
+ index += 2
63
+ text = +""
64
+ parts = []
65
+
66
+ while index < line.length
67
+ char = line[index]
68
+ if char == '"'
69
+ parts << { kind: :text, value: text } unless text.empty?
70
+ lexeme = line[start..index]
71
+ @tokens << token(:fstring, lexeme, parts, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index + 1)
72
+ return index + 1
73
+ end
74
+
75
+ if char == "#" && line[index + 1] == "{"
76
+ parts << { kind: :text, value: text } unless text.empty?
77
+ text = +""
78
+ expr_start = index + 2
79
+ expr_end = scan_format_interpolation_end(line, expr_start, line_number, start + 1, recover: @recovery_errors)
80
+ raw_source = line[expr_start...expr_end]
81
+ if raw_source.strip.empty?
82
+ raise LexError.new("empty format interpolation", line: line_number, column: index + 1, path: @path)
83
+ end
84
+
85
+ source, format_spec = split_format_interpolation_source(raw_source)
86
+ parts << { kind: :expr, source:, format_spec:, line: line_number, column: expr_start + 1 }
87
+ index = expr_end + 1
88
+ next
89
+ end
90
+
91
+ if char == "\\"
92
+ next_char = line[index + 1]
93
+ raise LexError.new("unterminated format string literal", line: line_number, column: start + 1, path: @path) unless next_char
94
+
95
+ text << decode_escape(next_char)
96
+ index += 2
97
+ next
98
+ end
99
+
100
+ text << char
101
+ index += 1
102
+ end
103
+
104
+ if @recovery_errors
105
+ @recovery_errors << LexError.new("unterminated format string literal", line: line_number, column: start + 1, path: @path)
106
+ parts << { kind: :text, value: text } unless text.empty?
107
+ lexeme = line[start..]
108
+ @tokens << token(:fstring, lexeme, parts, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + line.length)
109
+ return line.length
110
+ end
111
+
112
+ raise LexError.new("unterminated format string literal", line: line_number, column: start + 1, path: @path)
113
+ end
114
+
115
+ def scan_format_interpolation_end(line, index, line_number, column, recover: false)
116
+ depth = 1
117
+
118
+ while index < line.length
119
+ char = line[index]
120
+
121
+ if char == '"'
122
+ index = skip_string_contents(line, index, line_number)
123
+ next
124
+ end
125
+
126
+ if char == "{"
127
+ depth += 1
128
+ index += 1
129
+ next
130
+ end
131
+
132
+ if char == "}"
133
+ depth -= 1
134
+ return index if depth == 0
135
+
136
+ index += 1
137
+ next
138
+ end
139
+
140
+ index += 1
141
+ end
142
+
143
+ if recover
144
+ @recovery_errors << LexError.new("unterminated format interpolation", line: line_number, column:, path: @path) if @recovery_errors
145
+ return line.length
146
+ end
147
+
148
+ raise LexError.new("unterminated format interpolation", line: line_number, column:, path: @path)
149
+ end
150
+
151
+ def skip_string_contents(line, index, line_number)
152
+ index += 1
153
+
154
+ while index < line.length
155
+ char = line[index]
156
+ return index + 1 if char == '"'
157
+
158
+ if char == "\\"
159
+ raise LexError.new("unterminated string literal", line: line_number, column: index + 1, path: @path) unless line[index + 1]
160
+
161
+ index += 2
162
+ next
163
+ end
164
+
165
+ index += 1
166
+ end
167
+
168
+ raise LexError.new("unterminated string literal", line: line_number, column: index + 1, path: @path)
169
+ end
170
+
171
+ def split_format_interpolation_source(source)
172
+ depth = 0
173
+ format_spec_index = nil
174
+ index = 0
175
+
176
+ while index < source.length
177
+ char = source[index]
178
+
179
+ if char == '"'
180
+ index = skip_interpolation_string_contents(source, index)
181
+ next
182
+ end
183
+
184
+ case char
185
+ when "(", "[", "{"
186
+ depth += 1
187
+ when ")", "]", "}"
188
+ depth -= 1 if depth.positive?
189
+ when ":"
190
+ suffix = source[(index + 1)..]
191
+ format_spec_index = index if depth.zero? && format_spec_suffix?(suffix)
192
+ end
193
+
194
+ index += 1
195
+ end
196
+
197
+ return [source, nil] unless format_spec_index
198
+
199
+ [source[0...format_spec_index], source[(format_spec_index + 1)..]]
200
+ end
201
+
202
+ def skip_interpolation_string_contents(source, index)
203
+ index += 1
204
+
205
+ while index < source.length
206
+ char = source[index]
207
+ return index + 1 if char == '"'
208
+
209
+ if char == "\\"
210
+ index += 2
211
+ next
212
+ end
213
+
214
+ index += 1
215
+ end
216
+
217
+ source.length
218
+ end
219
+
220
+ def format_spec_suffix?(source)
221
+ source && source.strip.match?(/\A(?:\.\d+|[xXoObB])\z/)
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class Lexer
5
+ # Heredoc lexing (`<<-TAG`, `c<<-TAG`, `f<<-TAG`) including content
6
+ # dedenting, terminator detection, and recovery.
7
+ module Heredocs
8
+ private
9
+
10
+ def lex_heredoc(lines, line_index, index, line_number, line_offset, cstring:, format: false)
11
+ line = lines.fetch(line_index).delete_suffix("\n").b
12
+ prefix_length = heredoc_prefix(cstring:, format:).length
13
+ tag_start = index + prefix_length
14
+ tag_end = tag_start
15
+ tag_end += 1 while tag_end < line.length && identifier_part?(line[tag_end])
16
+ tag = line[tag_start...tag_end]
17
+ remainder = line[tag_end..] || ""
18
+
19
+ unless heredoc_context_allowed?
20
+ return lex_symbol(line, index, line_number, line_offset:)
21
+ end
22
+
23
+ unless remainder.strip.empty?
24
+ raise LexError.new("unexpected characters after heredoc tag", line: line_number, column: tag_end + 1, path: @path)
25
+ end
26
+
27
+ content_lines = []
28
+ terminator_line = nil
29
+ terminator_line_number = nil
30
+ terminator_line_offset = nil
31
+ terminator_has_newline = false
32
+ last_line = line
33
+ last_line_number = line_number
34
+ last_line_offset = line_offset
35
+ last_line_has_newline = lines.fetch(line_index).end_with?("\n")
36
+ resync_line_offset = nil
37
+ resync_line_number = nil
38
+ scan_line_number = line_number + 1
39
+ scan_line_offset = line_offset + lines.fetch(line_index).bytesize
40
+ scan_line_index = line_index + 1
41
+
42
+ content_min_indent = nil
43
+
44
+ while scan_line_index < lines.length
45
+ raw_line = lines.fetch(scan_line_index)
46
+ raw_text = raw_line.delete_suffix("\n")
47
+ if heredoc_terminator?(raw_text, tag)
48
+ terminator_line = raw_text
49
+ terminator_line_number = scan_line_number
50
+ terminator_line_offset = scan_line_offset
51
+ terminator_has_newline = raw_line.end_with?("\n")
52
+ break
53
+ end
54
+
55
+ if @recovery_errors && content_min_indent&.positive? && top_level_resync_line?(raw_text)
56
+ resync_line_offset = scan_line_offset
57
+ resync_line_number = scan_line_number
58
+ break
59
+ end
60
+
61
+ content_lines << raw_line
62
+ last_line = raw_text
63
+ last_line_number = scan_line_number
64
+ last_line_offset = scan_line_offset
65
+ last_line_has_newline = raw_line.end_with?("\n")
66
+ scan_line_offset += raw_line.bytesize
67
+ scan_line_number += 1
68
+ scan_line_index += 1
69
+
70
+ unless raw_text.strip.empty?
71
+ line_indent = leading_space_count(raw_text)
72
+ content_min_indent = line_indent if content_min_indent.nil? || line_indent < content_min_indent
73
+ end
74
+ end
75
+
76
+ if terminator_line.nil? && @recovery_errors && resync_line_number.nil? && scan_line_index < lines.length
77
+ trailing_text = lines.fetch(scan_line_index).delete_suffix("\n")
78
+ if top_level_resync_line?(trailing_text)
79
+ resync_line_offset = scan_line_offset
80
+ resync_line_number = scan_line_number
81
+ end
82
+ end
83
+
84
+ if terminator_line.nil?
85
+ if @recovery_errors
86
+ @recovery_errors << LexError.new("unterminated heredoc literal", line: line_number, column: index + 1, path: @path)
87
+ start_offset = line_offset + index
88
+ end_offset = resync_line_offset || scan_line_offset
89
+ lexeme = @source.byteslice(start_offset, end_offset - start_offset)
90
+ value = dedent_heredoc_content(content_lines)
91
+ content_margin = heredoc_content_margin(content_lines)
92
+
93
+ literal = if format
94
+ parse_format_heredoc_parts(value, start_line: line_number + 1, start_column: content_margin + 1)
95
+ else
96
+ value
97
+ end
98
+ token_type = if format
99
+ :fstring
100
+ elsif cstring
101
+ :cstring
102
+ else
103
+ :string
104
+ end
105
+
106
+ @tokens << token(token_type, lexeme, literal, line_number, index + 1, start_offset:, end_offset:)
107
+ emit_line_newline(last_line, last_line_number, last_line_offset, last_line_has_newline)
108
+ return resync_line_number ? (resync_line_number - line_number) : (scan_line_index - line_index)
109
+ end
110
+
111
+ raise LexError.new("unterminated heredoc literal", line: line_number, column: index + 1, path: @path)
112
+ end
113
+
114
+ start_offset = line_offset + index
115
+ end_offset = terminator_line_offset + terminator_line.bytesize
116
+ lexeme = @source.byteslice(start_offset, end_offset - start_offset)
117
+ value = dedent_heredoc_content(content_lines)
118
+ content_margin = heredoc_content_margin(content_lines)
119
+
120
+ literal = if format
121
+ parse_format_heredoc_parts(value, start_line: line_number + 1, start_column: content_margin + 1)
122
+ else
123
+ value
124
+ end
125
+ token_type = if format
126
+ :fstring
127
+ elsif cstring
128
+ :cstring
129
+ else
130
+ :string
131
+ end
132
+
133
+ @tokens << token(token_type, lexeme, literal, line_number, index + 1, start_offset:, end_offset:)
134
+ emit_line_newline(terminator_line, terminator_line_number, terminator_line_offset, terminator_has_newline)
135
+
136
+ (scan_line_index - line_index) + 1
137
+ end
138
+
139
+ def heredoc_start?(line, index, cstring: false, format: false)
140
+ prefix = heredoc_prefix(cstring:, format:)
141
+ line[index, prefix.length] == prefix && identifier_start?(line[index + prefix.length])
142
+ end
143
+
144
+ def heredoc_prefix(cstring: false, format: false)
145
+ operator = "<<-"
146
+ if cstring
147
+ "c#{operator}"
148
+ elsif format
149
+ "f#{operator}"
150
+ else
151
+ operator
152
+ end
153
+ end
154
+
155
+ def heredoc_context_allowed?
156
+ allowed = %i[
157
+ newline indent dedent
158
+ equal plus_equal minus_equal star_equal slash_equal percent_equal
159
+ amp_equal pipe_equal caret_equal shift_left_equal shift_right_equal
160
+ lparen lbracket comma colon
161
+ return defer if else while match in
162
+ or and not out inout
163
+ plus minus star slash percent amp pipe caret shift_left shift_right
164
+ less less_equal greater greater_equal equal_equal bang_equal
165
+ ]
166
+ previous_type = @tokens.last&.type
167
+ previous_type.nil? || allowed.include?(previous_type)
168
+ end
169
+
170
+ def heredoc_terminator?(line, tag)
171
+ line.match?(Regexp.new("\\A *#{Regexp.escape(tag)} *\\z"))
172
+ end
173
+
174
+ def heredoc_content_margin(raw_lines)
175
+ raw_lines
176
+ .map { |raw_line| raw_line.delete_suffix("\n") }
177
+ .reject { |text| text.strip.empty? }
178
+ .map { |text| leading_space_count(text) }
179
+ .min || 0
180
+ end
181
+
182
+ def dedent_heredoc_content(raw_lines)
183
+ dedent_heredoc_lines(raw_lines).each_with_index.map do |text, index|
184
+ text + (raw_lines[index].end_with?("\n") ? "\n" : "")
185
+ end.join
186
+ end
187
+
188
+ def dedent_heredoc_lines(raw_lines)
189
+ margin = heredoc_content_margin(raw_lines)
190
+
191
+ raw_lines.map do |raw_line|
192
+ text = raw_line.delete_suffix("\n")
193
+ text = text.strip.empty? ? "" : text[margin..]
194
+ text || ""
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end