mt-lang 0.2.17 → 0.2.18

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: a2b1f14318677bf9232d4163ad6aac3905650056716de05bf89762f08e1aa7d5
4
- data.tar.gz: af90e63fd1c9fd21f6af721c1c039b61159d931d20eaa3eaca3b41369da053c1
3
+ metadata.gz: 35157666294e3aadb1c8106b1ccbacdf0d548f06a009b99c2a7799e76436e320
4
+ data.tar.gz: acc6091f6f22e2bbe340ebba54f49dc0b195849294f6086959027751a6cbfee1
5
5
  SHA512:
6
- metadata.gz: cef8b0d7f55c4d9496f41d02b0ace0df87f233ff8c24892768543ff3518e68271a2e4613a7654cef661506d42835d1ea8baa9b3d84225614aae420c0d105a0a4
7
- data.tar.gz: b55024abb91b7fdf2694b4be82bf972f1410aa01b949952d30fb49be517fddd501467ebc55fcf3a6dc1a1eb01334709aa74c26a231348026e3197cc5b163b74c
6
+ metadata.gz: 831ed7f14aaef5be511434b7697f0f0d23896d78fd2cfd5ef6037fee9c9909dbce3cc6c51bd6d9b1c95dae2dbeb7990b1cece93f455990f3f7c4e911dc64e24a
7
+ data.tar.gz: 6621e043744173fe77a457a3823a6b38032b4f12a71f96f707539154578bf9b7c95d8e688d36ba7d44404148ed4f85a47b55a3a00d81fb13e466fa86e9bc1be7
data/README.md CHANGED
@@ -855,7 +855,7 @@ Core modules in `std/`:
855
855
  - `std.option.Option[T]` — optional value with `is_some`, `is_none`, `unwrap`, `expect`, `unwrap_or`, `unwrap_or_else` (auto-imported via prelude)
856
856
  - `std.result.Result[T, E]` — fallible computation with `is_success`, `is_failure`, `unwrap`, `unwrap_error`, `unwrap_or`, `unwrap_or_else`, `ok`, `error`, `map_error` (auto-imported via prelude)
857
857
 
858
- **Collections**: `std.vec.Vec[T]`, `std.deque.Deque[T]`, `std.map.Map[K,V]`, `std.set.Set[T]`, `std.ordered_map.OrderedMap[K,V]`, `std.ordered_set.OrderedSet[T]`, `std.binary_heap.BinaryHeap[T]`, `std.priority_queue.PriorityQueue[T]`, `std.linked_map.LinkedMap[K,V]`, `std.linked_set.LinkedSet[T]`, `std.counter.Counter[T]`, `std.multiset.MultiSet[T]`, `std.queue.Queue[T]`, `std.stack.Stack[T]`
858
+ **Collections**: `std.vec.Vec[T]`, `std.deque.Deque[T]`, `std.map.Map[K,V]`, `std.set.Set[T]`, `std.ordered_map.OrderedMap[K,V]`, `std.ordered_set.OrderedSet[T]`, `std.binary_heap.BinaryHeap[T]`, `std.priority_queue.PriorityQueue[T]`, `std.linked_map.LinkedMap[K,V]`, `std.linked_set.LinkedSet[T]`, `std.counter.Counter[T]`, `std.multiset.MultiSet[T]`, `std.queue.Queue[T]`, `std.stack.Stack[T]`, `std.bitset.Bitset`, `std.spatial.SpatialGrid[T]`, `std.ring_buffer.RingBuffer[T]`, `std.sparse_set.SparseSet[T]`, `std.lru_cache.LruCache[K,V]`
859
859
 
860
860
  **Serialization**: `std.json`, `std.toml`, `std.uri`, `std.serialize`
861
861
 
@@ -872,7 +872,7 @@ Core modules in `std/`:
872
872
  **Other**: `std.bytes`, `std.ctype`, `std.asset_pack`, `std.cell`
873
873
 
874
874
  See module source for full method surface. Iterator forms:
875
- - Pointer-returning (`next() -> nullable ptr[T]`): `Vec`, `Deque`, `BinaryHeap`/`PriorityQueue`/`OrderedSet` (read-only), `OrderedMap.keys`/`Map.keys`/`Set`/`LinkedMap.keys`/`LinkedSet`/`Counter.keys`/`MultiSet.values`, `Queue`/`Stack` (mutable)
875
+ - Pointer-returning (`next() -> nullable ptr[T]`): `Vec`, `Deque`, `BinaryHeap`/`PriorityQueue`/`OrderedSet` (read-only), `OrderedMap.keys`/`Map.keys`/`Set`/`LinkedMap.keys`/`LinkedSet`/`Counter.keys`/`MultiSet.values`, `Queue`/`Stack` (mutable), `RingBuffer`, `SparseSet`
876
876
  - `next() -> bool` + `current()`: `OrderedMap.entries`/`iter`, `Map.entries`/`iter`, `LinkedMap.entries`/`iter`, `Counter.counts`/`entries`/`iter`, `MultiSet.entries`/`iter`, `SnapshotValues`/`SnapshotEntries`
