mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 16:07:01 +00:00
Else if chains
This commit is contained in:
@@ -109,6 +109,26 @@ fn main() {
|
|||||||
let i = 100
|
let i = 100
|
||||||
for i in 0..3 { ... } // loop vars are scoped to the loop
|
for i in 0..3 { ... } // loop vars are scoped to the loop
|
||||||
println(i) // 100
|
println(i) // 100
|
||||||
|
|
||||||
|
if score >= 90 { // else-if chains
|
||||||
|
grade = "A"
|
||||||
|
} else if score >= 80 {
|
||||||
|
grade = "B"
|
||||||
|
} else {
|
||||||
|
grade = "F"
|
||||||
|
}
|
||||||
|
|
||||||
|
match day { // match on any comparable value
|
||||||
|
"sat" {
|
||||||
|
println("weekend")
|
||||||
|
}
|
||||||
|
"sun" {
|
||||||
|
println("weekend")
|
||||||
|
}
|
||||||
|
else { // optional fallback arm
|
||||||
|
println("workday")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -120,9 +140,12 @@ fn main() {
|
|||||||
`for i in 0...10 { }`; loop variables are scoped to the loop body
|
`for i in 0...10 { }`; loop variables are scoped to the loop body
|
||||||
- `break` / `continue` inside `while` and `for` loops (in `for` loops
|
- `break` / `continue` inside `while` and `for` loops (in `for` loops
|
||||||
`continue` advances the loop variable / iterator first)
|
`continue` advances the loop variable / iterator first)
|
||||||
|
- else-if chains: `if a { } else if b { } else { }`
|
||||||
|
- `match`: `match expr { v1 { } v2 { } else { } }` — arms test equality on
|
||||||
|
any comparable value (ints, strings, ...); the `else` arm is optional
|
||||||
- operators: `+ - * / %`, `== != < <= > >=`, `and or not`, unary `-`
|
- operators: `+ - * / %`, `== != < <= > >=`, `and or not`, unary `-`
|
||||||
- statements: `let`, assignment, `if/else`, `while`, `for`, `break`,
|
- statements: `let`, assignment, `if/else`, `match`, `while`, `for`,
|
||||||
`continue`, `return`, `assert`, calls, `print(...)` / `println(...)`
|
`break`, `continue`, `return`, `assert`, calls, `print(...)` / `println(...)`
|
||||||
- comments: `//`
|
- comments: `//`
|
||||||
|
|
||||||
## Assembly
|
## Assembly
|
||||||
|
|||||||
@@ -190,6 +190,40 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
}
|
}
|
||||||
g.emit_label(end_l)
|
g.emit_label(end_l)
|
||||||
}
|
}
|
||||||
|
.match_stmt {
|
||||||
|
// match x { v1 {..} v2 {..} else {..} } → subject := x; a chain of
|
||||||
|
// equality tests jumping to the matching arm; else falls through.
|
||||||
|
subj_idx := g.new_local()
|
||||||
|
end_l := g.new_label()
|
||||||
|
g.gen_expr(st.expr)!
|
||||||
|
g.emit_store(subj_idx)
|
||||||
|
for i, arm in st.arms {
|
||||||
|
next_l := g.new_label()
|
||||||
|
g.emit_load(subj_idx)
|
||||||
|
g.gen_expr(arm.val)!
|
||||||
|
g.code << op_eq
|
||||||
|
g.code << op_jz
|
||||||
|
g.code << obj.encode_i64(0)
|
||||||
|
g.fixups << Fixup{ name: next_l, off: u32(g.code.len) - 8 }
|
||||||
|
for s in arm.body {
|
||||||
|
g.gen_stmt(s)!
|
||||||
|
}
|
||||||
|
g.code << op_jmp
|
||||||
|
g.code << obj.encode_i64(0)
|
||||||
|
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
||||||
|
g.emit_label(next_l)
|
||||||
|
if i == st.arms.len - 1 && !st.has_else {
|
||||||
|
// no else: fall through to the end label
|
||||||
|
g.emit_label(end_l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if st.has_else {
|
||||||
|
for s in st.els_body {
|
||||||
|
g.gen_stmt(s)!
|
||||||
|
}
|
||||||
|
g.emit_label(end_l)
|
||||||
|
}
|
||||||
|
}
|
||||||
.while_stmt {
|
.while_stmt {
|
||||||
loop_l := g.new_label()
|
loop_l := g.new_label()
|
||||||
end_l := g.new_label()
|
end_l := g.new_label()
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ pub enum TokKind {
|
|||||||
kw_while
|
kw_while
|
||||||
kw_for
|
kw_for
|
||||||
kw_in
|
kw_in
|
||||||
|
kw_match
|
||||||
kw_break
|
kw_break
|
||||||
kw_continue
|
kw_continue
|
||||||
kw_return
|
kw_return
|
||||||
@@ -253,6 +254,7 @@ fn (mut l Lexer) lex_ident(line int) Tok {
|
|||||||
'while' { TokKind.kw_while }
|
'while' { TokKind.kw_while }
|
||||||
'for' { TokKind.kw_for }
|
'for' { TokKind.kw_for }
|
||||||
'in' { TokKind.kw_in }
|
'in' { TokKind.kw_in }
|
||||||
|
'match' { TokKind.kw_match }
|
||||||
'break' { TokKind.kw_break }
|
'break' { TokKind.kw_break }
|
||||||
'continue' { TokKind.kw_continue }
|
'continue' { TokKind.kw_continue }
|
||||||
'return' { TokKind.kw_return }
|
'return' { TokKind.kw_return }
|
||||||
|
|||||||
+55
-8
@@ -7,7 +7,8 @@
|
|||||||
// stmt := 'let' IDENT '=' expr
|
// stmt := 'let' IDENT '=' expr
|
||||||
// | IDENT '=' expr
|
// | IDENT '=' expr
|
||||||
// | postfix '=' expr (a[i] = v)
|
// | postfix '=' expr (a[i] = v)
|
||||||
// | 'if' cond block ['else' block]
|
// | 'if' cond block ['else' ('if' ... | block)]
|
||||||
|
// | 'match' expr '{' (expr block | 'else' block)* '}'
|
||||||
// | 'while' cond block
|
// | 'while' cond block
|
||||||
// | 'for' IDENT 'in' range block (range := expr '..' expr | expr '...' expr)
|
// | 'for' IDENT 'in' range block (range := expr '..' expr | expr '...' expr)
|
||||||
// | 'for' IDENT 'in' expr block (iterate an array)
|
// | 'for' IDENT 'in' expr block (iterate an array)
|
||||||
@@ -58,6 +59,7 @@ pub enum StmtKind {
|
|||||||
assign_stmt
|
assign_stmt
|
||||||
index_assign
|
index_assign
|
||||||
if_stmt
|
if_stmt
|
||||||
|
match_stmt
|
||||||
while_stmt
|
while_stmt
|
||||||
for_range_stmt
|
for_range_stmt
|
||||||
for_in_stmt
|
for_in_stmt
|
||||||
@@ -67,6 +69,13 @@ pub enum StmtKind {
|
|||||||
assert_stmt
|
assert_stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchArm is a single `value { body }` arm of a match statement.
|
||||||
|
pub struct MatchArm {
|
||||||
|
pub mut:
|
||||||
|
val Expr
|
||||||
|
body []Stmt
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Stmt {
|
pub struct Stmt {
|
||||||
pub mut:
|
pub mut:
|
||||||
kind StmtKind
|
kind StmtKind
|
||||||
@@ -77,6 +86,9 @@ pub mut:
|
|||||||
idx Expr // index_assign: the index expression
|
idx Expr // index_assign: the index expression
|
||||||
body []Stmt
|
body []Stmt
|
||||||
els []Stmt
|
els []Stmt
|
||||||
|
arms []MatchArm // match_stmt: the arms (val + body)
|
||||||
|
has_else bool // match_stmt: a trailing else arm exists
|
||||||
|
els_body []Stmt // match_stmt: body of the else arm
|
||||||
has_val bool
|
has_val bool
|
||||||
inclusive bool // for_range_stmt: `..` (false) vs `...` (true)
|
inclusive bool // for_range_stmt: `..` (false) vs `...` (true)
|
||||||
line int
|
line int
|
||||||
@@ -194,15 +206,31 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
|||||||
e := p.parse_expr()!
|
e := p.parse_expr()!
|
||||||
return Stmt{ kind: .let_stmt, target: name.lit, expr: e, line: t.line }
|
return Stmt{ kind: .let_stmt, target: name.lit, expr: e, line: t.line }
|
||||||
} .kw_if {
|
} .kw_if {
|
||||||
|
return p.parse_if(t)!
|
||||||
|
}
|
||||||
|
.kw_match {
|
||||||
p.advance()
|
p.advance()
|
||||||
cond := p.parse_cond()!
|
subject := p.parse_expr()!
|
||||||
body := p.parse_block()!
|
p.expect(.lbrace, "'{'")!
|
||||||
mut els := []Stmt{}
|
mut arms := []MatchArm{}
|
||||||
if p.cur().kind == .kw_else {
|
mut has_else := false
|
||||||
p.advance()
|
mut els_body := []Stmt{}
|
||||||
els = p.parse_block()!
|
for p.cur().kind != .rbrace {
|
||||||
|
if p.cur().kind == .eof {
|
||||||
|
return error('unexpected end of file inside match (missing "}")')
|
||||||
|
}
|
||||||
|
if p.cur().kind == .kw_else {
|
||||||
|
p.advance()
|
||||||
|
els_body = p.parse_block()!
|
||||||
|
has_else = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val := p.parse_expr()!
|
||||||
|
body := p.parse_block()!
|
||||||
|
arms << MatchArm{ val: val, body: body }
|
||||||
}
|
}
|
||||||
return Stmt{ kind: .if_stmt, cond: cond, body: body, els: els, line: t.line }
|
p.expect(.rbrace, "'}'")!
|
||||||
|
return Stmt{ kind: .match_stmt, expr: subject, arms: arms, has_else: has_else, els_body: els_body, line: t.line }
|
||||||
}
|
}
|
||||||
.kw_while {
|
.kw_while {
|
||||||
p.advance()
|
p.advance()
|
||||||
@@ -284,6 +312,25 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse_if parses `if cond block ['else' ('if' ... | block)]`. An `else if`
|
||||||
|
// chain is represented by putting the nested if-statement in the else list,
|
||||||
|
// so codegen needs no special casing.
|
||||||
|
fn (mut p Parser) parse_if(t Tok) !Stmt {
|
||||||
|
p.advance()
|
||||||
|
cond := p.parse_cond()!
|
||||||
|
body := p.parse_block()!
|
||||||
|
mut els := []Stmt{}
|
||||||
|
if p.cur().kind == .kw_else {
|
||||||
|
p.advance()
|
||||||
|
if p.cur().kind == .kw_if {
|
||||||
|
els << p.parse_if(p.cur())!
|
||||||
|
} else {
|
||||||
|
els = p.parse_block()!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Stmt{ kind: .if_stmt, cond: cond, body: body, els: els, line: t.line }
|
||||||
|
}
|
||||||
|
|
||||||
// bin_node allocates a binary-operator node. It takes copies of the operands
|
// bin_node allocates a binary-operator node. It takes copies of the operands
|
||||||
// so that `&l`/`&r` target fresh heap objects (taking the address of a local
|
// so that `&l`/`&r` target fresh heap objects (taking the address of a local
|
||||||
// that is later reassigned would create a self-referential node).
|
// that is later reassigned would create a self-referential node).
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// match.vr — else-if chains and match statements.
|
||||||
|
// vr run examples/match.vr
|
||||||
|
|
||||||
|
// classify returns a label using an else-if chain.
|
||||||
|
fn classify(n) {
|
||||||
|
if n < 0 {
|
||||||
|
return "negative"
|
||||||
|
} else if n == 0 {
|
||||||
|
return "zero"
|
||||||
|
} else if n < 10 {
|
||||||
|
return "small"
|
||||||
|
} else {
|
||||||
|
return "big"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// describe names a number with a match statement.
|
||||||
|
fn describe(n) {
|
||||||
|
match n {
|
||||||
|
0 {
|
||||||
|
println("nothing")
|
||||||
|
}
|
||||||
|
1 {
|
||||||
|
println("lonely")
|
||||||
|
}
|
||||||
|
2 {
|
||||||
|
println("a pair")
|
||||||
|
}
|
||||||
|
7 {
|
||||||
|
println("lucky")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println("just " + n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// else-if chain
|
||||||
|
println(classify(-5))
|
||||||
|
println(classify(0))
|
||||||
|
println(classify(7))
|
||||||
|
println(classify(100))
|
||||||
|
|
||||||
|
// match with else
|
||||||
|
describe(0)
|
||||||
|
describe(1)
|
||||||
|
describe(2)
|
||||||
|
describe(7)
|
||||||
|
describe(42)
|
||||||
|
|
||||||
|
// match on strings
|
||||||
|
let day = "fri"
|
||||||
|
match day {
|
||||||
|
"sat" {
|
||||||
|
println("weekend!")
|
||||||
|
}
|
||||||
|
"sun" {
|
||||||
|
println("weekend!")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println("workday")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// match inside a loop (break/continue still work)
|
||||||
|
let found = 0
|
||||||
|
for i in 0..10 {
|
||||||
|
match i {
|
||||||
|
4 {
|
||||||
|
found = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// keep looking
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("found " + found)
|
||||||
|
assert found == 4
|
||||||
|
}
|
||||||
@@ -125,6 +125,66 @@ fn test_break_continue() {
|
|||||||
assert odds[2] == 5
|
assert odds[2] == 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_else_if() {
|
||||||
|
let score = 85
|
||||||
|
let grade = ""
|
||||||
|
if score >= 90 {
|
||||||
|
grade = "A"
|
||||||
|
} else if score >= 80 {
|
||||||
|
grade = "B"
|
||||||
|
} else if score >= 70 {
|
||||||
|
grade = "C"
|
||||||
|
} else {
|
||||||
|
grade = "F"
|
||||||
|
}
|
||||||
|
assert grade == "B"
|
||||||
|
assert 10 > 5
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_match() {
|
||||||
|
let total = 0
|
||||||
|
match 2 {
|
||||||
|
1 {
|
||||||
|
total = total + 1
|
||||||
|
}
|
||||||
|
2 {
|
||||||
|
total = total + 10
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
total = total + 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert total == 10 // matched the 2 arm
|
||||||
|
|
||||||
|
// match on strings
|
||||||
|
let word = "cat"
|
||||||
|
let sound = ""
|
||||||
|
match word {
|
||||||
|
"dog" {
|
||||||
|
sound = "woof"
|
||||||
|
}
|
||||||
|
"cat" {
|
||||||
|
sound = "meow"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sound = "?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert sound == "meow"
|
||||||
|
|
||||||
|
// no else arm: nothing runs when nothing matches
|
||||||
|
let hit = 0
|
||||||
|
match 99 {
|
||||||
|
1 {
|
||||||
|
hit = 1
|
||||||
|
}
|
||||||
|
2 {
|
||||||
|
hit = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert hit == 0
|
||||||
|
}
|
||||||
|
|
||||||
fn test_failing() {
|
fn test_failing() {
|
||||||
// this one is meant to fail — shows up in `vr test` output
|
// this one is meant to fail — shows up in `vr test` output
|
||||||
assert 1 == 2
|
assert 1 == 2
|
||||||
|
|||||||
Reference in New Issue
Block a user