Skip to content

Commit ca2befc

Browse files
authored
Merge pull request #47 from mgomes/mgomes/v0-16-0o
v0.16.0: control flow and error handling
2 parents a5dfc0e + dcc3744 commit ca2befc

18 files changed

+1274
-39
lines changed

ROADMAP.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -176,17 +176,17 @@ Goal: make types expressive enough for real workflows while keeping runtime chec
176176

177177
### Type Semantics
178178

179-
- [ ] Specify variance/invariance rules for container assignments.
180-
- [ ] Specify nullability interactions with unions (`T?` vs `T | nil`).
181-
- [ ] Define coercion policy (no coercion vs explicit coercion helpers).
182-
- [ ] Decide strictness for unknown keyword args under typed signatures.
179+
- [x] Specify variance/invariance rules for container assignments.
180+
- [x] Specify nullability interactions with unions (`T?` vs `T | nil`).
181+
- [x] Define coercion policy (no coercion vs explicit coercion helpers).
182+
- [x] Decide strictness for unknown keyword args under typed signatures.
183183

184184
### Runtime and Parser
185185

186186
- [x] Extend parser grammar for generic and union type expressions.
187187
- [x] Extend type resolver and internal type representation.
188188
- [x] Add runtime validators for composite/union types.
189-
- [ ] Add contract interop so capability contracts can reuse type validators.
189+
- [x] Add contract interop so capability contracts can reuse type validators.
190190

191191
### Testing and Docs
192192

@@ -209,37 +209,37 @@ Goal: improve language ergonomics for complex script logic and recovery behavior
209209

210210
### Control Flow
211211

212-
- [ ] Add `while` loops.
213-
- [ ] Add `until` loops.
214-
- [ ] Add loop control keywords: `break` and `next`.
215-
- [ ] Add `case/when` expression support (if approved).
216-
- [ ] Define behavior for nested loop control and block boundaries.
212+
- [x] Add `while` loops.
213+
- [x] Add `until` loops.
214+
- [x] Add loop control keywords: `break` and `next`.
215+
- [x] Add `case/when` expression support (if approved).
216+
- [x] Define behavior for nested loop control and block boundaries.
217217

218218
### Error Handling Constructs
219219

220220
- [ ] Add structured error handling syntax (`begin/rescue/ensure` or equivalent).
221221
- [ ] Add typed error matching where feasible.
222222
- [ ] Define re-raise semantics and stack preservation.
223-
- [ ] Ensure runtime errors preserve original position and call frames.
223+
- [x] Ensure runtime errors preserve original position and call frames.
224224

225225
### Runtime Behavior
226226

227-
- [ ] Ensure new control flow integrates with step quota accounting.
228-
- [ ] Ensure new constructs integrate with recursion/memory quotas.
229-
- [ ] Validate behavior inside class methods, blocks, and capability callbacks.
227+
- [x] Ensure new control flow integrates with step quota accounting.
228+
- [x] Ensure new constructs integrate with recursion/memory quotas.
229+
- [x] Validate behavior inside class methods, blocks, and capability callbacks.
230230

231231
### Testing and Docs
232232

233-
- [ ] Add parser/runtime tests for each new control flow construct.
234-
- [ ] Add nested control flow tests for edge cases.
235-
- [ ] Add docs updates in `docs/control-flow.md` and `docs/errors.md`.
236-
- [ ] Add examples under `examples/control_flow/` for each new feature.
233+
- [x] Add parser/runtime tests for each new control flow construct.
234+
- [x] Add nested control flow tests for edge cases.
235+
- [x] Add docs updates in `docs/control-flow.md` and `docs/errors.md`.
236+
- [x] Add examples under `examples/control_flow/` for each new feature.
237237

238238
### v0.16.0 Definition of Done
239239

240-
- [ ] No regressions in existing `if/for/range` behavior.
240+
- [x] No regressions in existing `if/for/range` behavior.
241241
- [ ] Structured error handling works with assertions and runtime errors.
242-
- [ ] Coverage includes nested/edge control-flow paths.
242+
- [x] Coverage includes nested/edge control-flow paths.
243243

244244
---
245245

docs/control-flow.md

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,92 @@
11
# Control Flow and Ranges
22

