Enum methods and more

This commit is contained in:
allexanderbergmns
2026-08-24 18:04:55 +02:00
parent 9504a9301b
commit 50712c5f17
7 changed files with 319 additions and 11 deletions
+56 -2
View File
@@ -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,12 +517,18 @@ 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 {
// check if it's an enum variant (e.g., Color.red)
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 { idx := g.locals[e.name] or {
return error('unknown variable "${e.name}" at line ${e.line}') return error('unknown variable "${e.name}" at line ${e.line}')
} }
g.code << op_load g.code << op_load
g.code << obj.encode_i64(i64(idx)) g.code << obj.encode_i64(i64(idx))
} }
}
.unary { .unary {
g.gen_expr(*e.right)! g.gen_expr(*e.right)!
if e.op == .kw_not { if e.op == .kw_not {
@@ -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 ''
} }
+4
View File
@@ -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
View File
@@ -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'")!
+59
View File
@@ -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!")
}
+86
View File
@@ -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!")
}
+23
View File
@@ -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)
}
+26
View File
@@ -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!")
}