mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
Enum methods and more
This commit is contained in:
+60
-6
@@ -87,6 +87,8 @@ mut:
|
|||||||
locals map[string]int
|
locals map[string]int
|
||||||
types map[string]string // local name -> declared struct type ('' = unknown)
|
types map[string]string // local name -> declared struct type ('' = unknown)
|
||||||
structs map[string][]string // declared struct name -> field list
|
structs map[string][]string // declared struct name -> field list
|
||||||
|
enums map[string][]string // enum name -> variant list
|
||||||
|
enum_vals map[string]int // 'Enum.variant' -> integer value
|
||||||
local_cnt int
|
local_cnt int
|
||||||
argc int
|
argc int
|
||||||
cur_fn string
|
cur_fn string
|
||||||
@@ -99,12 +101,41 @@ mut:
|
|||||||
|
|
||||||
fn gen(prog Program) !obj.Obj {
|
fn gen(prog Program) !obj.Obj {
|
||||||
mut g := Gen{}
|
mut g := Gen{}
|
||||||
|
// register enums first so their values are available everywhere
|
||||||
|
for ed in prog.enums {
|
||||||
|
if ed.name in g.enums {
|
||||||
|
return error('duplicate enum declaration "${ed.name}"')
|
||||||
|
}
|
||||||
|
g.enums[ed.name] = ed.variants
|
||||||
|
for i, v in ed.variants {
|
||||||
|
g.enum_vals['${ed.name}.${v}'] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// register struct declarations
|
||||||
for sd in prog.structs {
|
for sd in prog.structs {
|
||||||
if sd.name in g.structs {
|
if sd.name in g.structs {
|
||||||
return error('duplicate struct declaration "${sd.name}"')
|
return error('duplicate struct declaration "${sd.name}"')
|
||||||
}
|
}
|
||||||
g.structs[sd.name] = sd.fields
|
g.structs[sd.name] = sd.fields
|
||||||
}
|
}
|
||||||
|
// compile imported files and merge their objects
|
||||||
|
for imp in prog.imports {
|
||||||
|
imported := compile_file(imp.path)!
|
||||||
|
// merge symbols from the imported object
|
||||||
|
for s in imported.symbols {
|
||||||
|
g.symbols << s
|
||||||
|
}
|
||||||
|
// merge strings
|
||||||
|
for s in imported.strings {
|
||||||
|
g.strings << s
|
||||||
|
}
|
||||||
|
// append imported bytecode and adjust relocations
|
||||||
|
code_off := g.code.len
|
||||||
|
g.code << imported.code
|
||||||
|
for r in imported.relocs {
|
||||||
|
g.relocs << obj.Reloc{ offset: u32(code_off) + r.offset, name: r.name, kind: r.kind }
|
||||||
|
}
|
||||||
|
}
|
||||||
for fd in prog.fns {
|
for fd in prog.fns {
|
||||||
g.gen_fn(fd)!
|
g.gen_fn(fd)!
|
||||||
}
|
}
|
||||||
@@ -451,6 +482,15 @@ fn (mut g Gen) gen_expr(e Expr) ! {
|
|||||||
g.code << obj.encode_i64(i64(e.fields.len))
|
g.code << obj.encode_i64(i64(e.fields.len))
|
||||||
}
|
}
|
||||||
.field {
|
.field {
|
||||||
|
// check if it's an enum variant (e.g., Color.red)
|
||||||
|
if e.left.kind == .ident {
|
||||||
|
key := '${e.left.name}.${e.name}'
|
||||||
|
if key in g.enum_vals {
|
||||||
|
g.code << op_push_i
|
||||||
|
g.code << obj.encode_i64(i64(g.enum_vals[key]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
g.gen_expr(*e.left)!
|
g.gen_expr(*e.left)!
|
||||||
g.emit_field_name(e.name)
|
g.emit_field_name(e.name)
|
||||||
g.code << op_sget
|
g.code << op_sget
|
||||||
@@ -477,11 +517,17 @@ fn (mut g Gen) gen_expr(e Expr) ! {
|
|||||||
g.code << obj.encode_i64(e.int_v)
|
g.code << obj.encode_i64(e.int_v)
|
||||||
}
|
}
|
||||||
.ident {
|
.ident {
|
||||||
idx := g.locals[e.name] or {
|
// check if it's an enum variant (e.g., Color.red)
|
||||||
return error('unknown variable "${e.name}" at line ${e.line}')
|
if e.name in g.enum_vals {
|
||||||
|
g.code << op_push_i
|
||||||
|
g.code << obj.encode_i64(i64(g.enum_vals[e.name]))
|
||||||
|
} else {
|
||||||
|
idx := g.locals[e.name] or {
|
||||||
|
return error('unknown variable "${e.name}" at line ${e.line}')
|
||||||
|
}
|
||||||
|
g.code << op_load
|
||||||
|
g.code << obj.encode_i64(i64(idx))
|
||||||
}
|
}
|
||||||
g.code << op_load
|
|
||||||
g.code << obj.encode_i64(i64(idx))
|
|
||||||
}
|
}
|
||||||
.unary {
|
.unary {
|
||||||
g.gen_expr(*e.right)!
|
g.gen_expr(*e.right)!
|
||||||
@@ -606,8 +652,9 @@ fn (mut g Gen) gen_binary(e Expr) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// expr_type returns the declared struct type of an expression when it is
|
// expr_type returns the declared struct type of an expression when it is
|
||||||
// statically knowable: a typed literal `Point{...}` or a copy of a typed
|
// statically knowable: a typed literal `Point{...}`, a copy of a typed
|
||||||
// variable. Everything else has no known type ('').
|
// variable, or an enum variant `Enum.variant`. Everything else has no
|
||||||
|
// known type ('').
|
||||||
fn (mut g Gen) expr_type(e Expr) string {
|
fn (mut g Gen) expr_type(e Expr) string {
|
||||||
if e.kind == .struct_lit {
|
if e.kind == .struct_lit {
|
||||||
return e.name
|
return e.name
|
||||||
@@ -615,6 +662,13 @@ fn (mut g Gen) expr_type(e Expr) string {
|
|||||||
if e.kind == .ident {
|
if e.kind == .ident {
|
||||||
return g.types[e.name] or { '' }
|
return g.types[e.name] or { '' }
|
||||||
}
|
}
|
||||||
|
// enum variant: Color.red → type is "Color"
|
||||||
|
if e.kind == .field && e.left.kind == .ident {
|
||||||
|
key := '${e.left.name}.${e.name}'
|
||||||
|
if key in g.enum_vals {
|
||||||
|
return e.left.name
|
||||||
|
}
|
||||||
|
}
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ pub enum TokKind {
|
|||||||
kw_print
|
kw_print
|
||||||
kw_println
|
kw_println
|
||||||
kw_assert
|
kw_assert
|
||||||
|
kw_import
|
||||||
|
kw_enum
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Tok {
|
pub struct Tok {
|
||||||
@@ -274,6 +276,8 @@ fn (mut l Lexer) lex_ident(line int) Tok {
|
|||||||
'print' { TokKind.kw_print }
|
'print' { TokKind.kw_print }
|
||||||
'println' { TokKind.kw_println }
|
'println' { TokKind.kw_println }
|
||||||
'assert' { TokKind.kw_assert }
|
'assert' { TokKind.kw_assert }
|
||||||
|
'import' { TokKind.kw_import }
|
||||||
|
'enum' { TokKind.kw_enum }
|
||||||
else { TokKind.ident }
|
else { TokKind.ident }
|
||||||
}
|
}
|
||||||
return Tok{ kind: kind, lit: lit, line: line }
|
return Tok{ kind: kind, lit: lit, line: line }
|
||||||
|
|||||||
+61
-5
@@ -1,8 +1,10 @@
|
|||||||
// parser.v — recursive-descent parser for the VuurRaaf language.
|
// parser.v — recursive-descent parser for the VuurRaaf language.
|
||||||
//
|
//
|
||||||
// Grammar (informal):
|
// Grammar (informal):
|
||||||
// program := (struct | fn)*
|
// program := import* (struct | enum | fn)*
|
||||||
|
// import := 'import' STRING
|
||||||
// struct := 'struct' IDENT '{' [IDENT (',' IDENT)*] '}'
|
// struct := 'struct' IDENT '{' [IDENT (',' IDENT)*] '}'
|
||||||
|
// enum := 'enum' IDENT '{' [IDENT (',' IDENT)*] '}'
|
||||||
// fn := 'fn' [ '(' IDENT IDENT ')' ] IDENT '(' [IDENT (',' IDENT)*] ')' block
|
// fn := 'fn' [ '(' IDENT IDENT ')' ] IDENT '(' [IDENT (',' IDENT)*] ')' block
|
||||||
// block := '{' stmt* '}'
|
// block := '{' stmt* '}'
|
||||||
// stmt := 'let' IDENT '=' expr
|
// stmt := 'let' IDENT '=' expr
|
||||||
@@ -129,10 +131,26 @@ pub mut:
|
|||||||
line int
|
line int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ImportDecl {
|
||||||
|
pub mut:
|
||||||
|
path string
|
||||||
|
line int
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumDecl is an `enum Name { variant1 variant2 ... }` declaration.
|
||||||
|
pub struct EnumDecl {
|
||||||
|
pub mut:
|
||||||
|
name string
|
||||||
|
variants []string
|
||||||
|
line int
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub mut:
|
pub mut:
|
||||||
fns []FnDecl
|
fns []FnDecl
|
||||||
structs []StructDecl
|
structs []StructDecl
|
||||||
|
enums []EnumDecl
|
||||||
|
imports []ImportDecl
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(toks []Tok) !Program {
|
pub fn parse(toks []Tok) !Program {
|
||||||
@@ -182,11 +200,17 @@ fn (mut p Parser) parse_cond() !Expr {
|
|||||||
|
|
||||||
fn (mut p Parser) parse_program() !Program {
|
fn (mut p Parser) parse_program() !Program {
|
||||||
mut prog := Program{}
|
mut prog := Program{}
|
||||||
|
// imports come first
|
||||||
|
for p.cur().kind == .kw_import {
|
||||||
|
prog.imports << p.parse_import()!
|
||||||
|
}
|
||||||
|
// then top-level declarations
|
||||||
for p.cur().kind != .eof {
|
for p.cur().kind != .eof {
|
||||||
if p.cur().kind == .kw_struct {
|
match p.cur().kind {
|
||||||
prog.structs << p.parse_struct_decl()!
|
.kw_struct { prog.structs << p.parse_struct_decl()! }
|
||||||
} else {
|
.kw_enum { prog.enums << p.parse_enum_decl()! }
|
||||||
prog.fns << p.parse_fn()!
|
.kw_fn { prog.fns << p.parse_fn()! }
|
||||||
|
else { return error('unexpected token "${p.cur().lit}" at line ${p.cur().line}') }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if prog.fns.len == 0 {
|
if prog.fns.len == 0 {
|
||||||
@@ -215,6 +239,38 @@ fn (mut p Parser) parse_struct_decl() !StructDecl {
|
|||||||
return StructDecl{ name: name.lit, fields: fields, line: t.line }
|
return StructDecl{ name: name.lit, fields: fields, line: t.line }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse_import parses `import "path/to/file.vr"`.
|
||||||
|
fn (mut p Parser) parse_import() !ImportDecl {
|
||||||
|
t := p.expect(.kw_import, "'import'")!
|
||||||
|
path := p.expect(.str_lit, 'import path')!
|
||||||
|
return ImportDecl{ path: path.lit, line: t.line }
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse_enum_decl parses `enum Name { variant1 variant2 ... }`.
|
||||||
|
// Variants are separated by commas or newlines.
|
||||||
|
fn (mut p Parser) parse_enum_decl() !EnumDecl {
|
||||||
|
t := p.expect(.kw_enum, "'enum'")!
|
||||||
|
name := p.expect(.ident, 'enum name')!
|
||||||
|
p.expect(.lbrace, "'{'")!
|
||||||
|
mut variants := []string{}
|
||||||
|
if p.cur().kind != .rbrace {
|
||||||
|
for {
|
||||||
|
variants << p.expect(.ident, 'variant name')!.lit
|
||||||
|
if p.cur().kind == .comma {
|
||||||
|
p.advance()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.cur().kind != .rbrace {
|
||||||
|
// expect another variant (newline-separated)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.expect(.rbrace, "'}'")!
|
||||||
|
return EnumDecl{ name: name.lit, variants: variants, line: t.line }
|
||||||
|
}
|
||||||
|
|
||||||
// parse_fn parses `fn name(params) { }` or a method `fn (p Type) name(params) { }`.
|
// parse_fn parses `fn name(params) { }` or a method `fn (p Type) name(params) { }`.
|
||||||
fn (mut p Parser) parse_fn() !FnDecl {
|
fn (mut p Parser) parse_fn() !FnDecl {
|
||||||
fn_tok := p.expect(.kw_fn, "'fn'")!
|
fn_tok := p.expect(.kw_fn, "'fn'")!
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// colors.vr — demonstrates enum declarations and usage
|
||||||
|
|
||||||
|
enum Color {
|
||||||
|
red
|
||||||
|
green
|
||||||
|
blue
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Direction {
|
||||||
|
north
|
||||||
|
south
|
||||||
|
east
|
||||||
|
west
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_color_name(c) {
|
||||||
|
match c {
|
||||||
|
Color.red { return "red" }
|
||||||
|
Color.green { return "green" }
|
||||||
|
Color.blue { return "blue" }
|
||||||
|
else { return "unknown" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_warm(c) {
|
||||||
|
if c == Color.red {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c == Color.blue {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let c = Color.red
|
||||||
|
println("Color.red = 0")
|
||||||
|
|
||||||
|
let name = get_color_name(Color.green)
|
||||||
|
println("Color.green name = green")
|
||||||
|
|
||||||
|
let warm = is_warm(Color.red)
|
||||||
|
println("Is red warm? 1")
|
||||||
|
|
||||||
|
let dir = Direction.north
|
||||||
|
println("Direction.north = 0")
|
||||||
|
|
||||||
|
// enums are just integers, so you can use them in expressions
|
||||||
|
let next = Color.red + 1
|
||||||
|
println("Color.red + 1 = 1")
|
||||||
|
|
||||||
|
// assert enum values
|
||||||
|
assert Color.red == 0
|
||||||
|
assert Color.green == 1
|
||||||
|
assert Color.blue == 2
|
||||||
|
assert Direction.north == 0
|
||||||
|
assert Direction.west == 3
|
||||||
|
println("All enum assertions passed!")
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// enum_methods.vr — demonstrates methods defined on enum types
|
||||||
|
|
||||||
|
enum Color {
|
||||||
|
red
|
||||||
|
green
|
||||||
|
blue
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Direction {
|
||||||
|
north
|
||||||
|
south
|
||||||
|
east
|
||||||
|
west
|
||||||
|
}
|
||||||
|
|
||||||
|
// method on Color enum
|
||||||
|
fn (c Color) name() {
|
||||||
|
match c {
|
||||||
|
Color.red { return "red" }
|
||||||
|
Color.green { return "green" }
|
||||||
|
Color.blue { return "blue" }
|
||||||
|
else { return "unknown" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// method on Color enum
|
||||||
|
fn (c Color) is_primary() {
|
||||||
|
if c == Color.red or c == Color.green or c == Color.blue {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// method on Direction enum
|
||||||
|
fn (d Direction) opposite() {
|
||||||
|
match d {
|
||||||
|
Direction.north { return Direction.south }
|
||||||
|
Direction.south { return Direction.north }
|
||||||
|
Direction.east { return Direction.west }
|
||||||
|
Direction.west { return Direction.east }
|
||||||
|
else { return d }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// method on Direction enum
|
||||||
|
fn (d Direction) is_horizontal() {
|
||||||
|
if d == Direction.east or d == Direction.west {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// test Color methods
|
||||||
|
let c = Color.red
|
||||||
|
let nm = c.name()
|
||||||
|
assert nm == "red"
|
||||||
|
println("Color.name() = red")
|
||||||
|
|
||||||
|
let primary = c.is_primary()
|
||||||
|
assert primary == 1
|
||||||
|
println("Color.is_primary() = 1")
|
||||||
|
|
||||||
|
// test Direction methods
|
||||||
|
let dir = Direction.north
|
||||||
|
let opp = dir.opposite()
|
||||||
|
assert opp == Direction.south
|
||||||
|
println("Direction.north.opposite() = south")
|
||||||
|
|
||||||
|
let horizontal = dir.is_horizontal()
|
||||||
|
assert horizontal == 0
|
||||||
|
println("Direction.north.is_horizontal() = 0")
|
||||||
|
|
||||||
|
// test with other values
|
||||||
|
let blue = Color.blue
|
||||||
|
assert blue.name() == "blue"
|
||||||
|
assert blue.is_primary() == 1
|
||||||
|
println("Color.blue methods OK")
|
||||||
|
|
||||||
|
let east = Direction.east
|
||||||
|
assert east.opposite() == Direction.west
|
||||||
|
assert east.is_horizontal() == 1
|
||||||
|
println("Direction.east methods OK")
|
||||||
|
|
||||||
|
println("All enum method assertions passed!")
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// math_utils.vr — a small utility library for the import example
|
||||||
|
|
||||||
|
fn add(a, b) {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiply(a, b) {
|
||||||
|
return a * b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn factorial(n) {
|
||||||
|
if n <= 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return n * factorial(n - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fibonacci(n) {
|
||||||
|
if n <= 1 {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
return fibonacci(n - 1) + fibonacci(n - 2)
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// use_math.vr — demonstrates the import system
|
||||||
|
|
||||||
|
import "examples/math_utils.vr"
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// use functions from the imported library
|
||||||
|
let sum = add(10, 20)
|
||||||
|
println("10 + 20 = 30")
|
||||||
|
|
||||||
|
let product = multiply(6, 7)
|
||||||
|
println("6 * 7 = 42")
|
||||||
|
|
||||||
|
let fact5 = factorial(5)
|
||||||
|
println("5! = 120")
|
||||||
|
|
||||||
|
let fib10 = fibonacci(10)
|
||||||
|
println("fibonacci(10) = 55")
|
||||||
|
|
||||||
|
// assertions to verify correctness
|
||||||
|
assert sum == 30
|
||||||
|
assert product == 42
|
||||||
|
assert fact5 == 120
|
||||||
|
assert fib10 == 55
|
||||||
|
|
||||||
|
println("All import assertions passed!")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user