speedcat/src/ast.odin
Slendi 81b0c139ef Add initial support for structs
There is an issue with calling C functions sadly because LLVM seems to
prefer passing each element as its own parameter than "packing" them
into 64 bit registers.

Signed-off-by: Slendi <slendi@socopon.com>
2024-03-26 13:32:51 +02:00

396 lines
7.6 KiB
Odin

package main
import "core:c/libc"
import "core:fmt"
NodeKind :: enum {
Integer,
Float,
Character,
String,
Identifier,
Pointer,
Array,
//
Block,
BinaryExpression,
UnaryExpression,
FieldAccess,
IndexAccess,
FunctionCall,
VariableDeclaration,
Cast,
BitwiseCast,
Ret,
StructInitializer,
//
ExternFunction,
Function,
Struct,
Enum,
Union,
Use,
//
If,
For,
// Done at type checking, should replace all instances of FieldAccess with these
// The value will be the FULL PATH to the field, only for module access
StructFieldAccess,
EnumFieldAccess,
UnionFieldAccess,
ModuleFieldAccess, // ModuleField$Field
// Example for std.println: std$println
}
Node :: struct {
kind: NodeKind,
range: TextRange,
children: [dynamic]^Node,
value: TokenValue,
value_token_kind: TokenKind,
return_type: ^Type,
}
node_create_value :: proc(kind: NodeKind, range: TextRange, value: TokenValue) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = kind,
range = range,
value = value,
}
return
}
node_create_pointer :: proc(range: TextRange, value: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Pointer,
range = range,
children = {value},
}
return
}
node_create_array :: proc(range: TextRange, size: u64, value: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Array,
range = range,
children = {value},
value = size,
}
return
}
node_create_block :: proc(range: TextRange, children: [dynamic]^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Block,
range = range,
children = children,
}
return
}
node_create_binary :: proc(kind: TokenKind, range: TextRange, left: ^Node, right: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .BinaryExpression,
range = range,
children = {left, right},
value_token_kind = kind,
}
return
}
node_create_unary :: proc(kind: TokenKind, range: TextRange, operand: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .UnaryExpression,
range = range,
children = {operand},
value_token_kind = kind,
}
return
}
node_create_field_access :: proc(range: TextRange, left: ^Node, right: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .FieldAccess,
range = range,
children = {left, right},
}
return
}
node_create_index_access :: proc(range: TextRange, left: ^Node, right: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .IndexAccess,
range = range,
children = {left, right},
}
return
}
node_create_function_call :: proc(range: TextRange, name: ^Node, args: [dynamic]^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .FunctionCall,
range = range,
children = {name},
}
for arg in args {
append(&ret.children, arg)
}
return
}
node_create_extern_function :: proc(
range: TextRange,
name: [dynamic]u8,
return_type: ^Node,
args: [dynamic]^Node,
) -> (
ret: ^Node,
) {
ret = new(Node)
ret^ = {
kind = .ExternFunction,
range = range,
children = {return_type},
value = name,
}
for arg in args {
append(&ret.children, arg)
}
return
}
node_create_function :: proc(
range: TextRange,
name: [dynamic]u8,
return_type, body: ^Node,
args: [dynamic]^Node,
) -> (
ret: ^Node,
) {
ret = new(Node)
ret^ = {
kind = .Function,
range = range,
children = {return_type, body},
value = name,
}
for arg in args {
append(&ret.children, arg)
}
return
}
node_print :: proc(node: ^Node, indent := 0) {
for i in 0 ..< indent {
fmt.printf(" ")
}
if node == nil {
fmt.println("nil")
return
}
fmt.printf("{}: {} ", node.kind, "TODO")
data, ok := node.value.([dynamic]u8)
if ok {
fmt.printf("\"")
for i in 0 ..< len(data) {
fmt.printf("%c", data[i])
}
fmt.printf("\" ")
} else {
fmt.printf("{} ", node.value)
}
if node.value_token_kind != .Invalid {
fmt.printf("{} ", node.value_token_kind)
}
if node.return_type != nil {
fmt.printf("-> {} ", node.return_type)
}
fmt.println("")
for child in node.children {
node_print(child, indent + 1)
}
}
parse_use_path :: proc(path: [dynamic]u8) -> (ret: [dynamic]u8) {
ret = path
// Check if the path ends with ".cat", if not, append it.
len_path := len(path)
if len(path) < 4 ||
path[len_path - 4] != '.' ||
path[len_path - 3] != 'c' ||
path[len_path - 2] != 'a' ||
path[len_path - 1] != 't' {
append(&ret, '.')
append(&ret, 'c')
append(&ret, 'a')
append(&ret, 't')
}
return
}
parse_use_path2 :: proc(path: [dynamic]u8) -> (ret: [dynamic]u8) {
ret = path
new_alias := [dynamic]u8{}
for i in 0 ..< len(path) {
if path[i] == '.' {
break
}
if path[i] == '/' {
clear(&new_alias)
continue
}
if libc.isalnum(i32(path[i])) == 0 {
append(&new_alias, '_')
} else {
append(&new_alias, path[i])
}
}
return
}
node_create_use :: proc(range: TextRange, path, alias: [dynamic]u8) -> (ret: ^Node) {
path_ := path
// Check if the path ends with ".cat", if not, append it.
path_ = parse_use_path(path)
ret = new(Node)
ret^ = {
kind = .Use,
range = range,
value = path_,
}
if len(alias) != 0 {
append(&ret.children, node_create_value(.Identifier, range, alias))
} else {
// Get the filename, and trucate the extension, then replace any special characters with _
new_alias := [dynamic]u8{}
new_alias = parse_use_path2(path_)
if len(new_alias) == 0 {
panic("Invalid alias for use statement")
}
append(&ret.children, node_create_value(.Identifier, range, new_alias))
}
return
}
node_create_if :: proc(range: TextRange, condition, then, else_: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .If,
range = range,
children = {condition, then},
}
else_mut := else_
if else_mut != nil {
if else_.kind == .If {
else_mut = node_create_block(else_.range, {else_})
}
append(&ret.children, else_mut)
}
return
}
node_create_for :: proc(range: TextRange, init, condition, step, body: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .For,
range = range,
children = {init, condition, step, body},
}
return
}
node_create_variable :: proc(range: TextRange, name, type_, value: ^Node, is_const: bool) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .VariableDeclaration,
range = range,
children = {name, type_, value},
value = is_const,
}
return
}
node_create_cast :: proc(range: TextRange, value, type_: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Cast,
range = range,
children = {value, type_},
}
return
}
node_create_bitwise_cast :: proc(range: TextRange, value, type_: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .BitwiseCast,
range = range,
children = {value, type_},
}
return
}
node_create_ret :: proc(range: TextRange, value: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Ret,
range = range,
children = {value},
}
return
}
node_create_struct_initializer :: proc(range: TextRange, name: [dynamic]u8, fields: [dynamic]^Node) -> (ret: ^Node) {
ret = new(Node)
if ret == nil {
panic("Failed to allocate node")
}
ret^ = {
kind = .StructInitializer,
range = range,
children = {},
value = name,
}
for field in fields {
append(&ret.children, field)
}
return
}
node_create_struct_enum_or_union :: proc(
range: TextRange,
kind: NodeKind,
name: [dynamic]u8,
fields: [dynamic]^Node,
) -> (
ret: ^Node,
) {
ret = new(Node)
ret^ = {
kind = kind,
range = range,
value = name,
}
append(&ret.children, node_create_value(.Identifier, range, name))
for field in fields {
append(&ret.children, field)
}
return
}