Add support for enums in the parser

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
Slendi 2024-04-29 16:42:28 +03:00
parent ea9c622865
commit 95057fd359
7 changed files with 370 additions and 289 deletions

364
pong.cat
View File

@ -1,179 +1,193 @@
struct Vec2 {
x y: f32,
enum MyEnum {
EnumItem1,
EnumItem2 = 1,
EnumItem3,
EnumItem4,
}
struct Rect {
pos size: Vec2,
enum MyTypedEnum : i8 {
EnumItem1,
EnumItem2,
EnumItem3 = 105,
EnumItem4,
}
let CONSTPOS :: .Vec2 {20.0 20.0}
let CONSTSZ :: .Vec2 {100.0 100.0}
let CONST :: .Rect {(CONSTPOS) (CONSTSZ)}
\ FIXME: LLVM IR geneartion is borked
\ let CONST :: .Rect { .Vec2 { 20.0 20.0 } .Vec2 { 100.0 100.0 } }
struct Color {
r g b a: u8,
}
let KEY_R :: 82
fn InitWindow(w h: i32, title: []u8)
fn CloseWindow
fn BeginDrawing
fn SetTargetFPS(fps: i32)
fn EndDrawing
fn DrawFPS(x y: i32)
fn WindowShouldClose i32
fn TextSubtext(text: []u8, start end: i32) []u8
fn IsKeyPressed(key: i32) u1
struct Rectangle {
x y w h: f32,
}
fn ColorToRaylib(c: Color) u32 {
ret c.a as u32 << 24 as u32 | c.b as u32 << 16 as u32 | c.g as u32 << 8 as u32 | c.r
}
fn ClearBackgroundWrap(c: Color) {
fn ClearBackground(c: u32)
ClearBackground (ColorToRaylib c)
}
fn DrawRectangleWrap(x y w h: i32, c: Color) {
fn DrawRectangle(x y w h: i32, c: u32)
DrawRectangle x y w h (ColorToRaylib c)
}
fn DrawCircleWrap(x y: i32, r: f32, c: Color) {
fn DrawCircle(x y: i32, r: f32, c: u32)
DrawCircle x y r (ColorToRaylib c)
}
fn DrawRectangleRecWrap(r: Rectangle, c: Color) {
fn DrawRectangleRec(r: Rectangle, c: u32)
DrawRectangleRec r (ColorToRaylib c)
}
fn DrawTextWrap(text: []u8, x y size: i32, c: Color) {
fn DrawText(text: []u8, x y size: i32, c: u32)
DrawText text x y size (ColorToRaylib c)
}
let WHITE :: .Color {255 255 255 255}
let BLACK :: .Color {0 0 0 255}
let RED :: .Color {255 0 0 255}
let BLUE :: .Color {0 0 255 255}
InitWindow 800 450 Raylib
SetTargetFPS 60
fn GetScreenWidth i32
fn GetScreenHeight i32
fn DrawRect(rect: Rect, c: Color) {
DrawRectangleWrap (rect.pos.x as i32) (rect.pos.y as i32) (rect.size.x as i32) (rect.size.y as i32) c
}
let logox := GetScreenWidth / 2 - 128
let logoy := GetScreenHeight / 2 - 128
\ FIXME: Cannot inline
let fc := 0
let lc := 0
let top_side_rec_width := 16
let left_side_rec_height := 16
let bottom_side_rec_width := 16
let right_side_rec_height := 16
let state := 0
let alpha := 1.0
for WindowShouldClose == 0 {
if state == 0 {
if ++fc == 120 {
state = 1
fc = 0
}
} elif state == 1 {
top_side_rec_width = top_side_rec_width + 4
left_side_rec_height = left_side_rec_height + 4
if top_side_rec_width == 256 {
state = 2
}
} elif state == 2 {
bottom_side_rec_width = bottom_side_rec_width + 4
right_side_rec_height = right_side_rec_height + 4
if bottom_side_rec_width == 256 {
state = 3
}
} elif state == 3 {
if ++fc / 12 != 0 {
++lc
fc = 0
}
if lc >= 10 {
alpha = alpha - 0.01
if alpha <= 0.0 {
alpha = 0.0
state = 4
}
}
} elif IsKeyPressed(KEY_R) { \
fc = lc = 0
top_side_rec_width = 16
left_side_rec_height = 16
bottom_side_rec_width = 16
right_side_rec_height = 16
state = 0
alpha = 1.0
}
BeginDrawing
ClearBackgroundWrap WHITE
DrawFPS 20 20
DrawRect CONST RED
if state == 0 {
if (fc/15)%2 != 0 {
DrawRectangleWrap logox logoy 16 16 BLACK
}
} elif state == 1 {
DrawRectangleWrap logox logoy top_side_rec_width 16 BLACK
DrawRectangleWrap logox logoy 16 left_side_rec_height BLACK
} elif state == 2 {
DrawRectangleWrap logox logoy top_side_rec_width 16 BLACK
DrawRectangleWrap logox logoy 16 left_side_rec_height BLACK
DrawRectangleWrap logox+240 logoy 16 right_side_rec_height BLACK
DrawRectangleWrap logox logoy+240 bottom_side_rec_width 16 BLACK
} elif state == 3 {
let colorw := .Color{255 255 255 (alpha*255.0) as u8}
let colorb := .Color{0 0 0 (alpha*255.0) as u8}
\ FIXME: ADD field access assignment
\color.a = (alpha * 255.0) as u8
DrawRectangleWrap logox logoy top_side_rec_width 16 colorb
DrawRectangleWrap logox logoy+16 16 left_side_rec_height-32 colorb
DrawRectangleWrap logox+240 logoy+16 16 right_side_rec_height-32 cbolor
DrawRectangleWrap logox logoy+240 bottom_side_rec_width 16 colorb
DrawRectangleWrap GetScreenWidth/2-112 GetScreenHeight/2-112 224 224 colorw
DrawTextWrap (TextSubtext raylib 0 lc) GetScreenWidth/2-44 GetScreenHeight/2+48 50 colorb
} else {
DrawTextWrap "[R] REPLAY" 340 200 20 BLACK
}
EndDrawing
}
CloseWindow
\struct Vec2 {
\ x y: f32,
\}
\
\struct Rect {
\ pos size: Vec2,
\}
\
\let CONSTPOS :: .Vec2 {20.0 20.0}
\let CONSTSZ :: .Vec2 {100.0 100.0}
\let CONST :: .Rect {(CONSTPOS) (CONSTSZ)}
\
\\ FIXME: LLVM IR geneartion is borked
\\ let CONST :: .Rect { .Vec2 { 20.0 20.0 } .Vec2 { 100.0 100.0 } }
\
\struct Color {
\ r g b a: u8,
\}
\
\let KEY_R :: 82
\
\fn InitWindow(w h: i32, title: []u8)
\fn CloseWindow
\fn BeginDrawing
\fn SetTargetFPS(fps: i32)
\fn EndDrawing
\fn DrawFPS(x y: i32)
\fn WindowShouldClose i32
\fn TextSubtext(text: []u8, start end: i32) []u8
\fn IsKeyPressed(key: i32) u1
\
\struct Rectangle {
\ x y w h: f32,
\}
\
\fn ColorToRaylib(c: Color) u32 {
\ ret c.a as u32 << 24 as u32 | c.b as u32 << 16 as u32 | c.g as u32 << 8 as u32 | c.r
\}
\
\fn ClearBackgroundWrap(c: Color) {
\ fn ClearBackground(c: u32)
\ ClearBackground (ColorToRaylib c)
\}
\
\fn DrawRectangleWrap(x y w h: i32, c: Color) {
\ fn DrawRectangle(x y w h: i32, c: u32)
\ DrawRectangle x y w h (ColorToRaylib c)
\}
\
\fn DrawCircleWrap(x y: i32, r: f32, c: Color) {
\ fn DrawCircle(x y: i32, r: f32, c: u32)
\ DrawCircle x y r (ColorToRaylib c)
\}
\
\fn DrawRectangleRecWrap(r: Rectangle, c: Color) {
\ fn DrawRectangleRec(r: Rectangle, c: u32)
\ DrawRectangleRec r (ColorToRaylib c)
\}
\
\fn DrawTextWrap(text: []u8, x y size: i32, c: Color) {
\ fn DrawText(text: []u8, x y size: i32, c: u32)
\ DrawText text x y size (ColorToRaylib c)
\}
\
\let WHITE :: .Color {255 255 255 255}
\let BLACK :: .Color {0 0 0 255}
\let RED :: .Color {255 0 0 255}
\let BLUE :: .Color {0 0 255 255}
\
\InitWindow 800 450 Raylib
\SetTargetFPS 60
\
\fn GetScreenWidth i32
\fn GetScreenHeight i32
\
\fn DrawRect(rect: Rect, c: Color) {
\ DrawRectangleWrap (rect.pos.x as i32) (rect.pos.y as i32) (rect.size.x as i32) (rect.size.y as i32) c
\}
\
\let logox := GetScreenWidth / 2 - 128
\let logoy := GetScreenHeight / 2 - 128
\
\\ FIXME: Cannot inline
\let fc := 0
\let lc := 0
\let top_side_rec_width := 16
\let left_side_rec_height := 16
\let bottom_side_rec_width := 16
\let right_side_rec_height := 16
\
\let state := 0
\let alpha := 1.0
\
\for WindowShouldClose == 0 {
\ if state == 0 {
\ if ++fc == 120 {
\ state = 1
\ fc = 0
\ }
\ } elif state == 1 {
\ top_side_rec_width = top_side_rec_width + 4
\ left_side_rec_height = left_side_rec_height + 4
\
\ if top_side_rec_width == 256 {
\ state = 2
\ }
\ } elif state == 2 {
\ bottom_side_rec_width = bottom_side_rec_width + 4
\ right_side_rec_height = right_side_rec_height + 4
\
\ if bottom_side_rec_width == 256 {
\ state = 3
\ }
\ } elif state == 3 {
\ if ++fc / 12 != 0 {
\ ++lc
\ fc = 0
\ }
\
\ if lc >= 10 {
\ alpha = alpha - 0.01
\ if alpha <= 0.0 {
\ alpha = 0.0
\ state = 4
\ }
\ }
\ } elif IsKeyPressed(KEY_R) { \
\ fc = lc = 0
\
\ top_side_rec_width = 16
\ left_side_rec_height = 16
\ bottom_side_rec_width = 16
\ right_side_rec_height = 16
\
\ state = 0
\ alpha = 1.0
\ }
\
\ BeginDrawing
\ ClearBackgroundWrap WHITE
\ DrawFPS 20 20
\
\ DrawRect CONST RED
\
\ if state == 0 {
\ if (fc/15)%2 != 0 {
\ DrawRectangleWrap logox logoy 16 16 BLACK
\ }
\ } elif state == 1 {
\ DrawRectangleWrap logox logoy top_side_rec_width 16 BLACK
\ DrawRectangleWrap logox logoy 16 left_side_rec_height BLACK
\ } elif state == 2 {
\ DrawRectangleWrap logox logoy top_side_rec_width 16 BLACK
\ DrawRectangleWrap logox logoy 16 left_side_rec_height BLACK
\
\ DrawRectangleWrap logox+240 logoy 16 right_side_rec_height BLACK
\ DrawRectangleWrap logox logoy+240 bottom_side_rec_width 16 BLACK
\ } elif state == 3 {
\ let colorw := .Color{255 255 255 (alpha*255.0) as u8}
\ let colorb := .Color{0 0 0 (alpha*255.0) as u8}
\ \ FIXME: ADD field access assignment
\ \color.a = (alpha * 255.0) as u8
\ DrawRectangleWrap logox logoy top_side_rec_width 16 colorb
\ DrawRectangleWrap logox logoy+16 16 left_side_rec_height-32 colorb
\
\ DrawRectangleWrap logox+240 logoy+16 16 right_side_rec_height-32 cbolor
\ DrawRectangleWrap logox logoy+240 bottom_side_rec_width 16 colorb
\
\ DrawRectangleWrap GetScreenWidth/2-112 GetScreenHeight/2-112 224 224 colorw
\
\ DrawTextWrap (TextSubtext raylib 0 lc) GetScreenWidth/2-44 GetScreenHeight/2+48 50 colorb
\ } else {
\ DrawTextWrap "[R] REPLAY" 340 200 20 BLACK
\ }
\
\ EndDrawing
\}
\CloseWindow
\

View File

@ -47,6 +47,18 @@ NodeKind :: enum {
// Example for std.println: std$println
}
EnumValueItem :: struct {
range: SourceLocation,
name: [dynamic]u8,
value: u64,
resolved: bool, // Used during parsing to know if a enum item has a resolved value or not
}
EnumValue :: struct {
values: [dynamic]EnumValueItem,
type: ^Node,
}
Node :: struct {
kind: NodeKind,
range: SourceLocation,
@ -54,6 +66,7 @@ Node :: struct {
value: TokenValue,
value_token_kind: TokenKind,
return_type: ^Type,
enum_value: EnumValue,
}
node_create_value :: proc(kind: NodeKind, range: SourceLocation, value: TokenValue) -> (ret: ^Node) {
@ -223,8 +236,26 @@ node_print :: proc(node: ^Node, indent := 0) {
}
fmt.println("")
for child in node.children {
node_print(child, indent + 1)
if node.kind == .Enum {
for &item in node.enum_value.values {
for i in 0 ..= indent {
fmt.printf(" ")
}
fmt.printf("%s = 0x%016x Resolved?={} at ", item.name, item.value, item.resolved)
fmt.printf(
"\x1B[32m%s\x1b[90m:\x1B[36m{}:{}\x1B[0m->\x1B[36m{}:{}\x1B[0m\n",
item.range.file,
item.range.range.start.line,
item.range.range.start.column,
item.range.range.end.line,
item.range.range.end.column,
)
}
} else {
for child in node.children {
node_print(child, indent + 1)
}
}
}
@ -380,7 +411,7 @@ node_create_struct_initializer :: proc(
return
}
node_create_struct_enum_or_union :: proc(
node_create_struct_or_union :: proc(
range: SourceLocation,
kind: NodeKind,
name: [dynamic]u8,
@ -401,6 +432,15 @@ node_create_struct_enum_or_union :: proc(
return
}
node_create_enum :: proc(range: SourceLocation, data: EnumValue) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Enum,
enum_value = data,
}
return
}
node_clone :: proc(node: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = node^

View File

@ -46,52 +46,54 @@ main :: proc() {
os.exit(1)
}
}
clear(&g_message_list)
type_check(ast, nil)
if len(g_message_list) > 0 {
contains_errors := false
for &msg in g_message_list {
message_print(&msg, &data)
if msg.level == .Error || msg.level == .Fatal {
contains_errors = true
}
}
if contains_errors {
os.exit(1)
}
}
//clear(&g_message_list)
//type_check(ast, nil)
//if len(g_message_list) > 0 {
// contains_errors := false
// for &msg in g_message_list {
// message_print(&msg, &data)
// if msg.level == .Error || msg.level == .Fatal {
// contains_errors = true
// }
// }
// if contains_errors {
// os.exit(1)
// }
//}
name: string
if handle == os.stdin {
name = "stdin"
} else {
name = os.args[1]
}
name_dyn := [dynamic]u8{}
for ch in transmute([]u8)name {
append(&name_dyn, u8(ch))
}
module_name := parse_use_path2(name_dyn)
module_name = main_module_name_from_filename(module_name)
if len(module_name) == 0 {
clear(&module_name)
append(&module_name, 'm')
append(&module_name, 'a')
append(&module_name, 'i')
append(&module_name, 'n')
}
ctx := LLVMContextCreate()
defer LLVMContextDispose(ctx)
module := LLVMModuleCreateWithNameInContext(cstring(raw_data(module_name[:])), ctx)
defer LLVMDisposeModule(module)
builder := LLVMCreateBuilderInContext(ctx)
node_print(ast)
generate_llvm(ctx, module, builder, ast)
append(&module_name, '.')
append(&module_name, 'l')
append(&module_name, 'l')
LLVMPrintModuleToFile(module, cstring(raw_data(module_name[:])), nil)
// name: string
// if handle == os.stdin {
// name = "stdin"
// } else {
// name = os.args[1]
// }
// name_dyn := [dynamic]u8{}
// for ch in transmute([]u8)name {
// append(&name_dyn, u8(ch))
// }
// module_name := parse_use_path2(name_dyn)
// module_name = main_module_name_from_filename(module_name)
// if len(module_name) == 0 {
// clear(&module_name)
// append(&module_name, 'm')
// append(&module_name, 'a')
// append(&module_name, 'i')
// append(&module_name, 'n')
// }
// ctx := LLVMContextCreate()
// defer LLVMContextDispose(ctx)
// module := LLVMModuleCreateWithNameInContext(cstring(raw_data(module_name[:])), ctx)
// defer LLVMDisposeModule(module)
// builder := LLVMCreateBuilderInContext(ctx)
//
// generate_llvm(ctx, module, builder, ast)
//
// append(&module_name, '.')
// append(&module_name, 'l')
// append(&module_name, 'l')
// LLVMPrintModuleToFile(module, cstring(raw_data(module_name[:])), nil)
}
main_module_name_from_filename :: proc(fname: [dynamic]u8) -> (module_name: [dynamic]u8) {

View File

@ -154,7 +154,7 @@ parser_parse_struct_definition :: proc(parser: ^Parser) -> ^Node {
expect(parser, .OpenBrace)
fields := parser_parse_definitions(parser, .CloseBrace)
expect(parser, .CloseBrace)
n := node_create_struct_enum_or_union(range, .Struct, name, fields)
n := node_create_struct_or_union(range, .Struct, name, fields)
n.value = is_packed
return n
}
@ -163,7 +163,86 @@ parser_parse_struct_definition :: proc(parser: ^Parser) -> ^Node {
parser_parse_enum_definition :: proc(parser: ^Parser) -> ^Node {
range := parser.tok.range
expect(parser, .Enum)
panic("TODO, enum not implemented yet")
enum_value := EnumValue {
values = [dynamic]EnumValueItem{},
type = nil,
}
name: [dynamic]u8
if parser.tok.kind == .Identifier {
name = parser.tok.value.([dynamic]u8)
}
if !expect(parser, .Identifier) {
if accept(parser, .Colon) {
for parser.tok.kind != .OpenBrace && parser.tok.kind != .EOF && parser.tok.kind != .Semicolon {
parser_next(parser)
}
}
if accept(parser, .OpenBrace) {
for parser.tok.kind != .CloseBrace && parser.tok.kind != .EOF {
parser_next(parser)
}
}
return nil
}
type_node: ^Node = nil
if accept(parser, .Colon) {
type_node = parser_parse_type(parser)
} else {
type_node = node_create_value(.Identifier, range, [dynamic]u8{'i', '3', '2'})
}
expect(parser, .OpenBrace)
for parser.tok.kind != .CloseBrace && parser.tok.kind != .EOF {
item_range := parser.tok.range
item := EnumValueItem{}
item.range = item_range
item.resolved = false
if parser.tok.kind != .Identifier {
append(&g_message_list, message_create(.Error, "Expected identifier in enum value list", parser.tok.range))
for parser.tok.kind != .CloseBrace && parser.tok.kind != .EOF {
parser_next(parser)
}
return nil
}
name := parser.tok.value.([dynamic]u8)
item.name = name
parser_next(parser)
value := transmute(u64)(i64(-1))
if accept(parser, .Assign) {
if parser.tok.kind != .Integer {
append(
&g_message_list,
message_create(.Error, "Only integers can be values for enums", parser.tok.range),
)
continue
}
item.resolved = true
value = parser.tok.value.(u64)
item_range.range.end = parser.tok.range.range.end
parser_next(parser)
}
item.value = value
append(&enum_value.values, item)
if parser.tok.kind == .CloseBrace {
continue
}
expect(parser, .Comma)
}
range.range.end = parser.tok.range.range.end
parser_next(parser)
// FIXME: Resolve items
enum_value.type = type_node
return node_create_enum(range, enum_value)
}
@(private = "file")
@ -180,7 +259,7 @@ parser_parse_union_definition :: proc(parser: ^Parser) -> ^Node {
expect(parser, .OpenBrace)
fields := parser_parse_definitions(parser, .CloseBrace)
expect(parser, .CloseBrace)
return node_create_struct_enum_or_union(range, .Union, name, fields)
return node_create_struct_or_union(range, .Union, name, fields)
}
@(private = "file")

View File

@ -1,63 +0,0 @@
use "fmt.cat"
use lib "directory/long_library_name"
use "directory/library'with'special'chars"
\ This is a comment, it should be ignored by the compiler
fmt.printf "%i + %i = %i File length: %i\n" a b a + b (io.file_size "file.txt")
fmt.println "Hello world!"
let a := 123 \ This is another comment, that should be ignored by the compiler
let uninitialized : u32
let multiple variables here : u32 = 1 2 3
let string : str, number : i32
if a == 1 {
lol
} elif b == 2 {
kek
} else {
aaaaaaa
}
for {
\ Infinite loop
}
for a > 0 {
\ Countdown loop
fmt.printf "%i\n" a--
}
\for let i : i32 = 0; i < 20; i++ {
\ \ Loop that goes up to 19
\}
\
\for let i : i32 in 0..<20 {
\ \ Shorthand for above
\}
fn name {}
fn name returntype {}
fn name() {}
fn name(param1 param2 param3: i32, param4: u32) u32 { }
struct StructName {
field1 field2 field3: i32,
field4: u32,
}
union MyUnion {
data: StructName,
some_other_data: EnumName,
}
\enum EnumName {
\ Value1,
\ Value2,
\ Value3,
\ Value4,
\}
\EnumName.Value1

View File

@ -1,2 +1,9 @@
a++
enum TokenKind {
Identifier,
Number,
}
struct Token {
kind: TokenKind,
}

View File

@ -15,11 +15,13 @@
- [ ] Add support for nested structure initializers (LLVM)
- [ ] Add support for setting values in struct (uses second ret val)
- [ ] Enums
- [x] Add support for enums in the parser
- [ ] Add support for enums in the type checker
- [ ] Add support for enums in the LLVM emitter
- [ ] Add support for member accesses in the type checker
- [ ] Add support for member accesses in the LLVM emitter
- [ ] Unions
- [ ] Add support for unions in the parser
- [ ] Add support for unions in the type checker
- [ ] Add support for unions in the LLVM emitter
- [ ] Add support for member accesses in the type checker