mt-lang 0.2.8 → 0.2.10

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: fcc2383591d95c6043caebcb271aba6b70650528dafbb1a3cd8b906e74a5a6b6
4
- data.tar.gz: 2e264acc4e563b29caf921062ca9156f92869d101b1f16f6516f9b412061a1c9
3
+ metadata.gz: 9047d861f94f1e5b66f531712466e32779a0a86bd02db452e36cd8990980ee5c
4
+ data.tar.gz: 3e9d1c7f06e70e2a9e3b84b8df4e8ee64b92be6e34f6bc970ca0b2ef69683c8e
5
5
  SHA512:
6
- metadata.gz: 17ef473e87741d685323aa279e434ce56a24b7c30cc4b48f61cbb7d4b7c49d47300c7cbad1e4211740fc35ecc29bf36ee941fa0b4bb18ac0fc19d04aee69d7ab
7
- data.tar.gz: 9952dc4f2a306b443cbd57c4840965c8c7e870f8f9f976643b063635c64e371c0f8e903ea3446b124cd0b7e9ddf6754807c924e90426c1bfe789124bc08d6d44
6
+ metadata.gz: 4144e09654664b0dd4eaf7d697fe077381c965902564297d32002807be4b9100861ca8c7c7e065b983e90b91dba4b86f22f52eaa4eae47fa1d68dee1cb1acc36
7
+ data.tar.gz: a712ee592463d961523bd2c8481be6993843f277c51bd3947f34bd8d3e32ae8d245ead8985f31d2621cefd8cc1ec705eba1749aca99324cdd3061adb5ef13ce2
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.2.8"
6
+ VERSION = "0.2.10"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -227,8 +227,8 @@ module MilkTea
227
227
  RangeExpr = Data.define(:start_expr, :end_expr, :line, :column)
228
228
  ExpressionList = Data.define(:elements, :line, :column)
229
229
  IfExpr = Data.define(:condition, :then_expression, :else_expression)
230
- MatchExpr = Data.define(:expression, :arms, :line, :column, :length) do
231
- def initialize(expression:, arms:, line: nil, column: nil, length: nil) = super
230
+ MatchExpr = Data.define(:expression, :arms, :line, :column, :length, :desugared_from_is) do
231
+ def initialize(expression:, arms:, line: nil, column: nil, length: nil, desugared_from_is: false) = super
232
232
  end
233
233
  UnsafeExpr = Data.define(:expression, :line, :column, :length) do
234
234
  def initialize(expression:, line: nil, column: nil, length: nil) = super
@@ -141,6 +141,7 @@ module MilkTea
141
141
  line:,
142
142
  column:,
143
143
  length: previous.lexeme.length,
144
+ desugared_from_is: true,
144
145
  )
145
146
  end
146
147
  expr
@@ -8,19 +8,222 @@ module MilkTea
8
8
 
9
9
  KEYWORD_HOVER_INFO = {
10
10
  'size_of' => {
11
- signature: 'size_of(expr) -> int',
12
- docs: '`size_of(expr)` evaluates the compile-time size (in bytes) of the expression\'s type.',
11
+ signature: 'size_of(Type) -> ptr_uint',
12
+ docs: '`size_of(Type)` evaluates the compile-time size (in bytes) of the type.',
13
13
  },
14
14
  'align_of' => {
15
- signature: 'align_of(expr) -> int',
16
- docs: '`align_of(expr)` evaluates the compile-time alignment (in bytes) of the expression\'s type.',
15
+ signature: 'align_of(Type) -> ptr_uint',
16
+ docs: '`align_of(Type)` evaluates the compile-time alignment (in bytes) of the type.',
17
17
  },
18
18
  'offset_of' => {
19
- signature: 'offset_of(Type, field) -> int',
19
+ signature: 'offset_of(Type, field) -> ptr_uint',
20
20
  docs: '`offset_of(Type, field)` evaluates the compile-time byte offset of `field` within `Type`.',
21
21
  },
22
22
  }.freeze
23
23
 
