mt-lang 0.3.12 → 0.3.13

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: bc80c3a3240cbcf4770dd067aa33559e74e3f139322b8fdb757316f3c7faf8af
4
- data.tar.gz: a623e5ecaa9617aa161a83e96c4cb0e7a75b08855edae27fb56c8ddc47c7cd75
3
+ metadata.gz: ccbf498d3496c9dac904d8f2b43442e971507874b856149171b78f7eed90e949
4
+ data.tar.gz: '090885ac2d6181ba4cabb1ef6ed5c5a6e79cb3e60fe4fa61315c6f2404d5e00f'
5
5
  SHA512:
6
- metadata.gz: c90aa51a21411fdcfe612f2ded69afdde6e055a012a4e886f5aa9ac69110d39a8bf93a36dde0543d1375a8bfa75ffd2f287eb082833a28b42efe19ab45c62ae6
7
- data.tar.gz: a87ac20f1228f059e3508818be58e11e5491a90299e56d5ab569ab3ebf1ae3dc1f7ddff8761321754bd2bfc8c520dc9ac3876360714ced2baf6e06a82bbf635d
6
+ metadata.gz: '08b68224eb3b428b8fab0845a98f79605c06f95ffc66a6a653586da99f230720f177781ac481debee8aede3cda843f34f12fb953f62846fc8d7048d3b97726c1'
7
+ data.tar.gz: cd69cddcf5b0cf2bb86fe4443c502c214fc73dda6318532f14e9548b8bbc7b84186956b9bf4a975148ad0f45d8bb9b86f1093956c089af293f1755ca2c8b6589
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.12"
6
+ VERSION = "0.3.13"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -5,7 +5,7 @@ require "open3"
5
5
  require "tempfile"
6
6
  require_relative "../tooling/formatter"
7
7
  require_relative "../core/token"
8
- require_relative "../core/types/types"
8
+ require_relative "../core/types"
9
9
  require_relative "bindgen/ast_parser"
10
10
  require_relative "bindgen/declaration"
11
11
  require_relative "bindgen/type_mapper"
@@ -3,7 +3,7 @@
3
3
  require "json"
4
4
  require_relative "../tooling/formatter"
5
5
  require_relative "../core/token"
6
- require_relative "../core/types/types"
6
+ require_relative "../core/types"
7
7
 
8
8
  require_relative "imported_bindings/generator"
9
9
  require_relative "imported_bindings/method_source"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fiddle"
4
- require_relative "types"
4
+ require_relative "../types"
5
5
 
6
6
  module MilkTea
7
7
  module Layout
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fiddle"
4
- require_relative "registry"
5
- require_relative "visitor"
4
+ require_relative "types/registry"
5
+ require_relative "types/visitor"
6
6
 
7
7
  module MilkTea
8
8
  module Types
data/lib/milk_tea/core.rb CHANGED
@@ -9,7 +9,7 @@ require_relative "core/lexer"
9
9
  require_relative "core/cst"
10
10
  require_relative "core/cst_builder"
11
11
  require_relative "core/ast"
12
- require_relative "core/types/types"
12
+ require_relative "core/types"
13
13
  require_relative "core/binding_types"
14
14
  require_relative "core/compile_time"
15
15
  require_relative "core/compatibility_helpers"
@@ -229,6 +229,7 @@ module MilkTea
229
229
 
230
230
  def lex_command
231
231
  path = nil
232
+ sexpr = false
232
233
 
233
234
  args = @argv.dup
234
235
  @argv = []
@@ -236,6 +237,11 @@ module MilkTea
236
237
  arg = args.shift
237
238
  next if arg == "--"
238
239
 
240
+ if arg == "--sexpr"
241
+ sexpr = true
242
+ next
243
+ end
244
+
239
245
  if path.nil?
240
246
  path = arg
241
247
  else
@@ -252,15 +258,24 @@ module MilkTea
252
258
  end
253
259
 
254
260
  tokens = Lexer.lex(read_source_file(path), path: path)
255
- @out.write(PP.pp(tokens, +""))
261
+ if sexpr
262
+ @out.puts(SexprDumper.dump_tokens(tokens))
263
+ else
264
+ @out.write(PP.pp(tokens, +""))
265
+ end
256
266
  0
257
267
  end
258
268
 
259
269
  def parse_command
270
+ sexpr = false
260
271
  args = @argv.dup
261
272
  @argv = []
262
273
  until args.empty?
263
274
  arg = args.shift
275
+ if arg == "--sexpr"
276
+ sexpr = true
277
+ next
278
+ end
264
279
  @argv << arg
265
280
  end
266
281
 
@@ -283,9 +298,13 @@ module MilkTea
283
298
  paths.each_with_index do |path, index|