3-
VibeScript supports familiar Ruby control structures:
3+
VibeScript supports these control-flow forms:
44

5-
- `if/elsif/else` expressions
6-
- `for` loops iterating arrays or ranges
7-
- Numeric ranges via `start..finish`
5+
- `if` / `elsif` / `else`
6+
- `case` / `when` expressions
7+
- `for` loops over arrays and ranges
8+
- `while` and `until` loops
9+
- loop control with `break` and `next`
10+
- numeric ranges via `start..finish`
811

9-
Example range usage:
12+
## `for` loops
1013

1114
```vibe
12-
def fizzbuzz(limit)
13-
for n in 1..limit
14-
if n % 15 == 0
15-
puts("FizzBuzz")
16-
elsif n % 3 == 0
17-
puts("Fizz")
18-
elsif n % 5 == 0
19-
puts("Buzz")
20-
else
21-
puts(n)
15+
def sum_first_five()
16+
total = 0
17+
for n in 1..5
18+
total = total + n
19+
end
20+
total
21+
end
22+
```
23+
24+
## `case` / `when` expressions
25+
26+
`case` evaluates to the matching branch expression (or `nil` when no branch matches and no `else` is provided).
27+
28+
```vibe
29+
def label(score)
30+
case score
31+
when 100
32+
"perfect"
33+
when 90, 95
34+
"great"
35+
else
36+
"ok"
37+
end
38+
end
39+
```
40+
41+
## `while` and `until`
42+
43+
```vibe
44+
def countdown(n)
45+
out = []
46+
while n > 0
47+
out = out + [n]
48+
n = n - 1
49+
end
50+
out
51+
end
52+
53+
def count_up(limit)
54+
out = []
55+
n = 0
56+
until n >= limit
57+
out = out + [n]
58+
n = n + 1
59+
end
60+
out
61+
end
62+
```
63+
64+
## `break` and `next`
65+
66+
```vibe
67+
def odds_under_five()
68+
out = []
69+
for n in [1, 2, 3, 4, 5]
70+
if n == 5
71+
break
72+
end
73+
if n % 2 == 0
74+
next
2275
end
76+
out = out + [n]
2377
end
78+
out
2479
end
2580
```
2681

27-
See `examples/loops/` and `examples/ranges/` for runnable scripts.
82+
Semantics:
83+
84+
- In nested loops, `break` and `next` target the nearest active loop.
85+
- `break`/`next` used outside any loop raise runtime errors.
86+
- `break`/`next` cannot cross call boundaries (for example from block callbacks back into outer loops).
87+
88+
## Quotas
89+
90+
Loop execution participates in step and memory quotas. Infinite loops will terminate with quota errors when limits are exceeded.
91+
92+
See `examples/control_flow/`, `examples/loops/`, and `examples/ranges/` for runnable scripts.