877
877
 
878
878
  ## 12. Strings, Text, And Builders
data/docs/index.html CHANGED
@@ -2164,6 +2164,11 @@ function attach(window: ref[Window]) -> Result[void, EventError]:
2164
2164
  <tr><td><code>std.multiset</code></td><td><code>MultiSet[T]</code></td><td>Values read-only; entries <code>current()</code></td><td>Bag with per-element counts</td></tr>
2165
2165
  <tr><td><code>std.queue</code></td><td><code>Queue[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>FIFO facade over Deque</td></tr>
2166
2166
  <tr><td><code>std.stack</code></td><td><code>Stack[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>LIFO facade over Deque</td></tr>
2167
+ <tr><td><code>std.bitset</code></td><td><code>Bitset</code></td><td>&mdash;</td><td>Bit-level set with popcount</td></tr>
2168
+ <tr><td><code>std.spatial</code></td><td><code>SpatialGrid[T]</code></td><td>&mdash;</td><td>Uniform spatial hash grid (returns Vec)</td></tr>
2169
+ <tr><td><code>std.ring_buffer</code></td><td><code>RingBuffer[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>Fixed-capacity circular buffer</td></tr>
2170
+ <tr><td><code>std.sparse_set</code></td><td><code>SparseSet[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>O(1) insert/remove; dense iteration</td></tr>
2171
+ <tr><td><code>std.lru_cache</code></td><td><code>LruCache[K,V]</code></td><td><code>entries()</code></td><td>Capacity-bounded LRU eviction map</td></tr>
2167
2172
  </table>
2168
2173
  </div>
2169
2174
 
@@ -1129,6 +1129,11 @@ The current shipped collection modules in `std` are:
1129
1129
  - `std.multiset.MultiSet[T]`: insertion-ordered bag built on `Counter[T]`, with `create`, `with_capacity`, `len`, `total_count`, `distinct_len`, `capacity`, `is_empty`, `count`, `contains`, `values`, `entries`, `iter`, `is_subset`, `union_with`, `intersection`, `difference`, `symmetric_difference`, `clear`, `release`, `reserve`, `insert`, `add`, `remove_one`, and `remove_all`.
1130
1130
  - `std.queue.Queue[T]`: FIFO facade over `Deque[T]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `clear`, `release`, `reserve`, `enqueue`, and `dequeue`.
1131
1131
  - `std.stack.Stack[T]`: LIFO facade over `Deque[T]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `clear`, `release`, `reserve`, `push`, and `pop`.
1132
+ - `std.bitset.Bitset`: bit-level set backed by a heap-allocated word array, with `create`, `with_capacity`, `len`, `is_empty`, `set`, `clear`, `test`, `toggle`, `count`, `any`, `all`, `none`, `find_first_set`, `find_first_clear`, `clear_all`, `reserve`, and `release`.
1133
+ - `std.spatial.SpatialGrid[T]`: uniform spatial hash grid with `create`, `with_origin`, `insert`, `insert_many`, `query_radius`, `clear`, `release`, `cell_count`, `occupied_cells`, and `entity_count`.
1134
+ - `std.ring_buffer.RingBuffer[T]`: fixed-capacity circular buffer that overwrites oldest entries when full, with `with_capacity`, `len`, `capacity`, `is_empty`, `is_full`, `iter`, `get`, `at`, `peek`, `push`, `pop`, `clear`, and `release`.
1135
+ - `std.sparse_set.SparseSet[T]`: O(1) insert/remove/contains with cache-friendly dense iteration, keyed by `ptr_uint` handle, with `create`, `with_capacity`, `len`, `is_empty`, `iter`, `get`, `at`, `contains`, `key_at`, `insert`, `remove`, `clear`, `reserve`, and `release`.
1136
+ - `std.lru_cache.LruCache[K, V]`: capacity-bounded linked hash map with LRU eviction, keyed by the canonical `hash[K](...)` and `equal[K](...)` hooks, with `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `get`, `at`, `contains`, `set`, `remove`, `clear`, and `release`.
1132
1137
  - `std.linked_map_view.SnapshotValues[K, V]`: read-only snapshot view over `LinkedMap` values in insertion order, with `create(values: linked_map.Entries[K, V])` and `iter`.
1133
1138
  - `std.linked_map_view.SnapshotEntries[K, V]`: read-only snapshot view over `LinkedMap` entries in insertion order, with `create(values: linked_map.Entries[K, V])` and `iter`.
1134
1139
 
@@ -1151,7 +1156,10 @@ Iterator notes for those collection modules:
1151
1156
  - `MultiSet.values()` uses the pointer-returning iterator form with read-only value pointers in first-seen order.
1152
1157
  - `MultiSet.entries()` and `MultiSet.iter()` use the `next() -> bool` plus `current()` iterator form and yield immutable `{ value, count }` snapshots.
1153
1158
  - `Queue.iter()` and `Stack.iter()` use the same mutable pointer-returning iterator form as `Deque.iter()`, and `peek()` returns a mutable element pointer because changing an element value does not violate FIFO/LIFO ordering invariants.
1159
+ - `RingBuffer.iter()` uses the pointer-returning iterator form with mutable element pointers.
1160
+ - `SparseSet.iter()` uses the pointer-returning iterator form with mutable element pointers in dense order.
1154
1161
  - `SnapshotValues.iter()` and `SnapshotEntries.iter()` use the `next() -> bool` plus `current()` iterator form in insertion order.
1162
+ - `SpatialGrid` does not expose a direct iterator; query results are returned as a `Vec[T]`.
1155
1163
 
1156
1164
  ## 8. Strings, C Strings, And Format Strings
1157
1165
 
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.17"
6
+ VERSION = "0.2.18"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -122,11 +122,12 @@ module MilkTea
122
122
  start_line = [binding.ast.respond_to?(:line) ? binding.ast.line : nil, snapshots.first.line].compact.min
123
123
  end_line = snapshots.last.line
124
124
 
125
- # Generic function bodies are not analysed during structural checking,
126
- # so the frame only has the initial snapshot at the declaration line.
127
- # Extend end_line to cover the actual body so that completion and hover
128
- # lookups for parameters resolve correctly inside the body.
129
- if snapshots.length == 1 && binding.ast.respond_to?(:body) && binding.ast.body
125
+ # Extend end_line to cover the actual function body so that completion
126
+ # and hover lookups resolve correctly even when snapshots don't reach
127
+ # the final body line (common for generic functions whose bodies are
128
+ # only partially analysed, and functions whose last statement does not
129
+ # record a snapshot).
130
+ if binding.ast.respond_to?(:body) && binding.ast.body
130
131
  body_end = last_ast_line(binding.ast.body)
131
132
  end_line = body_end if body_end && body_end > end_line
132
133
  end
@@ -334,6 +334,7 @@ module MilkTea
334
334
  when AST::LocalDecl
335
335
  check_expr_names(stmt.value, scopes, binding, names) if stmt.value
336
336
  names.add(stmt.name) if stmt.name
337
+ try_record_generic_local_snapshot(stmt, scopes, binding) if stmt.name && stmt.value
337
338
  when AST::Assignment
338
339
  check_expr_names(stmt.target, scopes, binding, names)
339
340
  check_expr_names(stmt.value, scopes, binding, names)
@@ -457,6 +458,138 @@ module MilkTea
457
458
  raise_sema_error("unknown name #{name}", node)
458
459
  end
459
460
 
461
+ # When a generic method body contains a let/var declaration, always record a
462
+ # completion snapshot so hover and completion can discover the local, even
463
+ # when full type inference is impossible. The inferred type is used when
464
+ # available; otherwise the explicit type annotation on the declaration is
465
+ # consulted. As a last resort a fallback primitive type is used so the
466
+ # binding is never silently dropped.
467
+ def try_record_generic_local_snapshot(stmt, scopes, binding)
468
+ type = infer_local_initializer_type(stmt, scopes) ||
469
+ explicit_declaration_type_for(stmt) ||
470
+ fallback_completion_type
471
+
472
+ decl_kind = stmt.kind == :var ? :var : :let
473
+ vb = value_binding(name: stmt.name, type: type, mutable: stmt.kind == :var, kind: decl_kind)
474
+ new_scopes = scopes.dup.unshift({ stmt.name => vb })
475
+ record_local_completion_snapshot(stmt.line, stmt.column, new_scopes)
476
+ end
477
+
478
+ def infer_local_initializer_type(stmt, scopes)
479
+ return nil unless stmt.value
480
+
481
+ infer_simple_local_type(stmt.value, scopes)
482
+ end
483
+
484
+ def explicit_declaration_type_for(stmt)
485
+ annotation = stmt.respond_to?(:type) ? stmt.type : nil
486
+ return nil unless annotation
487
+
488
+ name = annotation.respond_to?(:name) ? annotation.name : annotation.to_s
489
+ @ctx.types[name]
490
+ end
491
+
492
+ def fallback_completion_type
493
+ @ctx.types["int"] || @ctx.types.values.first
494
+ end
495
+
496
+ def infer_simple_local_type(value, scopes)
497
+ case value
498
+ when AST::Call
499
+ infer_call_return_type(value, scopes)
500
+ when AST::MemberAccess
501
+ infer_member_access_field_type(value, scopes)
502
+ when AST::Identifier
503
+ (binding = lookup_value(value.name, scopes)) ? binding.type : nil
504
+ when AST::IntegerLiteral
505
+ @ctx.types["int"]
506
+ when AST::FloatLiteral
507
+ @ctx.types["float"] || @ctx.types["double"]
508
+ when AST::CharLiteral
509
+ @ctx.types["ubyte"]
510
+ when AST::BooleanLiteral
511
+ @ctx.types["bool"]
512
+ when AST::StringLiteral
513
+ @ctx.types["str"]
514
+ when AST::PrefixCast
515
+ if value.target_type.respond_to?(:name)
516
+ @ctx.types[value.target_type.name] || @ctx.types["int"]
517
+ else
518
+ @ctx.types["int"]
519
+ end
520
+ end
521
+ end
522
+
523
+ def infer_member_access_field_type(value, scopes)
524
+ receiver_type = case value.receiver
525
+ when AST::Identifier
526
+ if value.receiver.name == "this"
527
+ (binding = lookup_value("this", scopes)) ? binding.type : nil
528
+ else
529
+ (binding = lookup_value(value.receiver.name, scopes)) ? binding.type : nil
530
+ end
531
+ end
532
+ return nil unless receiver_type
533
+
534
+ field_receiver = receiver_type_for_fields(receiver_type)
535
+ field_receiver&.respond_to?(:fields) ? field_receiver.fields[value.member] : nil
536
+ end
537
+
538
+ def infer_call_return_type(call, scopes)
539
+ callee = call.callee
540
+ callee = callee.callee while callee.is_a?(AST::Specialization)
541
+
542
+ case callee
543
+ when AST::Identifier
544
+ func = @ctx.top_level_functions[callee.name]
545
+ return func.type.return_type if func
546
+
547
+ member = callee.name
548
+ this_binding = lookup_value("this", scopes)
549
+ if this_binding && (rt = receiver_type_for_fields(this_binding.type))
550
+ method = @ctx.methods.dig(rt, member)
551
+ return method.type.return_type if method
552
+ end
553
+ when AST::MemberAccess
554
+ name = resolve_receiver_name_for_call_inference(callee, scopes)
555
+ member = callee.member
556
+ return nil unless name
557
+
558
+ if (import = @ctx.imports[name])
559
+ func = import.functions[member]
560
+ return func.type.return_type if func
561
+ end
562
+
563
+ receiver_type = @ctx.types[name]
564
+ if receiver_type && @ctx.methods.key?(receiver_type)
565
+ method = @ctx.methods[receiver_type]["static:#{member}"] || @ctx.methods[receiver_type][member]
566
+ return method.type.return_type if method
567
+ end
568
+ end
569
+ nil
570
+ end
571
+
572
+ def resolve_receiver_name_for_call_inference(callee, scopes)
573
+ case callee.receiver
574
+ when AST::Identifier
575
+ if callee.receiver.name == "this"
576
+ binding = lookup_value("this", scopes)
577
+ return nil unless binding
578
+ this_type = receiver_type_for_fields(binding.type)
579
+ this_type&.name
580
+ else
581
+ callee.receiver.name
582
+ end
583
+ end
584
+ end
585
+
586
+ def receiver_type_for_fields(type)
587
+ type = type.base while type.is_a?(Types::Nullable)
588
+ type = type.arguments.first if type.respond_to?(:name) && type.name == 'ref' && type.respond_to?(:arguments) && type.arguments&.length == 1
589
+ type = type.definition if type.respond_to?(:definition)
590
+ type
591
+ end
592
+
460
593
  # Scans generic method bodies for assignments to this through a
461
594
  # non-editable receiver. Full body checking is deferred to call-site
462
595
  # specialization, but immutable-this violations are type-independent.
@@ -491,8 +624,10 @@ module MilkTea
491
624
  with_scope(binding.body_params) do |scopes|
492
625
  start_local_completion_frame(binding, scopes)
493
626
  if binding.type_params.any?
494
- check_generic_method_immutable_this(binding, scopes)
495
- check_generic_method_names(binding, scopes)
627
+ if binding.ast.respond_to?(:body)
628
+ check_generic_method_immutable_this(binding, scopes)
629
+ check_generic_method_names(binding, scopes)
630
+ end
496
631
  return
497
632
  end
498
633
 
@@ -22,7 +22,19 @@ module MilkTea
22
22
  if @nullability_flow_result && idx + 1 < statements.length
23
23
  apply_nullability_continuation_refinements!(nested_scopes, statements[idx + 1])
24
24
  end
25
- record_local_completion_snapshot(statement_end_line(statement), 1_000_000, nested_scopes)
25
+ end_line = statement_end_line(statement)
26
+ # When the statement spans multiple lines (e.g. let … else:),
27
+ # record an extra snapshot at the declaration line so that
28
+ # hover and completion lookups on the declaration line itself
29
+ # can still find the new binding.
30
+ if end_line && statement.respond_to?(:line) && statement.line && end_line > statement.line
31
+ record_local_completion_snapshot(
32
+ statement.line,
33
+ statement.respond_to?(:column) ? statement.column : 0,
34
+ nested_scopes,
35
+ )
36
+ end
37
+ record_local_completion_snapshot(end_line, 1_000_000, nested_scopes)
26
38
  rescue SemanticError => e
27
39
  if @collecting_errors
28
40
  @structural_errors << e
@@ -1264,9 +1264,14 @@ module MilkTea
1264
1264
 
1265
1265
  header_line_tokens = non_trivia_tokens_on_line(tokens, current.line)
1266
1266
  header = header_line_tokens.first
1267
+ type_index = 1
1268
+ if header&.type == :public
1269
+ header = header_line_tokens[1]
1270
+ type_index = 2
1271
+ end
1267
1272
  return nil unless [:struct, :union].include?(header&.type)
1268
1273
 
1269
- type_token = header_line_tokens[1]
1274
+ type_token = header_line_tokens[type_index]
1270
1275
  return nil unless type_token&.type == :identifier
1271
1276
 
1272
1277
  return resolve_type_receiver_info(facts, type_token.lexeme, type_token.lexeme)
@@ -462,7 +462,9 @@ module MilkTea
462
462
  struct_type = find_struct_containing_field(facts, field_name)
463
463
  return nil unless struct_type
464
464
 
465
- changes = collect_struct_field_changes(uri, field_name, new_name)
465
+ changes = {}
466
+ edits = collect_struct_field_changes(uri, field_name, new_name)
467
+ changes[uri] = edits if edits
466
468
  related_uris = @workspace.related_open_document_uris(uri)
467
469
  related_uris.each do |related_uri|
468
470
  next if related_uri == uri
data/std/lru_cache.mt ADDED
@@ -0,0 +1,75 @@
1
+ import std.linked_map as linked_map
2
+
3
+ public struct LruCache[K, V]:
4
+ values: linked_map.LinkedMap[K, V]
5
+ max_capacity: ptr_uint
6
+
7
+
8
+ extending LruCache[K, V]:
9
+ public static function with_capacity(capacity: ptr_uint) -> LruCache[K, V]:
10
+ return LruCache[K, V](values = linked_map.LinkedMap[K, V].with_capacity(capacity), max_capacity = capacity)
11
+
12
+
13
+ public function len() -> ptr_uint:
14
+ return this.values.len()
15
+
16
+
17
+ public function capacity() -> ptr_uint:
18
+ return this.max_capacity
19
+
20
+
21
+ public function is_empty() -> bool:
22
+ return this.values.is_empty()
23
+
24
+
25
+ public function iter() -> linked_map.Entries[K, V]:
26
+ return this.values.entries()
27
+
28
+
29
+ public editable function get(key: K) -> ptr[V]?:
30
+ let removed = this.values.remove_entry(key)
31
+ match removed:
32
+ Option.some as entry:
33
+ this.values.set(key, entry.value.value)
34
+ return this.values.get(key)
35
+ Option.none:
36
+ return null
37
+
38
+
39
+ public editable function at(key: K) -> Option[V]:
40
+ let p = this.get(key) else:
41
+ return Option[V].none
42
+
43
+ unsafe:
44
+ return Option[V].some(value = read(p))
45
+
46
+
47
+ public function contains(key: K) -> bool:
48
+ return this.values.contains(key)
49
+
50
+
51
+ public editable function clear() -> void:
52
+ this.values.clear()
53
+
54
+
55
+ public editable function release() -> void:
56
+ this.values.release()
57
+
58
+
59
+ public editable function set(key: K, value: V) -> void:
60
+ this.values.remove(key)
61
+
62
+ if this.values.len() == this.max_capacity:
63
+ var key_iter = this.values.keys()
64
+ let first_key_ptr = key_iter.next() else:
65
+ return
66
+
67
+ unsafe:
68
+ let first_key = read(ptr[K]<-first_key_ptr)
69
+ this.values.remove(first_key)
70
+
71
+ this.values.set(key, value)
72
+
73
+
74
+ public editable function remove(key: K) -> bool:
75
+ return this.values.remove(key).is_some()
@@ -0,0 +1,131 @@
1
+ import std.mem.heap as heap
2
+
3
+ public struct RingBuffer[T]:
4
+ data: own[T]?
5
+ head: ptr_uint
6
+ len: ptr_uint
7
+ capacity: ptr_uint
8
+
9
+ public struct Iter[T]:
10
+ data: ptr[T]?
11
+ head: ptr_uint
12
+ len: ptr_uint
13
+ capacity: ptr_uint
14
+ index: ptr_uint
15
+
16
+
17
+ extending RingBuffer[T]:
18
+ public static function with_capacity(capacity: ptr_uint) -> RingBuffer[T]:
19
+ let data = heap.must_alloc[T](capacity)
20
+ return RingBuffer[T](data = data, head = 0, len = 0, capacity = capacity)
21
+
22
+
23
+ static function physical_index(head: ptr_uint, capacity: ptr_uint, index: ptr_uint) -> ptr_uint:
24
+ let raw = head + index
25
+ if raw >= capacity:
26
+ return raw - capacity
27
+
28
+ return raw
29
+
30
+
31
+ public function len() -> ptr_uint:
32
+ return this.len
33
+
34
+
35
+ public function capacity() -> ptr_uint:
36
+ return this.capacity
37
+
38
+
39
+ public function is_empty() -> bool:
40
+ return this.len == 0
41
+
42
+
43
+ public function is_full() -> bool:
44
+ return this.len == this.capacity
45
+
46
+
47
+ public function iter() -> Iter[T]:
48
+ return Iter[T](data = this.data, head = this.head, len = this.len, capacity = this.capacity, index = 0)
49
+
50
+
51
+ public function get(index: ptr_uint) -> ptr[T]?:
52
+ if index >= this.len:
53
+ return null
54
+
55
+ let data = this.data else:
56
+ fatal(c"ring_buffer.RingBuffer.get missing storage")
57
+
58
+ return unsafe: ptr[T]<-data + RingBuffer[T].physical_index(this.head, this.capacity, index)
59
+
60
+
61
+ public function at(index: ptr_uint) -> Option[T]:
62
+ let p = this.get(index) else:
63
+ return Option[T].none
64
+
65
+ unsafe:
66
+ return Option[T].some(value = read(p))
67
+
68
+
69
+ public function peek() -> ptr[T]?:
70
+ return this.get(0)
71
+
72
+
73
+ public editable function push(value: T) -> void:
74
+ let data = this.data else:
75
+ fatal(c"ring_buffer.RingBuffer.push missing storage")
76
+
77
+ let tail = RingBuffer[T].physical_index(this.head, this.capacity, this.len)
78
+
79
+ unsafe:
80
+ let data_ptr = ptr[T]<-data
81
+ read(data_ptr + tail) = value
82
+
83
+ if this.len == this.capacity:
84
+ this.head = RingBuffer[T].physical_index(this.head, this.capacity, 1)
85
+ else:
86
+ this.len += 1
87
+
88
+
89
+ public editable function pop() -> Option[T]:
90
+ if this.len == 0:
91
+ return Option[T].none
92
+
93
+ let data = this.data else:
94
+ fatal(c"ring_buffer.RingBuffer.pop missing storage")
95
+
96
+ unsafe:
97
+ let data_ptr = ptr[T]<-data
98
+ let value = read(data_ptr + this.head)
99
+ this.head = RingBuffer[T].physical_index(this.head, this.capacity, 1)
100
+ this.len -= 1
101
+ return Option[T].some(value = value)
102
+
103
+
104
+ public editable function clear() -> void:
105
+ this.head = 0
106
+ this.len = 0
107
+
108
+
109
+ public editable function release() -> void:
110
+ heap.release(this.data)
111
+ this.data = null
112
+ this.head = 0
113
+ this.len = 0
114
+ this.capacity = 0
115
+
116
+
117
+ extending Iter[T]:
118
+ public function iter() -> Iter[T]:
119
+ return this
120
+
121
+
122
+ public editable function next() -> ptr[T]?:
123
+ if this.index >= this.len:
124
+ return null
125
+
126
+ let data = this.data else:
127
+ fatal(c"ring_buffer.Iter.next missing storage")
128
+
129
+ let current_index = this.index
130
+ this.index += 1
131
+ return unsafe: ptr[T]<-data + RingBuffer[T].physical_index(this.head, this.capacity, current_index)
data/std/sparse_set.mt ADDED
@@ -0,0 +1,170 @@
1
+ import std.vec as vec
2
+
3
+ const SPARSE_EMPTY: ptr_uint = ~ptr_uint<-0
4
+
5
+ public struct SparseSet[T]:
6
+ sparse: vec.Vec[ptr_uint]
7
+ dense: vec.Vec[T]
8
+ keys: vec.Vec[ptr_uint]
9
+
10
+ public struct Iter[T]:
11
+ data: ptr[T]?
12
+ index: ptr_uint
13
+ len: ptr_uint
14
+
15
+
16
+ extending SparseSet[T]:
17
+ public static function create() -> SparseSet[T]:
18
+ return SparseSet[T](
19
+ sparse = vec.Vec[ptr_uint].create(),
20
+ dense = vec.Vec[T].create(),
21
+ keys = vec.Vec[ptr_uint].create(),
22
+ )
23
+
24
+
25
+ public static function with_capacity(capacity: ptr_uint) -> SparseSet[T]:
26
+ var result = SparseSet[T].create()
27
+ result.reserve(capacity)
28
+ return result
29
+
30
+
31
+ public function len() -> ptr_uint:
32
+ return this.dense.len()
33
+
34
+
35
+ public function is_empty() -> bool:
36
+ return this.dense.is_empty()
37
+
38
+
39
+ public function iter() -> Iter[T]:
40
+ let view = this.dense.as_span()
41
+ return Iter[T](data = view.data, index = 0, len = view.len)
42
+
43
+
44
+ public function get(key: ptr_uint) -> ptr[T]?:
45
+ if key >= this.sparse.len():
46
+ return null
47
+
48
+ let position = this.sparse.get(key) else:
49
+ fatal(c"sparse_set.SparseSet.get missing sparse storage")
50
+
51
+ unsafe:
52
+ let index = read(ptr[ptr_uint]<-position)
53
+ if index == SPARSE_EMPTY:
54
+ return null
55
+
56
+ return this.dense.get(index)
57
+
58
+
59
+ public function at(key: ptr_uint) -> Option[T]:
60
+ let p = this.get(key) else:
61
+ return Option[T].none
62
+
63
+ unsafe:
64
+ return Option[T].some(value = read(p))
65
+
66
+
67
+ public function contains(key: ptr_uint) -> bool:
68
+ return this.get(key) != null
69
+
70
+
71
+ public function key_at(dense_index: ptr_uint) -> ptr_uint:
72
+ let key_ptr = this.keys.get(dense_index) else:
73
+ fatal(c"sparse_set.SparseSet.key_at missing keys storage")
74
+
75
+ unsafe:
76
+ return read(ptr[ptr_uint]<-key_ptr)
77
+
78
+
79
+ public editable function clear() -> void:
80
+ this.sparse.clear()
81
+ this.dense.clear()
82
+ this.keys.clear()
83
+
84
+
85
+ public editable function release() -> void:
86
+ this.sparse.release()
87
+ this.dense.release()
88
+ this.keys.release()
89
+
90
+
91
+ public editable function reserve(min_capacity: ptr_uint) -> void:
92
+ this.dense.reserve(min_capacity)
93
+ this.keys.reserve(min_capacity)
94
+
95
+
96
+ public editable function insert(key: ptr_uint, value: T) -> bool:
97
+ while key >= this.sparse.len():
98
+ this.sparse.push(SPARSE_EMPTY)
99
+
100
+ let position = this.sparse.get(key) else:
101
+ fatal(c"sparse_set.SparseSet.insert missing sparse storage")
102
+
103
+ unsafe:
104
+ if read(ptr[ptr_uint]<-position) != SPARSE_EMPTY:
105
+ return false
106
+
107
+ let dense_index = this.dense.len()
108
+ this.dense.push(value)
109
+ this.keys.push(key)
110
+
111
+ unsafe:
112
+ read(ptr[ptr_uint]<-position) = dense_index
113
+
114
+ return true
115
+
116
+
117
+ public editable function remove(key: ptr_uint) -> bool:
118
+ if key >= this.sparse.len():
119
+ return false
120
+
121
+ let dense_index_ptr = this.sparse.get(key) else:
122
+ fatal(c"sparse_set.SparseSet.remove missing sparse storage")
123
+
124
+ unsafe:
125
+ let dense_index = read(ptr[ptr_uint]<-dense_index_ptr)
126
+ if dense_index == SPARSE_EMPTY:
127
+ return false
128
+
129
+ let last_index = this.dense.len() - 1
130
+ if dense_index != last_index:
131
+ let last_value = this.dense.get(last_index) else:
132
+ fatal(c"sparse_set.SparseSet.remove missing last dense value")
133
+ let dense_slot = this.dense.get(dense_index) else:
134
+ fatal(c"sparse_set.SparseSet.remove missing dense slot")
135
+ read(dense_slot) = read(last_value)
136
+
137
+ let last_key = this.keys.get(last_index) else:
138
+ fatal(c"sparse_set.SparseSet.remove missing last key")
139
+ let moved_key = read(ptr[ptr_uint]<-last_key)
140
+
141
+ let key_slot = this.keys.get(dense_index) else:
142
+ fatal(c"sparse_set.SparseSet.remove missing key slot")
143
+ read(ptr[ptr_uint]<-key_slot) = moved_key
144
+
145
+ let moved_sparse_ptr = this.sparse.get(moved_key) else:
146
+ fatal(c"sparse_set.SparseSet.remove missing sparse entry for moved key")
147
+ read(ptr[ptr_uint]<-moved_sparse_ptr) = dense_index
148
+
149
+ this.dense.pop()
150
+ this.keys.pop()
151
+ read(ptr[ptr_uint]<-dense_index_ptr) = SPARSE_EMPTY
152
+
153
+ return true
154
+
155
+
156
+ extending Iter[T]:
157
+ public function iter() -> Iter[T]:
158
+ return this
159
+
160
+
161
+ public editable function next() -> ptr[T]?:
162
+ if this.index >= this.len:
163
+ return null
164
+
165
+ let data = this.data else:
166
+ fatal(c"sparse_set.Iter.next missing storage")
167
+
168
+ let current_index = this.index
169
+ this.index += 1
170
+ return unsafe: ptr[T]<-data + current_index
data/std/spatial.mt CHANGED
@@ -9,7 +9,53 @@ public struct SpatialGrid[T]:
9
9
  origin_y: float
10
10
 
11
11
 
12
+ function cell_index[T](grid: ref[SpatialGrid[T]], x: float, y: float) -> Option[ptr_uint]:
13
+ let cx = int<-(float<-((x - grid.origin_x) / grid.cell_size))
14
+ let cy = int<-(float<-((y - grid.origin_y) / grid.cell_size))
15
+ if cx < 0 or cy < 0 or uint<-cx >= grid.cols or uint<-cy >= grid.rows:
16
+ return Option[ptr_uint].none()
17
+ return Option[ptr_uint].some(value = ptr_uint<-cx + ptr_uint<-cy * ptr_uint<-grid.cols)
18
+
19
+
12
20
  extending SpatialGrid[T]:
21
+ public static function create(cell_size: float, width: float, height: float) -> SpatialGrid[T]:
22
+ var cols: uint = uint<-(float<-width / cell_size)
23
+ var rows: uint = uint<-(float<-height / cell_size)
24
+ if cols == 0:
25
+ cols = 1
26
+ if rows == 0:
27
+ rows = 1
28
+
29
+ var cells = vec.Vec[vec.Vec[T]].create()
30
+ var total: ptr_uint = ptr_uint<-cols * ptr_uint<-rows
31
+ var i: ptr_uint = 0
32
+ while i < total:
33
+ cells.push(vec.Vec[T].create())
34
+ i += 1
35
+
36
+ return SpatialGrid[T](
37
+ cells = cells,
38
+ cell_size = cell_size,
39
+ cols = cols,
40
+ rows = rows,
41
+ origin_x = 0.0,
42
+ origin_y = 0.0
43
+ )
44
+
45
+
46
+ public static function with_origin(
47
+ cell_size: float,
48
+ width: float,
49
+ height: float,
50
+ origin_x: float,
51
+ origin_y: float
52
+ ) -> SpatialGrid[T]:
53
+ var grid = SpatialGrid[T].create(cell_size, width, height)
54
+ grid.origin_x = origin_x
55
+ grid.origin_y = origin_y
56
+ return grid
57
+
58
+
13
59
  public editable function release() -> void:
14
60
  var i: ptr_uint = 0
15
61
  while i < this.cells.len():
@@ -20,53 +66,6 @@ extending SpatialGrid[T]:
20
66
  this.cells.release()
21
67
 
22
68
 
23
- function cell_index[T](grid: ref[SpatialGrid[T]], x: float, y: float) -> Option[ptr_uint]:
24
- let cx = int<-(float<-((x - grid.origin_x) / grid.cell_size))
25
- let cy = int<-(float<-((y - grid.origin_y) / grid.cell_size))
26
- if cx < 0 or cy < 0 or uint<-cx >= grid.cols or uint<-cy >= grid.rows:
27
- return Option[ptr_uint].none()
28
- return Option[ptr_uint].some(value = ptr_uint<-cx + ptr_uint<-cy * ptr_uint<-grid.cols)
29
-
30
-
31
- public function new[T](cell_size: float, width: float, height: float) -> SpatialGrid[T]:
32
- var cols: uint = uint<-(float<-width / cell_size)
33
- var rows: uint = uint<-(float<-height / cell_size)
34
- if cols == 0:
35
- cols = 1
36
- if rows == 0:
37
- rows = 1
38
-
39
- var cells = vec.Vec[vec.Vec[T]].create()
40
- var total: ptr_uint = ptr_uint<-cols * ptr_uint<-rows
41
- var i: ptr_uint = 0
42
- while i < total:
43
- cells.push(vec.Vec[T].create())
44
- i += 1
45
-
46
- return SpatialGrid[T](
47
- cells = cells,
48
- cell_size = cell_size,
49
- cols = cols,
50
- rows = rows,
51
- origin_x = 0.0,
52
- origin_y = 0.0
53
- )
54
-
55
-
56
- public function new_with_origin[T](
57
- cell_size: float,
58
- width: float,
59
- height: float,
60
- origin_x: float,
61
- origin_y: float
62
- ) -> SpatialGrid[T]:
63
- var grid = new[T](cell_size, width, height)
64
- grid.origin_x = origin_x
65
- grid.origin_y = origin_y
66
- return grid
67
-
68
-
69
- extending SpatialGrid[T]:
70
69
  public editable function insert(entity: T, x: float, y: float) -> void:
71
70
  let idx_opt = cell_index(ref_of(this), x, y)
72
71
  match idx_opt:
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.17
4
+ version: 0.2.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Long (Teefan) Tran
@@ -498,6 +498,7 @@ files:
498
498
  - std/linked_map_view.mt
499
499
  - std/linked_set.mt
500
500
  - std/log.mt
501
+ - std/lru_cache.mt
501
502
  - std/map.mt
502
503
  - std/math.mt
503
504
  - std/mem/arena.mt
@@ -543,6 +544,7 @@ files:
543
544
  - std/raylib/tracy_gpu.mt
544
545
  - std/raymath.mt
545
546
  - std/result.mt
547
+ - std/ring_buffer.mt
546
548
  - std/rlgl.mt
547
549
  - std/rpng.mt
548
550
  - std/rres.mt
@@ -550,6 +552,7 @@ files:
550
552
  - std/sdl3/runtime.mt
551
553
  - std/serialize.mt
552
554
  - std/set.mt
555
+ - std/sparse_set.mt
553
556
  - std/spatial.mt
554
557
  - std/sqlite3.mt
555
558
  - std/stack.mt
@@ -583,7 +586,7 @@ metadata:
583
586
  homepage_uri: https://teefan.github.io/mt-lang/
584
587
  source_code_uri: https://github.com/teefan/mt-lang
585
588
  post_install_message: |
586
- Milk Tea 0.2.17 installed!
589
+ Milk Tea 0.2.18 installed!
587
590
 
588
591
  System requirements:
589
592
  - A C compiler (gcc or clang) must be available on PATH