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 { enum MyEnum {
x y: f32, EnumItem1,
EnumItem2 = 1,
EnumItem3,
EnumItem4,
} }
struct Rect { enum MyTypedEnum : i8 {
pos size: Vec2, EnumItem1,
EnumItem2,
EnumItem3 = 105,
EnumItem4,
} }
let CONSTPOS :: .Vec2 {20.0 20.0} \struct Vec2 {
let CONSTSZ :: .Vec2 {100.0 100.0} \ x y: f32,
let CONST :: .Rect {(CONSTPOS) (CONSTSZ)} \}
\
\ FIXME: LLVM IR geneartion is borked \struct Rect {
\ let CONST :: .Rect { .Vec2 { 20.0 20.0 } .Vec2 { 100.0 100.0 } } \ pos size: Vec2,
\}
struct Color { \
r g b a: u8, \let CONSTPOS :: .Vec2 {20.0 20.0}
} \let CONSTSZ :: .Vec2 {100.0 100.0}
\let CONST :: .Rect {(CONSTPOS) (CONSTSZ)}
let KEY_R :: 82 \
\\ FIXME: LLVM IR geneartion is borked
fn InitWindow(w h: i32, title: []u8) \\ let CONST :: .Rect { .Vec2 { 20.0 20.0 } .Vec2 { 100.0 100.0 } }
fn CloseWindow \
fn BeginDrawing \struct Color {
fn SetTargetFPS(fps: i32) \ r g b a: u8,
fn EndDrawing \}
fn DrawFPS(x y: i32) \
fn WindowShouldClose i32 \let KEY_R :: 82
fn TextSubtext(text: []u8, start end: i32) []u8 \
fn IsKeyPressed(key: i32) u1 \fn InitWindow(w h: i32, title: []u8)
\fn CloseWindow
struct Rectangle { \fn BeginDrawing
x y w h: f32, \fn SetTargetFPS(fps: i32)
} \fn EndDrawing
\fn DrawFPS(x y: i32)
fn ColorToRaylib(c: Color) u32 { \fn WindowShouldClose i32
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 TextSubtext(text: []u8, start end: i32) []u8
} \fn IsKeyPressed(key: i32) u1
\
fn ClearBackgroundWrap(c: Color) { \struct Rectangle {
fn ClearBackground(c: u32) \ x y w h: f32,
ClearBackground (ColorToRaylib c) \}
} \
\fn ColorToRaylib(c: Color) u32 {
fn DrawRectangleWrap(x y w h: i32, c: Color) { \ 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 DrawRectangle(x y w h: i32, c: u32) \}
DrawRectangle x y w h (ColorToRaylib c) \
} \fn ClearBackgroundWrap(c: Color) {
\ fn ClearBackground(c: u32)
fn DrawCircleWrap(x y: i32, r: f32, c: Color) { \ ClearBackground (ColorToRaylib c)
fn DrawCircle(x y: i32, r: f32, c: u32) \}
DrawCircle x y r (ColorToRaylib c) \
} \fn DrawRectangleWrap(x y w h: i32, c: Color) {
\ fn DrawRectangle(x y w h: i32, c: u32)
fn DrawRectangleRecWrap(r: Rectangle, c: Color) { \ DrawRectangle x y w h (ColorToRaylib c)
fn DrawRectangleRec(r: Rectangle, c: u32) \}
DrawRectangleRec r (ColorToRaylib c) \
} \fn DrawCircleWrap(x y: i32, r: f32, c: Color) {
\ fn DrawCircle(x y: i32, r: f32, c: u32)
fn DrawTextWrap(text: []u8, x y size: i32, c: Color) { \ DrawCircle x y r (ColorToRaylib c)
fn DrawText(text: []u8, x y size: i32, c: u32) \}
DrawText text x y size (ColorToRaylib c) \
} \fn DrawRectangleRecWrap(r: Rectangle, c: Color) {
\ fn DrawRectangleRec(r: Rectangle, c: u32)
let WHITE :: .Color {255 255 255 255} \ DrawRectangleRec r (ColorToRaylib c)
let BLACK :: .Color {0 0 0 255} \}
let RED :: .Color {255 0 0 255} \
let BLUE :: .Color {0 0 255 255} \fn DrawTextWrap(text: []u8, x y size: i32, c: Color) {
\ fn DrawText(text: []u8, x y size: i32, c: u32)
InitWindow 800 450 Raylib \ DrawText text x y size (ColorToRaylib c)
SetTargetFPS 60 \}
\
fn GetScreenWidth i32 \let WHITE :: .Color {255 255 255 255}
fn GetScreenHeight i32 \let BLACK :: .Color {0 0 0 255}
\let RED :: .Color {255 0 0 255}
fn DrawRect(rect: Rect, c: Color) { \let BLUE :: .Color {0 0 255 255}
DrawRectangleWrap (rect.pos.x as i32) (rect.pos.y as i32) (rect.size.x as i32) (rect.size.y as i32) c \
} \InitWindow 800 450 Raylib
\SetTargetFPS 60
let logox := GetScreenWidth / 2 - 128 \
let logoy := GetScreenHeight / 2 - 128 \fn GetScreenWidth i32
\fn GetScreenHeight i32
\ FIXME: Cannot inline \
let fc := 0 \fn DrawRect(rect: Rect, c: Color) {
let lc := 0 \ DrawRectangleWrap (rect.pos.x as i32) (rect.pos.y as i32) (rect.size.x as i32) (rect.size.y as i32) c
let top_side_rec_width := 16 \}
let left_side_rec_height := 16 \
let bottom_side_rec_width := 16 \let logox := GetScreenWidth / 2 - 128
let right_side_rec_height := 16 \let logoy := GetScreenHeight / 2 - 128
\
let state := 0 \\ FIXME: Cannot inline
let alpha := 1.0 \let fc := 0
\let lc := 0
for WindowShouldClose == 0 { \let top_side_rec_width := 16
if state == 0 { \let left_side_rec_height := 16
if ++fc == 120 { \let bottom_side_rec_width := 16
state = 1 \let right_side_rec_height := 16
fc = 0 \
} \let state := 0
} elif state == 1 { \let alpha := 1.0
top_side_rec_width = top_side_rec_width + 4 \
left_side_rec_height = left_side_rec_height + 4 \for WindowShouldClose == 0 {
\ if state == 0 {
if top_side_rec_width == 256 { \ if ++fc == 120 {
state = 2 \ state = 1
} \ fc = 0
} elif state == 2 { \ }
bottom_side_rec_width = bottom_side_rec_width + 4 \ } elif state == 1 {
right_side_rec_height = right_side_rec_height + 4 \ top_side_rec_width = top_side_rec_width + 4
\ left_side_rec_height = left_side_rec_height + 4
if bottom_side_rec_width == 256 { \
state = 3 \ if top_side_rec_width == 256 {
} \ state = 2
} elif state == 3 { \ }
if ++fc / 12 != 0 { \ } elif state == 2 {
++lc \ bottom_side_rec_width = bottom_side_rec_width + 4
fc = 0 \ right_side_rec_height = right_side_rec_height + 4
} \
\ if bottom_side_rec_width == 256 {
if lc >= 10 { \ state = 3
alpha = alpha - 0.01 \ }
if alpha <= 0.0 { \ } elif state == 3 {
alpha = 0.0 \ if ++fc / 12 != 0 {
state = 4 \ ++lc
} \ fc = 0
} \ }
} elif IsKeyPressed(KEY_R) { \ \
fc = lc = 0 \ if lc >= 10 {
\ alpha = alpha - 0.01
top_side_rec_width = 16 \ if alpha <= 0.0 {
left_side_rec_height = 16 \ alpha = 0.0
bottom_side_rec_width = 16 \ state = 4
right_side_rec_height = 16 \ }
\ }
state = 0 \ } elif IsKeyPressed(KEY_R) { \
alpha = 1.0 \ fc = lc = 0
} \
\ top_side_rec_width = 16
BeginDrawing \ left_side_rec_height = 16
ClearBackgroundWrap WHITE \ bottom_side_rec_width = 16
DrawFPS 20 20 \ right_side_rec_height = 16
\
DrawRect CONST RED \ state = 0
\ alpha = 1.0
if state == 0 { \ }
if (fc/15)%2 != 0 { \
DrawRectangleWrap logox logoy 16 16 BLACK \ BeginDrawing
} \ ClearBackgroundWrap WHITE
} elif state == 1 { \ DrawFPS 20 20
DrawRectangleWrap logox logoy top_side_rec_width 16 BLACK \
DrawRectangleWrap logox logoy 16 left_side_rec_height BLACK \ DrawRect CONST RED
} elif state == 2 { \
DrawRectangleWrap logox logoy top_side_rec_width 16 BLACK \ if state == 0 {
DrawRectangleWrap logox logoy 16 left_side_rec_height BLACK \ if (fc/15)%2 != 0 {
\ DrawRectangleWrap logox logoy 16 16 BLACK
DrawRectangleWrap logox+240 logoy 16 right_side_rec_height BLACK \ }
DrawRectangleWrap logox logoy+240 bottom_side_rec_width 16 BLACK \ } elif state == 1 {
} elif state == 3 { \ DrawRectangleWrap logox logoy top_side_rec_width 16 BLACK
let colorw := .Color{255 255 255 (alpha*255.0) as u8} \ DrawRectangleWrap logox logoy 16 left_side_rec_height BLACK
let colorb := .Color{0 0 0 (alpha*255.0) as u8} \ } elif state == 2 {
\ FIXME: ADD field access assignment \ DrawRectangleWrap logox logoy top_side_rec_width 16 BLACK
\color.a = (alpha * 255.0) as u8 \ DrawRectangleWrap logox logoy 16 left_side_rec_height BLACK
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 right_side_rec_height BLACK
\ DrawRectangleWrap logox logoy+240 bottom_side_rec_width 16 BLACK
DrawRectangleWrap logox+240 logoy+16 16 right_side_rec_height-32 cbolor \ } elif state == 3 {
DrawRectangleWrap logox logoy+240 bottom_side_rec_width 16 colorb \ let colorw := .Color{255 255 255 (alpha*255.0) as u8}
\ let colorb := .Color{0 0 0 (alpha*255.0) as u8}
DrawRectangleWrap GetScreenWidth/2-112 GetScreenHeight/2-112 224 224 colorw \ \ FIXME: ADD field access assignment
\ \color.a = (alpha * 255.0) as u8
DrawTextWrap (TextSubtext raylib 0 lc) GetScreenWidth/2-44 GetScreenHeight/2+48 50 colorb \ DrawRectangleWrap logox logoy top_side_rec_width 16 colorb
} else { \ DrawRectangleWrap logox logoy+16 16 left_side_rec_height-32 colorb
DrawTextWrap "[R] REPLAY" 340 200 20 BLACK \
} \ DrawRectangleWrap logox+240 logoy+16 16 right_side_rec_height-32 cbolor
\ DrawRectangleWrap logox logoy+240 bottom_side_rec_width 16 colorb
EndDrawing \
} \ DrawRectangleWrap GetScreenWidth/2-112 GetScreenHeight/2-112 224 224 colorw
CloseWindow \
\ 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 // 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 { Node :: struct {
kind: NodeKind, kind: NodeKind,
range: SourceLocation, range: SourceLocation,
@ -54,6 +66,7 @@ Node :: struct {
value: TokenValue, value: TokenValue,
value_token_kind: TokenKind, value_token_kind: TokenKind,
return_type: ^Type, return_type: ^Type,
enum_value: EnumValue,
} }
node_create_value :: proc(kind: NodeKind, range: SourceLocation, value: TokenValue) -> (ret: ^Node) { 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("") fmt.println("")
for child in node.children { if node.kind == .Enum {
node_print(child, indent + 1) 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 return
} }
node_create_struct_enum_or_union :: proc( node_create_struct_or_union :: proc(
range: SourceLocation, range: SourceLocation,
kind: NodeKind, kind: NodeKind,
name: [dynamic]u8, name: [dynamic]u8,
@ -401,6 +432,15 @@ node_create_struct_enum_or_union :: proc(
return 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) { node_clone :: proc(node: ^Node) -> (ret: ^Node) {
ret = new(Node) ret = new(Node)
ret^ = node^ ret^ = node^

View File

@ -46,52 +46,54 @@ main :: proc() {
os.exit(1) os.exit(1)
} }
} }
clear(&g_message_list) //clear(&g_message_list)
type_check(ast, nil) //type_check(ast, nil)
if len(g_message_list) > 0 { //if len(g_message_list) > 0 {
contains_errors := false // contains_errors := false
for &msg in g_message_list { // for &msg in g_message_list {
message_print(&msg, &data) // message_print(&msg, &data)
if msg.level == .Error || msg.level == .Fatal { // if msg.level == .Error || msg.level == .Fatal {
contains_errors = true // contains_errors = true
} // }
} // }
if contains_errors { // if contains_errors {
os.exit(1) // os.exit(1)
} // }
} //}
name: string node_print(ast)
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) // name: string
// if handle == os.stdin {
append(&module_name, '.') // name = "stdin"
append(&module_name, 'l') // } else {
append(&module_name, 'l') // name = os.args[1]
LLVMPrintModuleToFile(module, cstring(raw_data(module_name[:])), nil) // }
// 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) { 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) expect(parser, .OpenBrace)
fields := parser_parse_definitions(parser, .CloseBrace) fields := parser_parse_definitions(parser, .CloseBrace)
expect(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 n.value = is_packed
return n return n
} }
@ -163,7 +163,86 @@ parser_parse_struct_definition :: proc(parser: ^Parser) -> ^Node {
parser_parse_enum_definition :: proc(parser: ^Parser) -> ^Node { parser_parse_enum_definition :: proc(parser: ^Parser) -> ^Node {
range := parser.tok.range range := parser.tok.range
expect(parser, .Enum) 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") @(private = "file")
@ -180,7 +259,7 @@ parser_parse_union_definition :: proc(parser: ^Parser) -> ^Node {
expect(parser, .OpenBrace) expect(parser, .OpenBrace)
fields := parser_parse_definitions(parser, .CloseBrace) fields := parser_parse_definitions(parser, .CloseBrace)
expect(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") @(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 nested structure initializers (LLVM)
- [ ] Add support for setting values in struct (uses second ret val) - [ ] Add support for setting values in struct (uses second ret val)
- [ ] Enums - [ ] Enums
- [x] Add support for enums in the parser
- [ ] Add support for enums in the type checker - [ ] Add support for enums in the type checker
- [ ] Add support for enums in the LLVM emitter - [ ] Add support for enums in the LLVM emitter
- [ ] Add support for member accesses in the type checker - [ ] Add support for member accesses in the type checker
- [ ] Add support for member accesses in the LLVM emitter - [ ] Add support for member accesses in the LLVM emitter
- [ ] Unions - [ ] Unions
- [ ] Add support for unions in the parser
- [ ] Add support for unions in the type checker - [ ] Add support for unions in the type checker
- [ ] Add support for unions in the LLVM emitter - [ ] Add support for unions in the LLVM emitter
- [ ] Add support for member accesses in the type checker - [ ] Add support for member accesses in the type checker