docs/errors.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,31 @@ division by zero
3737
at calculate (7:7)
3838
```
3939

40+
## Type Errors
41+
42+
Typed argument and return checks include:
43+
44+
- parameter or function context
45+
- expected type
46+
- actual runtime type
47+
48+
```text
49+
argument payload expected { id: string, score: int }, got { id: string, score: string }
50+
```
51+
52+
For composite values, actual types include shape/element detail (`array<int | string>`, `{ id: string, ... }`) to make fixes local and explicit.
53+
54+
## Loop Control Errors
55+
56+
Loop control diagnostics are explicit:
57+
58+
- `break used outside of loop`
59+
- `next used outside of loop`
60+
- `break cannot cross call boundary`
61+
- `next cannot cross call boundary`
62+
63+
These boundary errors happen when `break`/`next` are raised inside called blocks/functions and attempt to escape into an outer loop.
64+
4065
## REPL Debugging
4166

4267
The REPL stores the previous failure. Use:

docs/typing.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,43 @@ Typed blocks catch callback mismatches at runtime with errors that include param
182182

183183
Duration methods like `ago`/`after` return `Time`. Typed signatures use `time` or `time?` for those values.
184184

185+
## Type semantics
186+
187+
### Container compatibility rules
188+
189+
Container annotations are checked by validating contained values against the declared type.
190+
191+
- `array<T>` validates every element with `T`.
192+
- `hash<K, V>` validates every key with `K` and every value with `V`.
193+
194+
Practical consequence:
195+
196+
- `array<int>` is valid for `array<number>` because each `int` satisfies `number`.
197+
- `array<number>` is only valid for `array<int>` when all elements are actually ints.
198+
199+
### `T?` and `T | nil`
200+
201+
`T?` and `T | nil` are equivalent at runtime: both accept `nil` and values matching `T`.
202+
203+
- Prefer `T?` for simple optional values.
204+
- Use `T | nil` when you are already expressing a broader union (for example `int | string | nil`).
205+
206+
### Coercion policy
207+
208+
Typed checks do not perform implicit coercion.
209+
210+
- `int` does not accept `"1"`.
211+
- `string` does not accept `1`.
212+
213+
Convert explicitly before calling typed boundaries (for example `.to_i`, `.to_f`, `.string`, parsers/builders for time and duration).
214+
215+
### Unknown keyword args under typed signatures
216+
217+
Unknown keyword arguments are strict for all function calls, including typed signatures.
218+
219+
- Extra keyword arguments raise `unexpected keyword argument <name>`.
220+
- This behavior is the same for typed and untyped functions.
221+
185222
## Notes and limitations
186223

187224
- Types are nominal by kind.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
def label(score)
2+
case score
3+
when 100
4+
"perfect"
5+
when 90, 95
6+
"great"
7+
else
8+
"ok"
9+
end
10+
end
11+
12+
def run()
13+
[label(100), label(95), label(70)]
14+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
def odds_below(limit)
2+
out = []
3+
n = 0
4+
while n < limit
5+
n = n + 1
6+
if n == limit
7+
break
8+
end
9+
if n % 2 == 0
10+
next
11+
end
12+
out = out + [n]
13+
end
14+
out
15+
end
16+
17+
def run()
18+
odds_below(10)
19+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
def collect_until(limit)
2+
out = []
3+
n = 0
4+
until n >= limit
5+
out = out + [n]
6+
n = n + 1
7+
end
8+
out
9+
end
10+
11+
def run()
12+
collect_until(5)
13+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
def countdown(start)
2+
out = []
3+
n = start
4+
while n > 0
5+
out = out + [n]
6+
n = n - 1
7+
end
8+
out
9+
end
10+
11+
def run()
12+
countdown(5)
13+
end

vibes/ast.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,38 @@ type ForStmt struct {
122122
func (s *ForStmt) stmtNode() {}
123123
func (s *ForStmt) Pos() Position { return s.position }
124124

125+
type WhileStmt struct {
126+
Condition Expression
127+
Body []Statement
128+
position Position
129+
}
130+
131+
func (s *WhileStmt) stmtNode() {}
132+
func (s *WhileStmt) Pos() Position { return s.position }
133+
134+
type UntilStmt struct {
135+
Condition Expression
136+
Body []Statement
137+
position Position
138+
}
139+
140+
func (s *UntilStmt) stmtNode() {}
141+
func (s *UntilStmt) Pos() Position { return s.position }
142+
143+
type BreakStmt struct {
144+
position Position
145+
}
146+
147+
func (s *BreakStmt) stmtNode() {}
148+
func (s *BreakStmt) Pos() Position { return s.position }
149+
150+
type NextStmt struct {
151+
position Position
152+
}
153+
154+
func (s *NextStmt) stmtNode() {}
155+
func (s *NextStmt) Pos() Position { return s.position }
156+
125157
type Identifier struct {
126158
Name string
127159
position Position
@@ -276,6 +308,21 @@ type RangeExpr struct {
276308
func (e *RangeExpr) exprNode() {}
277309
func (e *RangeExpr) Pos() Position { return e.position }
278310

311+
type CaseWhenClause struct {
312+
Values []Expression
313+
Result Expression
314+
}
315+
316+
type CaseExpr struct {
317+
Target Expression
318+
Clauses []CaseWhenClause
319+
ElseExpr Expression
320+
position Position
321+
}
322+
323+
func (e *CaseExpr) exprNode() {}
324+
func (e *CaseExpr) Pos() Position { return e.position }
325+
279326
type BlockLiteral struct {
280327
Params []Param
281328
Body []Statement

0 commit comments

Comments
 (0)