mt-lang 0.3.16 → 0.3.17

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5cd3c1ef5e380b8d0c5d4e20f829400d01c6fdfd3e495e8c98050f4af8b59ba
4
- data.tar.gz: 0e083d1780c9d591b996604a8b489726fa0f0c6fb85b8dc03da87b681383d5ca
3
+ metadata.gz: e2f84289b1bcb599ca81cbada0b1e1a925d9962ffe186471b84026f372fb358b
4
+ data.tar.gz: 566b98b015518f583e31fa2f0a75753c3b0abfd5a4047210945b3209c3caf1f3
5
5
  SHA512:
6
- metadata.gz: 44f866e7d6bf6fc4998e0a9054bf898b27ed47ef38149de1881baf1d810e845b94e3d1f3ff304486654ea339329d726a10d969bcff4021943cc98b03bfe3fe01
7
- data.tar.gz: 0fa7237c14aa9e038b4d51ae08a5971430d0e47b97fb19caf619aeb80a5e562de99aae1cf342a11ffea41e3b2268a128454bbf022b3e9182ddd94ff2ff0aff45
6
+ metadata.gz: ee114072cb26ed2527e0b3703cb7f8a913e37234e6937b3a307ce72363e8a6e10a34e0a149d508a8c02e64c1a0a0f5e9b1ae482b76f068bfb6bf0b8784f34a44
7
+ data.tar.gz: 9b29c88530cf6110381123ef8135434caa47aa85abf955a8bd9f25d93fa1b8059691d52804c17e4080a2a3d0ad916aa689470ec8d7719a7186fa62eddb026525
@@ -521,7 +521,6 @@ The type system must stay simple, explicit, and close to C.
521
521
  ### Primitive types
522
522
 
523
523
  - `bool`
524
- - `byte`
525
524
  - `char`
526
525
  - `byte`, `short`, `int`, `long`
527
526
  - `ubyte`, `ushort`, `uint`, `ulong`
@@ -587,7 +586,7 @@ Notes:
587
586
  - If low-level code needs to validate raw `array[char, N]` storage as text, that conversion belongs in an explicit helper or imported boundary, not as a built-in method family on raw arrays.
588
587
  - Addressable `str_buffer[N]` values also coerce to `span[char]`, so writable foreign text APIs can still accept builders directly when they do not want a second application-facing text abstraction.
589
588
  - `str_buffer[N]` is not an ABI type. Raw bindings still spell writable text as `ptr[char]` or `span[char]`; `str_buffer[N]` is the caller-side text object.
590
- - `str_buffer[N]` has a built-in text surface: `.clear()`, `.assign(str)`, `.append(str)`, `.len()`, `.capacity()`, `.as_str()`, and `.as_cstr()`.
589
+ - `str_buffer[N]` has a built-in text surface: `.clear()`, `.assign(str)`, `.append(str)`, `.assign_format(str)`, `.append_format(str)`, `.len()`, `.capacity()`, `.as_str()`, and `.as_cstr()`.
591
590
  - `.assign(...)` replaces the current contents and traps at runtime if the new text exceeds capacity.
592
591
  - `.append(...)` extends the current contents and traps at runtime if the appended text would exceed capacity.
593
592
  - `.len()` returns the tracked text length, revalidating UTF-8 and rescanning for the trailing NUL if the builder was passed through a writable `span[char]` or `ptr[char]` alias.
@@ -650,7 +649,7 @@ joins the path with underscores (e.g., `module_Rectangle_Edge`).
650
649
 
651
650
  #### Enums
652
651
 
653
- Enums always have an explicit backing type.
652
+ Enums use an integer backing type, defaulting to `int` when omitted; members auto-increment from 0 or the previous explicit value.
654
653
 
655
654
  ```mt
656
655
  enum WeaponKind: ubyte
@@ -661,7 +660,7 @@ enum WeaponKind: ubyte
661
660
 
662
661
  #### Flags
663
662
 
664
- Flags are named bitmasks with a fixed integer backing type.
663
+ Flags are named bitmasks with an integer backing type (defaulting to `int` when omitted).
665
664
 
666
665
  ```mt
667
666
  flags WindowFlags: uint
@@ -1007,7 +1007,7 @@ Rules:
1007
1007
  - `ivec2` `ivec3` `ivec4` — integer vectors with `.x` `.y` `.z` `.w` fields
1008
1008
  - `mat3` `mat4` — column-major matrices; `mat3` has columns `.col0`–`.col2` (each `vec3`), `mat4` has `.col0`–`.col3` (each `vec4`)
1009
1009
  - `quat` — quaternion with `.x` `.y` `.z` `.w` fields; memory-layout compatible with `vec4`
1010
- - `simd[T, N]` — SIMD vector with `N` lanes of numeric primitive type `T`; total width must be 128 or 256 bits. Supports component-wise arithmetic (`+` `-` `*` `/`), bitwise (`&` `|` `^` `~`), shift (`<<` `>>`), and compound assignment. Lane access via compile-time constant index `[i]`. Lowers to GCC/Clang `__attribute__((__vector_size__))`.
1010
+ - `simd[T, N]` — SIMD vector with `N` lanes of numeric primitive type `T`; total width must be 128 or 256 bits. Supports component-wise arithmetic (`+` `-` `*` `/`, and `%` for integer lanes), bitwise (`&` `|` `^` `~`), shift (`<<` `>>`), and compound assignment. Lane access via compile-time constant index `[i]`. Lowers to GCC/Clang `__attribute__((__vector_size__))`.
1011
1011
 
1012
1012
  Primitive type names are reserved. They cannot be reused for value bindings, parameters, locals, import aliases, or type parameters.
1013
1013
 
@@ -1043,7 +1043,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
1043
1043
  - `Option[T]`
1044
1044
  - `Result[T, E]`
1045
1045
  - `SoA[T, N]` — Structure-of-Arrays: transforms `T`'s fields into separate arrays of length `N`; access as `soa[i].field`