24
+ LANGUAGE_KEYWORD_HOVER_INFO = {
25
+ 'let' => {
26
+ signature: 'let',
27
+ docs: 'Immutable local variable declaration: `let name = value` or `let name: Type`. Supports `else` guard with `T?`, `Option[T]`, or `Result[T, E]`.',
28
+ },
29
+ 'var' => {
30
+ signature: 'var',
31
+ docs: 'Mutable local or module-level variable: `var name: Type = initializer`. Supports `else` guard. Module `var` requires explicit type.',
32
+ },
33
+ 'const' => {
34
+ signature: 'const',
35
+ docs: 'Compile-time constant, requires explicit type and initializer. Block-bodied form `const NAME -> TYPE:` evaluates at compile time.',
36
+ },
37
+ 'function' => {
38
+ signature: 'function',
39
+ docs: 'Ordinary function declaration: `function name(params) -> ReturnType:`. Parameters are non-rebindable. Return type defaults to `void`.',
40
+ },
41
+ 'async' => {
42
+ signature: 'async',
43
+ docs: 'Marks a function as async; the declared return type is lifted to `Task[T]`. Use `await` inside async functions.',
44
+ },
45
+ 'await' => {
46
+ signature: 'await',
47
+ docs: 'Awaits a `Task[T]` inside an async function, yielding the unwrapped `T`. Only allowed in async function bodies.',
48
+ },
49
+ 'struct' => {
50
+ signature: 'struct',
51
+ docs: 'Value-type struct with named, typed fields: `struct Name: field: Type`. Supports generics, nested structs, and `implements`.',
52
+ },
53
+ 'enum' => {
54
+ signature: 'enum',
55
+ docs: 'Integer-backed enumeration: `enum Name: BackingType`. Backing type defaults to `int`. Values auto-increment from 0 or the last explicit value.',
56
+ },
57
+ 'flags' => {
58
+ signature: 'flags',
59
+ docs: 'Bitmask type backed by an integer primitive: `flags Name: uint`. Members must be compile-time integer constants.',
60
+ },
61
+ 'union' => {
62
+ signature: 'union',
63
+ docs: 'Overlapped storage union: `union Name: field1: Type1, field2: Type2`. All fields share the same memory.',
64
+ },
65
+ 'variant' => {
66
+ signature: 'variant',
67
+ docs: 'Tagged union: `variant Name: arm1(field: Type), arm2`. Arms may carry named payload fields. No-payload arms are bare identifiers.',
68
+ },
69
+ 'opaque' => {
70
+ signature: 'opaque',
71
+ docs: 'Externally-defined type with unknown layout: `opaque Name`. May declare `implements` for interface conformance.',
72
+ },
73
+ 'type' => {
74
+ signature: 'type',
75
+ docs: 'Type alias: `type Name = ExistingType`. Generic type parameters are supported.',
76
+ },
77
+ 'interface' => {
78
+ signature: 'interface',
79
+ docs: 'Declares a method contract: `interface Name: function method(params) -> T`. Implemented via `implements`; used at runtime via `dyn[Interface]`.',
80
+ },
81
+ 'extending' => {
82
+ signature: 'extending',
83
+ docs: 'Extends an existing type with methods: `extending Type: function method():`. Supports `function`, `editable function`, and `static function`.',
84
+ },
85
+ 'implements' => {
86
+ signature: 'implements',
87
+ docs: 'Nominal interface conformance on `struct` or `opaque`: `struct Foo implements Interface`. Must be declared on the type definition.',
88
+ },
89
+ 'editable' => {
90
+ signature: 'editable',
91
+ docs: 'Method receiver modifier: `editable function`. Grants mutable access to `this`. Used in interfaces and extending blocks.',
92
+ },
93
+ 'static' => {
94
+ signature: 'static',
95
+ docs: 'Static method modifier: `static function`. No `this` receiver. Used in interfaces and extending blocks for constructors and utilities.',
96
+ },
97
+ 'import' => {
98
+ signature: 'import',
99
+ docs: 'Module import: `import module.path` or `import module.path as alias`. Module lookup resolves `a.b.c` to `a/b/c.mt`.',
100
+ },
101
+ 'public' => {
102
+ signature: 'public',
103
+ docs: 'Export visibility modifier: `public function`, `public type`, etc. Rejected on `extending`, `external`, and `static_assert` declarations.',
104
+ },
105
+ 'if' => {
106
+ signature: 'if',
107
+ docs: 'Conditional branch: `if condition:`. Condition must be `bool`. Supports `else if` and `else`. Inline form: `if cond: stmt else: stmt`.',
108
+ },
109
+ 'else' => {
110
+ signature: 'else',
111
+ docs: 'Else branch for `if`. Chains as `else if condition:` for additional branches. Supports inline single-statement form `else: stmt`.',
112
+ },
113
+ 'match' => {
114
+ signature: 'match',
115
+ docs: 'Pattern match on enum, variant, integer, `str`, or tuple. Expression form produces a value. Must be exhaustive without `_` wildcard.',
116
+ },
117
+ 'for' => {
118
+ signature: 'for',
119
+ docs: 'Loop: `for i in 0..count:` (exclusive range) or `for item in iterable:`. Multi-iteration `for left, right in xs, ys:` iterates arrays/spans in lockstep.',
120
+ },
121
+ 'while' => {
122
+ signature: 'while',
123
+ docs: 'Conditional loop: `while condition:`. Condition must be `bool`. Supports `break`, `continue`, and `defer` inside the body.',
124
+ },
125
+ 'return' => {
126
+ signature: 'return',
127
+ docs: 'Returns a value from a function. Not allowed inside `defer` blocks.',
128
+ },
129
+ 'break' => {
130
+ signature: 'break',
131
+ docs: 'Exits the nearest enclosing `for`, `while`, or `parallel for` loop.',
132
+ },
133
+ 'continue' => {
134
+ signature: 'continue',
135
+ docs: 'Skips to the next iteration of the nearest enclosing `for` or `while` loop.',
136
+ },
137
+ 'defer' => {
138
+ signature: 'defer',
139
+ docs: 'Defers an expression or block to function exit: `defer expr` or `defer:`. Multiple defers execute in LIFO order. `return` is not allowed inside.',
140
+ },
141
+ 'unsafe' => {
142
+ signature: 'unsafe',
143
+ docs: 'Required for pointer indexing, raw pointer dereference, pointer arithmetic, pointer casts, and `reinterpret[...]`. Single-expr or block form.',
144
+ },
145
+ 'external' => {
146
+ signature: 'external',
147
+ docs: 'Raw C ABI surface: `external function name(params) -> T`. No body, supports variadic `...`. Also marks external files with `external` header.',
148
+ },
149
+ 'foreign' => {
150
+ signature: 'foreign',
151
+ docs: 'Foreign function bridging: `foreign function name(params) -> T = c.FuncName`. Supports `in`, `out`, `inout`, and `consuming` parameter modes.',
152
+ },
153
+ 'consuming' => {
154
+ signature: 'consuming',
155
+ docs: 'Foreign function parameter mode: takes ownership of the argument. The caller\'s binding is consumed. Only on `foreign function` params.',
156
+ },
157
+ 'when' => {
158
+ signature: 'when',
159
+ docs: 'Compile-time conditional: `when CONSTANT:`. Only the chosen branch is type-checked and emitted. Requires `else` unless exhaustive.',
160
+ },
161
+ 'inline' => {
162
+ signature: 'inline',
163
+ docs: 'Compile-time unrolling modifier: `inline for` (loop unrolling), `inline while`, `inline match`, `inline if`. Only the active branch emits code.',
164
+ },
165
+ 'emit' => {
166
+ signature: 'emit',
167
+ docs: 'Emits a declaration at compile time: `emit function ...`. Only allowed inside `const function` or `inline` bodies.',
168
+ },
169
+ 'parallel' => {
170
+ signature: 'parallel',
171
+ docs: 'Concurrency construct: `parallel for i in 0..N:` (data-parallel loop) or `parallel:` block (concurrent statement dispatch). Uses OS threads via libuv.',
172
+ },
173
+ 'detach' => {
174
+ signature: 'detach',
175
+ docs: 'Spawns work on a separate thread, returning a `Handle`: `let h = detach func()`. Use `gather` to wait. Supports global function calls only.',
176
+ },
177
+ 'gather' => {
178
+ signature: 'gather',
179
+ docs: 'Blocks until one or more `detach` handles complete: `gather h1, h2`. Takes one or more `Handle` values.',
180
+ },
181
+ 'event' => {
182
+ signature: 'event',
183
+ docs: 'Typed publisher/subscriber: `event name[capacity]` or `event name[capacity](PayloadType)`. Supports `subscribe`, `emit`, `unsubscribe`, `wait`.',
184
+ },
185
+ 'attribute' => {
186
+ signature: 'attribute',
187
+ docs: 'Declares a reusable declaration attribute: `attribute[target, ...] name(params)`. Targets: struct, field, callable, const, event, enum, flags, union, variant.',
188
+ },
189
+ 'proc' => {
190
+ signature: 'proc',
191
+ docs: 'Ref-counted closure: `proc(params...) -> T: body`. Captures values by value. Storable in structs, arrays, and tuples.',
192
+ },
193
+ 'fn' => {
194
+ signature: 'fn',
195
+ docs: 'Function pointer type: `fn(params...) -> T`. Points to module-level functions. No captured state; capture-free.',
196
+ },
197
+ 'dyn' => {
198
+ signature: 'dyn',
199
+ docs: 'Runtime interface value: `dyn[Interface]`. A fat pointer carrying a data pointer and vtable. Constructed via `adapt[Interface](value)`.',
200
+ },
201
+ 'is' => {
202
+ signature: 'is',
203
+ docs: 'Variant arm membership test: `expr is Variant.arm`. Desugars to a `match` expression evaluating to `bool`. Supports `not` negation.',
204
+ },
205
+ 'pass' => {
206
+ signature: 'pass',
207
+ docs: 'Explicit no-op statement for intentionally empty block bodies.',
208
+ },
209
+ 'null' => {
210
+ signature: 'null',
211
+ docs: 'Null value for nullable types (`T?`). Use typed `null[T]` when context cannot determine the target type.',
212
+ },
213
+ 'true' => {
214
+ signature: 'true',
215
+ docs: 'Boolean literal `true`. Type is `bool`.',
216
+ },
217
+ 'false' => {
218
+ signature: 'false',
219
+ docs: 'Boolean literal `false`. Type is `bool`.',
220
+ },
221
+ 'static_assert' => {
222
+ signature: 'static_assert',
223
+ docs: 'Compile-time assertion: `static_assert(condition, message)`. Fails compilation if the compile-time condition is false.',
224
+ },
225
+ }.freeze
226
+
24
227
  def handle_hover(params)
