speedcat/ast.odin
Slendi da19db1b23 Add type checker
Signed-off-by: Slendi <slendi@socopon.com>
2024-02-28 15:07:06 +02:00

323 lines
5.5 KiB
Odin

package main
import "core:fmt"
import "core:c/libc"
NodeKind :: enum {
Integer,
Float,
Character,
String,
Identifier,
//
Block,
BinaryExpression,
UnaryExpression,
FieldAccess,
IndexAccess,
FunctionCall,
VariableDeclaration,
Cast,
BitwiseCast,
//
Function,
Struct,
Enum,
Union,
Use,
//
If,
For,
}
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_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_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)
}
}
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.
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(&path_, '.')
append(&path_, 'c')
append(&path_, 'a')
append(&path_, 't')
}
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{}
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])
}
}
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},
}
if else_ != nil {
append(&ret.children, else_)
}
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_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,
}
for field in fields {
append(&ret.children, field)
}
return
}