284
299
  ast = make_module_loader(path, locked: resolution[:locked], platform: ModuleLoader.default_host_platform).load_file(path)
285
300
  if multiple
286
- @out.puts("# --- #{path} ---")
301
+ @out.puts("# --- #{path} ---") unless sexpr
302
+ end
303
+ if sexpr
304
+ @out.puts(SexprDumper.dump_ast(ast))
305
+ else
306
+ @out.write(PrettyPrinter.format_ast(ast))
287
307
  end
288
- @out.write(PrettyPrinter.format_ast(ast))
289
308
  @out.puts if multiple && index < paths.length - 1
290
309
  end
291
310
  0
@@ -764,10 +783,15 @@ module MilkTea
764
783
  end
765
784
 
766
785
  def lower_command
786
+ sexpr = false
767
787
  args = @argv.dup
768
788
  @argv = []
769
789
  until args.empty?
770
790
  arg = args.shift
791
+ if arg == "--sexpr"
792
+ sexpr = true
793
+ next
794
+ end
771
795
  @argv << arg
772
796
  end
773
797
 
@@ -790,10 +814,13 @@ module MilkTea
790
814
  paths.each_with_index do |path, index|
791
815
  program = make_module_loader(path, locked: resolution[:locked], platform: ModuleLoader.default_host_platform).check_program(path)
792
816
  if multiple
793
- @out.puts("# --- #{path} ---")
817
+ @out.puts("# --- #{path} ---") unless sexpr
818
+ end
819
+ if sexpr
820
+ @out.puts(SexprDumper.dump_ir(Lowering.lower(program)))
821
+ else
822
+ @out.write(PrettyPrinter.format_ir(Lowering.lower(program)))
794
823
  end
795
- @out.write(PrettyPrinter.format_ir(Lowering.lower(program)))
796
- @out.puts if multiple && index < paths.length - 1
797
824
  end
798
825
  0
