Add command line arguments and make time printing nicer

This patch adds the following arguments:

1. `--dump-ast` or `-d`: This dumps the Abstract Syntax Tree
2. `--dont-emit-llvm` or `-L`: This skips the LLVM generation step only
   parsing and type checking. Useful for debugging.

Besides this, formatting of the time each compiler step took is also
improved and easier to read now.

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
Slendi 2024-05-04 13:27:25 +02:00
parent a225a5b1fa
commit 5af9845f93
5 changed files with 320 additions and 229 deletions

View File

@ -2,5 +2,5 @@ LLVMC=llvm-config
LLVM_LINKER=-lc++ $(shell ${LLVMC} --libs core --cxxflags --ldflags --system-libs|tr '\n' ' ')
all:
odin run src -o:none -debug -out:speedcat -extra-linker-flags:"${LLVM_LINKER}" -- pong.cat && \
odin run src -o:none -debug -out:speedcat -extra-linker-flags:"${LLVM_LINKER}" -- ${COMP_ARGS} pong.cat && \
clang -I/opt/local/include -L/opt/local/lib -lraylib -lm -framework Cocoa -framework OpenGL -framework IOKit pong.ll -o raylib

374
pong.cat
View File

@ -1,193 +1,193 @@
enum MyEnum {
EnumItem1,
EnumItem2 = 1,
EnumItem3,
EnumItem4,
}
enum MyTypedEnum : i8 {
EnumItem1,
EnumItem2,
EnumItem3 = 105,
EnumItem4,
}
\enum MyEnum {
\ EnumItem1,
\ EnumItem2 = 1,
\ EnumItem3,
\ EnumItem4,
\}
\
\enum MyTypedEnum : i8 {
\ EnumItem1,
\ EnumItem2,
\ EnumItem3 = 105,
\ EnumItem4,
\}
struct Vec2 {
x y: f32,
}
\struct Rect {
\ pos size: Vec2,
\}
\
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
\
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

52
src/argparser.odin Normal file
View File

@ -0,0 +1,52 @@
package main
Options :: struct {
file: string,
dump_ast: bool,
dont_emit_llvm: bool,
}
parse_args :: proc(args: ^[]string) -> (o: Options) {
o.file = "<stdin>"
o.dump_ast = false
o.dont_emit_llvm = false
for &arg, idx in args^ {
if idx == 0 {
continue
}
if len(arg) <= 1 {
continue
}
if arg[0] == '-' && arg[1] != '-' {
for ch, idx in arg {
if idx == 0 {
continue
}
switch (ch) {
case 'd':
o.dump_ast = true
case 'L':
o.dont_emit_llvm = true
case:
panic("Invalid command line switch")
}
}
} else if arg[0] == '-' && arg[1] == '-' {
switch (arg) {
case "--dump-ast":
o.dump_ast = true
case "--dont-emit-llvm":
o.dont_emit_llvm = true
case:
panic("Invalid command line switch")
}
} else {
o.file = arg
}
}
return
}

View File

@ -5,16 +5,19 @@ import "core:os"
import "core:time"
main :: proc() {
fmt.println("Speedcat bootstrap compiler")
fmt.println("===========================")
handle: os.Handle
file_name := "<stdin>"
if len(os.args) >= 2 {
options := parse_args(&os.args)
file_name := options.file
if file_name != "<stdin>" {
errno: os.Errno
handle, errno = os.open(os.args[1])
handle, errno = os.open(file_name)
if errno != 0 {
fmt.printf("Error opening file\n", errno)
return
}
file_name = os.args[1]
} else {
handle = os.stdin
}
@ -37,7 +40,6 @@ main :: proc() {
parser_time_start := time.now()
ast := parser_parse(&parser)
parser_duration := time.since(parser_time_start)
defer fmt.printf("Parsing took: {}ns\n", time.duration_nanoseconds(parser_duration))
if len(g_message_list) > 0 {
contains_errors := false
@ -56,7 +58,6 @@ main :: proc() {
type_check_start := time.now()
type_check(ast, nil)
type_check_duration := time.since(type_check_start)
defer fmt.printf("Type checker took: {}ns\n", time.duration_nanoseconds(type_check_duration))
if len(g_message_list) > 0 {
contains_errors := false
@ -71,39 +72,80 @@ main :: proc() {
}
}
node_print(ast)
if options.dump_ast {
node_print(ast)
}
// 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)
fmt.println("Time report")
fmt.println("===========")
most_printed := 0
printed := fmt.printf(
"Parsing took: % 10dns (%.3fms)\n",
time.duration_nanoseconds(parser_duration),
time.duration_milliseconds(parser_duration),
)
most_printed = max(printed, most_printed)
printed = fmt.printf(
"Type checker took: % 10dns (%.3fms)\n",
time.duration_nanoseconds(type_check_duration),
time.duration_milliseconds(type_check_duration),
)
most_printed = max(printed, most_printed)
llvm_duration: time.Duration = 0
defer {
for most_printed > 1 {
fmt.printf("-")
most_printed -= 1
}
fmt.printf(
"\nTotal time: % 10dns (%.3fms)\n\n",
time.duration_nanoseconds(llvm_duration + type_check_duration + parser_duration),
time.duration_milliseconds(llvm_duration + type_check_duration + parser_duration),
)
}
if options.dont_emit_llvm {
return
}
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)
llvm_start := time.now()
generate_llvm(ctx, module, builder, ast)
llvm_duration = time.since(llvm_start)
printed = fmt.printf(
"LLVM generation took: % 10dns (%.3fms)\n",
time.duration_nanoseconds(llvm_duration),
time.duration_milliseconds(llvm_duration),
)
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

@ -675,8 +675,6 @@ type_check :: proc(ast: ^Node, parent_ast: ^Node) {
type_check(child, ast)
}
node_print(ast)
struct_ := find_struct(ast.value.([dynamic]u8))
if struct_ == nil {
append(
@ -704,7 +702,6 @@ type_check :: proc(ast: ^Node, parent_ast: ^Node) {
idx := 0
for &child in ast.children {
fmt.printf("Comp {} and {} (struct f)\n", child.return_type.kind, struct_.fields[idx].type.kind)
ok, cast_required := compare_types(child.return_type, struct_.fields[idx].type)
if cast_required {
cast_ := node_create_cast({}, child, {})