25
228
  stages = new_perf_stages
26
229
  total_start = stages ? monotonic_time : nil
@@ -1260,10 +1463,9 @@ module MilkTea
1260
1463
  return nil unless token
1261
1464
 
1262
1465
  info = KEYWORD_HOVER_INFO[token.lexeme]
1263
- return nil unless info
1264
- return nil unless [:size_of, :align_of, :offset_of].include?(token.type)
1466
+ return info if info && [:size_of, :align_of, :offset_of].include?(token.type)
1265
1467
 
1266
- info
1468
+ LANGUAGE_KEYWORD_HOVER_INFO[token.lexeme]
1267
1469
  end
1268
1470
 
1269
1471
  def render_builtin_specialization(tokens)
@@ -58,7 +58,7 @@ module MilkTea
58
58
  KEYWORD_TOKEN_TYPES = Token::KEYWORDS.values.to_set.freeze
59
59
  DEFAULT_LIBRARY_TYPE_NAMES = Types::BUILTIN_TYPE_NAMES.to_set.freeze
60
60
  BUILTIN_FUNCTION_NAMES = %w[
61
- ref_of const_ptr_of ptr_of read fatal reinterpret array span zero default adapt get
61
+ ref_of const_ptr_of ptr_of read fatal reinterpret array span zero default adapt get field_of callable_of has_attribute attribute_of attribute_arg fields_of members_of attributes_of
62
62
  ].to_set.freeze