799
826
  end
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module SexprDumper
5
+ module_function
6
+
7
+ def dump_ir(program)
8
+ buf = +""
9
+ emit_value(program, buf)
10
+ buf
11
+ end
12
+
13
+ def dump_ast(ast)
14
+ buf = +""
15
+ emit_value(ast, buf)
16
+ buf
17
+ end
18
+
19
+ def dump_tokens(tokens)
20
+ buf = +""
21
+ buf << "("
22
+ tokens.each_with_index { |t, i| buf << " " if i > 0; emit_token(t, buf) }
23
+ buf << ")"
24
+ buf
25
+ end
26
+
27
+ # ── value dispatcher ──────────────────────────────────────────────
28
+
29
+ def emit_value(value, buf)
30
+ case value
31
+ when ::Data then emit_data(value, buf)
32
+ when Array then emit_array(value, buf)
33
+ when *TYPES_CLASSES then emit_types_object(value, buf)
34
+ when nil then buf << "nil"
35
+ when true then buf << "true"
36
+ when false then buf << "false"
37
+ when Integer then buf << value.to_s
38
+ when Float then buf << format_float(value)
39
+ when String then emit_string(value, buf)
40
+ when Symbol then buf << ":" << value.to_s.tr("_", "-")
41
+ else buf << escape_string(value.to_s)
42
+ end
43
+ end
44
+
45
+ # ── Data serialisation ────────────────────────────────────────────
46
+
47
+ def emit_data(node, buf)
48
+ type_name = node.class.name.split("::").last(2).join("::")
49
+ members = node.class.members
50
+ buf << "(" << type_name
51
+ members.each do |field|
52
+ val = node.public_send(field)
53
+ buf << " :" << field.to_s << " "
54
+ emit_value(val, buf)
55
+ end
56
+ buf << ")"
57
+ end
58
+
59
+ # ── Array serialisation ───────────────────────────────────────────
60
+
61
+ def emit_array(values, buf)
62
+ buf << "("
63
+ values.each_with_index do |v, i|
64
+ buf << " " if i > 0
65
+ emit_value(v, buf)
66
+ end
67
+ buf << ")"
68
+ end
69
+
70
+ # ── Token serialisation ───────────────────────────────────────────
71
+
72
+ def emit_token(token, buf)
73
+ buf << "(token :type " << emit_token_type(token.type)
74
+ buf << " :lexeme "
75
+ emit_string(token.lexeme, buf)
76
+ buf << " :literal "
77
+ emit_value(token.literal, buf)
78
+ buf << " :line " << token.line.to_s
79
+ buf << " :column " << token.column.to_s
80
+ buf << " :start_offset " << token.start_offset.to_s
81
+ buf << " :end_offset " << token.end_offset.to_s
82
+ buf << ")"
83
+ end
84
+
85
+ def emit_token_type(sym)
86
+ ":" + sym.to_s.tr("_", "-")
87
+ end
88
+
89
+ # ── Types serialisation ───────────────────────────────────────────
90
+
91
+ TYPES_SHORT_NAMES = {
92
+ "MilkTea::Types::Primitive" => "Primitive",
93
+ "MilkTea::Types::Null" => "Null",
94
+ "MilkTea::Types::Nullable" => "Nullable",
95
+ "MilkTea::Types::GenericInstance" => "Generic",
96
+ "MilkTea::Types::Span" => "Span",
97
+ "MilkTea::Types::Task" => "Task",
98
+ "MilkTea::Types::Function" => "Function",
99
+ "MilkTea::Types::Proc" => "Proc",
100
+ "MilkTea::Types::Tuple" => "Tuple",
101
+ "MilkTea::Types::Vector" => "Vector",
102
+ "MilkTea::Types::Matrix" => "Matrix",
103
+ "MilkTea::Types::Quaternion" => "Quaternion",
104
+ "MilkTea::Types::SoA" => "SoA",
105
+ "MilkTea::Types::Simd" => "Simd",
106
+ "MilkTea::Types::Struct" => "Struct",
107
+ "MilkTea::Types::StructInstance" => "StructInstance",
108
+ "MilkTea::Types::Union" => "Union",
109
+ "MilkTea::Types::Variant" => "Variant",
110
+ "MilkTea::Types::VariantInstance" => "VariantInstance",
111
+ "MilkTea::Types::VariantArmPayload" => "VariantArmPayload",
112
+ "MilkTea::Types::Enum" => "Enum",
113
+ "MilkTea::Types::Flags" => "Flags",
114
+ "MilkTea::Types::Opaque" => "Opaque",
115
+ "MilkTea::Types::StringView" => "StringView",
116
+ "MilkTea::Types::Dyn" => "Dyn",
117
+ "MilkTea::Types::Parameter" => "Parameter",
118
+ "MilkTea::Types::LiteralTypeArg" => "LiteralTypeArg",
119
+ "MilkTea::Types::TypeVar" => "TypeVar",
120
+ "MilkTea::Types::LifetimeRef" => "LifetimeRef",
121
+ "MilkTea::Types::Event" => "Event",
122
+ "MilkTea::Types::Subscription" => "Subscription",
123
+ "MilkTea::Types::Handle" => "Handle",
124
+ "MilkTea::Types::TypeType" => "TypeType",
125
+ "MilkTea::Types::Error" => "Error",
126
+ "MilkTea::Types::DynVtable" => "DynVtable",
127
+ }.freeze
128
+
129
+ TYPES_CLASSES = TYPES_SHORT_NAMES.keys.map do |name|
130
+ name.split("::").reduce(Object) { |mod, part| mod.const_get(part) }
131
+ end.freeze
132
+
133
+ def emit_types_object(type, buf)
134
+ short = TYPES_SHORT_NAMES[type.class.name] || type.class.name.split("::").last
135
+ buf << "(Types::" << short
136
+ emit_types_fields(type, buf)
137
+ buf << ")"
138
+ end
139
+
140
+ def emit_types_fields(type, buf)
141
+ case type
142
+ when Types::Primitive
143
+ buf << " :name "
144
+ emit_string(type.name, buf)
145
+ when Types::Null
146
+ buf << " :target_type "
147
+ emit_value(type.target_type, buf)
148
+ when Types::Nullable
149
+ buf << " :base "
150
+ emit_value(type.base, buf)
151
+ when Types::GenericInstance
152
+ buf << " :name "
153
+ emit_string(type.name, buf)
154
+ buf << " :arguments "
155
+ emit_array(type.arguments, buf)
156
+ when Types::Span
157
+ buf << " :element_type "
158
+ emit_value(type.element_type, buf)
159
+ when Types::Task
160
+ buf << " :result_type "
161
+ emit_value(type.result_type, buf)
162
+ when Types::Function
163
+ buf << " :name "
164
+ emit_string(type.name, buf)
165
+ buf << " :params "
166
+ emit_array(type.params, buf)
167
+ buf << " :return_type "
168
+ emit_value(type.return_type, buf)
169
+ buf << " :receiver_type "
170
+ emit_value(type.receiver_type, buf)
171
+ buf << " :receiver_editable "
172
+ emit_value(type.receiver_editable, buf)
173
+ buf << " :variadic "
174
+ emit_value(type.variadic, buf)
175
+ buf << " :external "
176
+ emit_value(type.external, buf)
177
+ when Types::Proc
178
+ buf << " :params "
179
+ emit_array(type.params, buf)
180
+ buf << " :return_type "
181
+ emit_value(type.return_type, buf)
182
+ when Types::Tuple
183
+ buf << " :element_types "
184
+ emit_array(type.element_types, buf)
185
+ buf << " :field_names "
186
+ emit_value(type.field_names, buf)
187
+ when Types::Vector
188
+ buf << " :name "
189
+ emit_string(type.name, buf)
190
+ when Types::Matrix
191
+ buf << " :name "
192
+ emit_string(type.name, buf)
193
+ when Types::Quaternion
194
+ buf << " :name "
195
+ emit_string(type.name, buf)
196
+ when Types::SoA
197
+ buf << " :element_type "
198
+ emit_value(type.element_type, buf)
199
+ buf << " :count "
200
+ emit_value(type.count, buf)
201
+ when Types::Simd
202
+ buf << " :element_type "
203
+ emit_value(type.element_type, buf)
204
+ buf << " :lane_count "
205
+ emit_value(type.lane_count, buf)
206
+ when Types::Struct
207
+ buf << " :name "
208
+ emit_string(type.name, buf)
209
+ buf << " :module_name "
210
+ emit_value(type.module_name, buf)
211
+ when Types::StructInstance
212
+ buf << " :name "
213
+ emit_string(type.name, buf)
214
+ buf << " :module_name "
215
+ emit_value(type.module_name, buf)
216
+ buf << " :arguments "
217
+ emit_array(type.arguments, buf)
218
+ when Types::Union
219
+ buf << " :name "
220
+ emit_string(type.name, buf)
221
+ buf << " :module_name "
222
+ emit_value(type.module_name, buf)
223
+ when Types::Variant
224
+ buf << " :name "
225
+ emit_string(type.name, buf)
226
+ buf << " :module_name "
227
+ emit_value(type.module_name, buf)
228
+ when Types::VariantInstance
229
+ buf << " :name "
230
+ emit_string(type.name, buf)
231
+ buf << " :module_name "
232
+ emit_value(type.module_name, buf)
233
+ buf << " :arguments "
234
+ emit_array(type.arguments, buf)
235
+ when Types::VariantArmPayload
236
+ buf << " :variant_name "
237
+ emit_string(type.variant_type.name, buf)
238
+ buf << " :arm_name "
239
+ emit_string(type.arm_name, buf)
240
+ when Types::Enum
241
+ buf << " :name "
242
+ emit_string(type.name, buf)
243
+ buf << " :module_name "
244
+ emit_value(type.module_name, buf)
245
+ when Types::Flags
246
+ buf << " :name "
247
+ emit_string(type.name, buf)
248
+ buf << " :module_name "
249
+ emit_value(type.module_name, buf)
250
+ when Types::Opaque
251
+ buf << " :name "
252
+ emit_string(type.name, buf)
253
+ buf << " :module_name "
254
+ emit_value(type.module_name, buf)
255
+ when Types::StringView
256
+ # no fields
257
+ when Types::Dyn
258
+ buf << " :interface_name "
259
+ emit_string(type.interface_binding.name, buf)
260
+ buf << " :type_arguments "
261
+ emit_array(type.type_arguments, buf)
262
+ when Types::Parameter
263
+ buf << " :name "
264
+ emit_string(type.name, buf)
265
+ buf << " :type "
266
+ emit_value(type.type, buf)
267
+ buf << " :mutable "
268
+ emit_value(type.mutable, buf)
269
+ buf << " :passing_mode "
270
+ emit_value(type.passing_mode, buf)
271
+ buf << " :boundary_type "
272
+ emit_value(type.boundary_type, buf)
273
+ when Types::LiteralTypeArg
274
+ buf << " :value "
275
+ emit_value(type.value, buf)
276
+ when Types::TypeVar
277
+ buf << " :name "
278
+ emit_string(type.name, buf)
279
+ when Types::LifetimeRef
280
+ buf << " :name "
281
+ emit_string(type.name, buf)
282
+ when Types::Event
283
+ buf << " :name "
284
+ emit_string(type.name, buf)
285
+ buf << " :capacity "
286
+ emit_value(type.capacity, buf)
287
+ buf << " :payload_type "
288
+ emit_value(type.payload_type, buf)
289
+ when Types::Subscription
290
+ # no fields (singleton)
291
+ when Types::Handle
292
+ # no fields (singleton)
293
+ else
294
+ _emit_generic_type(type, buf)
295
+ end
296
+ end
297
+
298
+ def _emit_generic_type(type, buf)
299
+ type.instance_variables.sort.each do |ivar|
300
+ next if skip_types_ivar?(ivar)
301
+ name = ivar.to_s.sub(/\A@/, "")
302
+ val = type.instance_variable_get(ivar)
303
+ next if val.is_a?(Hash) || val.is_a?(Set) || val.is_a?(Proc)
304
+ buf << " :" << name << " "
305
+ emit_value(val, buf)
306
+ end
307
+ end
308
+
309
+ def skip_types_ivar?(ivar)
310
+ %i[@hash @integer @float @numeric @pointer_sized_integer
311
+ @signed_integer @unsigned_integer @fixed_width_integer
312
+ @boolean @void @integer_width @float_width @backing_type
313
+ @arms @arm_names @arm_field_hashes @instances].include?(ivar)
314
+ end
315
+
316
+ # ── atom helpers ──────────────────────────────────────────────────
317
+
318
+ def emit_string(value, buf)
319
+ escaped = value.to_s
320
+ .gsub("\\", "\\\\")
321
+ .gsub('"', '\"')
322
+ .gsub("\n", '\\n')
323
+ .gsub("\t", '\\t')
324
+ .gsub("\r", '\\r')
325
+ buf << '"' << escaped << '"'
326
+ end
327
+
328
+ def escape_string(value)
329
+ value.to_s
330
+ .gsub("\\", "\\\\")
331
+ .gsub('"', '\"')
332
+ .gsub("\n", '\\n')
333
+ .gsub("\t", '\\t')
334
+ .gsub("\r", '\\r')
335
+ end
336
+
337
+ def format_float(value)
338
+ s = sprintf("%.17g", value)
339
+ s.include?(".") || s.include?("e") ? s : s + ".0"
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,420 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module SexprParser
5
+ module_function
6
+
7
+ # ── public API ────────────────────────────────────────────────────
8
+
9
+ def parse_ir(sexpr)
10
+ tokens = tokenize(sexpr)
11
+ _parse_value(tokens)
12
+ end
13
+
14
+ def parse_ast(sexpr)
15
+ tokens = tokenize(sexpr)
16
+ _parse_value(tokens)
17
+ end
18
+
19
+ def parse_tokens(sexpr)
20
+ tokens = tokenize(sexpr)
21
+ _parse_value(tokens)
22
+ end
23
+
24
+ # ── tokenizer ─────────────────────────────────────────────────────
25
+
26
+ Token = Data.define(:kind, :text, :line, :column)
27
+
28
+ def tokenize(source)
29
+ tokens = []
30
+ i = 0
31
+ len = source.length
32
+ line = 1
33
+ col = 1
34
+ while i < len
35
+ ch = source[i]
36
+ case ch
37
+ when "(", ")"
38
+ tokens << Token.new(kind: ch, text: ch, line:, column: col)
39
+ i += 1
40
+ col += 1
41
+ when '"'
42
+ j = i + 1
43
+ chars = +""
44
+ while j < len
45
+ if source[j] == "\\" && j + 1 < len
46
+ case source[j + 1]
47
+ when "n" then chars << "\n"
48
+ when "t" then chars << "\t"
49
+ when "r" then chars << "\r"
50
+ when '"' then chars << '"'
51
+ when "\\" then chars << "\\"
52
+ else chars << source[j + 1]
53
+ end
54
+ j += 2
55
+ elsif source[j] == '"'
56
+ j += 1
57
+ break
58
+ else
59
+ chars << source[j]
60
+ j += 1
61
+ end
62
+ end
63
+ tokens << Token.new(kind: "string", text: chars, line:, column: col)
64
+ col += j - i
65
+ i = j
66
+ when ":", "-", "0".."9"
67
+ # keyword, negative number, or number
68
+ j = i
69
+ j += 1 while j < len && source[j] != " " && source[j] != "\n" && source[j] != "(" && source[j] != ")" && source[j] != '"'
70
+ text = source[i...j]
71
+ if text.start_with?(":")
72
+ tokens << Token.new(kind: "keyword", text: text[1..], line:, column: col)
73
+ elsif text.match?(/\A-?[0-9]+\.[0-9]+([eE][+-]?[0-9]+)?\z/) || text.match?(/\A-?[0-9]+\.[0-9]*([eE][+-]?[0-9]+)?\z/)
74
+ tokens << Token.new(kind: "float", text:, line:, column: col)
75
+ elsif text.match?(/\A-?[0-9]+\z/)
76
+ tokens << Token.new(kind: "integer", text:, line:, column: col)
77
+ else
78
+ tokens << Token.new(kind: "atom", text:, line:, column: col)
79
+ end
80
+ col += j - i
81
+ i = j
82
+ when /\s/
83
+ if ch == "\n"
84
+ line += 1
85
+ col = 0
86
+ end
87
+ i += 1
88
+ col += 1
89
+ else
90
+ # atom (identifier, true, false, nil)
91
+ j = i
92
+ j += 1 while j < len && source[j] != " " && source[j] != "\n" && source[j] != "(" && source[j] != ")" && source[j] != '"'
93
+ text = source[i...j]
94
+ tokens << Token.new(kind: "atom", text:, line:, column: col)
95
+ col += j - i
96
+ i = j
97
+ end
98
+ end
99
+ tokens
100
+ end
101
+
102
+ # ── parser ────────────────────────────────────────────────────────
103
+
104
+ def _parse_value(tokens, index = [0])
105
+ token = tokens[index[0]]
106
+ return nil unless token
107
+
108
+ case token.kind
109
+ when "("
110
+ _parse_list(tokens, index)
111
+ when "string"
112
+ index[0] += 1
113
+ token.text
114
+ when "integer"
115
+ index[0] += 1
116
+ Integer(token.text)
117
+ when "float"
118
+ index[0] += 1
119
+ Float(token.text)
120
+ when "keyword"
121
+ index[0] += 1
122
+ token.text.gsub("-", "_").to_sym
123
+ when "atom"
124
+ index[0] += 1
125
+ case token.text
126
+ when "nil" then nil
127
+ when "true" then true
128
+ when "false" then false
129
+ else token.text
130
+ end
131
+ else
132
+ raise "unexpected token: #{token.inspect}"
133
+ end
134
+ end
135
+
136
+ def _parse_list(tokens, index)
137
+ index[0] += 1 # skip "("
138
+ if tokens[index[0]]&.kind == ")"
139
+ index[0] += 1 # skip ")"
140
+ return []
141
+ end
142
+
143
+ first = _parse_value(tokens, index)
144
+
145
+ if first.is_a?(String) && first.match?(/\A[A-Z]/)
146
+ _parse_typed_node(tokens, index, first, first)
147
+ else
148
+ # plain array — first element was the first value
149
+ result = [first]
150
+ while tokens[index[0]]&.kind != ")"
151
+ result << _parse_value(tokens, index)
152
+ end
153
+ index[0] += 1 # skip ")"
154
+ result
155
+ end
156
+ end
157
+
158
+ def _parse_typed_node(tokens, index, first_token, first_str)
159
+ type_name = _resolve_type_name(first_str)
160
+
161
+ # Collect keyword-value pairs
162
+ fields = {}
163
+ while tokens[index[0]]&.kind == "keyword"
164
+ kw = tokens[index[0]].text.gsub("-", "_").to_sym
165
+ index[0] += 1 # skip keyword
166
+ val = _parse_value(tokens, index)
167
+ fields[kw] = val
168
+ end
169
+
170
+ index[0] += 1 # skip ")"
171
+
172
+ if type_name.start_with?("Types::")
173
+ _construct_types_object(type_name, fields)
174
+ elsif type_name.start_with?("IR::")
175
+ _construct_ir_object(type_name, fields)
176
+ elsif type_name.start_with?("AST::")
177
+ _construct_ast_object(type_name, fields)
178
+ else
179
+ _construct_generic(type_name, fields)
180
+ end
181
+ end
182
+
183
+ # ── type name resolution ──────────────────────────────────────────
184
+
185
+ def _resolve_type_name(str)
186
+ # Already fully qualified ("Types::Primitive", "IR::Program")
187
+ return str if str.include?("::")
188
+
189
+ # Try common prefixes
190
+ [
191
+ "MilkTea::IR::#{str}",
192
+ "MilkTea::AST::#{str}",
193
+ "MilkTea::Types::#{str}",
194
+ ].each do |candidate|
195
+ begin
196
+ return candidate if Object.const_get(candidate)
197
+ rescue NameError
198
+ nil
199
+ end
200
+ end
201
+ str
202
+ end
203
+
204
+ # ── object constructors ───────────────────────────────────────────
205
+
206
+ def _construct_ir_object(type_name, fields)
207
+ name = type_name.split("::").last
208
+ klass = MilkTea::IR.const_get(name)
209
+ sym_fields = fields.transform_keys(&:to_sym)
210
+ klass.new(**sym_fields)
211
+ rescue NameError
212
+ _construct_generic(type_name, fields)
213
+ end
214
+
215
+ def _construct_ast_object(type_name, fields)
216
+ name = type_name.split("::").last
217
+ klass = MilkTea::AST.const_get(name)
218
+ sym_fields = fields.transform_keys(&:to_sym)
219
+ klass.new(**sym_fields)
220
+ rescue NameError
221
+ _construct_generic(type_name, fields)
222
+ end
223
+
224
+ def _construct_types_object(type_name, fields)
225
+ short = type_name.sub(/\ATypes::/, "")
226
+ case short
227
+ when "Primitive"
228
+ Types::Registry.primitive(fields[:name])
229
+ when "Null"
230
+ Types::Null.new(fields[:target_type])
231
+ when "Nullable"
232
+ Types::Registry.nullable(fields[:base])
233
+ when "Generic"
234
+ Types::Registry.generic_instance(fields[:name], fields[:arguments])
235
+ when "Span"
236
+ Types::Registry.span(fields[:element_type])
237
+ when "Task"
238
+ rt = fields[:result_type] || Types::Registry.primitive("void")
239
+ Types::Registry.task(rt)
240
+ when "Function"
241
+ Types::Registry.function(
242
+ fields[:name],
243
+ params: fields[:params] || [],
244
+ return_type: fields[:return_type],
245
+ receiver_type: fields[:receiver_type],
246
+ receiver_editable: fields[:receiver_editable] || false,
247
+ variadic: fields[:variadic] || false,
248
+ external: fields[:external] || false,
249
+ )
250
+ when "Proc"
251
+ Types::Registry.proc(params: fields[:params] || [], return_type: fields[:return_type])
252
+ when "Tuple"
253
+ Types::Registry.tuple(
254
+ fields.fetch(:element_types, []),
255
+ field_names: fields[:field_names],
256
+ )
257
+ when "Vector"
258
+ Types::Registry.generic_instance(fields[:name], [Types::LiteralTypeArg.new(fields[:name].delete_prefix("vec").to_i)])
259
+ when "Matrix"
260
+ Types::Registry.generic_instance(fields[:name], [Types::LiteralTypeArg.new(fields[:name].delete_prefix("mat").to_i)])
261
+ when "Quaternion"
262
+ Types::Registry.generic_instance("quat", [])
263
+ when "SoA"
264
+ Types::Registry.soa(fields[:element_type], count: fields[:count])
265
+ when "Simd"
266
+ Types::Registry.simd(fields[:element_type], lane_count: fields[:lane_count])
267
+ when "StringView"
268
+ Types::Registry.string_view
269
+ when "Parameter"
270
+ Types::Registry.parameter(
271
+ fields[:name],
272
+ fields[:type],
273
+ mutable: fields[:mutable] || false,
274
+ passing_mode: fields[:passing_mode]&.to_sym || :plain,
275
+ boundary_type: fields[:boundary_type],
276
+ )
277
+ when "LiteralTypeArg"
278
+ Types::LiteralTypeArg.new(fields[:value])
279
+ when "TypeVar"
280
+ Types::TypeVar.new(fields[:name])
281
+ when "LifetimeRef"
282
+ Types::LifetimeRef.new(fields[:name])
283
+ when "Dyn"
284
+ interface_binding = MilkTea::SemanticAnalyzer::InterfaceBinding.new(
285
+ name: fields[:interface_name],
286
+ methods: [],
287
+ ast: nil,
288
+ module_name: nil,
289
+ )
290
+ Types::Registry.dyn(interface_binding, fields.fetch(:type_arguments, []))
291
+ when "Struct", "StructInstance", "Union", "Variant", "VariantInstance",
292
+ "VariantArmPayload", "Enum", "Flags", "Opaque", "Event", "Subscription", "Handle",
293
+ "TypeType", "Error", "DynVtable", "ReflectionHandleType", "StructHandle",
294
+ "FieldHandle", "CallableHandle", "AttributeHandle", "MemberHandle",
295
+ "GenericStructDefinition", "GenericVariantDefinition"
296
+ _construct_standalone_type(short, fields)
297
+ else
298
+ _construct_standalone_type(short, fields)
299
+ end
300
+ end
301
+
302
+ def _construct_standalone_type(short, fields)
303
+ case short
304
+ when "TypeType"
305
+ Types::TypeType.new
306
+ when "Error"
307
+ Types::Error.new
308
+ when "Subscription"
309
+ Types::Subscription.new
310
+ when "Handle"
311
+ Types::Handle.new
312
+ when "Struct"
313
+ Types::Struct.new(
314
+ fields[:name],
315
+ module_name: fields[:module_name],
316
+ external: false,
317
+ packed: false,
318
+ alignment: nil,
319
+ linkage_name: nil,
320
+ lifetime_params: [],
321
+ )
322
+ when "Variant"
323
+ Types::Variant.new(fields[:name], module_name: fields[:module_name])
324
+ when "Union"
325
+ Types::Union.new(
326
+ fields[:name],
327
+ module_name: fields[:module_name],
328
+ external: false,
329
+ packed: false,
330
+ alignment: nil,
331
+ linkage_name: nil,
332
+ lifetime_params: [],
333
+ )
334
+ when "Opaque"
335
+ Types::Opaque.new(fields[:name], module_name: fields[:module_name], external: false, linkage_name: nil)
336
+ when "Enum"
337
+ Types::Enum.new(fields[:name], module_name: fields[:module_name], external: false)
338
+ when "Flags"
339
+ Types::Flags.new(fields[:name], module_name: fields[:module_name], external: false)
340
+ when "Event"
341
+ Types::Event.new(
342
+ fields[:name],
343
+ capacity: fields[:capacity],
344
+ payload_type: fields[:payload_type],
345
+ module_name: nil,
346
+ visibility: :private,
347
+ owner_type_name: nil,
348
+ )
349
+ when "StringView"
350
+ Types::Registry.string_view
351
+ else
352
+ _construct_types_fallback(short, fields)
353
+ end
354
+ end
355
+
356
+ def _construct_types_fallback(short, fields)
357
+ klass = begin
358
+ Types.const_get(short)
359
+ rescue NameError
360
+ return fields
361
+ end
362
+
363
+ begin
364
+ sym_fields = fields.transform_keys(&:to_sym)
365
+ klass.new(**sym_fields)
366
+ rescue StandardError
367
+ begin
368
+ args = fields.values
369
+ klass.new(*args)
370
+ rescue StandardError
371
+ begin
372
+ obj = klass.allocate
373
+ fields.each do |k, v|
374
+ ivar = "@#{k}"
375
+ obj.instance_variable_set(ivar, v) if obj.instance_variable_defined?(ivar) || obj.respond_to?(:"#{k}=")
376
+ rescue StandardError
377
+ nil
378
+ end
379
+ begin
380
+ obj.instance_variable_set(:@hash, obj.object_id.hash)
381
+ rescue StandardError
382
+ nil
383
+ end
384
+ obj
385
+ rescue StandardError
386
+ fields
387
+ end
388
+ end
389
+ end
390
+ end
391
+
392
+ def _construct_generic(type_name, fields)
393
+ klass = begin
394
+ name_parts = type_name.split("::")
395
+ if name_parts.length > 1
396
+ top = Object.const_get(name_parts.first)
397
+ rest = name_parts[1..].join("::")
398
+ top.const_get(rest)
399
+ else
400
+ Object.const_get(type_name)
401
+ end
402
+ rescue NameError
403
+ nil
404
+ end
405
+
406
+ if klass && klass.respond_to?(:members)
407
+ sym_fields = fields.transform_keys(&:to_sym)
408
+ klass.new(**sym_fields)
409
+ else
410
+ fields
411
+ end
412
+ end
413
+
414
+ # ── helpers ───────────────────────────────────────────────────────
415
+
416
+ def _unescape(str)
417
+ str.gsub("\\n", "\n").gsub("\\t", "\t").gsub("\\r", "\r").gsub('\\"', '"').gsub("\\\\", "\\")
418
+ end
419
+ end
420
+ end
@@ -7,6 +7,8 @@ require_relative "packages"
7
7
  require_relative "tooling/debug_map"