1046
- - `simd[T, N]` — SIMD vector: fixed-width vector of `N` numeric lanes. Constructed with `simd[T, N](lane0, lane1, ...)`. Supports component-wise `+` `-` `*` `/`, bitwise `&` `|` `^` `~`, shift `<<` `>>`, unary `-` `~`, and compound assignment. Lane access via compile-time constant `[i]`. Total width must be 128 or 256 bits. Lowers to GCC/Clang vector extensions.
1046
+ - `simd[T, N]` — SIMD vector: fixed-width vector of `N` numeric lanes. Constructed with `simd[T, N](lane0, lane1, ...)`. Supports component-wise `+` `-` `*` `/` (and `%` for integer lanes), bitwise `&` `|` `^` `~`, shift `<<` `>>`, unary `-` `~`, and compound assignment. Lane access via compile-time constant `[i]`. Total width must be 128 or 256 bits. Lowers to GCC/Clang vector extensions.
1047
1047
  - `dyn[InterfaceName]` — runtime interface value (fat pointer: data + vtable). Constructed via `adapt[Interface](value: ref[T])`. @see §3.5.
1048
1048
  - `atomic[T]` — atomic value for lock-free concurrent access. `T` must be a primitive integer or `bool`. Methods: `load() -> T`, `store(value: T)`, `add(value: T) -> T`, `sub(value: T) -> T`, `exchange(value: T) -> T`. All operations use sequential consistency. Lowers to C11 `_Atomic T` with `__atomic_*` builtins. `atomic[T]` is zero-initializable and sendable.