63
63
  BUILTIN_ASSOCIATED_HOOK_NAMES = %w[hash equal order].to_set.freeze
64
64
  BUILTIN_CALL_HOVER_INFO = {
@@ -68,7 +68,7 @@ module MilkTea
68
68
  },
69
69
  'ref_of' => {
70
70
  signature: 'builtin ref_of(value) -> ref[T]',
71
- docs: '`ref_of(x)` borrows a mutable safe lvalue as `ref[T]`.'
71
+ docs: '`ref_of(x)` borrows a safe lvalue as `ref[T]`.'
72
72
  },
73
73
  'const_ptr_of' => {
74
74
  signature: 'builtin const_ptr_of(value) -> const_ptr[T]',
@@ -76,7 +76,7 @@ module MilkTea
76
76
  },
77
77
  'ptr_of' => {
78
78
  signature: 'builtin ptr_of(value) -> ptr[T]',
79
- docs: '`ptr_of(x)` takes the address of a mutable safe lvalue as `ptr[T]`.'
79
+ docs: '`ptr_of(x)` takes the address of a safe lvalue as `ptr[T]`.'
80
80
  },
81
81
  'read' => {
82
82
  signature: 'builtin read(value) -> T',
@@ -130,6 +130,18 @@ module MilkTea
130
130
  signature: 'builtin attribute_of(target, attribute_name) -> attribute_handle',
131
131
  docs: '`attribute_of(target, attribute_name)` returns the applied attribute handle for the resolved target-and-attribute pair; use `has_attribute(...)` when absence is expected.'
132
132
  },
