compact compiler ids and spans to reduce memory usage
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
# compiler hardening follow-ups
|
# compiler hardening follow-ups
|
||||||
|
|
||||||
- migrate spans, AST/HIR/IR ids, and diagnostics from `int` to compact integer types
|
|
||||||
- prune unreachable function specializations before HIR construction and emission
|
- prune unreachable function specializations before HIR construction and emission
|
||||||
- support unary minus, including the signed i64 minimum literal boundary
|
- support unary minus, including the signed i64 minimum literal boundary
|
||||||
- move ignored example binaries into a dedicated build directory and remove `.review_tmp`
|
- move ignored example binaries into a dedicated build directory and remove `.review_tmp`
|
||||||
|
|||||||
@@ -29,3 +29,9 @@ The measured run reduced token size by 14.3% and peak tracked memory by 9.8%.
|
|||||||
After the iterative compiler-hardening work on 2026-06-12, the same benchmark
|
After the iterative compiler-hardening work on 2026-06-12, the same benchmark
|
||||||
reported 13,222,443 peak bytes and 40,117 allocations. The reusable traversal
|
reported 13,222,443 peak bytes and 40,117 allocations. The reusable traversal
|
||||||
stacks keep allocation count effectively unchanged from the interning baseline.
|
stacks keep allocation count effectively unchanged from the interning baseline.
|
||||||
|
|
||||||
|
After migrating persistent compiler references and spans to compact IDs on
|
||||||
|
2026-06-12, the benchmark reported 8,651,659 peak bytes and 40,117 allocations.
|
||||||
|
`Span` is 12 bytes and `Token` is 24 bytes; AST expressions, HIR expressions,
|
||||||
|
and IR instructions are 64, 88, and 88 bytes respectively. These sizes are
|
||||||
|
also printed by the benchmark to catch layout regressions.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import "../../compiler/ast"
|
import "../../compiler/ast"
|
||||||
import "../../compiler/checker"
|
import "../../compiler/checker"
|
||||||
import "../../compiler/hir"
|
import "../../compiler/hir"
|
||||||
|
import "../../compiler/ir"
|
||||||
import "../../compiler/lexer"
|
import "../../compiler/lexer"
|
||||||
import "../../compiler/parser"
|
import "../../compiler/parser"
|
||||||
import "../../compiler/source"
|
import "../../compiler/source"
|
||||||
@@ -72,6 +73,10 @@ main :: proc() {
|
|||||||
fmt.printf("allocation_count: %d\n", tracker.total_allocation_count)
|
fmt.printf("allocation_count: %d\n", tracker.total_allocation_count)
|
||||||
fmt.printf("token_count: %d\n", metrics.token_count)
|
fmt.printf("token_count: %d\n", metrics.token_count)
|
||||||
fmt.printf("token_size_bytes: %d\n", size_of(token.Token))
|
fmt.printf("token_size_bytes: %d\n", size_of(token.Token))
|
||||||
|
fmt.printf("span_size_bytes: %d\n", size_of(source.Span))
|
||||||
|
fmt.printf("ast_expr_size_bytes: %d\n", size_of(ast.Expr))
|
||||||
|
fmt.printf("hir_expr_size_bytes: %d\n", size_of(hir.Expr))
|
||||||
|
fmt.printf("ir_instruction_size_bytes: %d\n", size_of(ir.Instruction))
|
||||||
fmt.printf("unique_symbol_count: %d\n", metrics.unique_symbol_count)
|
fmt.printf("unique_symbol_count: %d\n", metrics.unique_symbol_count)
|
||||||
fmt.printf("stored_symbol_bytes: %d\n", metrics.stored_symbol_bytes)
|
fmt.printf("stored_symbol_bytes: %d\n", metrics.stored_symbol_bytes)
|
||||||
fmt.printf("diagnostic_count: %d\n", metrics.diagnostic_count)
|
fmt.printf("diagnostic_count: %d\n", metrics.diagnostic_count)
|
||||||
|
|||||||
+80
-26
@@ -4,9 +4,63 @@ import "../source"
|
|||||||
import "../symbol"
|
import "../symbol"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
INVALID_ID :: -1
|
Expr_Id :: distinct u32
|
||||||
|
Stmt_Id :: distinct u32
|
||||||
|
Function_Id :: distinct u32
|
||||||
|
Global_Id :: distinct u32
|
||||||
|
Import_Id :: distinct u32
|
||||||
|
File_Id :: distinct u32
|
||||||
|
Package_Id :: distinct u32
|
||||||
|
|
||||||
Type_Syntax :: enum {
|
INVALID_EXPR :: Expr_Id(0xffff_ffff)
|
||||||
|
INVALID_STMT :: Stmt_Id(0xffff_ffff)
|
||||||
|
INVALID_FUNCTION :: Function_Id(0xffff_ffff)
|
||||||
|
INVALID_GLOBAL :: Global_Id(0xffff_ffff)
|
||||||
|
INVALID_IMPORT :: Import_Id(0xffff_ffff)
|
||||||
|
INVALID_FILE :: File_Id(0xffff_ffff)
|
||||||
|
INVALID_PACKAGE :: Package_Id(0xffff_ffff)
|
||||||
|
|
||||||
|
expr_id :: proc(index: int) -> Expr_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_EXPR))
|
||||||
|
return Expr_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt_id :: proc(index: int) -> Stmt_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_STMT))
|
||||||
|
return Stmt_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
function_id :: proc(index: int) -> Function_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_FUNCTION))
|
||||||
|
return Function_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
global_id :: proc(index: int) -> Global_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_GLOBAL))
|
||||||
|
return Global_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
import_id :: proc(index: int) -> Import_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_IMPORT))
|
||||||
|
return Import_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
file_id :: proc(index: int) -> File_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_FILE))
|
||||||
|
return File_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
package_id :: proc(index: int) -> Package_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_PACKAGE))
|
||||||
|
return Package_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
index :: proc(id: $T, invalid: T, count: int) -> (int, bool) {
|
||||||
|
value := int(id)
|
||||||
|
return value, id != invalid && value < count
|
||||||
|
}
|
||||||
|
|
||||||
|
Type_Syntax :: enum u8 {
|
||||||
Invalid,
|
Invalid,
|
||||||
Int,
|
Int,
|
||||||
I8,
|
I8,
|
||||||
@@ -16,7 +70,7 @@ Type_Syntax :: enum {
|
|||||||
Void,
|
Void,
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr_Kind :: enum {
|
Expr_Kind :: enum u8 {
|
||||||
Invalid,
|
Invalid,
|
||||||
Integer,
|
Integer,
|
||||||
Name,
|
Name,
|
||||||
@@ -25,15 +79,15 @@ Expr_Kind :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expr :: struct {
|
Expr :: struct {
|
||||||
kind: Expr_Kind,
|
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
|
integer: i64,
|
||||||
|
args: []Expr_Id,
|
||||||
qualifier: symbol.Id,
|
qualifier: symbol.Id,
|
||||||
name: symbol.Id,
|
name: symbol.Id,
|
||||||
integer: i64,
|
left: Expr_Id,
|
||||||
left: int,
|
right: Expr_Id,
|
||||||
right: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
args: []int,
|
kind: Expr_Kind,
|
||||||
diagnostic: int,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Param :: struct {
|
Param :: struct {
|
||||||
@@ -42,7 +96,7 @@ Param :: struct {
|
|||||||
type: Type_Syntax,
|
type: Type_Syntax,
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt_Kind :: enum {
|
Stmt_Kind :: enum u8 {
|
||||||
Invalid,
|
Invalid,
|
||||||
Declaration,
|
Declaration,
|
||||||
Assignment,
|
Assignment,
|
||||||
@@ -56,49 +110,49 @@ Stmt :: struct {
|
|||||||
name: symbol.Id,
|
name: symbol.Id,
|
||||||
type: Type_Syntax,
|
type: Type_Syntax,
|
||||||
immutable: bool,
|
immutable: bool,
|
||||||
expr: int,
|
expr: Expr_Id,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Function :: struct {
|
Function :: struct {
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
name: symbol.Id,
|
name: symbol.Id,
|
||||||
pkg: int,
|
pkg: Package_Id,
|
||||||
file: int,
|
file: File_Id,
|
||||||
c_abi: bool,
|
c_abi: bool,
|
||||||
has_body: bool,
|
has_body: bool,
|
||||||
params: []Param,
|
params: []Param,
|
||||||
result: Type_Syntax,
|
result: Type_Syntax,
|
||||||
body: []int,
|
body: []Stmt_Id,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Global :: struct {
|
Global :: struct {
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
name: symbol.Id,
|
name: symbol.Id,
|
||||||
pkg: int,
|
pkg: Package_Id,
|
||||||
file: int,
|
file: File_Id,
|
||||||
type: Type_Syntax,
|
type: Type_Syntax,
|
||||||
immutable: bool,
|
immutable: bool,
|
||||||
expr: int,
|
expr: Expr_Id,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Import :: struct {
|
Import :: struct {
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
alias: symbol.Id,
|
alias: symbol.Id,
|
||||||
path: string,
|
path: string,
|
||||||
pkg: int,
|
pkg: Package_Id,
|
||||||
file: int,
|
file: File_Id,
|
||||||
target: int,
|
target: Package_Id,
|
||||||
valid: bool,
|
valid: bool,
|
||||||
used: bool,
|
used: bool,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
File :: struct {
|
File :: struct {
|
||||||
source: int,
|
source: source.Source_Id,
|
||||||
pkg: int,
|
pkg: Package_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Package :: struct {
|
Package :: struct {
|
||||||
|
|||||||
+348
-262
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,7 @@ compile_package :: proc(input_path, output_path: string, link_arguments: []linke
|
|||||||
vmem.arena_allocator(&parser_arena),
|
vmem.arena_allocator(&parser_arena),
|
||||||
)
|
)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
|
source.print_all(&diagnostics)
|
||||||
fmt.eprintln("failed to load root package directory:", input_path)
|
fmt.eprintln("failed to load root package directory:", input_path)
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|||||||
+73
-24
@@ -5,24 +5,73 @@ import "../symbol"
|
|||||||
import "../types"
|
import "../types"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
INVALID_ID :: -1
|
Expr_Id :: distinct u32
|
||||||
|
Stmt_Id :: distinct u32
|
||||||
|
Local_Id :: distinct u32
|
||||||
|
Function_Id :: distinct u32
|
||||||
|
Global_Id :: distinct u32
|
||||||
|
Ref :: distinct u32
|
||||||
|
|
||||||
Calling_Convention :: enum {
|
INVALID_EXPR :: Expr_Id(0xffff_ffff)
|
||||||
|
INVALID_STMT :: Stmt_Id(0xffff_ffff)
|
||||||
|
INVALID_LOCAL :: Local_Id(0xffff_ffff)
|
||||||
|
INVALID_FUNCTION :: Function_Id(0xffff_ffff)
|
||||||
|
INVALID_GLOBAL :: Global_Id(0xffff_ffff)
|
||||||
|
INVALID_REF :: Ref(0xffff_ffff)
|
||||||
|
|
||||||
|
expr_id :: proc(index: int) -> Expr_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_EXPR))
|
||||||
|
return Expr_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt_id :: proc(index: int) -> Stmt_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_STMT))
|
||||||
|
return Stmt_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
local_id :: proc(index: int) -> Local_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_LOCAL))
|
||||||
|
return Local_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
function_id :: proc(index: int) -> Function_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_FUNCTION))
|
||||||
|
return Function_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
global_id :: proc(index: int) -> Global_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_GLOBAL))
|
||||||
|
return Global_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
index :: proc(id: $T, invalid: T, count: int) -> (int, bool) {
|
||||||
|
value := int(id)
|
||||||
|
return value, id != invalid && value < count
|
||||||
|
}
|
||||||
|
|
||||||
|
local_ref :: proc(id: Local_Id) -> Ref {return Ref(id)}
|
||||||
|
function_ref :: proc(id: Function_Id) -> Ref {return Ref(id)}
|
||||||
|
global_ref :: proc(id: Global_Id) -> Ref {return Ref(id)}
|
||||||
|
as_local :: proc(ref: Ref) -> Local_Id {return Local_Id(ref)}
|
||||||
|
as_function :: proc(ref: Ref) -> Function_Id {return Function_Id(ref)}
|
||||||
|
as_global :: proc(ref: Ref) -> Global_Id {return Global_Id(ref)}
|
||||||
|
|
||||||
|
Calling_Convention :: enum u8 {
|
||||||
Brolang,
|
Brolang,
|
||||||
C,
|
C,
|
||||||
}
|
}
|
||||||
|
|
||||||
Implementation :: enum {
|
Implementation :: enum u8 {
|
||||||
Definition,
|
Definition,
|
||||||
Declaration,
|
Declaration,
|
||||||
}
|
}
|
||||||
|
|
||||||
Linkage :: enum {
|
Linkage :: enum u8 {
|
||||||
Internal,
|
Internal,
|
||||||
External,
|
External,
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr_Kind :: enum {
|
Expr_Kind :: enum u8 {
|
||||||
Invalid,
|
Invalid,
|
||||||
Integer,
|
Integer,
|
||||||
Local,
|
Local,
|
||||||
@@ -33,15 +82,15 @@ Expr_Kind :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expr :: struct {
|
Expr :: struct {
|
||||||
kind: Expr_Kind,
|
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
type: types.Type,
|
type: types.Type,
|
||||||
integer: i64,
|
integer: i64,
|
||||||
target: int,
|
args: []Expr_Id,
|
||||||
left: int,
|
target: Ref,
|
||||||
right: int,
|
left: Expr_Id,
|
||||||
args: []int,
|
right: Expr_Id,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
|
kind: Expr_Kind,
|
||||||
}
|
}
|
||||||
|
|
||||||
Local :: struct {
|
Local :: struct {
|
||||||
@@ -51,7 +100,7 @@ Local :: struct {
|
|||||||
parameter: bool,
|
parameter: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt_Kind :: enum {
|
Stmt_Kind :: enum u8 {
|
||||||
Declaration,
|
Declaration,
|
||||||
Assignment,
|
Assignment,
|
||||||
Return,
|
Return,
|
||||||
@@ -63,9 +112,9 @@ Stmt_Kind :: enum {
|
|||||||
Stmt :: struct {
|
Stmt :: struct {
|
||||||
kind: Stmt_Kind,
|
kind: Stmt_Kind,
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
local: int,
|
local: Local_Id,
|
||||||
expr: int,
|
expr: Expr_Id,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Function :: struct {
|
Function :: struct {
|
||||||
@@ -75,27 +124,27 @@ Function :: struct {
|
|||||||
implementation: Implementation,
|
implementation: Implementation,
|
||||||
linkage: Linkage,
|
linkage: Linkage,
|
||||||
is_main: bool,
|
is_main: bool,
|
||||||
params: []int,
|
params: []Local_Id,
|
||||||
result: types.Type,
|
result: types.Type,
|
||||||
locals: []Local,
|
locals: []Local,
|
||||||
body: []int,
|
body: []Stmt_Id,
|
||||||
direct_global_reads: [dynamic]int,
|
direct_global_reads: [dynamic]Global_Id,
|
||||||
calls: []int,
|
calls: []Function_Id,
|
||||||
problematic: bool,
|
problematic: bool,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Global :: struct {
|
Global :: struct {
|
||||||
name: symbol.Id,
|
name: symbol.Id,
|
||||||
type: types.Type,
|
type: types.Type,
|
||||||
expr: int,
|
expr: Expr_Id,
|
||||||
static_value: i64,
|
static_value: i64,
|
||||||
is_static: bool,
|
is_static: bool,
|
||||||
dependencies: [dynamic]int,
|
dependencies: [dynamic]Global_Id,
|
||||||
calls: []int,
|
calls: []Function_Id,
|
||||||
direct_problem: bool,
|
direct_problem: bool,
|
||||||
problematic: bool,
|
problematic: bool,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Module :: struct {
|
Module :: struct {
|
||||||
|
|||||||
+54
-12
@@ -5,24 +5,66 @@ import "../symbol"
|
|||||||
import "../types"
|
import "../types"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
INVALID_ID :: -1
|
Instruction_Id :: distinct u32
|
||||||
|
Local_Id :: distinct u32
|
||||||
|
Function_Id :: distinct u32
|
||||||
|
Global_Id :: distinct u32
|
||||||
|
Ref :: distinct u32
|
||||||
|
|
||||||
Calling_Convention :: enum {
|
INVALID_INSTRUCTION :: Instruction_Id(0xffff_ffff)
|
||||||
|
INVALID_LOCAL :: Local_Id(0xffff_ffff)
|
||||||
|
INVALID_FUNCTION :: Function_Id(0xffff_ffff)
|
||||||
|
INVALID_GLOBAL :: Global_Id(0xffff_ffff)
|
||||||
|
INVALID_REF :: Ref(0xffff_ffff)
|
||||||
|
|
||||||
|
instruction_id :: proc(index: int) -> Instruction_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_INSTRUCTION))
|
||||||
|
return Instruction_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
local_id :: proc(index: int) -> Local_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_LOCAL))
|
||||||
|
return Local_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
function_id :: proc(index: int) -> Function_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_FUNCTION))
|
||||||
|
return Function_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
global_id :: proc(index: int) -> Global_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_GLOBAL))
|
||||||
|
return Global_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
index :: proc(id: $T, invalid: T, count: int) -> (int, bool) {
|
||||||
|
value := int(id)
|
||||||
|
return value, id != invalid && value < count
|
||||||
|
}
|
||||||
|
|
||||||
|
local_ref :: proc(id: Local_Id) -> Ref {return Ref(id)}
|
||||||
|
function_ref :: proc(id: Function_Id) -> Ref {return Ref(id)}
|
||||||
|
global_ref :: proc(id: Global_Id) -> Ref {return Ref(id)}
|
||||||
|
as_local :: proc(ref: Ref) -> Local_Id {return Local_Id(ref)}
|
||||||
|
as_function :: proc(ref: Ref) -> Function_Id {return Function_Id(ref)}
|
||||||
|
as_global :: proc(ref: Ref) -> Global_Id {return Global_Id(ref)}
|
||||||
|
|
||||||
|
Calling_Convention :: enum u8 {
|
||||||
Brolang,
|
Brolang,
|
||||||
C,
|
C,
|
||||||
}
|
}
|
||||||
|
|
||||||
Implementation :: enum {
|
Implementation :: enum u8 {
|
||||||
Definition,
|
Definition,
|
||||||
Declaration,
|
Declaration,
|
||||||
}
|
}
|
||||||
|
|
||||||
Linkage :: enum {
|
Linkage :: enum u8 {
|
||||||
Internal,
|
Internal,
|
||||||
External,
|
External,
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode :: enum {
|
Opcode :: enum u8 {
|
||||||
Param,
|
Param,
|
||||||
Const,
|
Const,
|
||||||
Load_Global,
|
Load_Global,
|
||||||
@@ -38,15 +80,15 @@ Opcode :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Instruction :: struct {
|
Instruction :: struct {
|
||||||
op: Opcode,
|
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
type: types.Type,
|
type: types.Type,
|
||||||
integer: i64,
|
integer: i64,
|
||||||
target: int,
|
args: []Instruction_Id,
|
||||||
a: int,
|
target: Ref,
|
||||||
b: int,
|
a: Instruction_Id,
|
||||||
args: []int,
|
b: Instruction_Id,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
|
op: Opcode,
|
||||||
}
|
}
|
||||||
|
|
||||||
Function :: struct {
|
Function :: struct {
|
||||||
@@ -68,7 +110,7 @@ Global :: struct {
|
|||||||
static_value: i64,
|
static_value: i64,
|
||||||
initializer: []Instruction,
|
initializer: []Instruction,
|
||||||
problematic: bool,
|
problematic: bool,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Module :: struct {
|
Module :: struct {
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ append_token :: proc(
|
|||||||
kind: token.Kind,
|
kind: token.Kind,
|
||||||
start, end: int,
|
start, end: int,
|
||||||
id := symbol.INVALID,
|
id := symbol.INVALID,
|
||||||
diagnostic := -1,
|
diagnostic := source.INVALID_DIAGNOSTIC,
|
||||||
) {
|
) {
|
||||||
append(&stream.items, token.Token{
|
append(&stream.items, token.Token{
|
||||||
kind=kind,
|
kind=kind,
|
||||||
span=source.Span{file=source_file.id, start=start, end=end},
|
span=source.Span{file=source_file.id, start=source.Offset(start), end=source.Offset(end)},
|
||||||
symbol=id,
|
symbol=id,
|
||||||
diagnostic=diagnostic,
|
diagnostic=diagnostic,
|
||||||
})
|
})
|
||||||
@@ -74,7 +74,7 @@ lex :: proc(
|
|||||||
cursor += 1
|
cursor += 1
|
||||||
append_token(&stream, source_file, .Colon_Colon, start, cursor)
|
append_token(&stream, source_file, .Colon_Colon, start, cursor)
|
||||||
} else {
|
} else {
|
||||||
id := source.add(diagnostics, source.Span{file=source_file.id, start=start, end=cursor}, "expected a second ':'")
|
id := source.add(diagnostics, source.Span{file=source_file.id, start=source.Offset(start), end=source.Offset(cursor)}, "expected a second ':'")
|
||||||
append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id)
|
append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id)
|
||||||
}
|
}
|
||||||
case '=':
|
case '=':
|
||||||
@@ -111,7 +111,7 @@ lex :: proc(
|
|||||||
if cursor >= len(bytes) || (bytes[cursor] != '\\' && bytes[cursor] != '"') {
|
if cursor >= len(bytes) || (bytes[cursor] != '\\' && bytes[cursor] != '"') {
|
||||||
source.add(
|
source.add(
|
||||||
diagnostics,
|
diagnostics,
|
||||||
source.Span{file=source_file.id, start=max(cursor-1, start), end=min(cursor+1, len(bytes))},
|
source.Span{file=source_file.id, start=source.Offset(max(cursor-1, start)), end=source.Offset(min(cursor+1, len(bytes)))},
|
||||||
"import strings only support '\\\\' and '\\\"' escapes",
|
"import strings only support '\\\\' and '\\\"' escapes",
|
||||||
)
|
)
|
||||||
valid = false
|
valid = false
|
||||||
@@ -127,7 +127,7 @@ lex :: proc(
|
|||||||
} else {
|
} else {
|
||||||
id := source.add(
|
id := source.add(
|
||||||
diagnostics,
|
diagnostics,
|
||||||
source.Span{file=source_file.id, start=start, end=cursor},
|
source.Span{file=source_file.id, start=source.Offset(start), end=source.Offset(cursor)},
|
||||||
"unterminated import string",
|
"unterminated import string",
|
||||||
)
|
)
|
||||||
append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id)
|
append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id)
|
||||||
@@ -135,7 +135,7 @@ lex :: proc(
|
|||||||
case ';':
|
case ';':
|
||||||
id := source.add(
|
id := source.add(
|
||||||
diagnostics,
|
diagnostics,
|
||||||
source.Span{file=source_file.id, start=cursor, end=cursor+1},
|
source.Span{file=source_file.id, start=source.Offset(cursor), end=source.Offset(cursor+1)},
|
||||||
"semicolons are invalid; terminate statements with a newline",
|
"semicolons are invalid; terminate statements with a newline",
|
||||||
)
|
)
|
||||||
append_token(&stream, source_file, .Invalid, cursor, cursor+1, diagnostic=id)
|
append_token(&stream, source_file, .Invalid, cursor, cursor+1, diagnostic=id)
|
||||||
@@ -162,7 +162,7 @@ lex :: proc(
|
|||||||
} else {
|
} else {
|
||||||
id := source.addf(
|
id := source.addf(
|
||||||
diagnostics,
|
diagnostics,
|
||||||
source.Span{file=source_file.id, start=cursor, end=cursor+1},
|
source.Span{file=source_file.id, start=source.Offset(cursor), end=source.Offset(cursor+1)},
|
||||||
"invalid source byte 0x%02x",
|
"invalid source byte 0x%02x",
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
|
|||||||
+46
-43
@@ -49,11 +49,11 @@ sentinel :: proc(value_type: types.Type) -> i64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: int) -> bool {
|
valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: ir.Instruction_Id) -> bool {
|
||||||
return instruction_id >= 0 && instruction_id < len(instructions)
|
return instruction_id != ir.INVALID_INSTRUCTION && int(instruction_id) < len(instructions)
|
||||||
}
|
}
|
||||||
|
|
||||||
valid_value :: proc(instructions: []ir.Instruction, value_id: int, expected: types.Type) -> bool {
|
valid_value :: proc(instructions: []ir.Instruction, value_id: ir.Instruction_Id, expected: types.Type) -> bool {
|
||||||
if !valid_instruction(instructions, value_id) ||
|
if !valid_instruction(instructions, value_id) ||
|
||||||
!types.is_concrete_integer(expected) ||
|
!types.is_concrete_integer(expected) ||
|
||||||
!types.equal(instructions[value_id].type, expected) {
|
!types.equal(instructions[value_id].type, expected) {
|
||||||
@@ -68,7 +68,7 @@ valid_value :: proc(instructions: []ir.Instruction, value_id: int, expected: typ
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
write_operand :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, value_id: int, expected: types.Type) {
|
write_operand :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, value_id: ir.Instruction_Id, expected: types.Type) {
|
||||||
if !valid_value(instructions, value_id, expected) {
|
if !valid_value(instructions, value_id, expected) {
|
||||||
fmt.sbprintf(builder, "%d", sentinel(expected))
|
fmt.sbprintf(builder, "%d", sentinel(expected))
|
||||||
return
|
return
|
||||||
@@ -88,8 +88,8 @@ register_message :: proc(emitter: ^Emitter, text: string) -> int {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostic_message :: proc(emitter: ^Emitter, diagnostic: int, span: source.Span, fallback: string) -> int {
|
diagnostic_message :: proc(emitter: ^Emitter, diagnostic: source.Diagnostic_Id, span: source.Span, fallback: string) -> int {
|
||||||
if diagnostic >= 0 && diagnostic < len(emitter.diagnostics.items) {
|
if _, ok := source.diagnostic_index(diagnostic, len(emitter.diagnostics.items)); ok {
|
||||||
message := source.format(emitter.diagnostics, diagnostic, emitter.allocator)
|
message := source.format(emitter.diagnostics, diagnostic, emitter.allocator)
|
||||||
id := register_message(emitter, message)
|
id := register_message(emitter, message)
|
||||||
delete(message, emitter.allocator)
|
delete(message, emitter.allocator)
|
||||||
@@ -137,7 +137,7 @@ emit_recovery_value :: proc(emitter: ^Emitter, instruction_id: int, instruction:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_call_args :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, args: []int, param_types: []types.Type) {
|
emit_call_args :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, args: []ir.Instruction_Id, param_types: []types.Type) {
|
||||||
for arg, index in args {
|
for arg, index in args {
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
strings.write_string(builder, ", ")
|
strings.write_string(builder, ", ")
|
||||||
@@ -152,60 +152,62 @@ emit_instruction_stream :: proc(
|
|||||||
instructions: []ir.Instruction,
|
instructions: []ir.Instruction,
|
||||||
function: ir.Function,
|
function: ir.Function,
|
||||||
global_initializer := false,
|
global_initializer := false,
|
||||||
) -> int {
|
) -> ir.Instruction_Id {
|
||||||
return_value := -1
|
return_value := ir.INVALID_INSTRUCTION
|
||||||
after_return := false
|
after_return := false
|
||||||
for instruction, instruction_id in instructions {
|
for instruction, instruction_index in instructions {
|
||||||
|
instruction_id := ir.instruction_id(instruction_index)
|
||||||
if after_return {
|
if after_return {
|
||||||
fmt.sbprintf(&emitter.builder, "recover_after_return_%d:\n", instruction_id)
|
fmt.sbprintf(&emitter.builder, "recover_after_return_%d:\n", instruction_index)
|
||||||
after_return = false
|
after_return = false
|
||||||
}
|
}
|
||||||
switch instruction.op {
|
switch instruction.op {
|
||||||
case .Param, .Const:
|
case .Param, .Const:
|
||||||
case .Load_Global:
|
case .Load_Global:
|
||||||
if instruction.target < 0 || instruction.target >= len(emitter.module.globals) {
|
global_id := ir.as_global(instruction.target)
|
||||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid global reference")
|
if global_id == ir.INVALID_GLOBAL || int(global_id) >= len(emitter.module.globals) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid global reference")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
global := emitter.module.globals[instruction.target]
|
global := emitter.module.globals[global_id]
|
||||||
if !types.equal(instruction.type, global.type) {
|
if !types.equal(instruction.type, global.type) {
|
||||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid global reference type")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid global reference type")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if global.is_static {
|
if global.is_static {
|
||||||
fmt.sbprintf(
|
fmt.sbprintf(
|
||||||
&emitter.builder,
|
&emitter.builder,
|
||||||
" %%v%d = load %s, ptr @bro.g.%d\n",
|
" %%v%d = load %s, ptr @bro.g.%d\n",
|
||||||
instruction_id,
|
instruction_index,
|
||||||
llvm_type(global.type),
|
llvm_type(global.type),
|
||||||
instruction.target,
|
global_id,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
fmt.sbprintf(
|
fmt.sbprintf(
|
||||||
&emitter.builder,
|
&emitter.builder,
|
||||||
" %%v%d = call %s @bro.get.%d()\n",
|
" %%v%d = call %s @bro.get.%d()\n",
|
||||||
instruction_id,
|
instruction_index,
|
||||||
llvm_type(global.type),
|
llvm_type(global.type),
|
||||||
instruction.target,
|
global_id,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case .Alloca:
|
case .Alloca:
|
||||||
if !types.is_concrete_integer(instruction.type) {
|
if !types.is_concrete_integer(instruction.type) {
|
||||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid allocation type")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid allocation type")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = alloca %s\n", instruction_id, llvm_type(instruction.type))
|
fmt.sbprintf(&emitter.builder, " %%v%d = alloca %s\n", instruction_index, llvm_type(instruction.type))
|
||||||
case .Load:
|
case .Load:
|
||||||
if !valid_instruction(instructions, instruction.a) ||
|
if !valid_instruction(instructions, instruction.a) ||
|
||||||
instructions[instruction.a].op != .Alloca ||
|
instructions[instruction.a].op != .Alloca ||
|
||||||
!types.equal(instructions[instruction.a].type, instruction.type) {
|
!types.equal(instructions[instruction.a].type, instruction.type) {
|
||||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid load slot")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid load slot")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.sbprintf(
|
fmt.sbprintf(
|
||||||
&emitter.builder,
|
&emitter.builder,
|
||||||
" %%v%d = load %s, ptr %%v%d\n",
|
" %%v%d = load %s, ptr %%v%d\n",
|
||||||
instruction_id,
|
instruction_index,
|
||||||
llvm_type(instruction.type),
|
llvm_type(instruction.type),
|
||||||
instruction.a,
|
instruction.a,
|
||||||
)
|
)
|
||||||
@@ -214,7 +216,7 @@ emit_instruction_stream :: proc(
|
|||||||
instructions[instruction.a].op != .Alloca ||
|
instructions[instruction.a].op != .Alloca ||
|
||||||
!types.equal(instructions[instruction.a].type, instruction.type) ||
|
!types.equal(instructions[instruction.a].type, instruction.type) ||
|
||||||
!valid_value(instructions, instruction.b, instruction.type) {
|
!valid_value(instructions, instruction.b, instruction.type) {
|
||||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid store operand")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid store operand")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(instruction.type))
|
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(instruction.type))
|
||||||
@@ -225,50 +227,51 @@ emit_instruction_stream :: proc(
|
|||||||
!types.is_concrete_integer(instructions[instruction.a].type) ||
|
!types.is_concrete_integer(instructions[instruction.a].type) ||
|
||||||
!types.is_concrete_integer(instruction.type) ||
|
!types.is_concrete_integer(instruction.type) ||
|
||||||
instructions[instruction.a].type.bits >= instruction.type.bits {
|
instructions[instruction.a].type.bits >= instruction.type.bits {
|
||||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid widening operand")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid widening operand")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
from_type := instructions[instruction.a].type
|
from_type := instructions[instruction.a].type
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = sext %s ", instruction_id, llvm_type(from_type))
|
fmt.sbprintf(&emitter.builder, " %%v%d = sext %s ", instruction_index, llvm_type(from_type))
|
||||||
write_operand(&emitter.builder, instructions, instruction.a, from_type)
|
write_operand(&emitter.builder, instructions, instruction.a, from_type)
|
||||||
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type))
|
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type))
|
||||||
case .Add_Checked:
|
case .Add_Checked:
|
||||||
if !valid_value(instructions, instruction.a, instruction.type) ||
|
if !valid_value(instructions, instruction.a, instruction.type) ||
|
||||||
!valid_value(instructions, instruction.b, instruction.type) {
|
!valid_value(instructions, instruction.b, instruction.type) {
|
||||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid addition operand")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid addition operand")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
type_name := llvm_type(instruction.type)
|
type_name := llvm_type(instruction.type)
|
||||||
fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_id)
|
fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_index)
|
||||||
strings.write_string(&emitter.builder, "{ ")
|
strings.write_string(&emitter.builder, "{ ")
|
||||||
fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.sadd.with.overflow.%s(%s ", type_name, type_name, type_name)
|
fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.sadd.with.overflow.%s(%s ", type_name, type_name, type_name)
|
||||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type)
|
write_operand(&emitter.builder, instructions, instruction.a, instruction.type)
|
||||||
fmt.sbprintf(&emitter.builder, ", %s ", type_name)
|
fmt.sbprintf(&emitter.builder, ", %s ", type_name)
|
||||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type)
|
write_operand(&emitter.builder, instructions, instruction.b, instruction.type)
|
||||||
fmt.sbprintf(&emitter.builder, ")\n")
|
fmt.sbprintf(&emitter.builder, ")\n")
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = extractvalue ", instruction_id)
|
fmt.sbprintf(&emitter.builder, " %%v%d = extractvalue ", instruction_index)
|
||||||
strings.write_string(&emitter.builder, "{ ")
|
strings.write_string(&emitter.builder, "{ ")
|
||||||
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_id)
|
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_index)
|
||||||
fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_id)
|
fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_index)
|
||||||
strings.write_string(&emitter.builder, "{ ")
|
strings.write_string(&emitter.builder, "{ ")
|
||||||
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 1\n", type_name, instruction_id)
|
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 1\n", type_name, instruction_index)
|
||||||
fmt.sbprintf(
|
fmt.sbprintf(
|
||||||
&emitter.builder,
|
&emitter.builder,
|
||||||
" br i1 %%overflow%d, label %%overflow_trap%d, label %%overflow_continue%d\n",
|
" br i1 %%overflow%d, label %%overflow_trap%d, label %%overflow_continue%d\n",
|
||||||
instruction_id,
|
instruction_index,
|
||||||
instruction_id,
|
instruction_index,
|
||||||
instruction_id,
|
instruction_index,
|
||||||
)
|
)
|
||||||
fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_id)
|
fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_index)
|
||||||
message := diagnostic_message(emitter, -1, instruction.span, "signed integer addition overflow")
|
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "signed integer addition overflow")
|
||||||
emit_trap_call(emitter, message)
|
emit_trap_call(emitter, message)
|
||||||
fmt.sbprintf(&emitter.builder, " unreachable\noverflow_continue%d:\n", instruction_id)
|
fmt.sbprintf(&emitter.builder, " unreachable\noverflow_continue%d:\n", instruction_index)
|
||||||
case .Call:
|
case .Call:
|
||||||
if instruction.target < 0 || instruction.target >= len(emitter.module.functions) {
|
function_id := ir.as_function(instruction.target)
|
||||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid function specialization")
|
if function_id == ir.INVALID_FUNCTION || int(function_id) >= len(emitter.module.functions) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid function specialization")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
target := emitter.module.functions[instruction.target]
|
target := emitter.module.functions[function_id]
|
||||||
valid_args := len(instruction.args) == len(target.param_types)
|
valid_args := len(instruction.args) == len(target.param_types)
|
||||||
if valid_args {
|
if valid_args {
|
||||||
for arg, index in instruction.args {
|
for arg, index in instruction.args {
|
||||||
@@ -283,11 +286,11 @@ emit_instruction_stream :: proc(
|
|||||||
target_result = types.I32
|
target_result = types.I32
|
||||||
}
|
}
|
||||||
if !valid_args || !types.equal(instruction.type, target_result) {
|
if !valid_args || !types.equal(instruction.type, target_result) {
|
||||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid function call operands")
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid function call operands")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if instruction.type.kind != .Void {
|
if instruction.type.kind != .Void {
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_id)
|
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
|
||||||
} else {
|
} else {
|
||||||
strings.write_string(&emitter.builder, " ")
|
strings.write_string(&emitter.builder, " ")
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-16
@@ -39,20 +39,20 @@ is_identifier :: proc(value: string) -> bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
find_package :: proc(state: ^State, path: string) -> int {
|
find_package :: proc(state: ^State, path: string) -> ast.Package_Id {
|
||||||
for pkg, id in state.module.packages {
|
for pkg, id in state.module.packages {
|
||||||
if pkg.path == path {
|
if pkg.path == path {
|
||||||
return id
|
return ast.package_id(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1
|
return ast.INVALID_PACKAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
add_placeholder :: proc(state: ^State, path: string) -> int {
|
add_placeholder :: proc(state: ^State, path: string) -> ast.Package_Id {
|
||||||
if existing := find_package(state, path); existing >= 0 {
|
if existing := find_package(state, path); existing != ast.INVALID_PACKAGE {
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
id := len(state.module.packages)
|
id := ast.package_id(len(state.module.packages))
|
||||||
append(&state.module.packages, ast.Package{
|
append(&state.module.packages, ast.Package{
|
||||||
path=strings.clone(path, state.allocator),
|
path=strings.clone(path, state.allocator),
|
||||||
name=symbol.intern(state.symbols, filepath.base(path)),
|
name=symbol.intern(state.symbols, filepath.base(path)),
|
||||||
@@ -103,7 +103,7 @@ resolve_import_path :: proc(state: ^State, importing_path, import_path: string)
|
|||||||
return joined, false
|
return joined, false
|
||||||
}
|
}
|
||||||
|
|
||||||
load_package :: proc(state: ^State, path: string, import_span: source.Span, is_root := false) -> int {
|
load_package :: proc(state: ^State, path: string, import_span: source.Span, is_root := false) -> ast.Package_Id {
|
||||||
canonical, ok := filepath.abs(path, state.allocator)
|
canonical, ok := filepath.abs(path, state.allocator)
|
||||||
if !ok || !os.is_dir(path) {
|
if !ok || !os.is_dir(path) {
|
||||||
if is_root {
|
if is_root {
|
||||||
@@ -111,7 +111,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
|||||||
if len(canonical) > 0 {
|
if len(canonical) > 0 {
|
||||||
delete(canonical, state.allocator)
|
delete(canonical, state.allocator)
|
||||||
}
|
}
|
||||||
return -1
|
return ast.INVALID_PACKAGE
|
||||||
}
|
}
|
||||||
placeholder := path
|
placeholder := path
|
||||||
if len(canonical) > 0 {
|
if len(canonical) > 0 {
|
||||||
@@ -124,12 +124,12 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
|||||||
}
|
}
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
if existing := find_package(state, canonical); existing >= 0 {
|
if existing := find_package(state, canonical); existing != ast.INVALID_PACKAGE {
|
||||||
delete(canonical, state.allocator)
|
delete(canonical, state.allocator)
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg_id := len(state.module.packages)
|
pkg_id := ast.package_id(len(state.module.packages))
|
||||||
append(&state.module.packages, ast.Package{
|
append(&state.module.packages, ast.Package{
|
||||||
path=canonical,
|
path=canonical,
|
||||||
name=symbol.intern(state.symbols, filepath.base(canonical)),
|
name=symbol.intern(state.symbols, filepath.base(canonical)),
|
||||||
@@ -152,13 +152,24 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
|||||||
}
|
}
|
||||||
|
|
||||||
for file_info in files {
|
for file_info in files {
|
||||||
|
if file_info.size < 0 || !source.fits_source_length(u64(file_info.size)) {
|
||||||
|
source.addf(state.diagnostics, import_span, "source file '%s' exceeds the 4 GiB source limit", file_info.fullpath)
|
||||||
|
state.root_failed = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
bytes, read_ok := os.read_entire_file(file_info.fullpath, state.sources.allocator)
|
bytes, read_ok := os.read_entire_file(file_info.fullpath, state.sources.allocator)
|
||||||
if !read_ok {
|
if !read_ok {
|
||||||
state.root_failed = true
|
state.root_failed = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !source.fits_source_length(u64(len(bytes))) {
|
||||||
|
source.addf(state.diagnostics, import_span, "source file '%s' exceeds the 4 GiB source limit", file_info.fullpath)
|
||||||
|
delete(bytes, state.sources.allocator)
|
||||||
|
state.root_failed = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
source_id := source.add_source_owned(state.sources, file_info.fullpath, bytes)
|
source_id := source.add_source_owned(state.sources, file_info.fullpath, bytes)
|
||||||
file_id := len(state.module.files)
|
file_id := ast.file_id(len(state.module.files))
|
||||||
append(&state.module.files, ast.File{source=source_id, pkg=pkg_id})
|
append(&state.module.files, ast.File{source=source_id, pkg=pkg_id})
|
||||||
stream := lexer.lex(&state.sources.items[source_id], state.diagnostics, state.symbols, state.token_allocator)
|
stream := lexer.lex(&state.sources.items[source_id], state.diagnostics, state.symbols, state.token_allocator)
|
||||||
parser.parse_into(&stream, &state.sources.items[source_id], state.diagnostics, state.module, pkg_id, file_id)
|
parser.parse_into(&stream, &state.sources.items[source_id], state.diagnostics, state.module, pkg_id, file_id)
|
||||||
@@ -169,7 +180,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
|||||||
import_count := len(state.module.imports)
|
import_count := len(state.module.imports)
|
||||||
for import_id in 0..<import_count {
|
for import_id in 0..<import_count {
|
||||||
import_item := state.module.imports[import_id]
|
import_item := state.module.imports[import_id]
|
||||||
if import_item.pkg != pkg_id || import_item.target >= 0 {
|
if import_item.pkg != pkg_id || import_item.target != ast.INVALID_PACKAGE {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if filepath.is_abs(import_item.path) {
|
if filepath.is_abs(import_item.path) {
|
||||||
@@ -181,7 +192,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
|||||||
target_path, target_ok := resolve_import_path(state, canonical, import_item.path)
|
target_path, target_ok := resolve_import_path(state, canonical, import_item.path)
|
||||||
target := load_package(state, target_path, import_item.span)
|
target := load_package(state, target_path, import_item.span)
|
||||||
state.module.imports[import_id].target = target
|
state.module.imports[import_id].target = target
|
||||||
if !target_ok || target < 0 || !state.module.packages[target].available {
|
if !target_ok || target == ast.INVALID_PACKAGE || !state.module.packages[target].available {
|
||||||
state.module.imports[import_id].valid = false
|
state.module.imports[import_id].valid = false
|
||||||
}
|
}
|
||||||
delete(target_path, state.allocator)
|
delete(target_path, state.allocator)
|
||||||
@@ -189,7 +200,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
|
|||||||
return pkg_id
|
return pkg_id
|
||||||
}
|
}
|
||||||
|
|
||||||
declaration_conflicts :: proc(module: ^ast.Module, pkg: int, name: symbol.Id) -> bool {
|
declaration_conflicts :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> bool {
|
||||||
for function in module.functions {
|
for function in module.functions {
|
||||||
if function.pkg == pkg && function.name == name {
|
if function.pkg == pkg && function.name == name {
|
||||||
return true
|
return true
|
||||||
@@ -205,7 +216,7 @@ declaration_conflicts :: proc(module: ^ast.Module, pkg: int, name: symbol.Id) ->
|
|||||||
|
|
||||||
validate_imports :: proc(state: ^State) {
|
validate_imports :: proc(state: ^State) {
|
||||||
for import_item, import_id in state.module.imports {
|
for import_item, import_id in state.module.imports {
|
||||||
if !symbol.is_valid(import_item.alias) && import_item.target >= 0 {
|
if !symbol.is_valid(import_item.alias) && import_item.target != ast.INVALID_PACKAGE {
|
||||||
state.module.imports[import_id].alias = state.module.packages[import_item.target].name
|
state.module.imports[import_id].alias = state.module.packages[import_item.target].name
|
||||||
}
|
}
|
||||||
alias := state.module.imports[import_id].alias
|
alias := state.module.imports[import_id].alias
|
||||||
@@ -260,7 +271,7 @@ load :: proc(
|
|||||||
allocator=allocator,
|
allocator=allocator,
|
||||||
}
|
}
|
||||||
root := load_package(&state, root_path, source.Span{}, true)
|
root := load_package(&state, root_path, source.Span{}, true)
|
||||||
if root != 0 && root >= 0 {
|
if root != ast.Package_Id(0) && root != ast.INVALID_PACKAGE {
|
||||||
state.root_failed = true
|
state.root_failed = true
|
||||||
}
|
}
|
||||||
validate_imports(&state)
|
validate_imports(&state)
|
||||||
|
|||||||
+92
-82
@@ -10,20 +10,20 @@ import "core:mem"
|
|||||||
State :: struct {
|
State :: struct {
|
||||||
hir_module: ^hir.Module,
|
hir_module: ^hir.Module,
|
||||||
instructions: [dynamic]ir.Instruction,
|
instructions: [dynamic]ir.Instruction,
|
||||||
local_values: []int,
|
local_values: []ir.Instruction_Id,
|
||||||
local_slots: []int,
|
local_slots: []ir.Instruction_Id,
|
||||||
expr_stack: [dynamic]Lower_Expr_Frame,
|
expr_stack: [dynamic]Lower_Expr_Frame,
|
||||||
allocator: mem.Allocator,
|
allocator: mem.Allocator,
|
||||||
}
|
}
|
||||||
|
|
||||||
append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> int {
|
append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> ir.Instruction_Id {
|
||||||
id := len(state.instructions)
|
id := ir.instruction_id(len(state.instructions))
|
||||||
append(&state.instructions, instruction)
|
append(&state.instructions, instruction)
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
clone_args :: proc(values: []int, allocator: mem.Allocator) -> []int {
|
clone_args :: proc(values: []ir.Instruction_Id, allocator: mem.Allocator) -> []ir.Instruction_Id {
|
||||||
result := make([]int, len(values), allocator)
|
result := make([]ir.Instruction_Id, len(values), allocator)
|
||||||
copy(result, values)
|
copy(result, values)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -37,14 +37,19 @@ sentinel :: proc(value_type: types.Type) -> i64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
append_recovery_value :: proc(state: ^State, span: source.Span, value_type: types.Type, diagnostic := -1) -> int {
|
append_recovery_value :: proc(
|
||||||
|
state: ^State,
|
||||||
|
span: source.Span,
|
||||||
|
value_type: types.Type,
|
||||||
|
diagnostic := source.INVALID_DIAGNOSTIC,
|
||||||
|
) -> ir.Instruction_Id {
|
||||||
append_instruction(state, ir.Instruction{
|
append_instruction(state, ir.Instruction{
|
||||||
op=.Trap,
|
op=.Trap,
|
||||||
span=span,
|
span=span,
|
||||||
type=types.VOID,
|
type=types.VOID,
|
||||||
target=-1,
|
target=ir.INVALID_REF,
|
||||||
a=-1,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
b=-1,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=diagnostic,
|
diagnostic=diagnostic,
|
||||||
})
|
})
|
||||||
fallback := value_type
|
fallback := value_type
|
||||||
@@ -56,22 +61,22 @@ append_recovery_value :: proc(state: ^State, span: source.Span, value_type: type
|
|||||||
span=span,
|
span=span,
|
||||||
type=fallback,
|
type=fallback,
|
||||||
integer=sentinel(fallback),
|
integer=sentinel(fallback),
|
||||||
target=-1,
|
target=ir.INVALID_REF,
|
||||||
a=-1,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
b=-1,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Lower_Expr_Frame :: struct {
|
Lower_Expr_Frame :: struct {
|
||||||
expr: int,
|
expr: hir.Expr_Id,
|
||||||
stage: u8,
|
stage: u8,
|
||||||
left: int,
|
left: ir.Instruction_Id,
|
||||||
arg_index: int,
|
arg_index: int,
|
||||||
args: []int,
|
args: []ir.Instruction_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||||
stack := state.expr_stack
|
stack := state.expr_stack
|
||||||
clear_dynamic_array(&stack)
|
clear_dynamic_array(&stack)
|
||||||
defer {
|
defer {
|
||||||
@@ -82,11 +87,11 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
|||||||
state.expr_stack = stack
|
state.expr_stack = stack
|
||||||
}
|
}
|
||||||
append(&stack, Lower_Expr_Frame{expr=expr_id})
|
append(&stack, Lower_Expr_Frame{expr=expr_id})
|
||||||
last := -1
|
last := ir.INVALID_INSTRUCTION
|
||||||
for len(stack) > 0 {
|
for len(stack) > 0 {
|
||||||
frame_index := len(stack)-1
|
frame_index := len(stack)-1
|
||||||
frame := stack[frame_index]
|
frame := stack[frame_index]
|
||||||
if frame.expr < 0 || frame.expr >= len(state.hir_module.exprs) {
|
if frame.expr == hir.INVALID_EXPR || int(frame.expr) >= len(state.hir_module.exprs) {
|
||||||
last = append_recovery_value(state, source.Span{}, types.I64)
|
last = append_recovery_value(state, source.Span{}, types.I64)
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
@@ -100,31 +105,33 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
|||||||
case .Integer:
|
case .Integer:
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
op=.Const, span=expr.span, type=expr.type, integer=expr.integer,
|
op=.Const, span=expr.span, type=expr.type, integer=expr.integer,
|
||||||
target=-1, a=-1, b=-1, diagnostic=-1,
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
case .Local:
|
case .Local:
|
||||||
last = -1
|
last = ir.INVALID_INSTRUCTION
|
||||||
if expr.target >= 0 && expr.target < len(state.local_slots) && state.local_slots[expr.target] >= 0 {
|
local := hir.as_local(expr.target)
|
||||||
|
if local != hir.INVALID_LOCAL && int(local) < len(state.local_slots) && state.local_slots[local] != ir.INVALID_INSTRUCTION {
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
op=.Load, span=expr.span, type=expr.type, target=-1,
|
op=.Load, span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
||||||
a=state.local_slots[expr.target], b=-1, diagnostic=-1,
|
a=state.local_slots[local], b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
} else if expr.target >= 0 && expr.target < len(state.local_values) &&
|
} else if local != hir.INVALID_LOCAL && int(local) < len(state.local_values) &&
|
||||||
state.local_values[expr.target] >= 0 {
|
state.local_values[local] != ir.INVALID_INSTRUCTION {
|
||||||
last = state.local_values[expr.target]
|
last = state.local_values[local]
|
||||||
}
|
}
|
||||||
if last < 0 {
|
if last == ir.INVALID_INSTRUCTION {
|
||||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||||
}
|
}
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
case .Global:
|
case .Global:
|
||||||
if expr.target < 0 || expr.target >= len(state.hir_module.globals) {
|
global := hir.as_global(expr.target)
|
||||||
|
if global == hir.INVALID_GLOBAL || int(global) >= len(state.hir_module.globals) {
|
||||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||||
} else {
|
} else {
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
op=.Load_Global, span=expr.span, type=expr.type, target=expr.target,
|
op=.Load_Global, span=expr.span, type=expr.type, target=ir.global_ref(ir.Global_Id(global)),
|
||||||
a=-1, b=-1, diagnostic=-1,
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
@@ -135,12 +142,13 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
|||||||
stack[frame_index].stage = 2
|
stack[frame_index].stage = 2
|
||||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||||
case .Call:
|
case .Call:
|
||||||
if expr.target < 0 || expr.target >= len(state.hir_module.functions) {
|
function := hir.as_function(expr.target)
|
||||||
|
if function == hir.INVALID_FUNCTION || int(function) >= len(state.hir_module.functions) {
|
||||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
stack[frame_index].args = make([]int, len(expr.args), state.allocator)
|
stack[frame_index].args = make([]ir.Instruction_Id, len(expr.args), state.allocator)
|
||||||
stack[frame_index].stage = 4
|
stack[frame_index].stage = 4
|
||||||
if len(expr.args) > 0 {
|
if len(expr.args) > 0 {
|
||||||
append(&stack, Lower_Expr_Frame{expr=expr.args[0]})
|
append(&stack, Lower_Expr_Frame{expr=expr.args[0]})
|
||||||
@@ -150,8 +158,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
|||||||
}
|
}
|
||||||
if frame.stage == 1 {
|
if frame.stage == 1 {
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
op=.Widen, span=expr.span, type=expr.type, target=-1,
|
op=.Widen, span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
||||||
a=last, b=-1, diagnostic=-1,
|
a=last, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
@@ -164,8 +172,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
|||||||
}
|
}
|
||||||
if frame.stage == 3 {
|
if frame.stage == 3 {
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
op=.Add_Checked, span=expr.span, type=expr.type, target=-1,
|
op=.Add_Checked, span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
||||||
a=frame.left, b=last, diagnostic=-1,
|
a=frame.left, b=last, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
@@ -180,8 +188,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
last = append_instruction(state, ir.Instruction{
|
last = append_instruction(state, ir.Instruction{
|
||||||
op=.Call, span=expr.span, type=expr.type, target=expr.target,
|
op=.Call, span=expr.span, type=expr.type, target=ir.function_ref(ir.Function_Id(hir.as_function(expr.target))),
|
||||||
a=-1, b=-1, args=stack[frame_index].args, diagnostic=-1,
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, args=stack[frame_index].args, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
stack[frame_index].args = nil
|
stack[frame_index].args = nil
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
@@ -194,8 +202,8 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
|||||||
state := State{
|
state := State{
|
||||||
hir_module=hir_module,
|
hir_module=hir_module,
|
||||||
allocator=allocator,
|
allocator=allocator,
|
||||||
local_values=make([]int, len(function.locals), allocator),
|
local_values=make([]ir.Instruction_Id, len(function.locals), allocator),
|
||||||
local_slots=make([]int, len(function.locals), allocator),
|
local_slots=make([]ir.Instruction_Id, len(function.locals), allocator),
|
||||||
}
|
}
|
||||||
state.instructions.allocator = allocator
|
state.instructions.allocator = allocator
|
||||||
state.expr_stack.allocator = allocator
|
state.expr_stack.allocator = allocator
|
||||||
@@ -205,17 +213,17 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
|||||||
delete(state.expr_stack)
|
delete(state.expr_stack)
|
||||||
}
|
}
|
||||||
for _, index in state.local_values {
|
for _, index in state.local_values {
|
||||||
state.local_values[index] = -1
|
state.local_values[index] = ir.INVALID_INSTRUCTION
|
||||||
state.local_slots[index] = -1
|
state.local_slots[index] = ir.INVALID_INSTRUCTION
|
||||||
}
|
}
|
||||||
for local_id in function.params {
|
for local_id in function.params {
|
||||||
param := append_instruction(&state, ir.Instruction{
|
param := append_instruction(&state, ir.Instruction{
|
||||||
op=.Param,
|
op=.Param,
|
||||||
type=function.locals[local_id].type,
|
type=function.locals[local_id].type,
|
||||||
target=local_id,
|
target=ir.local_ref(ir.Local_Id(local_id)),
|
||||||
a=-1,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
b=-1,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
state.local_values[local_id] = param
|
state.local_values[local_id] = param
|
||||||
}
|
}
|
||||||
@@ -225,10 +233,10 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
|||||||
switch statement.kind {
|
switch statement.kind {
|
||||||
case .Declaration:
|
case .Declaration:
|
||||||
value := lower_expr(&state, statement.expr)
|
value := lower_expr(&state, statement.expr)
|
||||||
if statement.local < 0 || statement.local >= len(function.locals) {
|
if statement.local == hir.INVALID_LOCAL || int(statement.local) >= len(function.locals) {
|
||||||
append_instruction(&state, ir.Instruction{
|
append_instruction(&state, ir.Instruction{
|
||||||
op=.Trap, span=statement.span, type=types.VOID,
|
op=.Trap, span=statement.span, type=types.VOID,
|
||||||
target=-1, a=-1, b=-1, diagnostic=statement.diagnostic,
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=statement.diagnostic,
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -238,34 +246,34 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
|||||||
op=.Alloca,
|
op=.Alloca,
|
||||||
span=statement.span,
|
span=statement.span,
|
||||||
type=local.type,
|
type=local.type,
|
||||||
target=statement.local,
|
target=ir.local_ref(ir.Local_Id(statement.local)),
|
||||||
a=-1,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
b=-1,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
state.local_slots[statement.local] = slot
|
state.local_slots[statement.local] = slot
|
||||||
append_instruction(&state, ir.Instruction{
|
append_instruction(&state, ir.Instruction{
|
||||||
op=.Store,
|
op=.Store,
|
||||||
span=statement.span,
|
span=statement.span,
|
||||||
type=local.type,
|
type=local.type,
|
||||||
target=-1,
|
target=ir.INVALID_REF,
|
||||||
a=slot,
|
a=slot,
|
||||||
b=value,
|
b=value,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
state.local_values[statement.local] = value
|
state.local_values[statement.local] = value
|
||||||
}
|
}
|
||||||
case .Assignment:
|
case .Assignment:
|
||||||
value := lower_expr(&state, statement.expr)
|
value := lower_expr(&state, statement.expr)
|
||||||
slot := -1
|
slot := ir.INVALID_INSTRUCTION
|
||||||
if statement.local >= 0 && statement.local < len(state.local_slots) {
|
if statement.local != hir.INVALID_LOCAL && int(statement.local) < len(state.local_slots) {
|
||||||
slot = state.local_slots[statement.local]
|
slot = state.local_slots[statement.local]
|
||||||
}
|
}
|
||||||
if slot < 0 || statement.local < 0 || statement.local >= len(function.locals) {
|
if slot == ir.INVALID_INSTRUCTION || statement.local == hir.INVALID_LOCAL || int(statement.local) >= len(function.locals) {
|
||||||
append_instruction(&state, ir.Instruction{
|
append_instruction(&state, ir.Instruction{
|
||||||
op=.Trap, span=statement.span, type=types.VOID,
|
op=.Trap, span=statement.span, type=types.VOID,
|
||||||
target=-1, a=-1, b=-1, diagnostic=statement.diagnostic,
|
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=statement.diagnostic,
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -273,21 +281,21 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
|||||||
op=.Store,
|
op=.Store,
|
||||||
span=statement.span,
|
span=statement.span,
|
||||||
type=function.locals[statement.local].type,
|
type=function.locals[statement.local].type,
|
||||||
target=-1,
|
target=ir.INVALID_REF,
|
||||||
a=slot,
|
a=slot,
|
||||||
b=value,
|
b=value,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
case .Return:
|
case .Return:
|
||||||
if statement.expr < 0 {
|
if statement.expr == hir.INVALID_EXPR {
|
||||||
append_instruction(&state, ir.Instruction{
|
append_instruction(&state, ir.Instruction{
|
||||||
op=.Return_Void,
|
op=.Return_Void,
|
||||||
span=statement.span,
|
span=statement.span,
|
||||||
type=types.VOID,
|
type=types.VOID,
|
||||||
target=-1,
|
target=ir.INVALID_REF,
|
||||||
a=-1,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
b=-1,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
value := lower_expr(&state, statement.expr)
|
value := lower_expr(&state, statement.expr)
|
||||||
@@ -295,10 +303,10 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
|||||||
op=.Return,
|
op=.Return,
|
||||||
span=statement.span,
|
span=statement.span,
|
||||||
type=function.result,
|
type=function.result,
|
||||||
target=-1,
|
target=ir.INVALID_REF,
|
||||||
a=value,
|
a=value,
|
||||||
b=-1,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case .Expression, .Sink:
|
case .Expression, .Sink:
|
||||||
@@ -308,9 +316,9 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
|||||||
op=.Trap,
|
op=.Trap,
|
||||||
span=statement.span,
|
span=statement.span,
|
||||||
type=types.VOID,
|
type=types.VOID,
|
||||||
target=-1,
|
target=ir.INVALID_REF,
|
||||||
a=-1,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
b=-1,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=statement.diagnostic,
|
diagnostic=statement.diagnostic,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -319,18 +327,18 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
|||||||
(state.instructions[len(state.instructions)-1].op != .Return &&
|
(state.instructions[len(state.instructions)-1].op != .Return &&
|
||||||
state.instructions[len(state.instructions)-1].op != .Return_Void) {
|
state.instructions[len(state.instructions)-1].op != .Return_Void) {
|
||||||
if function.result.kind == .Void {
|
if function.result.kind == .Void {
|
||||||
append_instruction(&state, ir.Instruction{op=.Return_Void, type=types.VOID, target=-1, a=-1, b=-1, diagnostic=-1})
|
append_instruction(&state, ir.Instruction{op=.Return_Void, type=types.VOID, target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC})
|
||||||
} else {
|
} else {
|
||||||
value := append_instruction(&state, ir.Instruction{
|
value := append_instruction(&state, ir.Instruction{
|
||||||
op=.Const,
|
op=.Const,
|
||||||
type=function.result,
|
type=function.result,
|
||||||
integer=sentinel(function.result),
|
integer=sentinel(function.result),
|
||||||
target=-1,
|
target=ir.INVALID_REF,
|
||||||
a=-1,
|
a=ir.INVALID_INSTRUCTION,
|
||||||
b=-1,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
append_instruction(&state, ir.Instruction{op=.Return, type=function.result, target=-1, a=value, b=-1, diagnostic=-1})
|
append_instruction(&state, ir.Instruction{op=.Return, type=function.result, target=ir.INVALID_REF, a=value, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state.instructions[:]
|
return state.instructions[:]
|
||||||
@@ -345,10 +353,10 @@ lower_global_initializer :: proc(hir_module: ^hir.Module, global: hir.Global, al
|
|||||||
append_instruction(&state, ir.Instruction{
|
append_instruction(&state, ir.Instruction{
|
||||||
op=.Return,
|
op=.Return,
|
||||||
type=global.type,
|
type=global.type,
|
||||||
target=-1,
|
target=ir.INVALID_REF,
|
||||||
a=value,
|
a=value,
|
||||||
b=-1,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
return state.instructions[:]
|
return state.instructions[:]
|
||||||
}
|
}
|
||||||
@@ -356,6 +364,7 @@ lower_global_initializer :: proc(hir_module: ^hir.Module, global: hir.Global, al
|
|||||||
lower :: proc(hir_module: ^hir.Module, allocator := context.allocator) -> ir.Module {
|
lower :: proc(hir_module: ^hir.Module, allocator := context.allocator) -> ir.Module {
|
||||||
module := ir.init_module(allocator)
|
module := ir.init_module(allocator)
|
||||||
for global in hir_module.globals {
|
for global in hir_module.globals {
|
||||||
|
_ = ir.global_id(len(module.globals))
|
||||||
append(&module.globals, ir.Global{
|
append(&module.globals, ir.Global{
|
||||||
name=global.name,
|
name=global.name,
|
||||||
type=global.type,
|
type=global.type,
|
||||||
@@ -371,6 +380,7 @@ lower :: proc(hir_module: ^hir.Module, allocator := context.allocator) -> ir.Mod
|
|||||||
for local_id, index in function.params {
|
for local_id, index in function.params {
|
||||||
param_types[index] = function.locals[local_id].type
|
param_types[index] = function.locals[local_id].type
|
||||||
}
|
}
|
||||||
|
_ = ir.function_id(len(module.functions))
|
||||||
append(&module.functions, ir.Function{
|
append(&module.functions, ir.Function{
|
||||||
link_name=fmt.aprintf("%s", function.link_name, allocator=allocator),
|
link_name=fmt.aprintf("%s", function.link_name, allocator=allocator),
|
||||||
calling_convention=.C if function.calling_convention == .C else .Brolang,
|
calling_convention=.C if function.calling_convention == .C else .Brolang,
|
||||||
|
|||||||
+58
-52
@@ -13,8 +13,8 @@ Parser :: struct {
|
|||||||
source_file: ^source.Source,
|
source_file: ^source.Source,
|
||||||
diagnostics: ^source.Diagnostics,
|
diagnostics: ^source.Diagnostics,
|
||||||
module: ast.Module,
|
module: ast.Module,
|
||||||
pkg: int,
|
pkg: ast.Package_Id,
|
||||||
file: int,
|
file: ast.File_Id,
|
||||||
cursor: int,
|
cursor: int,
|
||||||
delimiter_depth: int,
|
delimiter_depth: int,
|
||||||
}
|
}
|
||||||
@@ -22,10 +22,10 @@ Parser :: struct {
|
|||||||
MAX_EXPRESSION_NESTING :: 256
|
MAX_EXPRESSION_NESTING :: 256
|
||||||
|
|
||||||
token_text :: proc(parser: ^Parser, tok: token.Token) -> string {
|
token_text :: proc(parser: ^Parser, tok: token.Token) -> string {
|
||||||
if tok.span.start < 0 || tok.span.end < tok.span.start || tok.span.end > len(parser.source_file.text) {
|
if tok.span.end < tok.span.start || int(tok.span.end) > len(parser.source_file.text) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return parser.source_file.text[tok.span.start:tok.span.end]
|
return parser.source_file.text[int(tok.span.start):int(tok.span.end)]
|
||||||
}
|
}
|
||||||
|
|
||||||
span_from :: proc(first, last: source.Span) -> source.Span {
|
span_from :: proc(first, last: source.Span) -> source.Span {
|
||||||
@@ -61,19 +61,19 @@ skip_newlines :: proc(parser: ^Parser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
add_expr :: proc(parser: ^Parser, expr: ast.Expr) -> int {
|
add_expr :: proc(parser: ^Parser, expr: ast.Expr) -> ast.Expr_Id {
|
||||||
id := len(parser.module.exprs)
|
id := ast.expr_id(len(parser.module.exprs))
|
||||||
append(&parser.module.exprs, expr)
|
append(&parser.module.exprs, expr)
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid_expr :: proc(parser: ^Parser, span: source.Span, message: string) -> int {
|
invalid_expr :: proc(parser: ^Parser, span: source.Span, message: string) -> ast.Expr_Id {
|
||||||
id := source.add(parser.diagnostics, span, message)
|
id := source.add(parser.diagnostics, span, message)
|
||||||
return add_expr(parser, ast.Expr{
|
return add_expr(parser, ast.Expr{
|
||||||
kind=.Invalid,
|
kind=.Invalid,
|
||||||
span=span,
|
span=span,
|
||||||
left=ast.INVALID_ID,
|
left=ast.INVALID_EXPR,
|
||||||
right=ast.INVALID_ID,
|
right=ast.INVALID_EXPR,
|
||||||
diagnostic=id,
|
diagnostic=id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ skip_parenthesized :: proc(parser: ^Parser) -> source.Span {
|
|||||||
return span_from(start.span, end.span)
|
return span_from(start.span, end.span)
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int) -> int {
|
parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int) -> ast.Expr_Id {
|
||||||
if nesting >= MAX_EXPRESSION_NESTING {
|
if nesting >= MAX_EXPRESSION_NESTING {
|
||||||
span := skip_parenthesized(parser)
|
span := skip_parenthesized(parser)
|
||||||
return invalid_expr(parser, span, "expression nesting exceeds 256 levels")
|
return invalid_expr(parser, span, "expression nesting exceeds 256 levels")
|
||||||
@@ -139,7 +139,7 @@ parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Tok
|
|||||||
left_paren := advance(parser)
|
left_paren := advance(parser)
|
||||||
parser.delimiter_depth += 1
|
parser.delimiter_depth += 1
|
||||||
defer parser.delimiter_depth -= 1
|
defer parser.delimiter_depth -= 1
|
||||||
args: [dynamic]int
|
args: [dynamic]ast.Expr_Id
|
||||||
args.allocator = parser.module.allocator
|
args.allocator = parser.module.allocator
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
for current(parser).kind != .Right_Paren && current(parser).kind != .Eof {
|
for current(parser).kind != .Right_Paren && current(parser).kind != .Eof {
|
||||||
@@ -162,13 +162,13 @@ parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Tok
|
|||||||
qualifier=qualifier,
|
qualifier=qualifier,
|
||||||
name=name.symbol,
|
name=name.symbol,
|
||||||
args=args[:],
|
args=args[:],
|
||||||
left=ast.INVALID_ID,
|
left=ast.INVALID_EXPR,
|
||||||
right=ast.INVALID_ID,
|
right=ast.INVALID_EXPR,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
|
parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||||
tok := current(parser)
|
tok := current(parser)
|
||||||
#partial switch tok.kind {
|
#partial switch tok.kind {
|
||||||
case .Integer:
|
case .Integer:
|
||||||
@@ -181,9 +181,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
|
|||||||
kind=.Integer,
|
kind=.Integer,
|
||||||
span=tok.span,
|
span=tok.span,
|
||||||
integer=value,
|
integer=value,
|
||||||
left=ast.INVALID_ID,
|
left=ast.INVALID_EXPR,
|
||||||
right=ast.INVALID_ID,
|
right=ast.INVALID_EXPR,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
case .Identifier:
|
case .Identifier:
|
||||||
first := advance(parser)
|
first := advance(parser)
|
||||||
@@ -204,9 +204,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
|
|||||||
span=span_from(first.span, name.span),
|
span=span_from(first.span, name.span),
|
||||||
qualifier=qualifier,
|
qualifier=qualifier,
|
||||||
name=name.symbol,
|
name=name.symbol,
|
||||||
left=ast.INVALID_ID,
|
left=ast.INVALID_EXPR,
|
||||||
right=ast.INVALID_ID,
|
right=ast.INVALID_EXPR,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
case .Underscore:
|
case .Underscore:
|
||||||
advance(parser)
|
advance(parser)
|
||||||
@@ -231,8 +231,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
|
|||||||
return add_expr(parser, ast.Expr{
|
return add_expr(parser, ast.Expr{
|
||||||
kind=.Invalid,
|
kind=.Invalid,
|
||||||
span=tok.span,
|
span=tok.span,
|
||||||
left=ast.INVALID_ID,
|
left=ast.INVALID_EXPR,
|
||||||
right=ast.INVALID_ID,
|
right=ast.INVALID_EXPR,
|
||||||
diagnostic=tok.diagnostic,
|
diagnostic=tok.diagnostic,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -250,7 +250,7 @@ infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
|
|||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int) -> int {
|
parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int) -> ast.Expr_Id {
|
||||||
if nesting > MAX_EXPRESSION_NESTING {
|
if nesting > MAX_EXPRESSION_NESTING {
|
||||||
tok := current(parser)
|
tok := current(parser)
|
||||||
if tok.kind != .Newline && tok.kind != .Right_Brace && tok.kind != .Eof {
|
if tok.kind != .Newline && tok.kind != .Right_Brace && tok.kind != .Eof {
|
||||||
@@ -277,7 +277,7 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
|||||||
span=span_from(left_expr.span, right_expr.span),
|
span=span_from(left_expr.span, right_expr.span),
|
||||||
left=left,
|
left=left,
|
||||||
right=right,
|
right=right,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
if parser.delimiter_depth > 0 {
|
if parser.delimiter_depth > 0 {
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
@@ -286,17 +286,17 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
|||||||
return left
|
return left
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_expression :: proc(parser: ^Parser) -> int {
|
parse_expression :: proc(parser: ^Parser) -> ast.Expr_Id {
|
||||||
return parse_expression_bp(parser, 0, 0)
|
return parse_expression_bp(parser, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> int {
|
finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> source.Diagnostic_Id {
|
||||||
if current(parser).kind == .Newline {
|
if current(parser).kind == .Newline {
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
return -1
|
return source.INVALID_DIAGNOSTIC
|
||||||
}
|
}
|
||||||
if current(parser).kind == .Eof || allow_closing_brace && current(parser).kind == .Right_Brace {
|
if current(parser).kind == .Eof || allow_closing_brace && current(parser).kind == .Right_Brace {
|
||||||
return -1
|
return source.INVALID_DIAGNOSTIC
|
||||||
}
|
}
|
||||||
diagnostic := source.add(
|
diagnostic := source.add(
|
||||||
parser.diagnostics,
|
parser.diagnostics,
|
||||||
@@ -312,33 +312,33 @@ finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> int {
|
|||||||
return diagnostic
|
return diagnostic
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_return :: proc(parser: ^Parser) -> int {
|
parse_return :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||||
start := advance(parser)
|
start := advance(parser)
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
if current(parser).kind == .Underscore {
|
if current(parser).kind == .Underscore {
|
||||||
end := advance(parser)
|
end := advance(parser)
|
||||||
id := len(parser.module.statements)
|
id := ast.stmt_id(len(parser.module.statements))
|
||||||
append(&parser.module.statements, ast.Stmt{
|
append(&parser.module.statements, ast.Stmt{
|
||||||
kind=.Return,
|
kind=.Return,
|
||||||
span=span_from(start.span, end.span),
|
span=span_from(start.span, end.span),
|
||||||
name=end.symbol,
|
name=end.symbol,
|
||||||
expr=ast.INVALID_ID,
|
expr=ast.INVALID_EXPR,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
expr := parse_expression(parser)
|
expr := parse_expression(parser)
|
||||||
id := len(parser.module.statements)
|
id := ast.stmt_id(len(parser.module.statements))
|
||||||
append(&parser.module.statements, ast.Stmt{
|
append(&parser.module.statements, ast.Stmt{
|
||||||
kind=.Return,
|
kind=.Return,
|
||||||
span=span_from(start.span, parser.module.exprs[expr].span),
|
span=span_from(start.span, parser.module.exprs[expr].span),
|
||||||
expr=expr,
|
expr=expr,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_statement :: proc(parser: ^Parser) -> int {
|
parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||||
if current(parser).kind == .Keyword_Return {
|
if current(parser).kind == .Keyword_Return {
|
||||||
return parse_return(parser)
|
return parse_return(parser)
|
||||||
}
|
}
|
||||||
@@ -363,7 +363,7 @@ parse_statement :: proc(parser: ^Parser) -> int {
|
|||||||
kind = .Declaration
|
kind = .Declaration
|
||||||
immutable = operator.kind == .Colon_Colon
|
immutable = operator.kind == .Colon_Colon
|
||||||
}
|
}
|
||||||
id := len(parser.module.statements)
|
id := ast.stmt_id(len(parser.module.statements))
|
||||||
append(&parser.module.statements, ast.Stmt{
|
append(&parser.module.statements, ast.Stmt{
|
||||||
kind=kind,
|
kind=kind,
|
||||||
span=span_from(name.span, parser.module.exprs[expr].span),
|
span=span_from(name.span, parser.module.exprs[expr].span),
|
||||||
@@ -371,7 +371,7 @@ parse_statement :: proc(parser: ^Parser) -> int {
|
|||||||
type=type_syntax,
|
type=type_syntax,
|
||||||
immutable=immutable,
|
immutable=immutable,
|
||||||
expr=expr,
|
expr=expr,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
@@ -379,12 +379,12 @@ parse_statement :: proc(parser: ^Parser) -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expr := parse_expression(parser)
|
expr := parse_expression(parser)
|
||||||
id := len(parser.module.statements)
|
id := ast.stmt_id(len(parser.module.statements))
|
||||||
append(&parser.module.statements, ast.Stmt{
|
append(&parser.module.statements, ast.Stmt{
|
||||||
kind=.Expression,
|
kind=.Expression,
|
||||||
span=parser.module.exprs[expr].span,
|
span=parser.module.exprs[expr].span,
|
||||||
expr=expr,
|
expr=expr,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
@@ -446,6 +446,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
|||||||
if !ended_by_newline && current(parser).kind != .Eof {
|
if !ended_by_newline && current(parser).kind != .Eof {
|
||||||
_ = finish_statement(parser)
|
_ = finish_statement(parser)
|
||||||
}
|
}
|
||||||
|
_ = ast.function_id(len(parser.module.functions))
|
||||||
append(&parser.module.functions, ast.Function{
|
append(&parser.module.functions, ast.Function{
|
||||||
span=span_from(name.span, end.span),
|
span=span_from(name.span, end.span),
|
||||||
name=name.symbol,
|
name=name.symbol,
|
||||||
@@ -455,23 +456,23 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
|||||||
has_body=false,
|
has_body=false,
|
||||||
params=params,
|
params=params,
|
||||||
result=result,
|
result=result,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
advance(parser)
|
advance(parser)
|
||||||
|
|
||||||
body: [dynamic]int
|
body: [dynamic]ast.Stmt_Id
|
||||||
body.allocator = parser.module.allocator
|
body.allocator = parser.module.allocator
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||||
append(&body, parse_statement(parser))
|
append(&body, parse_statement(parser))
|
||||||
if diagnostic := finish_statement(parser, true); diagnostic >= 0 {
|
if diagnostic := finish_statement(parser, true); diagnostic != source.INVALID_DIAGNOSTIC {
|
||||||
statement_id := len(parser.module.statements)
|
statement_id := ast.stmt_id(len(parser.module.statements))
|
||||||
append(&parser.module.statements, ast.Stmt{
|
append(&parser.module.statements, ast.Stmt{
|
||||||
kind=.Invalid,
|
kind=.Invalid,
|
||||||
span=current(parser).span,
|
span=current(parser).span,
|
||||||
expr=ast.INVALID_ID,
|
expr=ast.INVALID_EXPR,
|
||||||
diagnostic=diagnostic,
|
diagnostic=diagnostic,
|
||||||
})
|
})
|
||||||
append(&body, statement_id)
|
append(&body, statement_id)
|
||||||
@@ -482,6 +483,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
|||||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after function body")
|
source.add(parser.diagnostics, current(parser).span, "expected '}' after function body")
|
||||||
end = func_token
|
end = func_token
|
||||||
}
|
}
|
||||||
|
_ = ast.function_id(len(parser.module.functions))
|
||||||
append(&parser.module.functions, ast.Function{
|
append(&parser.module.functions, ast.Function{
|
||||||
span=span_from(name.span, end.span),
|
span=span_from(name.span, end.span),
|
||||||
name=name.symbol,
|
name=name.symbol,
|
||||||
@@ -492,7 +494,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
|
|||||||
params=params,
|
params=params,
|
||||||
result=result,
|
result=result,
|
||||||
body=body[:],
|
body=body[:],
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,28 +524,30 @@ parse_import :: proc(parser: ^Parser, alias: token.Token, start: token.Token) {
|
|||||||
if path_token.kind != .Newline && path_token.kind != .Eof {
|
if path_token.kind != .Newline && path_token.kind != .Eof {
|
||||||
advance(parser)
|
advance(parser)
|
||||||
}
|
}
|
||||||
|
_ = ast.import_id(len(parser.module.imports))
|
||||||
append(&parser.module.imports, ast.Import{
|
append(&parser.module.imports, ast.Import{
|
||||||
span=start.span,
|
span=start.span,
|
||||||
alias=alias.symbol,
|
alias=alias.symbol,
|
||||||
pkg=parser.pkg,
|
pkg=parser.pkg,
|
||||||
file=parser.file,
|
file=parser.file,
|
||||||
target=-1,
|
target=ast.INVALID_PACKAGE,
|
||||||
valid=false,
|
valid=false,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
_ = finish_statement(parser)
|
_ = finish_statement(parser)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
advance(parser)
|
advance(parser)
|
||||||
|
_ = ast.import_id(len(parser.module.imports))
|
||||||
append(&parser.module.imports, ast.Import{
|
append(&parser.module.imports, ast.Import{
|
||||||
span=span_from(start.span, path_token.span),
|
span=span_from(start.span, path_token.span),
|
||||||
alias=alias.symbol,
|
alias=alias.symbol,
|
||||||
path=decode_import_path(parser, path_token),
|
path=decode_import_path(parser, path_token),
|
||||||
pkg=parser.pkg,
|
pkg=parser.pkg,
|
||||||
file=parser.file,
|
file=parser.file,
|
||||||
target=-1,
|
target=ast.INVALID_PACKAGE,
|
||||||
valid=true,
|
valid=true,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
_ = finish_statement(parser)
|
_ = finish_statement(parser)
|
||||||
}
|
}
|
||||||
@@ -606,6 +610,7 @@ parse_top_level :: proc(parser: ^Parser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expr := parse_expression(parser)
|
expr := parse_expression(parser)
|
||||||
|
_ = ast.global_id(len(parser.module.globals))
|
||||||
append(&parser.module.globals, ast.Global{
|
append(&parser.module.globals, ast.Global{
|
||||||
span=span_from(name.span, parser.module.exprs[expr].span),
|
span=span_from(name.span, parser.module.exprs[expr].span),
|
||||||
name=name.symbol,
|
name=name.symbol,
|
||||||
@@ -614,7 +619,7 @@ parse_top_level :: proc(parser: ^Parser) {
|
|||||||
type=type_syntax,
|
type=type_syntax,
|
||||||
immutable=operator.kind == .Colon_Colon,
|
immutable=operator.kind == .Colon_Colon,
|
||||||
expr=expr,
|
expr=expr,
|
||||||
diagnostic=-1,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
_ = finish_statement(parser)
|
_ = finish_statement(parser)
|
||||||
}
|
}
|
||||||
@@ -644,7 +649,8 @@ parse_into :: proc(
|
|||||||
source_file: ^source.Source,
|
source_file: ^source.Source,
|
||||||
diagnostics: ^source.Diagnostics,
|
diagnostics: ^source.Diagnostics,
|
||||||
module: ^ast.Module,
|
module: ^ast.Module,
|
||||||
pkg, file: int,
|
pkg: ast.Package_Id,
|
||||||
|
file: ast.File_Id,
|
||||||
) {
|
) {
|
||||||
parser := Parser{
|
parser := Parser{
|
||||||
tokens=stream,
|
tokens=stream,
|
||||||
|
|||||||
+63
-27
@@ -3,17 +3,48 @@ package source
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
|
Source_Id :: distinct u32
|
||||||
|
Diagnostic_Id :: distinct u32
|
||||||
|
Offset :: distinct u32
|
||||||
|
|
||||||
|
INVALID_SOURCE :: Source_Id(0xffff_ffff)
|
||||||
|
INVALID_DIAGNOSTIC :: Diagnostic_Id(0xffff_ffff)
|
||||||
|
|
||||||
|
source_id :: proc(index: int) -> Source_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_SOURCE))
|
||||||
|
return Source_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostic_id :: proc(index: int) -> Diagnostic_Id {
|
||||||
|
assert(index >= 0 && u64(index) < u64(INVALID_DIAGNOSTIC))
|
||||||
|
return Diagnostic_Id(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
source_index :: proc(id: Source_Id, count: int) -> (int, bool) {
|
||||||
|
index := int(id)
|
||||||
|
return index, id != INVALID_SOURCE && index < count
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostic_index :: proc(id: Diagnostic_Id, count: int) -> (int, bool) {
|
||||||
|
index := int(id)
|
||||||
|
return index, id != INVALID_DIAGNOSTIC && index < count
|
||||||
|
}
|
||||||
|
|
||||||
|
fits_source_length :: proc(length: u64) -> bool {
|
||||||
|
return length <= u64(max(Offset))
|
||||||
|
}
|
||||||
|
|
||||||
Span :: struct {
|
Span :: struct {
|
||||||
file: int,
|
file: Source_Id,
|
||||||
start: int,
|
start: Offset,
|
||||||
end: int,
|
end: Offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
Source :: struct {
|
Source :: struct {
|
||||||
id: int,
|
id: Source_Id,
|
||||||
path: string,
|
path: string,
|
||||||
text: string,
|
text: string,
|
||||||
line_starts: []int,
|
line_starts: []Offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
Store :: struct {
|
Store :: struct {
|
||||||
@@ -30,7 +61,7 @@ Diagnostics :: struct {
|
|||||||
source: ^Source,
|
source: ^Source,
|
||||||
store: ^Store,
|
store: ^Store,
|
||||||
items: [dynamic]Diagnostic,
|
items: [dynamic]Diagnostic,
|
||||||
index: map[Diagnostic_Key]int,
|
index: map[Diagnostic_Key]Diagnostic_Id,
|
||||||
allocator: mem.Allocator,
|
allocator: mem.Allocator,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,20 +86,22 @@ destroy_store :: proc(store: ^Store) {
|
|||||||
delete(store.items)
|
delete(store.items)
|
||||||
}
|
}
|
||||||
|
|
||||||
make_line_starts :: proc(text: string, allocator: mem.Allocator) -> []int {
|
make_line_starts :: proc(text: string, allocator: mem.Allocator) -> []Offset {
|
||||||
result: [dynamic]int
|
assert(fits_source_length(u64(len(text))))
|
||||||
|
result: [dynamic]Offset
|
||||||
result.allocator = allocator
|
result.allocator = allocator
|
||||||
append(&result, 0)
|
append(&result, 0)
|
||||||
for value, offset in transmute([]byte)text {
|
for value, offset in transmute([]byte)text {
|
||||||
if value == '\n' {
|
if value == '\n' {
|
||||||
append(&result, offset+1)
|
append(&result, Offset(offset+1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result[:]
|
return result[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> int {
|
add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> Source_Id {
|
||||||
id := len(store.items)
|
assert(fits_source_length(u64(len(text))))
|
||||||
|
id := source_id(len(store.items))
|
||||||
owned_text := string(text)
|
owned_text := string(text)
|
||||||
append(&store.items, Source{
|
append(&store.items, Source{
|
||||||
id=id,
|
id=id,
|
||||||
@@ -79,7 +112,7 @@ add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> int {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
add_source :: proc(store: ^Store, path, text: string) -> int {
|
add_source :: proc(store: ^Store, path, text: string) -> Source_Id {
|
||||||
owned := make([]byte, len(text), store.allocator)
|
owned := make([]byte, len(text), store.allocator)
|
||||||
copy(owned, transmute([]byte)text)
|
copy(owned, transmute([]byte)text)
|
||||||
return add_source_owned(store, path, owned)
|
return add_source_owned(store, path, owned)
|
||||||
@@ -111,51 +144,51 @@ destroy_diagnostics :: proc(diagnostics: ^Diagnostics) {
|
|||||||
delete(diagnostics.items)
|
delete(diagnostics.items)
|
||||||
}
|
}
|
||||||
|
|
||||||
add :: proc(diagnostics: ^Diagnostics, span: Span, message: string) -> int {
|
add :: proc(diagnostics: ^Diagnostics, span: Span, message: string) -> Diagnostic_Id {
|
||||||
key := Diagnostic_Key{span=span, message=message}
|
key := Diagnostic_Key{span=span, message=message}
|
||||||
if id, ok := diagnostics.index[key]; ok {
|
if id, ok := diagnostics.index[key]; ok {
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
id := len(diagnostics.items)
|
id := diagnostic_id(len(diagnostics.items))
|
||||||
cloned := fmt.aprintf("%s", message, allocator=diagnostics.allocator)
|
cloned := fmt.aprintf("%s", message, allocator=diagnostics.allocator)
|
||||||
append(&diagnostics.items, Diagnostic{span=span, message=cloned})
|
append(&diagnostics.items, Diagnostic{span=span, message=cloned})
|
||||||
diagnostics.index[Diagnostic_Key{span=span, message=cloned}] = id
|
diagnostics.index[Diagnostic_Key{span=span, message=cloned}] = id
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
addf :: proc(diagnostics: ^Diagnostics, span: Span, format: string, args: ..any) -> int {
|
addf :: proc(diagnostics: ^Diagnostics, span: Span, format: string, args: ..any) -> Diagnostic_Id {
|
||||||
message := fmt.aprintf(format, ..args, allocator=diagnostics.allocator)
|
message := fmt.aprintf(format, ..args, allocator=diagnostics.allocator)
|
||||||
key := Diagnostic_Key{span=span, message=message}
|
key := Diagnostic_Key{span=span, message=message}
|
||||||
if id, ok := diagnostics.index[key]; ok {
|
if id, ok := diagnostics.index[key]; ok {
|
||||||
delete(message, diagnostics.allocator)
|
delete(message, diagnostics.allocator)
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
id := len(diagnostics.items)
|
id := diagnostic_id(len(diagnostics.items))
|
||||||
append(&diagnostics.items, Diagnostic{span=span, message=message})
|
append(&diagnostics.items, Diagnostic{span=span, message=message})
|
||||||
diagnostics.index[Diagnostic_Key{span=span, message=message}] = id
|
diagnostics.index[Diagnostic_Key{span=span, message=message}] = id
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
line_and_column :: proc(source_file: ^Source, offset: int) -> (line, column: int) {
|
line_and_column :: proc(source_file: ^Source, offset: Offset) -> (line, column: int) {
|
||||||
if len(source_file.line_starts) > 0 {
|
if len(source_file.line_starts) > 0 {
|
||||||
limit := min(max(offset, 0), len(source_file.text))
|
limit := min(int(offset), len(source_file.text))
|
||||||
low := 0
|
low := 0
|
||||||
high := len(source_file.line_starts)
|
high := len(source_file.line_starts)
|
||||||
for low < high {
|
for low < high {
|
||||||
middle := low + (high-low)/2
|
middle := low + (high-low)/2
|
||||||
if source_file.line_starts[middle] <= limit {
|
if int(source_file.line_starts[middle]) <= limit {
|
||||||
low = middle+1
|
low = middle+1
|
||||||
} else {
|
} else {
|
||||||
high = middle
|
high = middle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
line = max(low, 1)
|
line = max(low, 1)
|
||||||
column = limit-source_file.line_starts[line-1]+1
|
column = limit-int(source_file.line_starts[line-1])+1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
line = 1
|
line = 1
|
||||||
column = 1
|
column = 1
|
||||||
limit := min(max(offset, 0), len(source_file.text))
|
limit := min(int(offset), len(source_file.text))
|
||||||
for byte_value in transmute([]byte)source_file.text[:limit] {
|
for byte_value in transmute([]byte)source_file.text[:limit] {
|
||||||
if byte_value == '\n' {
|
if byte_value == '\n' {
|
||||||
line += 1
|
line += 1
|
||||||
@@ -168,14 +201,17 @@ line_and_column :: proc(source_file: ^Source, offset: int) -> (line, column: int
|
|||||||
}
|
}
|
||||||
|
|
||||||
source_for_span :: proc(diagnostics: ^Diagnostics, span: Span) -> ^Source {
|
source_for_span :: proc(diagnostics: ^Diagnostics, span: Span) -> ^Source {
|
||||||
if diagnostics.store != nil && span.file >= 0 && span.file < len(diagnostics.store.items) {
|
if diagnostics.store != nil {
|
||||||
return &diagnostics.store.items[span.file]
|
if index, ok := source_index(span.file, len(diagnostics.store.items)); ok {
|
||||||
|
return &diagnostics.store.items[index]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return diagnostics.source
|
return diagnostics.source
|
||||||
}
|
}
|
||||||
|
|
||||||
format :: proc(diagnostics: ^Diagnostics, id: int, allocator := context.allocator) -> string {
|
format :: proc(diagnostics: ^Diagnostics, id: Diagnostic_Id, allocator := context.allocator) -> string {
|
||||||
if id < 0 || id >= len(diagnostics.items) {
|
index, ok := diagnostic_index(id, len(diagnostics.items))
|
||||||
|
if !ok {
|
||||||
path := "<unknown>"
|
path := "<unknown>"
|
||||||
if diagnostics.source != nil {
|
if diagnostics.source != nil {
|
||||||
path = diagnostics.source.path
|
path = diagnostics.source.path
|
||||||
@@ -184,7 +220,7 @@ format :: proc(diagnostics: ^Diagnostics, id: int, allocator := context.allocato
|
|||||||
}
|
}
|
||||||
return fmt.aprintf("%s: compiler recovery error", path, allocator=allocator)
|
return fmt.aprintf("%s: compiler recovery error", path, allocator=allocator)
|
||||||
}
|
}
|
||||||
diagnostic := diagnostics.items[id]
|
diagnostic := diagnostics.items[index]
|
||||||
source_file := source_for_span(diagnostics, diagnostic.span)
|
source_file := source_for_span(diagnostics, diagnostic.span)
|
||||||
if source_file == nil {
|
if source_file == nil {
|
||||||
return fmt.aprintf("<unknown>: error: %s", diagnostic.message, allocator=allocator)
|
return fmt.aprintf("<unknown>: error: %s", diagnostic.message, allocator=allocator)
|
||||||
@@ -202,7 +238,7 @@ format :: proc(diagnostics: ^Diagnostics, id: int, allocator := context.allocato
|
|||||||
|
|
||||||
print_all :: proc(diagnostics: ^Diagnostics) {
|
print_all :: proc(diagnostics: ^Diagnostics) {
|
||||||
for _, id in diagnostics.items {
|
for _, id in diagnostics.items {
|
||||||
message := format(diagnostics, id)
|
message := format(diagnostics, diagnostic_id(id))
|
||||||
fmt.eprintln(message)
|
fmt.eprintln(message)
|
||||||
delete(message)
|
delete(message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package token
|
|||||||
import "../source"
|
import "../source"
|
||||||
import "../symbol"
|
import "../symbol"
|
||||||
|
|
||||||
Kind :: enum {
|
Kind :: enum u8 {
|
||||||
Invalid,
|
Invalid,
|
||||||
Eof,
|
Eof,
|
||||||
Newline,
|
Newline,
|
||||||
@@ -32,10 +32,10 @@ Kind :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Token :: struct {
|
Token :: struct {
|
||||||
kind: Kind,
|
|
||||||
span: source.Span,
|
span: source.Span,
|
||||||
symbol: symbol.Id,
|
symbol: symbol.Id,
|
||||||
diagnostic: int,
|
diagnostic: source.Diagnostic_Id,
|
||||||
|
kind: Kind,
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream :: struct {
|
Stream :: struct {
|
||||||
|
|||||||
+101
-10
@@ -82,6 +82,52 @@ main :: func() void { _ = value }
|
|||||||
testing.expect_value(t, module.statements[module.functions[0].body[0]].name, sink_symbol)
|
testing.expect_value(t, module.statements[module.functions[0].body[0]].name, sink_symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
compact_ids_reserve_invalid_values_and_preserve_layout :: proc(t: ^testing.T) {
|
||||||
|
testing.expect_value(t, size_of(source.Span), 12)
|
||||||
|
testing.expect_value(t, size_of(token.Token), 24)
|
||||||
|
|
||||||
|
source_index, source_ok := source.source_index(source.Source_Id(0), 1)
|
||||||
|
testing.expect_value(t, source_index, 0)
|
||||||
|
testing.expect(t, source_ok)
|
||||||
|
_, source_invalid := source.source_index(source.INVALID_SOURCE, 1)
|
||||||
|
testing.expect(t, !source_invalid)
|
||||||
|
_, source_out_of_bounds := source.source_index(source.Source_Id(1), 1)
|
||||||
|
testing.expect(t, !source_out_of_bounds)
|
||||||
|
diagnostic_index, diagnostic_ok := source.diagnostic_index(source.Diagnostic_Id(0), 1)
|
||||||
|
testing.expect_value(t, diagnostic_index, 0)
|
||||||
|
testing.expect(t, diagnostic_ok)
|
||||||
|
_, diagnostic_invalid := source.diagnostic_index(source.INVALID_DIAGNOSTIC, 1)
|
||||||
|
testing.expect(t, !diagnostic_invalid)
|
||||||
|
|
||||||
|
expr_index, expr_ok := ast.index(ast.Expr_Id(0), ast.INVALID_EXPR, 1)
|
||||||
|
testing.expect_value(t, expr_index, 0)
|
||||||
|
testing.expect(t, expr_ok)
|
||||||
|
_, expr_invalid := ast.index(ast.INVALID_EXPR, ast.INVALID_EXPR, 1)
|
||||||
|
testing.expect(t, !expr_invalid)
|
||||||
|
|
||||||
|
function_index, function_ok := hir.index(hir.Function_Id(0), hir.INVALID_FUNCTION, 1)
|
||||||
|
testing.expect_value(t, function_index, 0)
|
||||||
|
testing.expect(t, function_ok)
|
||||||
|
_, function_invalid := hir.index(hir.INVALID_FUNCTION, hir.INVALID_FUNCTION, 1)
|
||||||
|
testing.expect(t, !function_invalid)
|
||||||
|
|
||||||
|
instruction_index, instruction_ok := ir.index(ir.Instruction_Id(0), ir.INVALID_INSTRUCTION, 1)
|
||||||
|
testing.expect_value(t, instruction_index, 0)
|
||||||
|
testing.expect(t, instruction_ok)
|
||||||
|
_, instruction_invalid := ir.index(ir.INVALID_INSTRUCTION, ir.INVALID_INSTRUCTION, 1)
|
||||||
|
testing.expect(t, !instruction_invalid)
|
||||||
|
|
||||||
|
spec_index, spec_ok := checker.spec_index(checker.Spec_Id(0), 1)
|
||||||
|
testing.expect_value(t, spec_index, 0)
|
||||||
|
testing.expect(t, spec_ok)
|
||||||
|
_, spec_invalid := checker.spec_index(checker.INVALID_SPEC, 1)
|
||||||
|
testing.expect(t, !spec_invalid)
|
||||||
|
|
||||||
|
testing.expect(t, source.fits_source_length(u64(0xffff_ffff)))
|
||||||
|
testing.expect(t, !source.fits_source_length(u64(0x1_0000_0000)))
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
lexer_preserves_newlines_and_skips_comments :: proc(t: ^testing.T) {
|
lexer_preserves_newlines_and_skips_comments :: proc(t: ^testing.T) {
|
||||||
source_file := source.Source{path="test.bro", text="# comment\nmain :: func() void {}\n"}
|
source_file := source.Source{path="test.bro", text="# comment\nmain :: func() void {}\n"}
|
||||||
@@ -196,7 +242,7 @@ main :: func() void {}
|
|||||||
|
|
||||||
c_symbol := symbol.intern(&symbols, "c")
|
c_symbol := symbol.intern(&symbols, "c")
|
||||||
for tok in stream.items {
|
for tok in stream.items {
|
||||||
if tok.span.start < len(text) && text[tok.span.start:tok.span.end] == "c" {
|
if int(tok.span.start) < len(text) && text[int(tok.span.start):int(tok.span.end)] == "c" {
|
||||||
testing.expect_value(t, tok.kind, token.Kind.Identifier)
|
testing.expect_value(t, tok.kind, token.Kind.Identifier)
|
||||||
testing.expect_value(t, tok.symbol, c_symbol)
|
testing.expect_value(t, tok.symbol, c_symbol)
|
||||||
}
|
}
|
||||||
@@ -415,8 +461,8 @@ multi_source_diagnostics_report_the_originating_file :: proc(t: ^testing.T) {
|
|||||||
|
|
||||||
testing.expect(t, loaded)
|
testing.expect(t, loaded)
|
||||||
found := false
|
found := false
|
||||||
for _, diagnostic_id in diagnostics.items {
|
for _, diagnostic_index in diagnostics.items {
|
||||||
message := source.format(&diagnostics, diagnostic_id)
|
message := source.format(&diagnostics, source.diagnostic_id(diagnostic_index))
|
||||||
if strings.contains(message, "/b.bro:2:9:") &&
|
if strings.contains(message, "/b.bro:2:9:") &&
|
||||||
strings.contains(message, "unknown package alias 'math'") {
|
strings.contains(message, "unknown package alias 'math'") {
|
||||||
found = true
|
found = true
|
||||||
@@ -788,7 +834,7 @@ main :: func() void {
|
|||||||
testing.expect(t, done_id >= 0)
|
testing.expect(t, done_id >= 0)
|
||||||
testing.expect(t, main_id >= 0)
|
testing.expect(t, main_id >= 0)
|
||||||
testing.expect_value(t, hir_module.statements[hir_module.functions[done_id].body[0]].kind, hir.Stmt_Kind.Return)
|
testing.expect_value(t, hir_module.statements[hir_module.functions[done_id].body[0]].kind, hir.Stmt_Kind.Return)
|
||||||
testing.expect_value(t, hir_module.statements[hir_module.functions[done_id].body[0]].expr, -1)
|
testing.expect_value(t, hir_module.statements[hir_module.functions[done_id].body[0]].expr, hir.INVALID_EXPR)
|
||||||
main := hir_module.functions[main_id]
|
main := hir_module.functions[main_id]
|
||||||
testing.expect_value(t, hir_module.statements[main.body[0]].kind, hir.Stmt_Kind.Expression)
|
testing.expect_value(t, hir_module.statements[main.body[0]].kind, hir.Stmt_Kind.Expression)
|
||||||
testing.expect_value(t, hir_module.statements[main.body[1]].kind, hir.Stmt_Kind.Sink)
|
testing.expect_value(t, hir_module.statements[main.body[1]].kind, hir.Stmt_Kind.Sink)
|
||||||
@@ -1243,14 +1289,59 @@ maximum_signed_i64_literal_parses_exactly :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, module.exprs[module.globals[0].expr].integer, i64(9223372036854775807))
|
testing.expect_value(t, module.exprs[module.globals[0].expr].integer, i64(9223372036854775807))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
malformed_hir_references_lower_to_valid_trapped_llvm :: proc(t: ^testing.T) {
|
||||||
|
hir_module := hir.init_module()
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
append(&hir_module.exprs, hir.Expr{
|
||||||
|
kind=.Local,
|
||||||
|
type=types.I8,
|
||||||
|
target=hir.local_ref(hir.INVALID_LOCAL),
|
||||||
|
left=hir.INVALID_EXPR,
|
||||||
|
right=hir.INVALID_EXPR,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append(&hir_module.statements, hir.Stmt{
|
||||||
|
kind=.Expression,
|
||||||
|
expr=hir.Expr_Id(0),
|
||||||
|
local=hir.INVALID_LOCAL,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
body := make([]hir.Stmt_Id, 1)
|
||||||
|
body[0] = hir.Stmt_Id(0)
|
||||||
|
append(&hir_module.functions, hir.Function{
|
||||||
|
name=symbol.INVALID,
|
||||||
|
link_name=strings.clone("main"),
|
||||||
|
calling_convention=.C,
|
||||||
|
implementation=.Definition,
|
||||||
|
linkage=.External,
|
||||||
|
is_main=true,
|
||||||
|
result=types.VOID,
|
||||||
|
body=body,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
ir_module := lower.lower(&hir_module)
|
||||||
|
defer ir.destroy_module(&ir_module)
|
||||||
|
source_file := source.Source{path="test.bro", text=""}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
text := llvm.emit(&ir_module, &diagnostics, &symbols)
|
||||||
|
defer delete(text)
|
||||||
|
|
||||||
|
testing.expect(t, strings.contains(text, "call void @bro.trap"))
|
||||||
|
testing.expect(t, !strings.contains(text, "%v-1"))
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
malformed_ir_emits_traps_and_typed_sentinels :: proc(t: ^testing.T) {
|
malformed_ir_emits_traps_and_typed_sentinels :: proc(t: ^testing.T) {
|
||||||
module := ir.init_module()
|
module := ir.init_module()
|
||||||
defer ir.destroy_module(&module)
|
defer ir.destroy_module(&module)
|
||||||
instructions := make([]ir.Instruction, 3)
|
instructions := make([]ir.Instruction, 3)
|
||||||
instructions[0] = ir.Instruction{op=.Store, type=types.I8, a=-1, b=-1, diagnostic=-1}
|
instructions[0] = ir.Instruction{op=.Store, type=types.I8, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
|
||||||
instructions[1] = ir.Instruction{op=.Add_Checked, type=types.I32, a=-1, b=-1, diagnostic=-1}
|
instructions[1] = ir.Instruction{op=.Add_Checked, type=types.I32, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
|
||||||
instructions[2] = ir.Instruction{op=.Return, type=types.I32, a=1, b=-1, diagnostic=-1}
|
instructions[2] = ir.Instruction{op=.Return, type=types.I32, a=ir.Instruction_Id(1), b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
|
||||||
append(&module.functions, ir.Function{
|
append(&module.functions, ir.Function{
|
||||||
link_name=strings.clone("main"),
|
link_name=strings.clone("main"),
|
||||||
calling_convention=.C,
|
calling_convention=.C,
|
||||||
@@ -1335,14 +1426,14 @@ deep_global_cycle_detection_uses_iterative_dfs :: proc(t: ^testing.T) {
|
|||||||
|
|
||||||
for id in 0..<count {
|
for id in 0..<count {
|
||||||
append(&ast_module.globals, ast.Global{name=name})
|
append(&ast_module.globals, ast.Global{name=name})
|
||||||
dependencies: [dynamic]int
|
dependencies: [dynamic]hir.Global_Id
|
||||||
dependencies.allocator = context.allocator
|
dependencies.allocator = context.allocator
|
||||||
append(&dependencies, (id+1)%count)
|
append(&dependencies, hir.global_id((id+1)%count))
|
||||||
append(&state.module.globals, hir.Global{name=name, dependencies=dependencies})
|
append(&state.module.globals, hir.Global{name=name, dependencies=dependencies})
|
||||||
}
|
}
|
||||||
states := make([]u8, count)
|
states := make([]u8, count)
|
||||||
defer delete(states)
|
defer delete(states)
|
||||||
checker.detect_global_cycles_visit(&state, 0, states)
|
checker.detect_global_cycles_visit(&state, hir.Global_Id(0), states)
|
||||||
|
|
||||||
testing.expect_value(t, len(diagnostics.items), 1)
|
testing.expect_value(t, len(diagnostics.items), 1)
|
||||||
for global in state.module.globals {
|
for global in state.module.globals {
|
||||||
|
|||||||
Reference in New Issue
Block a user