compact compiler ids and spans to reduce memory usage

This commit is contained in:
2026-06-12 16:55:16 +02:00
parent 99ba907f59
commit 4112b79c6b
16 changed files with 972 additions and 573 deletions
+80 -26
View File
@@ -4,9 +4,63 @@ import "../source"
import "../symbol"
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,
Int,
I8,
@@ -16,7 +70,7 @@ Type_Syntax :: enum {
Void,
}
Expr_Kind :: enum {
Expr_Kind :: enum u8 {
Invalid,
Integer,
Name,
@@ -25,15 +79,15 @@ Expr_Kind :: enum {
}
Expr :: struct {
kind: Expr_Kind,
span: source.Span,
integer: i64,
args: []Expr_Id,
qualifier: symbol.Id,
name: symbol.Id,
integer: i64,
left: int,
right: int,
args: []int,
diagnostic: int,
left: Expr_Id,
right: Expr_Id,
diagnostic: source.Diagnostic_Id,
kind: Expr_Kind,
}
Param :: struct {
@@ -42,7 +96,7 @@ Param :: struct {
type: Type_Syntax,
}
Stmt_Kind :: enum {
Stmt_Kind :: enum u8 {
Invalid,
Declaration,
Assignment,
@@ -56,49 +110,49 @@ Stmt :: struct {
name: symbol.Id,
type: Type_Syntax,
immutable: bool,
expr: int,
diagnostic: int,
expr: Expr_Id,
diagnostic: source.Diagnostic_Id,
}
Function :: struct {
span: source.Span,
name: symbol.Id,
pkg: int,
file: int,
pkg: Package_Id,
file: File_Id,
c_abi: bool,
has_body: bool,
params: []Param,
result: Type_Syntax,
body: []int,
diagnostic: int,
body: []Stmt_Id,
diagnostic: source.Diagnostic_Id,
}
Global :: struct {
span: source.Span,
name: symbol.Id,
pkg: int,
file: int,
pkg: Package_Id,
file: File_Id,
type: Type_Syntax,
immutable: bool,
expr: int,
diagnostic: int,
expr: Expr_Id,
diagnostic: source.Diagnostic_Id,
}
Import :: struct {
span: source.Span,
alias: symbol.Id,
path: string,
pkg: int,
file: int,
target: int,
pkg: Package_Id,
file: File_Id,
target: Package_Id,
valid: bool,
used: bool,
diagnostic: int,
diagnostic: source.Diagnostic_Id,
}
File :: struct {
source: int,
pkg: int,
source: source.Source_Id,
pkg: Package_Id,
}
Package :: struct {
File diff suppressed because it is too large Load Diff
+1
View File
@@ -56,6 +56,7 @@ compile_package :: proc(input_path, output_path: string, link_arguments: []linke
vmem.arena_allocator(&parser_arena),
)
if !loaded {
source.print_all(&diagnostics)
fmt.eprintln("failed to load root package directory:", input_path)
return 2
}
+73 -24
View File
@@ -5,24 +5,73 @@ import "../symbol"
import "../types"
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,
C,
}
Implementation :: enum {
Implementation :: enum u8 {
Definition,
Declaration,
}
Linkage :: enum {
Linkage :: enum u8 {
Internal,
External,
}
Expr_Kind :: enum {
Expr_Kind :: enum u8 {
Invalid,
Integer,
Local,
@@ -33,15 +82,15 @@ Expr_Kind :: enum {
}
Expr :: struct {
kind: Expr_Kind,
span: source.Span,
type: types.Type,
integer: i64,
target: int,
left: int,
right: int,
args: []int,
diagnostic: int,
args: []Expr_Id,
target: Ref,
left: Expr_Id,
right: Expr_Id,
diagnostic: source.Diagnostic_Id,
kind: Expr_Kind,
}
Local :: struct {
@@ -51,7 +100,7 @@ Local :: struct {
parameter: bool,
}
Stmt_Kind :: enum {
Stmt_Kind :: enum u8 {
Declaration,
Assignment,
Return,
@@ -63,9 +112,9 @@ Stmt_Kind :: enum {
Stmt :: struct {
kind: Stmt_Kind,
span: source.Span,
local: int,
expr: int,
diagnostic: int,
local: Local_Id,
expr: Expr_Id,
diagnostic: source.Diagnostic_Id,
}
Function :: struct {
@@ -75,27 +124,27 @@ Function :: struct {
implementation: Implementation,
linkage: Linkage,
is_main: bool,
params: []int,
params: []Local_Id,
result: types.Type,
locals: []Local,
body: []int,
direct_global_reads: [dynamic]int,
calls: []int,
body: []Stmt_Id,
direct_global_reads: [dynamic]Global_Id,
calls: []Function_Id,
problematic: bool,
diagnostic: int,
diagnostic: source.Diagnostic_Id,
}
Global :: struct {
name: symbol.Id,
type: types.Type,
expr: int,
expr: Expr_Id,
static_value: i64,
is_static: bool,
dependencies: [dynamic]int,
calls: []int,
dependencies: [dynamic]Global_Id,
calls: []Function_Id,
direct_problem: bool,
problematic: bool,
diagnostic: int,
diagnostic: source.Diagnostic_Id,
}
Module :: struct {
+54 -12
View File
@@ -5,24 +5,66 @@ import "../symbol"
import "../types"
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,
C,
}
Implementation :: enum {
Implementation :: enum u8 {
Definition,
Declaration,
}
Linkage :: enum {
Linkage :: enum u8 {
Internal,
External,
}
Opcode :: enum {
Opcode :: enum u8 {
Param,
Const,
Load_Global,
@@ -38,15 +80,15 @@ Opcode :: enum {
}
Instruction :: struct {
op: Opcode,
span: source.Span,
type: types.Type,
integer: i64,
target: int,
a: int,
b: int,
args: []int,
diagnostic: int,
args: []Instruction_Id,
target: Ref,
a: Instruction_Id,
b: Instruction_Id,
diagnostic: source.Diagnostic_Id,
op: Opcode,
}
Function :: struct {
@@ -68,7 +110,7 @@ Global :: struct {
static_value: i64,
initializer: []Instruction,
problematic: bool,
diagnostic: int,
diagnostic: source.Diagnostic_Id,
}
Module :: struct {
+7 -7
View File
@@ -34,11 +34,11 @@ append_token :: proc(
kind: token.Kind,
start, end: int,
id := symbol.INVALID,
diagnostic := -1,
diagnostic := source.INVALID_DIAGNOSTIC,
) {
append(&stream.items, token.Token{
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,
diagnostic=diagnostic,
})
@@ -74,7 +74,7 @@ lex :: proc(
cursor += 1
append_token(&stream, source_file, .Colon_Colon, start, cursor)
} 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)
}
case '=':
@@ -111,7 +111,7 @@ lex :: proc(
if cursor >= len(bytes) || (bytes[cursor] != '\\' && bytes[cursor] != '"') {
source.add(
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",
)
valid = false
@@ -127,7 +127,7 @@ lex :: proc(
} else {
id := source.add(
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",
)
append_token(&stream, source_file, .Invalid, start, cursor, diagnostic=id)
@@ -135,7 +135,7 @@ lex :: proc(
case ';':
id := source.add(
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",
)
append_token(&stream, source_file, .Invalid, cursor, cursor+1, diagnostic=id)
@@ -162,7 +162,7 @@ lex :: proc(
} else {
id := source.addf(
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",
value,
)
+46 -43
View File
@@ -49,11 +49,11 @@ sentinel :: proc(value_type: types.Type) -> i64 {
}
}
valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: int) -> bool {
return instruction_id >= 0 && instruction_id < len(instructions)
valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: ir.Instruction_Id) -> bool {
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) ||
!types.is_concrete_integer(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
}
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) {
fmt.sbprintf(builder, "%d", sentinel(expected))
return
@@ -88,8 +88,8 @@ register_message :: proc(emitter: ^Emitter, text: string) -> int {
return id
}
diagnostic_message :: proc(emitter: ^Emitter, diagnostic: int, span: source.Span, fallback: string) -> int {
if diagnostic >= 0 && diagnostic < len(emitter.diagnostics.items) {
diagnostic_message :: proc(emitter: ^Emitter, diagnostic: source.Diagnostic_Id, span: source.Span, fallback: string) -> int {
if _, ok := source.diagnostic_index(diagnostic, len(emitter.diagnostics.items)); ok {
message := source.format(emitter.diagnostics, diagnostic, emitter.allocator)
id := register_message(emitter, message)
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 {
if index > 0 {
strings.write_string(builder, ", ")
@@ -152,60 +152,62 @@ emit_instruction_stream :: proc(
instructions: []ir.Instruction,
function: ir.Function,
global_initializer := false,
) -> int {
return_value := -1
) -> ir.Instruction_Id {
return_value := ir.INVALID_INSTRUCTION
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 {
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
}
switch instruction.op {
case .Param, .Const:
case .Load_Global:
if instruction.target < 0 || instruction.target >= len(emitter.module.globals) {
emit_recovery_value(emitter, instruction_id, instruction, "invalid global reference")
global_id := ir.as_global(instruction.target)
if global_id == ir.INVALID_GLOBAL || int(global_id) >= len(emitter.module.globals) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid global reference")
continue
}
global := emitter.module.globals[instruction.target]
global := emitter.module.globals[global_id]
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
}
if global.is_static {
fmt.sbprintf(
&emitter.builder,
" %%v%d = load %s, ptr @bro.g.%d\n",
instruction_id,
instruction_index,
llvm_type(global.type),
instruction.target,
global_id,
)
} else {
fmt.sbprintf(
&emitter.builder,
" %%v%d = call %s @bro.get.%d()\n",
instruction_id,
instruction_index,
llvm_type(global.type),
instruction.target,
global_id,
)
}
case .Alloca:
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
}
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:
if !valid_instruction(instructions, instruction.a) ||
instructions[instruction.a].op != .Alloca ||
!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
}
fmt.sbprintf(
&emitter.builder,
" %%v%d = load %s, ptr %%v%d\n",
instruction_id,
instruction_index,
llvm_type(instruction.type),
instruction.a,
)
@@ -214,7 +216,7 @@ emit_instruction_stream :: proc(
instructions[instruction.a].op != .Alloca ||
!types.equal(instructions[instruction.a].type, 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
}
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(instruction.type) ||
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
}
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)
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type))
case .Add_Checked:
if !valid_value(instructions, instruction.a, 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
}
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, "{ ")
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)
fmt.sbprintf(&emitter.builder, ", %s ", type_name)
write_operand(&emitter.builder, instructions, instruction.b, instruction.type)
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, "{ ")
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_id)
fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_id)
fmt.sbprintf(&emitter.builder, "%s, i1 } %%pair%d, 0\n", type_name, instruction_index)
fmt.sbprintf(&emitter.builder, " %%overflow%d = extractvalue ", instruction_index)
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(
&emitter.builder,
" br i1 %%overflow%d, label %%overflow_trap%d, label %%overflow_continue%d\n",
instruction_id,
instruction_id,
instruction_id,
instruction_index,
instruction_index,
instruction_index,
)
fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_id)
message := diagnostic_message(emitter, -1, instruction.span, "signed integer addition overflow")
fmt.sbprintf(&emitter.builder, "overflow_trap%d:\n", instruction_index)
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "signed integer addition overflow")
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:
if instruction.target < 0 || instruction.target >= len(emitter.module.functions) {
emit_recovery_value(emitter, instruction_id, instruction, "invalid function specialization")
function_id := ir.as_function(instruction.target)
if function_id == ir.INVALID_FUNCTION || int(function_id) >= len(emitter.module.functions) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid function specialization")
continue
}
target := emitter.module.functions[instruction.target]
target := emitter.module.functions[function_id]
valid_args := len(instruction.args) == len(target.param_types)
if valid_args {
for arg, index in instruction.args {
@@ -283,11 +286,11 @@ emit_instruction_stream :: proc(
target_result = types.I32
}
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
}
if instruction.type.kind != .Void {
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_id)
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
} else {
strings.write_string(&emitter.builder, " ")
}
+27 -16
View File
@@ -39,20 +39,20 @@ is_identifier :: proc(value: string) -> bool {
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 {
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 {
if existing := find_package(state, path); existing >= 0 {
add_placeholder :: proc(state: ^State, path: string) -> ast.Package_Id {
if existing := find_package(state, path); existing != ast.INVALID_PACKAGE {
return existing
}
id := len(state.module.packages)
id := ast.package_id(len(state.module.packages))
append(&state.module.packages, ast.Package{
path=strings.clone(path, state.allocator),
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
}
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)
if !ok || !os.is_dir(path) {
if is_root {
@@ -111,7 +111,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
if len(canonical) > 0 {
delete(canonical, state.allocator)
}
return -1
return ast.INVALID_PACKAGE
}
placeholder := path
if len(canonical) > 0 {
@@ -124,12 +124,12 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
}
return id
}
if existing := find_package(state, canonical); existing >= 0 {
if existing := find_package(state, canonical); existing != ast.INVALID_PACKAGE {
delete(canonical, state.allocator)
return existing
}
pkg_id := len(state.module.packages)
pkg_id := ast.package_id(len(state.module.packages))
append(&state.module.packages, ast.Package{
path=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 {
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)
if !read_ok {
state.root_failed = true
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)
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})
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)
@@ -169,7 +180,7 @@ load_package :: proc(state: ^State, path: string, import_span: source.Span, is_r
import_count := len(state.module.imports)
for import_id in 0..<import_count {
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
}
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 := load_package(state, target_path, import_item.span)
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
}
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
}
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 {
if function.pkg == pkg && function.name == name {
return true
@@ -205,7 +216,7 @@ declaration_conflicts :: proc(module: ^ast.Module, pkg: int, name: symbol.Id) ->
validate_imports :: proc(state: ^State) {
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
}
alias := state.module.imports[import_id].alias
@@ -260,7 +271,7 @@ load :: proc(
allocator=allocator,
}
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
}
validate_imports(&state)
+92 -82
View File
@@ -10,20 +10,20 @@ import "core:mem"
State :: struct {
hir_module: ^hir.Module,
instructions: [dynamic]ir.Instruction,
local_values: []int,
local_slots: []int,
local_values: []ir.Instruction_Id,
local_slots: []ir.Instruction_Id,
expr_stack: [dynamic]Lower_Expr_Frame,
allocator: mem.Allocator,
}
append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> int {
id := len(state.instructions)
append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> ir.Instruction_Id {
id := ir.instruction_id(len(state.instructions))
append(&state.instructions, instruction)
return id
}
clone_args :: proc(values: []int, allocator: mem.Allocator) -> []int {
result := make([]int, len(values), allocator)
clone_args :: proc(values: []ir.Instruction_Id, allocator: mem.Allocator) -> []ir.Instruction_Id {
result := make([]ir.Instruction_Id, len(values), allocator)
copy(result, values)
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{
op=.Trap,
span=span,
type=types.VOID,
target=-1,
a=-1,
b=-1,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=diagnostic,
})
fallback := value_type
@@ -56,22 +61,22 @@ append_recovery_value :: proc(state: ^State, span: source.Span, value_type: type
span=span,
type=fallback,
integer=sentinel(fallback),
target=-1,
a=-1,
b=-1,
diagnostic=-1,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
Lower_Expr_Frame :: struct {
expr: int,
expr: hir.Expr_Id,
stage: u8,
left: int,
left: ir.Instruction_Id,
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
clear_dynamic_array(&stack)
defer {
@@ -82,11 +87,11 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
state.expr_stack = stack
}
append(&stack, Lower_Expr_Frame{expr=expr_id})
last := -1
last := ir.INVALID_INSTRUCTION
for len(stack) > 0 {
frame_index := len(stack)-1
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)
_ = pop(&stack)
continue
@@ -100,31 +105,33 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
case .Integer:
last = append_instruction(state, ir.Instruction{
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)
case .Local:
last = -1
if expr.target >= 0 && expr.target < len(state.local_slots) && state.local_slots[expr.target] >= 0 {
last = ir.INVALID_INSTRUCTION
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{
op=.Load, span=expr.span, type=expr.type, target=-1,
a=state.local_slots[expr.target], b=-1, diagnostic=-1,
op=.Load, span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=state.local_slots[local], b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
} else if expr.target >= 0 && expr.target < len(state.local_values) &&
state.local_values[expr.target] >= 0 {
last = state.local_values[expr.target]
} else if local != hir.INVALID_LOCAL && int(local) < len(state.local_values) &&
state.local_values[local] != ir.INVALID_INSTRUCTION {
last = state.local_values[local]
}
if last < 0 {
if last == ir.INVALID_INSTRUCTION {
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
}
_ = pop(&stack)
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)
} else {
last = append_instruction(state, ir.Instruction{
op=.Load_Global, span=expr.span, type=expr.type, target=expr.target,
a=-1, b=-1, diagnostic=-1,
op=.Load_Global, span=expr.span, type=expr.type, target=ir.global_ref(ir.Global_Id(global)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
}
_ = pop(&stack)
@@ -135,12 +142,13 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
stack[frame_index].stage = 2
append(&stack, Lower_Expr_Frame{expr=expr.left})
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)
_ = pop(&stack)
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
if len(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 {
last = append_instruction(state, ir.Instruction{
op=.Widen, span=expr.span, type=expr.type, target=-1,
a=last, b=-1, diagnostic=-1,
op=.Widen, span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=last, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = pop(&stack)
continue
@@ -164,8 +172,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
}
if frame.stage == 3 {
last = append_instruction(state, ir.Instruction{
op=.Add_Checked, span=expr.span, type=expr.type, target=-1,
a=frame.left, b=last, diagnostic=-1,
op=.Add_Checked, span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=frame.left, b=last, diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = pop(&stack)
continue
@@ -180,8 +188,8 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
}
}
last = append_instruction(state, ir.Instruction{
op=.Call, span=expr.span, type=expr.type, target=expr.target,
a=-1, b=-1, args=stack[frame_index].args, diagnostic=-1,
op=.Call, span=expr.span, type=expr.type, target=ir.function_ref(ir.Function_Id(hir.as_function(expr.target))),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, args=stack[frame_index].args, diagnostic=source.INVALID_DIAGNOSTIC,
})
stack[frame_index].args = nil
_ = pop(&stack)
@@ -194,8 +202,8 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
state := State{
hir_module=hir_module,
allocator=allocator,
local_values=make([]int, len(function.locals), allocator),
local_slots=make([]int, len(function.locals), allocator),
local_values=make([]ir.Instruction_Id, len(function.locals), allocator),
local_slots=make([]ir.Instruction_Id, len(function.locals), allocator),
}
state.instructions.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)
}
for _, index in state.local_values {
state.local_values[index] = -1
state.local_slots[index] = -1
state.local_values[index] = ir.INVALID_INSTRUCTION
state.local_slots[index] = ir.INVALID_INSTRUCTION
}
for local_id in function.params {
param := append_instruction(&state, ir.Instruction{
op=.Param,
type=function.locals[local_id].type,
target=local_id,
a=-1,
b=-1,
diagnostic=-1,
target=ir.local_ref(ir.Local_Id(local_id)),
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
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 {
case .Declaration:
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{
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
}
@@ -238,34 +246,34 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
op=.Alloca,
span=statement.span,
type=local.type,
target=statement.local,
a=-1,
b=-1,
diagnostic=-1,
target=ir.local_ref(ir.Local_Id(statement.local)),
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
state.local_slots[statement.local] = slot
append_instruction(&state, ir.Instruction{
op=.Store,
span=statement.span,
type=local.type,
target=-1,
target=ir.INVALID_REF,
a=slot,
b=value,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
state.local_values[statement.local] = value
}
case .Assignment:
value := lower_expr(&state, statement.expr)
slot := -1
if statement.local >= 0 && statement.local < len(state.local_slots) {
slot := ir.INVALID_INSTRUCTION
if statement.local != hir.INVALID_LOCAL && int(statement.local) < len(state.local_slots) {
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{
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
}
@@ -273,21 +281,21 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
op=.Store,
span=statement.span,
type=function.locals[statement.local].type,
target=-1,
target=ir.INVALID_REF,
a=slot,
b=value,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Return:
if statement.expr < 0 {
if statement.expr == hir.INVALID_EXPR {
append_instruction(&state, ir.Instruction{
op=.Return_Void,
span=statement.span,
type=types.VOID,
target=-1,
a=-1,
b=-1,
diagnostic=-1,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
value := lower_expr(&state, statement.expr)
@@ -295,10 +303,10 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
op=.Return,
span=statement.span,
type=function.result,
target=-1,
target=ir.INVALID_REF,
a=value,
b=-1,
diagnostic=-1,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
case .Expression, .Sink:
@@ -308,9 +316,9 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
op=.Trap,
span=statement.span,
type=types.VOID,
target=-1,
a=-1,
b=-1,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
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_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 {
value := append_instruction(&state, ir.Instruction{
op=.Const,
type=function.result,
integer=sentinel(function.result),
target=-1,
a=-1,
b=-1,
diagnostic=-1,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
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[:]
@@ -345,10 +353,10 @@ lower_global_initializer :: proc(hir_module: ^hir.Module, global: hir.Global, al
append_instruction(&state, ir.Instruction{
op=.Return,
type=global.type,
target=-1,
target=ir.INVALID_REF,
a=value,
b=-1,
diagnostic=-1,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
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 {
module := ir.init_module(allocator)
for global in hir_module.globals {
_ = ir.global_id(len(module.globals))
append(&module.globals, ir.Global{
name=global.name,
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 {
param_types[index] = function.locals[local_id].type
}
_ = ir.function_id(len(module.functions))
append(&module.functions, ir.Function{
link_name=fmt.aprintf("%s", function.link_name, allocator=allocator),
calling_convention=.C if function.calling_convention == .C else .Brolang,
+58 -52
View File
@@ -13,8 +13,8 @@ Parser :: struct {
source_file: ^source.Source,
diagnostics: ^source.Diagnostics,
module: ast.Module,
pkg: int,
file: int,
pkg: ast.Package_Id,
file: ast.File_Id,
cursor: int,
delimiter_depth: int,
}
@@ -22,10 +22,10 @@ Parser :: struct {
MAX_EXPRESSION_NESTING :: 256
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 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 {
@@ -61,19 +61,19 @@ skip_newlines :: proc(parser: ^Parser) {
}
}
add_expr :: proc(parser: ^Parser, expr: ast.Expr) -> int {
id := len(parser.module.exprs)
add_expr :: proc(parser: ^Parser, expr: ast.Expr) -> ast.Expr_Id {
id := ast.expr_id(len(parser.module.exprs))
append(&parser.module.exprs, expr)
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)
return add_expr(parser, ast.Expr{
kind=.Invalid,
span=span,
left=ast.INVALID_ID,
right=ast.INVALID_ID,
left=ast.INVALID_EXPR,
right=ast.INVALID_EXPR,
diagnostic=id,
})
}
@@ -131,7 +131,7 @@ skip_parenthesized :: proc(parser: ^Parser) -> source.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 {
span := skip_parenthesized(parser)
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)
parser.delimiter_depth += 1
defer parser.delimiter_depth -= 1
args: [dynamic]int
args: [dynamic]ast.Expr_Id
args.allocator = parser.module.allocator
skip_newlines(parser)
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,
name=name.symbol,
args=args[:],
left=ast.INVALID_ID,
right=ast.INVALID_ID,
diagnostic=-1,
left=ast.INVALID_EXPR,
right=ast.INVALID_EXPR,
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)
#partial switch tok.kind {
case .Integer:
@@ -181,9 +181,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
kind=.Integer,
span=tok.span,
integer=value,
left=ast.INVALID_ID,
right=ast.INVALID_ID,
diagnostic=-1,
left=ast.INVALID_EXPR,
right=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Identifier:
first := advance(parser)
@@ -204,9 +204,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
span=span_from(first.span, name.span),
qualifier=qualifier,
name=name.symbol,
left=ast.INVALID_ID,
right=ast.INVALID_ID,
diagnostic=-1,
left=ast.INVALID_EXPR,
right=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Underscore:
advance(parser)
@@ -231,8 +231,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> int {
return add_expr(parser, ast.Expr{
kind=.Invalid,
span=tok.span,
left=ast.INVALID_ID,
right=ast.INVALID_ID,
left=ast.INVALID_EXPR,
right=ast.INVALID_EXPR,
diagnostic=tok.diagnostic,
})
}
@@ -250,7 +250,7 @@ infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
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 {
tok := current(parser)
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),
left=left,
right=right,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
if parser.delimiter_depth > 0 {
skip_newlines(parser)
@@ -286,17 +286,17 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
return left
}
parse_expression :: proc(parser: ^Parser) -> int {
parse_expression :: proc(parser: ^Parser) -> ast.Expr_Id {
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 {
skip_newlines(parser)
return -1
return source.INVALID_DIAGNOSTIC
}
if current(parser).kind == .Eof || allow_closing_brace && current(parser).kind == .Right_Brace {
return -1
return source.INVALID_DIAGNOSTIC
}
diagnostic := source.add(
parser.diagnostics,
@@ -312,33 +312,33 @@ finish_statement :: proc(parser: ^Parser, allow_closing_brace := false) -> int {
return diagnostic
}
parse_return :: proc(parser: ^Parser) -> int {
parse_return :: proc(parser: ^Parser) -> ast.Stmt_Id {
start := advance(parser)
skip_newlines(parser)
if current(parser).kind == .Underscore {
end := advance(parser)
id := len(parser.module.statements)
id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.Return,
span=span_from(start.span, end.span),
name=end.symbol,
expr=ast.INVALID_ID,
diagnostic=-1,
expr=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return id
}
expr := parse_expression(parser)
id := len(parser.module.statements)
id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.Return,
span=span_from(start.span, parser.module.exprs[expr].span),
expr=expr,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return id
}
parse_statement :: proc(parser: ^Parser) -> int {
parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
if current(parser).kind == .Keyword_Return {
return parse_return(parser)
}
@@ -363,7 +363,7 @@ parse_statement :: proc(parser: ^Parser) -> int {
kind = .Declaration
immutable = operator.kind == .Colon_Colon
}
id := len(parser.module.statements)
id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=kind,
span=span_from(name.span, parser.module.exprs[expr].span),
@@ -371,7 +371,7 @@ parse_statement :: proc(parser: ^Parser) -> int {
type=type_syntax,
immutable=immutable,
expr=expr,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return id
}
@@ -379,12 +379,12 @@ parse_statement :: proc(parser: ^Parser) -> int {
}
expr := parse_expression(parser)
id := len(parser.module.statements)
id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.Expression,
span=parser.module.exprs[expr].span,
expr=expr,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
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 {
_ = finish_statement(parser)
}
_ = ast.function_id(len(parser.module.functions))
append(&parser.module.functions, ast.Function{
span=span_from(name.span, end.span),
name=name.symbol,
@@ -455,23 +456,23 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
has_body=false,
params=params,
result=result,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return
}
advance(parser)
body: [dynamic]int
body: [dynamic]ast.Stmt_Id
body.allocator = parser.module.allocator
skip_newlines(parser)
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
append(&body, parse_statement(parser))
if diagnostic := finish_statement(parser, true); diagnostic >= 0 {
statement_id := len(parser.module.statements)
if diagnostic := finish_statement(parser, true); diagnostic != source.INVALID_DIAGNOSTIC {
statement_id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.Invalid,
span=current(parser).span,
expr=ast.INVALID_ID,
expr=ast.INVALID_EXPR,
diagnostic=diagnostic,
})
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")
end = func_token
}
_ = ast.function_id(len(parser.module.functions))
append(&parser.module.functions, ast.Function{
span=span_from(name.span, end.span),
name=name.symbol,
@@ -492,7 +494,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
params=params,
result=result,
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 {
advance(parser)
}
_ = ast.import_id(len(parser.module.imports))
append(&parser.module.imports, ast.Import{
span=start.span,
alias=alias.symbol,
pkg=parser.pkg,
file=parser.file,
target=-1,
target=ast.INVALID_PACKAGE,
valid=false,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = finish_statement(parser)
return
}
advance(parser)
_ = ast.import_id(len(parser.module.imports))
append(&parser.module.imports, ast.Import{
span=span_from(start.span, path_token.span),
alias=alias.symbol,
path=decode_import_path(parser, path_token),
pkg=parser.pkg,
file=parser.file,
target=-1,
target=ast.INVALID_PACKAGE,
valid=true,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = finish_statement(parser)
}
@@ -606,6 +610,7 @@ parse_top_level :: proc(parser: ^Parser) {
}
expr := parse_expression(parser)
_ = ast.global_id(len(parser.module.globals))
append(&parser.module.globals, ast.Global{
span=span_from(name.span, parser.module.exprs[expr].span),
name=name.symbol,
@@ -614,7 +619,7 @@ parse_top_level :: proc(parser: ^Parser) {
type=type_syntax,
immutable=operator.kind == .Colon_Colon,
expr=expr,
diagnostic=-1,
diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = finish_statement(parser)
}
@@ -644,7 +649,8 @@ parse_into :: proc(
source_file: ^source.Source,
diagnostics: ^source.Diagnostics,
module: ^ast.Module,
pkg, file: int,
pkg: ast.Package_Id,
file: ast.File_Id,
) {
parser := Parser{
tokens=stream,
+63 -27
View File
@@ -3,17 +3,48 @@ package source
import "core:fmt"
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 {
file: int,
start: int,
end: int,
file: Source_Id,
start: Offset,
end: Offset,
}
Source :: struct {
id: int,
id: Source_Id,
path: string,
text: string,
line_starts: []int,
line_starts: []Offset,
}
Store :: struct {
@@ -30,7 +61,7 @@ Diagnostics :: struct {
source: ^Source,
store: ^Store,
items: [dynamic]Diagnostic,
index: map[Diagnostic_Key]int,
index: map[Diagnostic_Key]Diagnostic_Id,
allocator: mem.Allocator,
}
@@ -55,20 +86,22 @@ destroy_store :: proc(store: ^Store) {
delete(store.items)
}
make_line_starts :: proc(text: string, allocator: mem.Allocator) -> []int {
result: [dynamic]int
make_line_starts :: proc(text: string, allocator: mem.Allocator) -> []Offset {
assert(fits_source_length(u64(len(text))))
result: [dynamic]Offset
result.allocator = allocator
append(&result, 0)
for value, offset in transmute([]byte)text {
if value == '\n' {
append(&result, offset+1)
append(&result, Offset(offset+1))
}
}
return result[:]
}
add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> int {
id := len(store.items)
add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> Source_Id {
assert(fits_source_length(u64(len(text))))
id := source_id(len(store.items))
owned_text := string(text)
append(&store.items, Source{
id=id,
@@ -79,7 +112,7 @@ add_source_owned :: proc(store: ^Store, path: string, text: []byte) -> int {
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)
copy(owned, transmute([]byte)text)
return add_source_owned(store, path, owned)
@@ -111,51 +144,51 @@ destroy_diagnostics :: proc(diagnostics: ^Diagnostics) {
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}
if id, ok := diagnostics.index[key]; ok {
return id
}
id := len(diagnostics.items)
id := diagnostic_id(len(diagnostics.items))
cloned := fmt.aprintf("%s", message, allocator=diagnostics.allocator)
append(&diagnostics.items, Diagnostic{span=span, message=cloned})
diagnostics.index[Diagnostic_Key{span=span, message=cloned}] = 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)
key := Diagnostic_Key{span=span, message=message}
if id, ok := diagnostics.index[key]; ok {
delete(message, diagnostics.allocator)
return id
}
id := len(diagnostics.items)
id := diagnostic_id(len(diagnostics.items))
append(&diagnostics.items, Diagnostic{span=span, message=message})
diagnostics.index[Diagnostic_Key{span=span, message=message}] = 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 {
limit := min(max(offset, 0), len(source_file.text))
limit := min(int(offset), len(source_file.text))
low := 0
high := len(source_file.line_starts)
for low < high {
middle := low + (high-low)/2
if source_file.line_starts[middle] <= limit {
if int(source_file.line_starts[middle]) <= limit {
low = middle+1
} else {
high = middle
}
}
line = max(low, 1)
column = limit-source_file.line_starts[line-1]+1
column = limit-int(source_file.line_starts[line-1])+1
return
}
line = 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] {
if byte_value == '\n' {
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 {
if diagnostics.store != nil && span.file >= 0 && span.file < len(diagnostics.store.items) {
return &diagnostics.store.items[span.file]
if diagnostics.store != nil {
if index, ok := source_index(span.file, len(diagnostics.store.items)); ok {
return &diagnostics.store.items[index]
}
}
return diagnostics.source
}
format :: proc(diagnostics: ^Diagnostics, id: int, allocator := context.allocator) -> string {
if id < 0 || id >= len(diagnostics.items) {
format :: proc(diagnostics: ^Diagnostics, id: Diagnostic_Id, allocator := context.allocator) -> string {
index, ok := diagnostic_index(id, len(diagnostics.items))
if !ok {
path := "<unknown>"
if diagnostics.source != nil {
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)
}
diagnostic := diagnostics.items[id]
diagnostic := diagnostics.items[index]
source_file := source_for_span(diagnostics, diagnostic.span)
if source_file == nil {
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) {
for _, id in diagnostics.items {
message := format(diagnostics, id)
message := format(diagnostics, diagnostic_id(id))
fmt.eprintln(message)
delete(message)
}
+5 -5
View File
@@ -3,7 +3,7 @@ package token
import "../source"
import "../symbol"
Kind :: enum {
Kind :: enum u8 {
Invalid,
Eof,
Newline,
@@ -32,10 +32,10 @@ Kind :: enum {
}
Token :: struct {
kind: Kind,
span: source.Span,
symbol: symbol.Id,
diagnostic: int,
span: source.Span,
symbol: symbol.Id,
diagnostic: source.Diagnostic_Id,
kind: Kind,
}
Stream :: struct {