1049
1049
  - `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
@@ -1054,7 +1054,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
1054
1054
  - for pointer-like bases (`ptr[T]`, `const_ptr[T]`, `own[T]`, `cstr`, `fn(...)`, `proc(...)`, opaque), `T?` is a nullable pointer with `null` as the absent value
1055
1055
  - for non-pointer value bases (`int`, `bool`, `float`, structs, ...), `T?` is stored inline by value as a tagged optional (a presence flag plus the value); it copies by value with no hidden heap allocation or pointer aliasing
1056
1056
  - `null` expresses absence in any nullable context; the explicit typed `null[...]` form's target must be pointer-like
1057
- - in nullable pointer-like contexts, use `null` instead of `zero[ptr[T]]`
1057
+ - in nullable pointer-like contexts, `zero[ptr[T]]` is rejected; use `null` instead
1058
1058
  - at an FFI boundary (`external` / `foreign function` parameters and returns), only pointer-like `T?` is allowed; a non-pointer value nullable such as `int?` is rejected — use `ptr[T]?` or pass an explicit struct
1059
1059
 
1060
1060
  ### 6.4 Generics
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.3.16"
6
+ VERSION = "0.3.17"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -15,4 +15,4 @@ module MilkTea
15
15
  def each(&block) = @bindings.each(&block)
16
16
  def each_with_object(init, &block) = @bindings.each_with_object(init, &block)
17
17
  end
18
- end
18
+ end
@@ -38,6 +38,20 @@ module MilkTea
38
38
  # scanning a 600 KB source file) never trigger a false positive, yet low
39
39
  # enough that a genuinely infinite loop fires a clear fatal within ~1 s.
40
40
  LOOP_GUARD_MAX_ITERATIONS = 50_000_000
41
+ EQUALITY_OPERATORS = ["==", "!="].freeze
42
+
43
+ include AggregateUtils
44
+ include TypeSystem
45
+ include Reachability
46
+ include FeatureDetection
47
+ include TypeCollectors
48
+ include Statements
49
+ include ControlFlowEmission
50
+ include RuntimeHelpers
51
+ include FormatHelpers
52
+ include TypeDeclaration
53
+ include Expressions
54
+ include Reinterpret
41
55
 
42
56
  def self.emit(program, emit_line_directives: true, debug_guards: false)
43
57
  new(program, emit_line_directives:, debug_guards:).emit
@@ -294,20 +308,5 @@ module MilkTea
294
308
 
295
309
  lines.join("\n").rstrip + "\n"
296
310
  end
297
-
298
- EQUALITY_OPERATORS = ["==", "!="].freeze
299
-
300
- include AggregateUtils
301
- include TypeSystem
302
- include Reachability
303
- include FeatureDetection
304
- include TypeCollectors
305
- include Statements
306
- include ControlFlowEmission
307
- include RuntimeHelpers
308
- include FormatHelpers
309
- include TypeDeclaration
310
- include Expressions
311
- include Reinterpret
312
311
  end
313
312
  end
@@ -1,6 +1,185 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "compile_time/const_eval"
3
+ require_relative "types/layout"
4
+
5
+ module MilkTea
6
+ module ConstEval
7
+
8
+ def self.evaluate(expression, resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
9
+ Evaluator.new(
10
+ resolve_identifier:,
11
+ resolve_member_access:,
12
+ resolve_type_ref:,
13
+ resolve_call:,
14
+ ).evaluate(expression)
15
+ end
16
+
17
+ def self.equality_result(left, right)
18
+ return left == right if left.is_a?(Numeric) && right.is_a?(Numeric)
19
+ return left == right if left.is_a?(String) && right.is_a?(String)
20
+ return left == right if boolean_value?(left) && boolean_value?(right)
21
+ return left == right if left.is_a?(Types::Base) && right.is_a?(Types::Base)
22
+
23
+ nil
24
+ end
25
+
26
+ def self.boolean_value?(value)
27
+ value == true || value == false
28
+ end
29
+
30
+ class Evaluator
31
+ def initialize(resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
32
+ @resolve_identifier = resolve_identifier
33
+ @resolve_member_access = resolve_member_access
34
+ @resolve_type_ref = resolve_type_ref
35
+ @resolve_call = resolve_call
36
+ end
37
+
38
+ def evaluate(expression)
39
+ case expression
40
+ when AST::ErrorExpr
41
+ nil
42
+ when AST::ExpressionList
43
+ expression.elements.filter_map { |element| evaluate(element) }
44
+ when AST::IntegerLiteral, AST::FloatLiteral, AST::BooleanLiteral
45
+ expression.value
46
+ when AST::StringLiteral
47
+ expression.value
48
+ when AST::Identifier
49
+ @resolve_identifier&.call(expression)
50
+ when AST::MemberAccess
51
+ @resolve_member_access&.call(expression)
52
+ when AST::Call
53
+ @resolve_call&.call(expression)
54
+ when AST::Specialization
55
+ @resolve_call&.call(expression)
56
+ when AST::SizeofExpr
57
+ type = resolve_layout_type(expression.type)
58
+ type && Layout.size_of(type)
59
+ when AST::AlignofExpr
60
+ type = resolve_layout_type(expression.type)
61
+ type && Layout.alignment_of(type)
62
+ when AST::OffsetofExpr
63
+ type = resolve_layout_type(expression.type)
64
+ if type
65
+ result = Layout.offset_of(type, expression.field)
66
+ return result if result
67
+
68
+ id_expr = AST::Identifier.new(name: expression.field)
69
+ value = @resolve_identifier&.call(id_expr)
70
+ if value.is_a?(Types::FieldHandle)
71
+ return Layout.offset_of(type, value.field_name)
72
+ end
73
+ end
74
+ nil
75
+ when AST::UnaryOp
76
+ evaluate_unary(expression)
77
+ when AST::BinaryOp
78
+ evaluate_binary(expression)
79
+ when AST::IfExpr
80
+ condition = evaluate(expression.condition)
81
+ return unless ConstEval.boolean_value?(condition)
82
+
83
+ evaluate(condition ? expression.then_expression : expression.else_expression)
84
+ else
85
+ nil
86
+ end
87
+ end
88
+
89
+ def resolve_layout_type(type_ref)
90
+ return unless @resolve_type_ref
91
+
92
+ @resolve_type_ref.call(type_ref)
93
+ end
94
+
95
+ def evaluate_unary(expression)
96
+ operand = evaluate(expression.operand)
97
+
98
+ case expression.operator
99
+ when "+"
100
+ operand.is_a?(Numeric) ? operand : nil
101
+ when "-"
102
+ operand.is_a?(Numeric) ? -operand : nil
103
+ when "~"
104
+ operand.is_a?(Integer) ? ~operand : nil
105
+ when "not"
106
+ ConstEval.boolean_value?(operand) ? !operand : nil
107
+ end
108
+ end
109
+
110
+ def evaluate_binary(expression)
111
+ left = evaluate(expression.left)
112
+
113
+ case expression.operator
114
+ when "and"
115
+ return unless ConstEval.boolean_value?(left)
116
+ return false if left == false
117
+
118
+ right = evaluate(expression.right)
119
+ return right if ConstEval.boolean_value?(right)
120
+
121
+ return nil
122
+ when "or"
123
+ return unless ConstEval.boolean_value?(left)
124
+ return true if left == true
125
+
126
+ right = evaluate(expression.right)
127
+ return right if ConstEval.boolean_value?(right)
128
+
129
+ return nil
130
+ end
131
+
132
+ right = evaluate(expression.right)
133
+
134
+ case expression.operator
135
+ when "=="
136
+ ConstEval.equality_result(left, right)
137
+ when "!="
138
+ result = ConstEval.equality_result(left, right)
139
+ result.nil? ? nil : !result
140
+ when "+"
141
+ left.is_a?(Numeric) && right.is_a?(Numeric) ? left + right : nil
142
+ when "-"
143
+ left.is_a?(Numeric) && right.is_a?(Numeric) ? left - right : nil
144
+ when "*"
145
+ left.is_a?(Numeric) && right.is_a?(Numeric) ? left * right : nil
146
+ when "/"
147
+ return unless left.is_a?(Numeric) && right.is_a?(Numeric)
148
+ raise CompileTime::Error, "division by zero" if zero_numeric?(right)
149
+
150
+ left / right
151
+ when "%"
152
+ return unless left.is_a?(Integer) && right.is_a?(Integer)
153
+ raise CompileTime::Error, "modulo by zero" if right.zero?
154
+
155
+ left % right
156
+ when "<<"
157
+ left.is_a?(Integer) && right.is_a?(Integer) ? left << right : nil
158
+ when ">>"
159
+ left.is_a?(Integer) && right.is_a?(Integer) ? left >> right : nil
160
+ when "&"
161
+ left.is_a?(Integer) && right.is_a?(Integer) ? left & right : nil
162
+ when "|"
163
+ left.is_a?(Integer) && right.is_a?(Integer) ? left | right : nil
164
+ when "^"
165
+ left.is_a?(Integer) && right.is_a?(Integer) ? left ^ right : nil
166
+ when "<"
167
+ left.is_a?(Numeric) && right.is_a?(Numeric) ? left < right : nil
168
+ when "<="
169
+ left.is_a?(Numeric) && right.is_a?(Numeric) ? left <= right : nil
170
+ when ">"
171
+ left.is_a?(Numeric) && right.is_a?(Numeric) ? left > right : nil
172
+ when ">="
173
+ left.is_a?(Numeric) && right.is_a?(Numeric) ? left >= right : nil
174
+ end
175
+ end
176
+
177
+ def zero_numeric?(value)
178
+ (value.is_a?(Integer) && value.zero?) || (value.is_a?(Float) && value.zero?)
179
+ end
180
+ end
181
+ end
182
+ end
4
183
 
5
184
  module MilkTea
6
185
  module CompileTime
@@ -354,4 +533,4 @@ module MilkTea
354
533
  end
355
534
  end
356
535
  end
357
- end
536
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../compile_time/const_eval"
3
+ require_relative "../compile_time"
4
4
 
5
5
  module MilkTea
6
6
  module ControlFlow
@@ -3,7 +3,7 @@
3
3
  require_relative "types/predicates"
4
4
 
5
5
  module MilkTea
6
- module CompatibilityHelpers
6
+ module Intrinsics
7
7
  include Types::Predicates
8
8
 
9
9
  BUILTIN_CSTR = Types::Registry.primitive("cstr")
@@ -78,4 +78,4 @@ module MilkTea
78
78
  expression.is_a?(AST::StringLiteral) && !expression.cstring && expected_type == BUILTIN_CSTR
79
79
  end
80
80
  end
81
- end
81
+ end
@@ -106,6 +106,16 @@ module MilkTea
106
106
  BIN_DIGIT_BYTE = Array.new(256) { |b| b.chr.match?(/[01_]/) }.freeze
107
107
  SPACE_BYTE = " ".ord
108
108
 
109
+ include CharacterClasses
110
+ include Trivia
111
+ include Indentation
112
+ include Numbers
113
+ include Strings
114
+ include Heredocs
115
+ include FormatStrings
116
+ include Symbols
117
+ include Recovery
118
+
109
119
  def self.lex(source, path: nil, mode: :syntax_only, recovery_errors: nil)
110
120
  result = new(source, path: path, mode:, recovery_errors:).lex
111
121
  mode == :with_trivia ? result.tokens : result
@@ -170,16 +180,6 @@ module MilkTea
170
180
  @tokens
171
181
  end
172
182
 
173
- include CharacterClasses
174
- include Trivia
175
- include Indentation
176
- include Numbers
177
- include Strings
178
- include Heredocs
179
- include FormatStrings
180
- include Symbols
181
- include Recovery
182
-
183
183
  def lex_line(lines, line_index, line, line_number, line_offset, has_newline:)
184
184
  tab_index = line.index("\t")
185
185
  if tab_index
@@ -371,8 +371,6 @@ module MilkTea
371
371
  1
372
372
  end
373
373
 
374
- # ── newline emission ────────────────────────────────────────────
375
-
376
374
  def emit_line_newline(line, line_number, line_offset, has_newline)
377
375
  newline_start = line_offset + line.bytesize
378
376
  newline_end = has_newline ? (newline_start + 1) : newline_start
@@ -393,8 +391,6 @@ module MilkTea
393
391
  end
394
392
  end
395
393
 
396
- # ── token construction ──────────────────────────────────────────
397
-
398
394
  def token(type, lexeme, literal, line, column, start_offset:, end_offset:)
399
395
  Token.new(
400
396
  type:,
@@ -31,7 +31,7 @@ require_relative "lowering/functions"
31
31
  require_relative "lowering/async/analysis"
32
32
  require_relative "lowering/async/normalization"
33
33
  require_relative "lowering/async/lowering"
34
- require_relative "lowering/async"
34
+ require_relative "lowering/async/frame_builder"
35
35
  require_relative "lowering/block"
36
36
  require_relative "lowering/proc"
37
37
  require_relative "lowering/loops"
@@ -85,11 +85,27 @@ module MilkTea
85
85
  OrderResolution = Data.define(:target_type, :binding, :callee_name)
86
86
 
87
87
  class Lowerer
88
- include CompatibilityHelpers
88
+ include Intrinsics
89
89
 
90
90
  attr_accessor :bypass_sema_type_cache
91
91
  attr_reader :recorded_expr_types
92
92
 
93
+ include LowererScans
94
+ include LowererDeclarations
95
+ include LowererEvents
96
+ include LowererFunctions
97
+ include LowererAsync
98
+ include LowererBlock
99
+ include LowererProc
100
+ include LowererLoops
101
+ include LowererExpressions
102
+ include LowererCalls
103
+ include LowererForeignCstr
104
+ include LowererStrBuffer
105
+ include LowererResolve
106
+ include LowererDyn
107
+ include LowererUtils
108
+
93
109
  def initialize(program)
94
110
  @program = program
95
111
  @ctx = ModuleContext.new
@@ -358,21 +374,5 @@ module MilkTea
358
374
  ordered.concat(modules.values - ordered)
359
375
  ordered
360
376
  end
361
-
362
- include LowererScans
363
- include LowererDeclarations
364
- include LowererEvents
365
- include LowererFunctions
366
- include LowererAsync
367
- include LowererBlock
368
- include LowererProc
369
- include LowererLoops
370
- include LowererExpressions
371
- include LowererCalls
372
- include LowererForeignCstr
373
- include LowererStrBuffer
374
- include LowererResolve
375
- include LowererDyn
376
- include LowererUtils
377
377
  end
378
378
  end
@@ -1,13 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "module_loader/errors"
4
- require_relative "module_path_resolver"
5
- require_relative "module_binder"
6
- require_relative "async_runtime_installer"
7
- require_relative "prelude_installer"
8
-
9
3
  module MilkTea
4
+ class ModuleLoadError < StandardError
5
+ attr_reader :path, :line, :column
6
+
7
+ def initialize(message, path:, line: nil, column: nil)
8
+ @path = path
9
+ @line = line
10
+ @column = column
11
+ super("#{message}: #{path}")
12
+ end
13
+
14
+ def code
15
+ "module/error"
16
+ end
17
+ end
18
+
10
19
  class ModuleLoader
20
+ ImportResolution = Data.define(:modules, :errors)
21
+ ImportResolutionError = Data.define(:import, :error)
11
22
  Program = Data.define(:root_path, :root_analysis, :analyses_by_path, :analyses_by_module_name)
12
23
  PLATFORM_SUFFIXES = {
13
24
  "linux" => :linux,
@@ -90,6 +101,15 @@ module MilkTea
90
101
  MilkTea.host_platform
91
102
  end
92
103
 
104
+ def self.raise_platform_conflict!(path, pinned_platform, active_platform, error_class: nil)
105
+ if error_class == ModuleLoadError
106
+ raise ModuleLoadError.new("source file targets platform #{pinned_platform}; active platform is #{active_platform}", path:)
107
+ end
108
+
109
+ message = "source file #{path} targets platform #{pinned_platform}; active platform is #{active_platform}"
110
+ raise(error_class || ArgumentError, message)
111
+ end
112
+
93
113
  def initialize(module_roots: [MilkTea.root], package_graph: nil, shared_cache: nil, source_overrides: nil, platform: nil)
94
114
  @module_roots = module_roots.map { |root| File.expand_path(root.to_s) }
95
115
  @ast_cache = {}
@@ -380,15 +400,6 @@ module MilkTea
380
400
  @collecting_path_errors
381
401
  end
382
402
 
383
- def self.raise_platform_conflict!(path, pinned_platform, active_platform, error_class: nil)
384
- if error_class == ModuleLoadError
385
- raise ModuleLoadError.new("source file targets platform #{pinned_platform}; active platform is #{active_platform}", path:)
386
- end
387
-
388
- message = "source file #{path} targets platform #{pinned_platform}; active platform is #{active_platform}"
389
- raise(error_class || ArgumentError, message)
390
- end
391
-
392
403
  def check_path(path)
393
404
  resolved_path, ast, cached = check_module_cache(path)
394
405
  return cached if cached
@@ -155,6 +155,23 @@ module MilkTea
155
155
  next_token.nil? || %i[newline eof].include?(next_token.type)
156
156
  end
157
157
 
158
+ def advance
159
+ @current += 1 unless eof?
160
+ previous
161
+ end
162
+
163
+ def eof?
164
+ peek.type == :eof
165
+ end
166
+
167
+ def peek
168
+ @tokens[@current]
169
+ end
170
+
171
+ def previous
172
+ @tokens[@current - 1]
173
+ end
174
+
158
175
  def match(*types)
159
176
  return false unless types.any? { |type| check(type) }
160
177
 
@@ -219,23 +236,6 @@ module MilkTea
219
236
  true
220
237
  end
221
238
 
222
- def advance
223
- @current += 1 unless eof?
224
- previous
225
- end
226
-
227
- def eof?
228
- peek.type == :eof
229
- end
230
-
231
- def peek
232
- @tokens[@current]
233
- end
234
-
235
- def previous
236
- @tokens[@current - 1]
237
- end
238
-
239
239
  def error(token, message)
240
240
  ParseError.new(message, token:, path: @path)
241
241
  end
@@ -129,7 +129,7 @@ module MilkTea
129
129
  end
130
130
 
131
131
  class Checker
132
- include CompatibilityHelpers
132
+ include Intrinsics
133
133
 
134
134
  attr_reader :ctx
135
135
 
data/lib/milk_tea/core.rb CHANGED
@@ -10,11 +10,15 @@ require_relative "core/cst"
10
10
  require_relative "core/cst_builder"
11
11
  require_relative "core/ast"
12
12
  require_relative "core/types"
13
- require_relative "core/binding_types"
13
+ require_relative "core/bindings"
14
14
  require_relative "core/compile_time"
15
- require_relative "core/compatibility_helpers"
15
+ require_relative "core/intrinsics"
16
16
  require_relative "core/parser"
17
17
  require_relative "core/module_roots"
18
+ require_relative "core/module_path_resolver"
19
+ require_relative "core/module_binder"
20
+ require_relative "core/async_runtime_installer"
21
+ require_relative "core/prelude_installer"
18
22
  require_relative "core/module_loader"
19
23
  require_relative "core/semantic_analyzer"
20
24
  require_relative "core/ir"
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "thread"
5
+ require "timeout"
6
+
7
+ module MilkTea
8
+ module DAP
9
+ class LLDBDAPBackend
10
+ def initialize(adapter_command: ["lldb-dap"], on_event: nil, on_request: nil)
11
+ @adapter_command = adapter_command
12
+ @on_event = on_event
13
+ @on_request = on_request
14
+ @protocol = nil
15
+ @stdin = nil
16
+ @stdout = nil
17
+ @stderr = nil
18
+ @wait_thread = nil
19
+ @reader_thread = nil
20
+ @stderr_thread = nil
21
+ @write_mutex = Mutex.new
22
+ @pending_mutex = Mutex.new
23
+ @pending = {}
24
+ @next_seq = 1
25
+ end
26
+
27
+ def start!
28
+ return if running?
29
+
30
+ @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(*@adapter_command)
31
+ @protocol = Protocol.new(input: @stdout, output: @stdin)
32
+
33
+ @reader_thread = Thread.new { read_loop }
34
+ @stderr_thread = Thread.new { drain_stderr }
35
+ end
36
+
37
+ def running?
38
+ !@wait_thread.nil? && @wait_thread.alive?
39
+ end
40
+
41
+ def request(command, arguments = {}, timeout: 5)
42
+ start! unless running?
43
+
44
+ seq = next_seq
45
+ queue = Queue.new
46
+ @pending_mutex.synchronize do
47
+ @pending[seq] = queue
48
+ end
49
+
50
+ @write_mutex.synchronize do
51
+ @protocol.write_message({
52
+ seq: seq,
53
+ type: "request",
54
+ command: command,
55
+ arguments: arguments
56
+ })
57
+ end
58
+
59
+ Timeout.timeout(timeout) { queue.pop }
60
+ rescue Timeout::Error
61
+ {
62
+ "type" => "response",
63
+ "request_seq" => seq,
64
+ "success" => false,
65
+ "message" => "backend request timed out: #{command}"
66
+ }
67
+ ensure
68
+ @pending_mutex.synchronize { @pending.delete(seq) }
69
+ end
70
+
71
+ def stop!
72
+ @stdin&.close
73
+ @stdout&.close
74
+ @stderr&.close
75
+ if @wait_thread&.alive?
76
+ Process.kill("TERM", @wait_thread.pid)
77
+ end
78
+ rescue StandardError
79
+ nil
80
+ ensure
81
+ @wait_thread&.join(0.2)
82
+ @reader_thread&.join(0.2)
83
+ @stderr_thread&.join(0.2)
84
+ @stdin = nil
85
+ @stdout = nil
86
+ @stderr = nil
87
+ @wait_thread = nil
88
+ @reader_thread = nil
89
+ @stderr_thread = nil
90
+ end
91
+
92
+ def next_seq
93
+ seq = @next_seq
94
+ @next_seq += 1
95
+ seq
96
+ end
97
+
98
+ def read_loop
99
+ loop do
100
+ message = @protocol.read_message
101
+ break if message.nil?
102
+
103
+ if message["type"] == "response"
104
+ queue = @pending_mutex.synchronize { @pending[message["request_seq"]] }
105
+ queue&.push(message)
106
+ elsif message["type"] == "event"
107
+ @on_event&.call(message)
108
+ elsif message["type"] == "request"
109
+ handle_adapter_request(message)
110
+ end
111
+ end
112
+ rescue StandardError
113
+ nil
114
+ ensure
115
+ @pending_mutex.synchronize do
116
+ @pending.each_value do |queue|
117
+ queue.push({
118
+ "type" => "response",
119
+ "success" => false,
120
+ "message" => "backend closed"
121
+ })
122
+ end
123
+ @pending.clear
124
+ end
125
+ end
126
+
127
+ def drain_stderr
128
+ return unless @stderr
129
+
130
+ @stderr.each_line do |_line|
131
+ # Intentionally ignored in this bridge layer.
132
+ end
133
+ rescue StandardError
134
+ nil
135
+ end
136
+
137
+ def handle_adapter_request(message)
138
+ response = @on_request&.call(message)
139
+ response ||= {
140
+ "type" => "response",
141
+ "request_seq" => message["seq"],
142
+ "success" => false,
143
+ "command" => message["command"],
144
+ "message" => "unsupported reverse request: #{message['command']}"
145
+ }
146
+
147
+ @write_mutex.synchronize do
148
+ @protocol.write_message(response)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -22,7 +22,7 @@ module MilkTea
22
22
  @lldb_backend = if @backend_factory
23
23
  build_backend_via_factory(adapter_command)
24
24
  else
25
- Backends::LLDBDAP.new(
25
+ LLDBDAPBackend.new(
26
26
  adapter_command: adapter_command,
27
27
  on_event: method(:handle_backend_event),
28
28
  on_request: method(:handle_backend_request)
data/lib/milk_tea/dap.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "dap/protocol"
4
- require_relative "dap/backends/lldb_dap"
4
+ require_relative "dap/lldb_dap_backend"
5
5
  require_relative "dap/session"
6
6
  require_relative "dap/server"
data/lib/milk_tea/lsp.rb CHANGED
@@ -1,13 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module MilkTea
4
- module LSP
5
- # LSP implementation - language server protocol support
6
- end
7
- end
8
-
9
3
  require_relative 'lsp/protocol'
10
4
  require_relative 'lsp/dependency_resolution'
11
5
  require_relative 'lsp/workspace'
12
6
  require_relative 'lsp/diagnostics'
13
- require_relative 'lsp/server'
7
+ require_relative 'lsp/server'
@@ -118,4 +118,4 @@ module MilkTea
118
118
  nil
119
119
  end
120
120
  end
121
- end
121
+ end
@@ -18,5 +18,5 @@ require_relative "tooling/linter"
18
18
  require_relative "tooling/docs_app"
19
19
  require_relative "tooling/project_scaffold"
20
20
  require_relative "tooling/toolchain_cli"
21
- require_relative "bindings/cli"
21
+ require_relative "tooling/bindgen_cli"
22
22
  require_relative "tooling/cli"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mt-lang
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.16
4
+ version: 0.3.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Long (Teefan) Tran
@@ -158,7 +158,6 @@ files:
158
158
  - lib/milk_tea/bindings/bindgen/emitter.rb
159
159
  - lib/milk_tea/bindings/bindgen/overrides.rb
160
160
  - lib/milk_tea/bindings/bindgen/type_mapper.rb
161
- - lib/milk_tea/bindings/cli.rb
162
161
  - lib/milk_tea/bindings/imported_bindings.rb
163
162
  - lib/milk_tea/bindings/imported_bindings/defaults.rb
164
163
  - lib/milk_tea/bindings/imported_bindings/generator.rb
@@ -185,7 +184,7 @@ files:
185
184
  - lib/milk_tea/core.rb
186
185
  - lib/milk_tea/core/ast.rb
187
186
  - lib/milk_tea/core/async_runtime_installer.rb
188
- - lib/milk_tea/core/binding_types.rb
187
+ - lib/milk_tea/core/bindings.rb
189
188
  - lib/milk_tea/core/bindings/attribute_binding.rb
190
189
  - lib/milk_tea/core/bindings/function_binding.rb
191
190
  - lib/milk_tea/core/bindings/module_binding.rb
@@ -203,9 +202,7 @@ files:
203
202
  - lib/milk_tea/core/c_backend/type_collectors.rb
204
203
  - lib/milk_tea/core/c_backend/type_declaration.rb
205
204
  - lib/milk_tea/core/c_backend/type_system.rb
206
- - lib/milk_tea/core/compatibility_helpers.rb
207
205
  - lib/milk_tea/core/compile_time.rb
208
- - lib/milk_tea/core/compile_time/const_eval.rb
209
206
  - lib/milk_tea/core/control_flow.rb
210
207
  - lib/milk_tea/core/control_flow/builder.rb
211
208
  - lib/milk_tea/core/control_flow/constant_propagation.rb
@@ -218,6 +215,7 @@ files:
218
215
  - lib/milk_tea/core/control_flow/termination.rb
219
216
  - lib/milk_tea/core/cst.rb
220
217
  - lib/milk_tea/core/cst_builder.rb
218
+ - lib/milk_tea/core/intrinsics.rb
221
219
  - lib/milk_tea/core/ir.rb
222
220
  - lib/milk_tea/core/keywords.rb
223
221
  - lib/milk_tea/core/lexer.rb
@@ -232,8 +230,8 @@ files:
232
230
  - lib/milk_tea/core/lexer/trivia.rb
233
231
  - lib/milk_tea/core/lowering.rb
234
232
  - lib/milk_tea/core/lowering/artifacts.rb
235
- - lib/milk_tea/core/lowering/async.rb
236
233
  - lib/milk_tea/core/lowering/async/analysis.rb
234
+ - lib/milk_tea/core/lowering/async/frame_builder.rb
237
235
  - lib/milk_tea/core/lowering/async/lowering.rb
238
236
  - lib/milk_tea/core/lowering/async/normalization.rb
239
237
  - lib/milk_tea/core/lowering/block.rb
@@ -253,7 +251,6 @@ files:
253
251
  - lib/milk_tea/core/lowering/utils.rb
254
252
  - lib/milk_tea/core/module_binder.rb
255
253
  - lib/milk_tea/core/module_loader.rb
256
- - lib/milk_tea/core/module_loader/errors.rb
257
254
  - lib/milk_tea/core/module_path_resolver.rb
258
255
  - lib/milk_tea/core/module_roots.rb
259
256
  - lib/milk_tea/core/parser.rb
@@ -294,7 +291,7 @@ files:
294
291
  - lib/milk_tea/core/types/registry.rb
295
292
  - lib/milk_tea/core/types/visitor.rb
296
293
  - lib/milk_tea/dap.rb
297
- - lib/milk_tea/dap/backends/lldb_dap.rb
294
+ - lib/milk_tea/dap/lldb_dap_backend.rb
298
295
  - lib/milk_tea/dap/protocol.rb
299
296
  - lib/milk_tea/dap/server.rb
300
297
  - lib/milk_tea/dap/server/breakpoints.rb
@@ -361,6 +358,7 @@ files:
361
358
  - lib/milk_tea/packages/version.rb
362
359
  - lib/milk_tea/tooling.rb
363
360
  - lib/milk_tea/tooling/asset_pack.rb
361
+ - lib/milk_tea/tooling/bindgen_cli.rb
364
362
  - lib/milk_tea/tooling/build.rb
365
363
  - lib/milk_tea/tooling/build_cache.rb
366
364
  - lib/milk_tea/tooling/cli.rb
@@ -605,7 +603,7 @@ metadata:
605
603
  homepage_uri: https://teefan.github.io/mt-lang/
606
604
  source_code_uri: https://github.com/teefan/mt-lang
607
605
  post_install_message: |
608
- Milk Tea 0.3.16 installed!
606
+ Milk Tea 0.3.17 installed!
609
607
 
610
608
  System requirements:
611
609
  - A C compiler (gcc or clang) must be available on PATH
@@ -1,182 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../types/layout"
4
-
5
- module MilkTea
6
- module ConstEval
7
-
8
- def self.evaluate(expression, resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
9
- Evaluator.new(
10
- resolve_identifier:,
11
- resolve_member_access:,
12
- resolve_type_ref:,
13
- resolve_call:,
14
- ).evaluate(expression)
15
- end
16
-
17
- def self.equality_result(left, right)
18
- return left == right if left.is_a?(Numeric) && right.is_a?(Numeric)
19
- return left == right if left.is_a?(String) && right.is_a?(String)
20
- return left == right if boolean_value?(left) && boolean_value?(right)
21
- return left == right if left.is_a?(Types::Base) && right.is_a?(Types::Base)
22
-
23
- nil
24
- end
25
-
26
- def self.boolean_value?(value)
27
- value == true || value == false
28
- end
29
-
30
- class Evaluator
31
- def initialize(resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
32
- @resolve_identifier = resolve_identifier
33
- @resolve_member_access = resolve_member_access
34
- @resolve_type_ref = resolve_type_ref
35
- @resolve_call = resolve_call
36
- end
37
-
38
- def evaluate(expression)
39
- case expression
40
- when AST::ErrorExpr
41
- nil
42
- when AST::ExpressionList
43
- expression.elements.filter_map { |element| evaluate(element) }
44
- when AST::IntegerLiteral, AST::FloatLiteral, AST::BooleanLiteral
45
- expression.value
46
- when AST::StringLiteral
47
- expression.value
48
- when AST::Identifier
49
- @resolve_identifier&.call(expression)
50
- when AST::MemberAccess
51
- @resolve_member_access&.call(expression)
52
- when AST::Call
53
- @resolve_call&.call(expression)
54
- when AST::Specialization
55
- @resolve_call&.call(expression)
56
- when AST::SizeofExpr
57
- type = resolve_layout_type(expression.type)
58
- type && Layout.size_of(type)
59
- when AST::AlignofExpr
60
- type = resolve_layout_type(expression.type)
61
- type && Layout.alignment_of(type)
62
- when AST::OffsetofExpr
63
- type = resolve_layout_type(expression.type)
64
- if type
65
- result = Layout.offset_of(type, expression.field)
66
- return result if result
67
-
68
- id_expr = AST::Identifier.new(name: expression.field)
69
- value = @resolve_identifier&.call(id_expr)
70
- if value.is_a?(Types::FieldHandle)
71
- return Layout.offset_of(type, value.field_name)
72
- end
73
- end
74
- nil
75
- when AST::UnaryOp
76
- evaluate_unary(expression)
77
- when AST::BinaryOp
78
- evaluate_binary(expression)
79
- when AST::IfExpr
80
- condition = evaluate(expression.condition)
81
- return unless ConstEval.boolean_value?(condition)
82
-
83
- evaluate(condition ? expression.then_expression : expression.else_expression)
84
- else
85
- nil
86
- end
87
- end
88
-
89
- def resolve_layout_type(type_ref)
90
- return unless @resolve_type_ref
91
-
92
- @resolve_type_ref.call(type_ref)
93
- end
94
-
95
- def evaluate_unary(expression)
96
- operand = evaluate(expression.operand)
97
-
98
- case expression.operator
99
- when "+"
100
- operand.is_a?(Numeric) ? operand : nil
101
- when "-"
102
- operand.is_a?(Numeric) ? -operand : nil
103
- when "~"
104
- operand.is_a?(Integer) ? ~operand : nil
105
- when "not"
106
- ConstEval.boolean_value?(operand) ? !operand : nil
107
- end
108
- end
109
-
110
- def evaluate_binary(expression)
111
- left = evaluate(expression.left)
112
-
113
- case expression.operator
114
- when "and"
115
- return unless ConstEval.boolean_value?(left)
116
- return false if left == false
117
-
118
- right = evaluate(expression.right)
119
- return right if ConstEval.boolean_value?(right)
120
-
121
- return nil
122
- when "or"
123
- return unless ConstEval.boolean_value?(left)
124
- return true if left == true
125
-
126
- right = evaluate(expression.right)
127
- return right if ConstEval.boolean_value?(right)
128
-
129
- return nil
130
- end
131
-
132
- right = evaluate(expression.right)
133
-
134
- case expression.operator
135
- when "=="
136
- ConstEval.equality_result(left, right)
137
- when "!="
138
- result = ConstEval.equality_result(left, right)
139
- result.nil? ? nil : !result
140
- when "+"
141
- left.is_a?(Numeric) && right.is_a?(Numeric) ? left + right : nil
142
- when "-"
143
- left.is_a?(Numeric) && right.is_a?(Numeric) ? left - right : nil
144
- when "*"
145
- left.is_a?(Numeric) && right.is_a?(Numeric) ? left * right : nil
146
- when "/"
147
- return unless left.is_a?(Numeric) && right.is_a?(Numeric)
148
- raise CompileTime::Error, "division by zero" if zero_numeric?(right)
149
-
150
- left / right
151
- when "%"
152
- return unless left.is_a?(Integer) && right.is_a?(Integer)
153
- raise CompileTime::Error, "modulo by zero" if right.zero?
154
-
155
- left % right
156
- when "<<"
157
- left.is_a?(Integer) && right.is_a?(Integer) ? left << right : nil
158
- when ">>"
159
- left.is_a?(Integer) && right.is_a?(Integer) ? left >> right : nil
160
- when "&"
161
- left.is_a?(Integer) && right.is_a?(Integer) ? left & right : nil
162
- when "|"
163
- left.is_a?(Integer) && right.is_a?(Integer) ? left | right : nil
164
- when "^"
165
- left.is_a?(Integer) && right.is_a?(Integer) ? left ^ right : nil
166
- when "<"
167
- left.is_a?(Numeric) && right.is_a?(Numeric) ? left < right : nil
168
- when "<="
169
- left.is_a?(Numeric) && right.is_a?(Numeric) ? left <= right : nil
170
- when ">"
171
- left.is_a?(Numeric) && right.is_a?(Numeric) ? left > right : nil
172
- when ">="
173
- left.is_a?(Numeric) && right.is_a?(Numeric) ? left >= right : nil
174
- end
175
- end
176
-
177
- def zero_numeric?(value)
178
- (value.is_a?(Integer) && value.zero?) || (value.is_a?(Float) && value.zero?)
179
- end
180
- end
181
- end
182
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MilkTea
4
- class ModuleLoadError < StandardError
5
- attr_reader :path, :line, :column
6
-
7
- def initialize(message, path:, line: nil, column: nil)
8
- @path = path
9
- @line = line
10
- @column = column
11
- super("#{message}: #{path}")
12
- end
13
-
14
- def code
15
- "module/error"
16
- end
17
- end
18
-
19
- class ModuleLoader
20
- ImportResolution = Data.define(:modules, :errors)
21
- ImportResolutionError = Data.define(:import, :error)
22
- end
23
- end
@@ -1,156 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "open3"
4
- require "thread"
5
- require "timeout"
6
-
7
- module MilkTea
8
- module DAP
9
- module Backends
10
- # Thin bridge to an lldb-dap compatible adapter process.
11
- class LLDBDAP
12
- def initialize(adapter_command: ["lldb-dap"], on_event: nil, on_request: nil)
13
- @adapter_command = adapter_command
14
- @on_event = on_event
15
- @on_request = on_request
16
- @protocol = nil
17
- @stdin = nil
18
- @stdout = nil
19
- @stderr = nil
20
- @wait_thread = nil
21
- @reader_thread = nil
22
- @stderr_thread = nil
23
- @write_mutex = Mutex.new
24
- @pending_mutex = Mutex.new
25
- @pending = {}
26
- @next_seq = 1
27
- end
28
-
29
- def start!
30
- return if running?
31
-
32
- @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(*@adapter_command)
33
- @protocol = Protocol.new(input: @stdout, output: @stdin)
34
-
35
- @reader_thread = Thread.new { read_loop }
36
- @stderr_thread = Thread.new { drain_stderr }
37
- end
38
-
39
- def running?
40
- !@wait_thread.nil? && @wait_thread.alive?
41
- end
42
-
43
- def request(command, arguments = {}, timeout: 5)
44
- start! unless running?
45
-
46
- seq = next_seq
47
- queue = Queue.new
48
- @pending_mutex.synchronize do
49
- @pending[seq] = queue
50
- end
51
-
52
- @write_mutex.synchronize do
53
- @protocol.write_message({
54
- seq: seq,
55
- type: "request",
56
- command: command,
57
- arguments: arguments
58
- })
59
- end
60
-
61
- Timeout.timeout(timeout) { queue.pop }
62
- rescue Timeout::Error
63
- {
64
- "type" => "response",
65
- "request_seq" => seq,
66
- "success" => false,
67
- "message" => "backend request timed out: #{command}"
68
- }
69
- ensure
70
- @pending_mutex.synchronize { @pending.delete(seq) }
71
- end
72
-
73
- def stop!
74
- @stdin&.close
75
- @stdout&.close
76
- @stderr&.close
77
- if @wait_thread&.alive?
78
- Process.kill("TERM", @wait_thread.pid)
79
- end
80
- rescue StandardError
81
- nil
82
- ensure
83
- @wait_thread&.join(0.2)
84
- @reader_thread&.join(0.2)
85
- @stderr_thread&.join(0.2)
86
- @stdin = nil
87
- @stdout = nil
88
- @stderr = nil
89
- @wait_thread = nil
90
- @reader_thread = nil
91
- @stderr_thread = nil
92
- end
93
-
94
- def next_seq
95
- seq = @next_seq
96
- @next_seq += 1
97
- seq
98
- end
99
-
100
- def read_loop
101
- loop do
102
- message = @protocol.read_message
103
- break if message.nil?
104
-
105
- if message["type"] == "response"
106
- queue = @pending_mutex.synchronize { @pending[message["request_seq"]] }
107
- queue&.push(message)
108
- elsif message["type"] == "event"
109
- @on_event&.call(message)
110
- elsif message["type"] == "request"
111
- handle_adapter_request(message)
112
- end
113
- end
114
- rescue StandardError
115
- nil
116
- ensure
117
- @pending_mutex.synchronize do
118
- @pending.each_value do |queue|
119
- queue.push({
120
- "type" => "response",
121
- "success" => false,
122
- "message" => "backend closed"
123
- })
124
- end
125
- @pending.clear
126
- end
127
- end
128
-
129
- def drain_stderr
130
- return unless @stderr
131
-
132
- @stderr.each_line do |_line|
133
- # Intentionally ignored in this bridge layer.
134
- end
135
- rescue StandardError
136
- nil
137
- end
138
-
139
- def handle_adapter_request(message)
140
- response = @on_request&.call(message)
141
- response ||= {
142
- "type" => "response",
143
- "request_seq" => message["seq"],
144
- "success" => false,
145
- "command" => message["command"],
146
- "message" => "unsupported reverse request: #{message['command']}"
147
- }
148
-
149
- @write_mutex.synchronize do
150
- @protocol.write_message(response)
151
- end
152
- end
153
- end
154
- end
155
- end
156
- end