From 95057fd3592daa6b2d0ab7ca04ee3b40cdcb9ab0 Mon Sep 17 00:00:00 2001 From: Slendi Date: Mon, 29 Apr 2024 16:42:28 +0300 Subject: [PATCH] Add support for enums in the parser Signed-off-by: Slendi --- pong.cat | 364 +++++++++++++++++++++++++----------------------- src/ast.odin | 46 +++++- src/main.odin | 90 ++++++------ src/parser.odin | 85 ++++++++++- test.cat | 63 --------- testmin.cat | 9 +- todo_list.md | 2 + 7 files changed, 370 insertions(+), 289 deletions(-) delete mode 100644 test.cat diff --git a/pong.cat b/pong.cat index 241e7c3..6f032a5 100644 --- a/pong.cat +++ b/pong.cat @@ -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 +\ diff --git a/src/ast.odin b/src/ast.odin index 2ee9fa7..963a646 100644 --- a/src/ast.odin +++ b/src/ast.odin @@ -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^ diff --git a/src/main.odin b/src/main.odin index 3c2f438..1d0e644 100644 --- a/src/main.odin +++ b/src/main.odin @@ -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) { diff --git a/src/parser.odin b/src/parser.odin index 2ef7f33..8f7f059 100644 --- a/src/parser.odin +++ b/src/parser.odin @@ -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") diff --git a/test.cat b/test.cat deleted file mode 100644 index e5e9404..0000000 --- a/test.cat +++ /dev/null @@ -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 - diff --git a/testmin.cat b/testmin.cat index e29fbe9..3fb9f22 100644 --- a/testmin.cat +++ b/testmin.cat @@ -1,2 +1,9 @@ -a++ +enum TokenKind { + Identifier, + Number, +} + +struct Token { + kind: TokenKind, +} diff --git a/todo_list.md b/todo_list.md index 28f5eb0..31a0922 100644 --- a/todo_list.md +++ b/todo_list.md @@ -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