mt-lang 0.4.1 → 0.4.21
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 +4 -4
- data/README.md +11 -1
- data/docs/index.html +76 -2
- data/docs/language-design.md +2 -2
- data/docs/language-manual.md +15 -3
- data/docs/testing.md +91 -0
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/control_flow/builder.rb +10 -1
- data/lib/milk_tea/core/lowering/assertions.rb +100 -0
- data/lib/milk_tea/core/lowering/block.rb +5 -0
- data/lib/milk_tea/core/lowering/calls.rb +2 -0
- data/lib/milk_tea/core/lowering/resolve.rb +4 -0
- data/lib/milk_tea/core/lowering.rb +2 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +31 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +8 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +2 -1
- data/lib/milk_tea/lsp/server/completion.rb +9 -18
- data/lib/milk_tea/lsp/server/utilities.rb +2 -3
- data/lib/milk_tea/lsp/workspace/caches.rb +2 -0
- data/lib/milk_tea/lsp/workspace/definition_index.rb +12 -13
- data/lib/milk_tea/lsp/workspace/store.rb +6 -1
- data/lib/milk_tea/lsp/workspace/utilities.rb +50 -30
- data/lib/milk_tea/lsp/workspace.rb +5 -1
- data/lib/milk_tea/tooling/cli/commands/run.rb +3 -2
- data/lib/milk_tea/tooling/cli/commands/test.rb +39 -40
- data/lib/milk_tea/tooling/cli.rb +5 -4
- data/lib/milk_tea/tooling/linter/rules.rb +13 -2
- metadata +4 -3
- data/std/testing.mt +0 -266
data/std/testing.mt
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
# Standard library: testing core (T0 prototype)
|
|
2
|
-
#
|
|
3
|
-
# Minimal, in-language unit-testing surface — see docs/testing.md (§5, T0).
|
|
4
|
-
# A test function returns `Check` and propagates the first failure with `?`:
|
|
5
|
-
#
|
|
6
|
-
# import std.testing as t
|
|
7
|
-
#
|
|
8
|
-
# function test_math() -> t.Check:
|
|
9
|
-
# t.expect_equal_int(2 + 2, 4)?
|
|
10
|
-
# return t.ok()
|
|
11
|
-
#
|
|
12
|
-
# A hand-written runner (see docs/testing.md §6 for future compiler-driven
|
|
13
|
-
# discovery) calls `record` per test and `summarize` at the end:
|
|
14
|
-
#
|
|
15
|
-
# function main() -> int:
|
|
16
|
-
# var stats = t.Stats.create()
|
|
17
|
-
# stats = t.record(stats, "math", test_math())
|
|
18
|
-
# return t.summarize(stats)
|
|
19
|
-
|
|
20
|
-
import std.string as string
|
|
21
|
-
import std.fmt as fmt
|
|
22
|
-
import std.stdio as stdio
|
|
23
|
-
import std.str
|
|
24
|
-
import std.hash
|
|
25
|
-
|
|
26
|
-
# A test outcome: success carries no meaningful value; failure carries a
|
|
27
|
-
# `Failure`. `Result` is used (not a bespoke variant) so `?` propagation works.
|
|
28
|
-
|
|
29
|
-
# A failed (or skipped) expectation. `message` is owned and must be released by
|
|
30
|
-
# whoever consumes it (the runner does this in `record`). `is_skip` distinguishes
|
|
31
|
-
# a skip from a real assertion failure.
|
|
32
|
-
public struct Failure:
|
|
33
|
-
message: string.String
|
|
34
|
-
is_skip: bool
|
|
35
|
-
|
|
36
|
-
public type Check = Result[bool, Failure]
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# Tally of outcomes for a run. Value type; `record` returns an updated copy.
|
|
40
|
-
public struct Stats:
|
|
41
|
-
passed: int
|
|
42
|
-
failed: int
|
|
43
|
-
skipped: int
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
extending Stats:
|
|
47
|
-
public static function create() -> Stats:
|
|
48
|
-
return Stats(passed = 0, failed = 0, skipped = 0)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
# ── Outcome constructors ──────────────────────────────────────────────────
|
|
52
|
-
|
|
53
|
-
public function ok() -> Check:
|
|
54
|
-
return Result[bool, Failure].success(value = true)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
public function fail(message: str) -> Check:
|
|
58
|
-
return Result[bool, Failure].failure(error = Failure(message = string.String.from_str(message), is_skip = false))
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
public function skip(reason: str) -> Check:
|
|
62
|
-
return Result[bool, Failure].failure(error = Failure(message = string.String.from_str(reason), is_skip = true))
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
# ── Expectations ──────────────────────────────────────────────────────────
|
|
66
|
-
|
|
67
|
-
public function expect(condition: bool, message: str) -> Check:
|
|
68
|
-
if condition:
|
|
69
|
-
return ok()
|
|
70
|
-
|
|
71
|
-
return fail(message)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
public function expect_true(condition: bool) -> Check:
|
|
75
|
-
return expect(condition, "expected true")
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
public function expect_false(condition: bool) -> Check:
|
|
79
|
-
return expect(not condition, "expected false")
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
public function expect_equal_int(actual: int, expected: int) -> Check:
|
|
83
|
-
if actual == expected:
|
|
84
|
-
return ok()
|
|
85
|
-
|
|
86
|
-
var message = string.String.create()
|
|
87
|
-
message.append("expected ")
|
|
88
|
-
fmt.append_int(ref_of(message), expected)
|
|
89
|
-
message.append(", got ")
|
|
90
|
-
fmt.append_int(ref_of(message), actual)
|
|
91
|
-
return Result[bool, Failure].failure(error = Failure(message = message, is_skip = false))
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
public function expect_equal_str(actual: str, expected: str) -> Check:
|
|
95
|
-
var actual_string = string.String.from_str(actual)
|
|
96
|
-
var expected_string = string.String.from_str(expected)
|
|
97
|
-
let same = actual_string.equal(expected_string)
|
|
98
|
-
actual_string.release()
|
|
99
|
-
expected_string.release()
|
|
100
|
-
if same:
|
|
101
|
-
return ok()
|
|
102
|
-
|
|
103
|
-
var message = string.String.create()
|
|
104
|
-
message.append("expected [")
|
|
105
|
-
message.append(expected)
|
|
106
|
-
message.append("], got [")
|
|
107
|
-
message.append(actual)
|
|
108
|
-
message.append("]")
|
|
109
|
-
return Result[bool, Failure].failure(error = Failure(message = message, is_skip = false))
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
public function expect_equal_bool(actual: bool, expected: bool) -> Check:
|
|
113
|
-
if actual == expected:
|
|
114
|
-
return ok()
|
|
115
|
-
|
|
116
|
-
var message = string.String.create()
|
|
117
|
-
message.append("expected ")
|
|
118
|
-
fmt.append_bool(ref_of(message), expected)
|
|
119
|
-
message.append(", got ")
|
|
120
|
-
fmt.append_bool(ref_of(message), actual)
|
|
121
|
-
return Result[bool, Failure].failure(error = Failure(message = message, is_skip = false))
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
public function expect_not_equal_int(actual: int, expected: int) -> Check:
|
|
125
|
-
if actual != expected:
|
|
126
|
-
return ok()
|
|
127
|
-
|
|
128
|
-
var message = string.String.create()
|
|
129
|
-
message.append("expected not ")
|
|
130
|
-
fmt.append_int(ref_of(message), expected)
|
|
131
|
-
message.append(", got ")
|
|
132
|
-
fmt.append_int(ref_of(message), actual)
|
|
133
|
-
return Result[bool, Failure].failure(error = Failure(message = message, is_skip = false))
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
public function expect_not_equal_str(actual: str, expected: str) -> Check:
|
|
137
|
-
var actual_string = string.String.from_str(actual)
|
|
138
|
-
var expected_string = string.String.from_str(expected)
|
|
139
|
-
let same = actual_string.equal(expected_string)
|
|
140
|
-
actual_string.release()
|
|
141
|
-
expected_string.release()
|
|
142
|
-
if not same:
|
|
143
|
-
return ok()
|
|
144
|
-
|
|
145
|
-
var message = string.String.create()
|
|
146
|
-
message.append("expected not [")
|
|
147
|
-
message.append(expected)
|
|
148
|
-
message.append("], got [")
|
|
149
|
-
message.append(actual)
|
|
150
|
-
message.append("]")
|
|
151
|
-
return Result[bool, Failure].failure(error = Failure(message = message, is_skip = false))
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
public function expect_not_equal_bool(actual: bool, expected: bool) -> Check:
|
|
155
|
-
if actual != expected:
|
|
156
|
-
return ok()
|
|
157
|
-
|
|
158
|
-
var message = string.String.create()
|
|
159
|
-
message.append("expected not ")
|
|
160
|
-
fmt.append_bool(ref_of(message), expected)
|
|
161
|
-
message.append(", got ")
|
|
162
|
-
fmt.append_bool(ref_of(message), actual)
|
|
163
|
-
return Result[bool, Failure].failure(error = Failure(message = message, is_skip = false))
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
# Generic equality over any type with a canonical `T.equal` hook: primitives
|
|
167
|
-
# (import std.hash), `str` (import std.str), and user structs that define `equal`
|
|
168
|
-
# (or delegate to std.hash.equal_struct). On failure the actual/expected values
|
|
169
|
-
# are rendered via `std.fmt.format_value`, so `T` must be a primitive or a struct
|
|
170
|
-
# whose fields are themselves `format_value`-renderable.
|
|
171
|
-
public function expect_equal[T](actual: T, expected: T) -> Check:
|
|
172
|
-
if equal[T](actual, expected):
|
|
173
|
-
return ok()
|
|
174
|
-
|
|
175
|
-
var message = string.String.create()
|
|
176
|
-
message.append("expected ")
|
|
177
|
-
fmt.format_value[T](ref_of(message), const_ptr_of(expected))
|
|
178
|
-
message.append(", got ")
|
|
179
|
-
fmt.format_value[T](ref_of(message), const_ptr_of(actual))
|
|
180
|
-
return Result[bool, Failure].failure(error = Failure(message = message, is_skip = false))
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
public function expect_some[T](option: Option[T]) -> Check:
|
|
184
|
-
if option.is_some():
|
|
185
|
-
return ok()
|
|
186
|
-
|
|
187
|
-
return fail("expected Option.some, got Option.none")
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
public function expect_none[T](option: Option[T]) -> Check:
|
|
191
|
-
if option.is_none():
|
|
192
|
-
return ok()
|
|
193
|
-
|
|
194
|
-
return fail("expected Option.none, got Option.some")
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
public function expect_null[T](pointer: const_ptr[T]?) -> Check:
|
|
198
|
-
if pointer == null:
|
|
199
|
-
return ok()
|
|
200
|
-
|
|
201
|
-
return fail("expected null pointer, got non-null")
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
public function expect_not_null[T](pointer: const_ptr[T]?) -> Check:
|
|
205
|
-
if pointer != null:
|
|
206
|
-
return ok()
|
|
207
|
-
|
|
208
|
-
return fail("expected non-null pointer, got null")
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
public function expect_error[T, E](result: Result[T, E]) -> Check:
|
|
212
|
-
if result.is_failure():
|
|
213
|
-
return ok()
|
|
214
|
-
|
|
215
|
-
return fail("expected Result.failure, got Result.success")
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
# ── Runner (hand-written; compiler discovery is a later phase) ─────────────
|
|
219
|
-
|
|
220
|
-
public function record(stats: Stats, name: str, outcome: Check) -> Stats:
|
|
221
|
-
match outcome:
|
|
222
|
-
Result.success:
|
|
223
|
-
var line = string.String.create()
|
|
224
|
-
line.append("ok - ")
|
|
225
|
-
line.append(name)
|
|
226
|
-
stdio.print_line(line.as_str())
|
|
227
|
-
line.release()
|
|
228
|
-
return Stats(passed = stats.passed + 1, failed = stats.failed, skipped = stats.skipped)
|
|
229
|
-
Result.failure as payload:
|
|
230
|
-
let failure = payload.error
|
|
231
|
-
var line = string.String.create()
|
|
232
|
-
if failure.is_skip:
|
|
233
|
-
line.append("skip - ")
|
|
234
|
-
else:
|
|
235
|
-
line.append("FAIL - ")
|
|
236
|
-
|
|
237
|
-
line.append(name)
|
|
238
|
-
line.append(": ")
|
|
239
|
-
line.append(failure.message.as_str())
|
|
240
|
-
stdio.print_line(line.as_str())
|
|
241
|
-
line.release()
|
|
242
|
-
|
|
243
|
-
var owned_message = failure.message
|
|
244
|
-
owned_message.release()
|
|
245
|
-
|
|
246
|
-
if failure.is_skip:
|
|
247
|
-
return Stats(passed = stats.passed, failed = stats.failed, skipped = stats.skipped + 1)
|
|
248
|
-
|
|
249
|
-
return Stats(passed = stats.passed, failed = stats.failed + 1, skipped = stats.skipped)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
public function summarize(stats: Stats) -> int:
|
|
253
|
-
var line = string.String.create()
|
|
254
|
-
line.append("passed=")
|
|
255
|
-
fmt.append_int(ref_of(line), stats.passed)
|
|
256
|
-
line.append(" failed=")
|
|
257
|
-
fmt.append_int(ref_of(line), stats.failed)
|
|
258
|
-
line.append(" skipped=")
|
|
259
|
-
fmt.append_int(ref_of(line), stats.skipped)
|
|
260
|
-
stdio.print_line(line.as_str())
|
|
261
|
-
line.release()
|
|
262
|
-
|
|
263
|
-
if stats.failed > 0:
|
|
264
|
-
return 1
|
|
265
|
-
|
|
266
|
-
return 0
|