295 lines
6.4 KiB
Odin
295 lines
6.4 KiB
Odin
package ast
|
|
|
|
import "../source"
|
|
import "../symbol"
|
|
import "../types"
|
|
import "core:mem"
|
|
|
|
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
|
|
|
|
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 :: types.Type
|
|
|
|
Expr_Kind :: enum u8 {
|
|
Invalid,
|
|
Integer,
|
|
Float,
|
|
String,
|
|
Bool,
|
|
Array,
|
|
None,
|
|
Name,
|
|
Address,
|
|
Deref,
|
|
Index,
|
|
Slice,
|
|
Field,
|
|
Unwrap,
|
|
Orelse,
|
|
Struct_Literal,
|
|
Keyed,
|
|
Negate,
|
|
Not,
|
|
Add,
|
|
Eq,
|
|
Ne,
|
|
Lt,
|
|
Le,
|
|
Gt,
|
|
Ge,
|
|
And,
|
|
Or,
|
|
Call,
|
|
}
|
|
|
|
Expr :: struct {
|
|
span: source.Span,
|
|
integer: u64,
|
|
args: []Expr_Id,
|
|
qualifier: symbol.Id,
|
|
name: symbol.Id,
|
|
left: Expr_Id,
|
|
right: Expr_Id,
|
|
diagnostic: source.Diagnostic_Id,
|
|
kind: Expr_Kind,
|
|
}
|
|
|
|
Param :: struct {
|
|
name: symbol.Id,
|
|
span: source.Span,
|
|
type: Type_Syntax,
|
|
}
|
|
|
|
Stmt_Kind :: enum u8 {
|
|
Invalid,
|
|
Declaration,
|
|
Assignment,
|
|
Return,
|
|
Expression,
|
|
If,
|
|
}
|
|
|
|
Stmt :: struct {
|
|
kind: Stmt_Kind,
|
|
span: source.Span,
|
|
name: symbol.Id,
|
|
type: Type_Syntax,
|
|
immutable: bool,
|
|
target: Expr_Id,
|
|
expr: Expr_Id,
|
|
// `If` statements use `expr` as the condition, `body` as the then-block, and
|
|
// `else_body` as the else-block. An `else if` chain is represented as an
|
|
// `else_body` holding a single nested `If` statement.
|
|
body: []Stmt_Id,
|
|
else_body: []Stmt_Id,
|
|
diagnostic: source.Diagnostic_Id,
|
|
}
|
|
|
|
Function :: struct {
|
|
span: source.Span,
|
|
name: symbol.Id,
|
|
pkg: Package_Id,
|
|
file: File_Id,
|
|
c_abi: bool,
|
|
imported: bool,
|
|
has_body: bool,
|
|
variadic: bool,
|
|
params: []Param,
|
|
result: Type_Syntax,
|
|
body: []Stmt_Id,
|
|
link_name: string,
|
|
unsupported_reason: string,
|
|
diagnostic: source.Diagnostic_Id,
|
|
}
|
|
|
|
Global :: struct {
|
|
span: source.Span,
|
|
name: symbol.Id,
|
|
link_name: string,
|
|
pkg: Package_Id,
|
|
file: File_Id,
|
|
type: Type_Syntax,
|
|
immutable: bool,
|
|
external: bool,
|
|
writable: bool,
|
|
expr: Expr_Id,
|
|
diagnostic: source.Diagnostic_Id,
|
|
}
|
|
|
|
Import :: struct {
|
|
span: source.Span,
|
|
alias: symbol.Id,
|
|
path: string,
|
|
pkg: Package_Id,
|
|
file: File_Id,
|
|
target: Package_Id,
|
|
valid: bool,
|
|
used: bool,
|
|
diagnostic: source.Diagnostic_Id,
|
|
}
|
|
|
|
File :: struct {
|
|
source: source.Source_Id,
|
|
pkg: Package_Id,
|
|
}
|
|
|
|
Package :: struct {
|
|
path: string,
|
|
name: symbol.Id,
|
|
available: bool,
|
|
kind: Package_Kind,
|
|
}
|
|
|
|
Package_Kind :: enum u8 {
|
|
Native,
|
|
C_Header,
|
|
}
|
|
|
|
Unsupported :: struct {
|
|
pkg: Package_Id,
|
|
name: symbol.Id,
|
|
reason: string,
|
|
}
|
|
|
|
// Trampoline is a generated C wrapper source that must be compiled and linked
|
|
// alongside the program so an internal-linkage (`static inline`) C function can
|
|
// be called through an external symbol. `source` is the wrapper function only;
|
|
// `header` is the absolute path it must `#include` (emitted once per header).
|
|
Trampoline :: struct {
|
|
symbol: string,
|
|
source: string,
|
|
header: string,
|
|
}
|
|
|
|
Module :: struct {
|
|
exprs: [dynamic]Expr,
|
|
statements: [dynamic]Stmt,
|
|
functions: [dynamic]Function,
|
|
globals: [dynamic]Global,
|
|
imports: [dynamic]Import,
|
|
files: [dynamic]File,
|
|
packages: [dynamic]Package,
|
|
unsupported: [dynamic]Unsupported,
|
|
c_trampolines: [dynamic]Trampoline,
|
|
strings: [dynamic]string,
|
|
type_store: types.Store,
|
|
allocator: mem.Allocator,
|
|
}
|
|
|
|
init_module :: proc(allocator := context.allocator) -> Module {
|
|
module: Module
|
|
module.allocator = allocator
|
|
module.type_store = types.init_store(allocator)
|
|
module.exprs.allocator = allocator
|
|
module.statements.allocator = allocator
|
|
module.functions.allocator = allocator
|
|
module.globals.allocator = allocator
|
|
module.imports.allocator = allocator
|
|
module.files.allocator = allocator
|
|
module.packages.allocator = allocator
|
|
module.unsupported.allocator = allocator
|
|
module.c_trampolines.allocator = allocator
|
|
module.strings.allocator = allocator
|
|
return module
|
|
}
|
|
|
|
destroy_module :: proc(module: ^Module) {
|
|
for expr in module.exprs {
|
|
delete(expr.args, module.allocator)
|
|
}
|
|
for statement in module.statements {
|
|
delete(statement.body, module.allocator)
|
|
delete(statement.else_body, module.allocator)
|
|
}
|
|
for function in module.functions {
|
|
delete(function.params, module.allocator)
|
|
delete(function.body, module.allocator)
|
|
delete(function.link_name, module.allocator)
|
|
delete(function.unsupported_reason, module.allocator)
|
|
}
|
|
for import_item in module.imports {
|
|
delete(import_item.path, module.allocator)
|
|
}
|
|
for global in module.globals {
|
|
delete(global.link_name, module.allocator)
|
|
}
|
|
for pkg in module.packages {
|
|
delete(pkg.path, module.allocator)
|
|
}
|
|
for item in module.unsupported {
|
|
delete(item.reason, module.allocator)
|
|
}
|
|
for trampoline in module.c_trampolines {
|
|
delete(trampoline.symbol, module.allocator)
|
|
delete(trampoline.source, module.allocator)
|
|
delete(trampoline.header, module.allocator)
|
|
}
|
|
for value in module.strings {
|
|
delete(value, module.allocator)
|
|
}
|
|
delete(module.exprs)
|
|
delete(module.statements)
|
|
delete(module.functions)
|
|
delete(module.globals)
|
|
delete(module.imports)
|
|
delete(module.files)
|
|
delete(module.packages)
|
|
delete(module.unsupported)
|
|
delete(module.c_trampolines)
|
|
delete(module.strings)
|
|
types.destroy_store(&module.type_store)
|
|
}
|