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>
170 lines
3.8 KiB
Odin
170 lines
3.8 KiB
Odin
package main
|
|
|
|
import "core:fmt"
|
|
import "core:os"
|
|
import "core:time"
|
|
|
|
main :: proc() {
|
|
fmt.println("Speedcat bootstrap compiler")
|
|
fmt.println("===========================")
|
|
|
|
handle: os.Handle
|
|
options := parse_args(&os.args)
|
|
file_name := options.file
|
|
if file_name != "<stdin>" {
|
|
errno: os.Errno
|
|
handle, errno = os.open(file_name)
|
|
if errno != 0 {
|
|
fmt.printf("Error opening file\n", errno)
|
|
return
|
|
}
|
|
} else {
|
|
handle = os.stdin
|
|
}
|
|
defer os.close(handle)
|
|
|
|
data, err := os.read_entire_file_from_handle(handle)
|
|
if !err {
|
|
fmt.printf("Error reading file\n", err)
|
|
return
|
|
}
|
|
|
|
u8_arr: [dynamic]u8
|
|
for ch in data {
|
|
append(&u8_arr, u8(ch))
|
|
}
|
|
|
|
lexer := lexer_create(&u8_arr, file_name)
|
|
parser := parser_create(lexer)
|
|
|
|
parser_time_start := time.now()
|
|
ast := parser_parse(&parser)
|
|
parser_duration := time.since(parser_time_start)
|
|
|
|
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_start := time.now()
|
|
type_check(ast, nil)
|
|
type_check_duration := time.since(type_check_start)
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
if options.dump_ast {
|
|
node_print(ast)
|
|
}
|
|
|
|
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) {
|
|
temp_name := [dynamic]u8{}
|
|
for ch in fname {
|
|
if ch == '/' || ch == '\\' {
|
|
clear(&temp_name)
|
|
} else {
|
|
append(&temp_name, ch)
|
|
}
|
|
}
|
|
|
|
for ch in temp_name {
|
|
if ch == '.' {
|
|
break
|
|
}
|
|
append(&module_name, ch)
|
|
}
|
|
|
|
return
|
|
}
|