8
8
  require_relative "tooling/debug_info_formatter"
9
9
  require_relative "tooling/asset_pack"
10
+ require_relative "tooling/sexpr_dumper"
11
+ require_relative "tooling/sexpr_parser"
10
12
  require_relative "tooling/build"
11
13
  require_relative "tooling/run"
12
14
  require_relative "tooling/cst_formatter"
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.12
4
+ version: 0.3.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Long (Teefan) Tran
@@ -288,10 +288,10 @@ files:
288
288
  - lib/milk_tea/core/semantic_analyzer/type_declaration.rb
289
289
  - lib/milk_tea/core/token.rb
290
290
  - lib/milk_tea/core/token_stream.rb
291
+ - lib/milk_tea/core/types.rb
291
292
  - lib/milk_tea/core/types/layout.rb
292
293
  - lib/milk_tea/core/types/predicates.rb
293
294
  - lib/milk_tea/core/types/registry.rb
294
- - lib/milk_tea/core/types/types.rb
295
295
  - lib/milk_tea/core/types/visitor.rb
296
296
  - lib/milk_tea/dap.rb
297
297
  - lib/milk_tea/dap/backends/lldb_dap.rb
@@ -385,6 +385,8 @@ files:
385
385
  - lib/milk_tea/tooling/public/css/docs.css
386
386
  - lib/milk_tea/tooling/public/js/docs.js
387
387
  - lib/milk_tea/tooling/run.rb
388
+ - lib/milk_tea/tooling/sexpr_dumper.rb
389
+ - lib/milk_tea/tooling/sexpr_parser.rb
388
390
  - lib/milk_tea/tooling/templates/wasm_shell.html
389
391
  - lib/milk_tea/tooling/toolchain_cli.rb
390
392
  - lib/milk_tea/tooling/views/404.erb
@@ -603,7 +605,7 @@ metadata:
603
605
  homepage_uri: https://teefan.github.io/mt-lang/
604
606
  source_code_uri: https://github.com/teefan/mt-lang
605
607
  post_install_message: |
606
- Milk Tea 0.3.12 installed!
608
+ Milk Tea 0.3.13 installed!
607
609
 
608
610
  System requirements:
609
611
  - A C compiler (gcc or clang) must be available on PATH