133
+ 'fields_of' => {
134
+ signature: 'builtin fields_of(Type) -> array[field_handle, N]',
135
+ docs: '`fields_of(Type)` returns all struct fields as a compile-time `array[field_handle, N]`, in declaration order. Use with `inline for` for reflective field iteration.'
136
+ },
137
+ 'members_of' => {
138
+ signature: 'builtin members_of(Type) -> array[member_handle, N]',
139
+ docs: '`members_of(Type)` returns all members of an enum or flags type as a compile-time `array[member_handle, N]`. Each handle exposes `.name` and `.value`.'
140
+ },
141
+ 'attributes_of' => {
142
+ signature: 'builtin attributes_of(target [, name]) -> array[attribute_handle, N]',
143
+ docs: '`attributes_of(target)` returns all attributes applied to a type, field, or callable as a compile-time array. `attributes_of(target, name)` filters by attribute kind.'
144
+ },
133
145
  }.freeze
134
146
  OPERATOR_TOKEN_TYPES = %i[
135
147
  amp colon comma caret dot lparen rparen pipe lbracket rbracket question
@@ -586,6 +586,8 @@ module MilkTea
586
586
  return unless statement.type
587
587
  return unless statement.value
588
588
 
589
+ return if statement.type.nullable
590
+
589
591
  declared_name = type_ref_name(statement.type)
590
592
  return unless declared_name
591
593
 
@@ -666,6 +668,7 @@ module MilkTea
666
668
  # enum/int/str bool-matches are never misreported.
667
669
  def check_prefer_is_variant(match_expr)
668
670
  return unless @sema_facts
671
+ return if match_expr.desugared_from_is
669
672
 
670
673
  arms = match_expr.arms
671
674
  return unless arms.size == 2
@@ -771,6 +774,8 @@ module MilkTea
771
774
  return if stmts.any?(&:nil?)
772
775
  return unless stmts.all? { |s| inline_simple_statement?(s) }
773
776
 
777
+ return if visually_inline_if?(statement, stmts)
778
+
774
779
  emit_conciseness_hint(
775
780
  "prefer-inline-if",
776
781
  line: statement.line,
@@ -779,6 +784,15 @@ module MilkTea
779
784
  )
780
785
  end
781
786
 
787
+ def visually_inline_if?(statement, stmts)
788
+ n = statement.branches.length
789
+ statement.branches.each_with_index.all? { |b, i|
790
+ stmts[i].respond_to?(:line) && stmts[i].line == b.line
791
+ } && (
792
+ stmts[n].respond_to?(:line) && statement.else_line && stmts[n].line == statement.else_line
793
+ )
794
+ end
795
+
782
796
  # prefer-conditional-expression: if/match whose every branch either
783
797
  # returns a value or assigns the same lvalue can become an expression form
784
798
  # (`return if …: … else: …` or `x = match …`).
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.2.8
4
+ version: 0.2.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Long (Teefan) Tran
@@ -583,7 +583,7 @@ metadata:
583
583
  homepage_uri: https://teefan.github.io/mt-lang/
584
584
  source_code_uri: https://github.com/teefan/mt-lang
585
585
  post_install_message: |
586
- Milk Tea 0.2.8 installed!
586
+ Milk Tea 0.2.10 installed!
587
587
 
588
588
  System requirements:
589
589
  - A C compiler (gcc or clang) must be available on PATH