mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-10 05:11:37 -07:00
Named return value act as variables; Code reorganization
This commit is contained in:
+12
-10
@@ -309,26 +309,26 @@ write_type :: proc(buf: ^String_Buffer, ti: ^Type_Info) {
|
||||
}
|
||||
|
||||
|
||||
_parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: bool) {
|
||||
_parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok: bool) {
|
||||
is_digit :: inline proc(r: rune) -> bool{
|
||||
return '0' <= r && r <= '9';
|
||||
}
|
||||
|
||||
result := 0;
|
||||
i := 0;
|
||||
for i < len(s[offset..]) {
|
||||
c := rune(s[offset+i]);
|
||||
new_offset = offset;
|
||||
n := len(s[new_offset..]);
|
||||
for new_offset < n {
|
||||
c := rune(s[new_offset]);
|
||||
if !is_digit(c) do break;
|
||||
i += 1;
|
||||
new_offset += 1;
|
||||
|
||||
result *= 10;
|
||||
result += int(c)-'0';
|
||||
}
|
||||
|
||||
return result, offset+i, i != 0;
|
||||
ok = new_offset > offset;
|
||||
return;
|
||||
}
|
||||
|
||||
_arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset, arg_count: int) -> (index, offset: int, ok: bool) {
|
||||
_arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset, arg_count: int) -> (index, new_offset: int, ok: bool) {
|
||||
parse_arg_number :: proc(format: string) -> (int, int, bool) {
|
||||
if len(format) < 3 do return 0, 1, false;
|
||||
|
||||
@@ -350,7 +350,9 @@ _arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset, arg_c
|
||||
return arg_index, offset, false;
|
||||
}
|
||||
fi.reordered = true;
|
||||
index, width, ok := parse_arg_number(format[offset..]);
|
||||
|
||||
width: int;
|
||||
index, width, ok = parse_arg_number(format[offset..]);
|
||||
if ok && 0 <= index && index < arg_count {
|
||||
return index, offset+width, true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
|
||||
return nil, true;
|
||||
}
|
||||
|
||||
data := make([]byte, int(length));
|
||||
data = make([]byte, int(length));
|
||||
if data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
+1
-2
@@ -14,7 +14,7 @@ parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) {
|
||||
case "0", "f", "F", "false", "FALSE", "False":
|
||||
return false, true;
|
||||
}
|
||||
return ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_digit_value :: proc(r: rune) -> int {
|
||||
@@ -428,7 +428,6 @@ digits := "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
|
||||
is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
|
||||
neg := false;
|
||||
if is_signed {
|
||||
switch bit_size {
|
||||
case 8:
|
||||
|
||||
+21
-1
@@ -648,15 +648,35 @@ using_in :: proc() {
|
||||
println(f);
|
||||
}
|
||||
|
||||
named_proc_parameters :: proc() {
|
||||
foo0 :: proc() -> int {
|
||||
return 123;
|
||||
}
|
||||
foo1 :: proc() -> (a: int) {
|
||||
a = 123;
|
||||
return;
|
||||
}
|
||||
foo2 :: proc() -> (a, b: int) {
|
||||
// Named return values act like variables within the scope
|
||||
a = 321;
|
||||
b = 567;
|
||||
return b, a;
|
||||
}
|
||||
fmt.println("foo0 =", foo0());
|
||||
fmt.println("foo1 =", foo1());
|
||||
fmt.println("foo2 =", foo2());
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
general_stuff();
|
||||
when false {
|
||||
general_stuff();
|
||||
default_struct_values();
|
||||
union_type();
|
||||
parametric_polymorphism();
|
||||
threading_example();
|
||||
array_programming();
|
||||
using_in();
|
||||
named_proc_parameters();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1108,6 +1108,12 @@ bool check_binary_op(Checker *c, Operand *o, Token op) {
|
||||
error(op, "Operator '%.*s' is only allowed with numeric or pointer expressions", LIT(op.string));
|
||||
return false;
|
||||
}
|
||||
#if defined(NO_POINTER_ARITHMETIC)
|
||||
if (is_type_pointer(type)) {
|
||||
error(o->expr, "Pointer arithmetic is not supported");
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (is_type_pointer(type)) {
|
||||
o->type = t_int;
|
||||
}
|
||||
@@ -1118,6 +1124,7 @@ bool check_binary_op(Checker *c, Operand *o, Token op) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case Token_Mul:
|
||||
case Token_Quo:
|
||||
@@ -1644,6 +1651,12 @@ Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offs
|
||||
return operand;
|
||||
}
|
||||
|
||||
#if defined(NO_POINTER_ARITHMETIC)
|
||||
operand.mode = Addressing_Invalid;
|
||||
error(operand.expr, "Pointer arithmetic is not supported");
|
||||
return operand;
|
||||
#else
|
||||
|
||||
Type *base_ptr = base_type(ptr->type); GB_ASSERT(base_ptr->kind == Type_Pointer);
|
||||
Type *elem = base_ptr->Pointer.elem;
|
||||
i64 elem_size = type_size_of(c->allocator, elem);
|
||||
@@ -1670,6 +1683,7 @@ Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offs
|
||||
}
|
||||
|
||||
return operand;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
+50
-109
@@ -1165,135 +1165,76 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool first_is_field_value = false;
|
||||
if (rs->results.count > 0) {
|
||||
bool fail = false;
|
||||
first_is_field_value = (rs->results[0]->kind == AstNode_FieldValue);
|
||||
for_array(i, rs->results) {
|
||||
AstNode *arg = rs->results[i];
|
||||
bool mix = false;
|
||||
if (first_is_field_value) {
|
||||
mix = arg->kind != AstNode_FieldValue;
|
||||
} else {
|
||||
mix = arg->kind == AstNode_FieldValue;
|
||||
}
|
||||
if (mix) {
|
||||
error(arg, "Mixture of 'field = value' and value elements in a procedure all is not allowed");
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
// bool first_is_field_value = false;
|
||||
// if (rs->results.count > 0) {
|
||||
// bool fail = false;
|
||||
// first_is_field_value = (rs->results[0]->kind == AstNode_FieldValue);
|
||||
// for_array(i, rs->results) {
|
||||
// AstNode *arg = rs->results[i];
|
||||
// bool mix = false;
|
||||
// if (first_is_field_value) {
|
||||
// mix = arg->kind != AstNode_FieldValue;
|
||||
// } else {
|
||||
// mix = arg->kind == AstNode_FieldValue;
|
||||
// }
|
||||
// if (mix) {
|
||||
// error(arg, "Mixture of 'field = value' and value elements in a procedure all is not allowed");
|
||||
// fail = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (fail) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if (fail) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
Type *proc_type = c->proc_stack[c->proc_stack.count-1];
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
isize result_count = 0;
|
||||
bool has_named_results = pt->has_named_results;
|
||||
if (pt->results) {
|
||||
result_count = proc_type->Proc.results->Tuple.variables.count;
|
||||
}
|
||||
|
||||
|
||||
isize result_count_excluding_defaults = result_count;
|
||||
for (isize i = result_count-1; i >= 0; i--) {
|
||||
Entity *e = pt->results->Tuple.variables[i];
|
||||
if (e->kind == Entity_TypeName) {
|
||||
break;
|
||||
}
|
||||
// isize result_count_excluding_defaults = result_count;
|
||||
// for (isize i = result_count-1; i >= 0; i--) {
|
||||
// Entity *e = pt->results->Tuple.variables[i];
|
||||
// if (e->kind == Entity_TypeName) {
|
||||
// break;
|
||||
// }
|
||||
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
if (e->Variable.default_value.kind != ExactValue_Invalid ||
|
||||
e->Variable.default_is_nil) {
|
||||
result_count_excluding_defaults--;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// GB_ASSERT(e->kind == Entity_Variable);
|
||||
// if (e->Variable.default_value.kind != ExactValue_Invalid ||
|
||||
// e->Variable.default_is_nil) {
|
||||
// result_count_excluding_defaults--;
|
||||
// continue;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
|
||||
Array<Operand> operands = {};
|
||||
defer (array_free(&operands));
|
||||
|
||||
if (first_is_field_value) {
|
||||
array_init_count(&operands, heap_allocator(), rs->results.count);
|
||||
for_array(i, rs->results) {
|
||||
AstNode *arg = rs->results[i];
|
||||
ast_node(fv, FieldValue, arg);
|
||||
check_expr(c, &operands[i], fv->value);
|
||||
}
|
||||
} else {
|
||||
// if (first_is_field_value) {
|
||||
// array_init_count(&operands, heap_allocator(), rs->results.count);
|
||||
// for_array(i, rs->results) {
|
||||
// AstNode *arg = rs->results[i];
|
||||
// ast_node(fv, FieldValue, arg);
|
||||
// check_expr(c, &operands[i], fv->value);
|
||||
// }
|
||||
// } else {
|
||||
array_init(&operands, heap_allocator(), 2*rs->results.count);
|
||||
check_unpack_arguments(c, nullptr, -1, &operands, rs->results, false);
|
||||
}
|
||||
// }
|
||||
|
||||
|
||||
if (first_is_field_value) {
|
||||
bool *visited = gb_alloc_array(c->allocator, bool, result_count);
|
||||
|
||||
for_array(i, rs->results) {
|
||||
AstNode *arg = rs->results[i];
|
||||
ast_node(fv, FieldValue, arg);
|
||||
if (fv->field->kind != AstNode_Ident) {
|
||||
gbString expr_str = expr_to_string(fv->field);
|
||||
error(arg, "Invalid parameter name '%s' in return statement", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
continue;
|
||||
}
|
||||
String name = fv->field->Ident.token.string;
|
||||
isize index = lookup_procedure_result(pt, name);
|
||||
if (index < 0) {
|
||||
error(arg, "No result named '%.*s' for this procedure type", LIT(name));
|
||||
continue;
|
||||
}
|
||||
if (visited[index]) {
|
||||
error(arg, "Duplicate result '%.*s' in return statement", LIT(name));
|
||||
continue;
|
||||
}
|
||||
|
||||
visited[index] = true;
|
||||
Operand *o = &operands[i];
|
||||
Entity *e = pt->results->Tuple.variables[index];
|
||||
check_assignment(c, &operands[i], e->type, str_lit("return statement"));
|
||||
}
|
||||
|
||||
for (isize i = 0; i < result_count; i++) {
|
||||
if (!visited[i]) {
|
||||
Entity *e = pt->results->Tuple.variables[i];
|
||||
if (is_blank_ident(e->token)) {
|
||||
continue;
|
||||
}
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
if (e->Variable.default_value.kind != ExactValue_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e->Variable.default_is_nil) {
|
||||
continue;
|
||||
}
|
||||
|
||||
gbString str = type_to_string(e->type);
|
||||
error(node, "Return value '%.*s' of type '%s' is missing in return statement",
|
||||
LIT(e->token.string), str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (result_count == 0 && rs->results.count > 0) {
|
||||
if (result_count == 0 && rs->results.count > 0) {
|
||||
error(rs->results[0], "No return values expected");
|
||||
} else if (operands.count > result_count) {
|
||||
if (result_count_excluding_defaults < result_count) {
|
||||
error(node, "Expected a maximum of %td return values, got %td", result_count, operands.count);
|
||||
} else {
|
||||
error(node, "Expected %td return values, got %td", result_count, operands.count);
|
||||
}
|
||||
} else if (operands.count < result_count_excluding_defaults) {
|
||||
if (result_count_excluding_defaults < result_count) {
|
||||
error(node, "Expected a minimum of %td return values, got %td", result_count_excluding_defaults, operands.count);
|
||||
} else {
|
||||
error(node, "Expected %td return values, got %td", result_count_excluding_defaults, operands.count);
|
||||
}
|
||||
} else if (has_named_results && operands.count == 0) {
|
||||
// Okay
|
||||
} else if (operands.count != result_count) {
|
||||
error(node, "Expected %td return values, got %td", result_count, operands.count);
|
||||
} else {
|
||||
isize max_count = rs->results.count;
|
||||
for (isize i = 0; i < max_count; i++) {
|
||||
|
||||
@@ -1423,10 +1423,16 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *_results) {
|
||||
token = name->Ident.token;
|
||||
}
|
||||
|
||||
if (is_blank_ident(token)) {
|
||||
error(name, "Result value cannot be a blank identifer `_`");
|
||||
}
|
||||
|
||||
Entity *param = make_entity_param(c->allocator, scope, token, type, false, false);
|
||||
param->flags |= EntityFlag_Result;
|
||||
param->Variable.default_value = value;
|
||||
param->Variable.default_is_nil = default_is_nil;
|
||||
array_add(&variables, param);
|
||||
add_entity(c, scope, name, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1673,6 +1679,12 @@ bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array
|
||||
}
|
||||
}
|
||||
|
||||
if (result_count > 0) {
|
||||
Entity *first = results->Tuple.variables[0];
|
||||
type->Proc.has_named_results = first->token.string != "";
|
||||
}
|
||||
|
||||
|
||||
ProcCallingConvention cc = pt->calling_convention;
|
||||
if (cc == ProcCC_ForeignBlockDefault) {
|
||||
cc = ProcCC_CDecl;
|
||||
|
||||
+4
-376
@@ -1,150 +1,6 @@
|
||||
#include "entity.cpp"
|
||||
|
||||
enum ExprKind {
|
||||
Expr_Expr,
|
||||
Expr_Stmt,
|
||||
};
|
||||
|
||||
// Statements and Declarations
|
||||
enum StmtFlag {
|
||||
Stmt_BreakAllowed = 1<<0,
|
||||
Stmt_ContinueAllowed = 1<<1,
|
||||
Stmt_FallthroughAllowed = 1<<2,
|
||||
|
||||
Stmt_CheckScopeDecls = 1<<5,
|
||||
};
|
||||
|
||||
struct BuiltinProc {
|
||||
String name;
|
||||
isize arg_count;
|
||||
bool variadic;
|
||||
ExprKind kind;
|
||||
};
|
||||
enum BuiltinProcId {
|
||||
BuiltinProc_Invalid,
|
||||
|
||||
BuiltinProc_len,
|
||||
BuiltinProc_cap,
|
||||
|
||||
// BuiltinProc_new,
|
||||
BuiltinProc_make,
|
||||
// BuiltinProc_free,
|
||||
|
||||
// BuiltinProc_reserve,
|
||||
// BuiltinProc_clear,
|
||||
// BuiltinProc_append,
|
||||
// BuiltinProc_delete,
|
||||
|
||||
BuiltinProc_size_of,
|
||||
BuiltinProc_align_of,
|
||||
BuiltinProc_offset_of,
|
||||
BuiltinProc_type_of,
|
||||
BuiltinProc_type_info_of,
|
||||
|
||||
BuiltinProc_compile_assert,
|
||||
|
||||
BuiltinProc_swizzle,
|
||||
|
||||
BuiltinProc_complex,
|
||||
BuiltinProc_real,
|
||||
BuiltinProc_imag,
|
||||
BuiltinProc_conj,
|
||||
|
||||
// BuiltinProc_slice_ptr,
|
||||
// BuiltinProc_slice_to_bytes,
|
||||
|
||||
BuiltinProc_expand_to_tuple,
|
||||
|
||||
BuiltinProc_min,
|
||||
BuiltinProc_max,
|
||||
BuiltinProc_abs,
|
||||
BuiltinProc_clamp,
|
||||
|
||||
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
|
||||
|
||||
BuiltinProc_COUNT,
|
||||
};
|
||||
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT(""), 0, false, Expr_Stmt},
|
||||
|
||||
{STR_LIT("len"), 1, false, Expr_Expr},
|
||||
{STR_LIT("cap"), 1, false, Expr_Expr},
|
||||
|
||||
// {STR_LIT("new"), 1, false, Expr_Expr},
|
||||
{STR_LIT("make"), 1, true, Expr_Expr},
|
||||
// {STR_LIT("free"), 1, false, Expr_Stmt},
|
||||
|
||||
// {STR_LIT("reserve"), 2, false, Expr_Stmt},
|
||||
// {STR_LIT("clear"), 1, false, Expr_Stmt},
|
||||
// {STR_LIT("append"), 1, true, Expr_Expr},
|
||||
// {STR_LIT("delete"), 2, false, Expr_Stmt},
|
||||
|
||||
{STR_LIT("size_of"), 1, false, Expr_Expr},
|
||||
{STR_LIT("align_of"), 1, false, Expr_Expr},
|
||||
{STR_LIT("offset_of"), 2, false, Expr_Expr},
|
||||
{STR_LIT("type_of"), 1, false, Expr_Expr},
|
||||
{STR_LIT("type_info_of"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("compile_assert"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("swizzle"), 1, true, Expr_Expr},
|
||||
|
||||
{STR_LIT("complex"), 2, false, Expr_Expr},
|
||||
{STR_LIT("real"), 1, false, Expr_Expr},
|
||||
{STR_LIT("imag"), 1, false, Expr_Expr},
|
||||
{STR_LIT("conj"), 1, false, Expr_Expr},
|
||||
|
||||
// {STR_LIT("slice_ptr"), 2, true, Expr_Expr},
|
||||
// {STR_LIT("slice_to_bytes"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("expand_to_tuple"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("min"), 2, false, Expr_Expr},
|
||||
{STR_LIT("max"), 2, false, Expr_Expr},
|
||||
{STR_LIT("abs"), 1, false, Expr_Expr},
|
||||
{STR_LIT("clamp"), 3, false, Expr_Expr},
|
||||
|
||||
{STR_LIT(""), 0, true, Expr_Expr}, // DIRECTIVE
|
||||
};
|
||||
|
||||
|
||||
#include "types.cpp"
|
||||
|
||||
enum AddressingMode {
|
||||
Addressing_Invalid, // invalid addressing mode
|
||||
Addressing_NoValue, // no value (void in C)
|
||||
Addressing_Value, // computed value (rvalue)
|
||||
Addressing_Immutable, // immutable computed value (const rvalue)
|
||||
Addressing_Variable, // addressable variable (lvalue)
|
||||
Addressing_Constant, // constant
|
||||
Addressing_Type, // type
|
||||
Addressing_Builtin, // built-in procedure
|
||||
Addressing_ProcGroup, // procedure group (overloaded procedure)
|
||||
Addressing_MapIndex, // map index expression -
|
||||
// lhs: acts like a Variable
|
||||
// rhs: acts like OptionalOk
|
||||
Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check)
|
||||
};
|
||||
|
||||
// Operand is used as an intermediate value whilst checking
|
||||
// Operands store an addressing mode, the expression being evaluated,
|
||||
// its type and node, and other specific information for certain
|
||||
// addressing modes
|
||||
// Its zero-value is a valid "invalid operand"
|
||||
struct Operand {
|
||||
AddressingMode mode;
|
||||
Type * type;
|
||||
ExactValue value;
|
||||
AstNode * expr;
|
||||
BuiltinProcId builtin_id;
|
||||
Entity * proc_group;
|
||||
};
|
||||
|
||||
struct TypeAndValue {
|
||||
AddressingMode mode;
|
||||
Type * type;
|
||||
ExactValue value;
|
||||
};
|
||||
|
||||
bool is_operand_value(Operand o) {
|
||||
switch (o.mode) {
|
||||
@@ -166,79 +22,6 @@ bool is_operand_undef(Operand o) {
|
||||
|
||||
|
||||
|
||||
struct BlockLabel {
|
||||
String name;
|
||||
AstNode *label; // AstNode_Label;
|
||||
};
|
||||
|
||||
// DeclInfo is used to store information of certain declarations to allow for "any order" usage
|
||||
struct DeclInfo {
|
||||
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
|
||||
Scope * scope;
|
||||
|
||||
Entity ** entities;
|
||||
isize entity_count;
|
||||
|
||||
AstNode * type_expr;
|
||||
AstNode * init_expr;
|
||||
Array<AstNode *> init_expr_list;
|
||||
Array<AstNode *> attributes;
|
||||
AstNode * proc_lit; // AstNode_ProcLit
|
||||
Type * gen_proc_type; // Precalculated
|
||||
|
||||
PtrSet<Entity *> deps;
|
||||
Array<BlockLabel> labels;
|
||||
};
|
||||
|
||||
// ProcedureInfo stores the information needed for checking a procedure
|
||||
|
||||
|
||||
struct ProcedureInfo {
|
||||
AstFile * file;
|
||||
Token token;
|
||||
DeclInfo * decl;
|
||||
Type * type; // Type_Procedure
|
||||
AstNode * body; // AstNode_BlockStmt
|
||||
u64 tags;
|
||||
bool generated_from_polymorphic;
|
||||
};
|
||||
|
||||
// ExprInfo stores information used for "untyped" expressions
|
||||
struct ExprInfo {
|
||||
bool is_lhs; // Debug info
|
||||
AddressingMode mode;
|
||||
Type * type; // Type_Basic
|
||||
ExactValue value;
|
||||
};
|
||||
|
||||
ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue value) {
|
||||
ExprInfo ei = {is_lhs, mode, type, value};
|
||||
return ei;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct Scope {
|
||||
AstNode * node;
|
||||
Scope * parent;
|
||||
Scope * prev, *next;
|
||||
Scope * first_child;
|
||||
Scope * last_child;
|
||||
Map<Entity *> elements; // Key: String
|
||||
PtrSet<Entity *> implicit;
|
||||
|
||||
Array<Scope *> shared;
|
||||
Array<AstNode *> delayed_file_decls;
|
||||
PtrSet<Scope *> imported;
|
||||
PtrSet<Scope *> exported; // NOTE(bhall): Contains 'using import' too
|
||||
bool is_proc;
|
||||
bool is_global;
|
||||
bool is_file;
|
||||
bool is_init;
|
||||
bool is_struct;
|
||||
bool has_been_imported; // This is only applicable to file scopes
|
||||
AstFile * file;
|
||||
};
|
||||
gb_global Scope *universal_scope = nullptr;
|
||||
|
||||
void scope_reset(Scope *scope) {
|
||||
@@ -257,7 +40,7 @@ i32 is_scope_an_ancestor(Scope *parent, Scope *child) {
|
||||
isize i = 0;
|
||||
while (child != nullptr) {
|
||||
if (parent == child) {
|
||||
return true;
|
||||
return i;
|
||||
}
|
||||
child = child->parent;
|
||||
i++;
|
||||
@@ -265,18 +48,6 @@ i32 is_scope_an_ancestor(Scope *parent, Scope *child) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
struct EntityGraphNode;
|
||||
typedef PtrSet<EntityGraphNode *> EntityGraphNodeSet;
|
||||
|
||||
struct EntityGraphNode {
|
||||
Entity * entity; // Procedure, Variable, Constant
|
||||
EntityGraphNodeSet pred;
|
||||
EntityGraphNodeSet succ;
|
||||
isize index; // Index in array/queue
|
||||
isize dep_count;
|
||||
};
|
||||
|
||||
void entity_graph_node_set_destroy(EntityGraphNodeSet *s) {
|
||||
if (s->hashes.data != nullptr) {
|
||||
ptr_set_destroy(s);
|
||||
@@ -326,9 +97,6 @@ void entity_graph_node_swap(EntityGraphNode **data, isize i, isize j) {
|
||||
|
||||
|
||||
|
||||
struct ImportGraphNode;
|
||||
typedef PtrSet<ImportGraphNode *> ImportGraphNodeSet;
|
||||
|
||||
void import_graph_node_set_destroy(ImportGraphNodeSet *s) {
|
||||
if (s->hashes.data != nullptr) {
|
||||
ptr_set_destroy(s);
|
||||
@@ -350,16 +118,6 @@ void import_graph_node_set_remove(ImportGraphNodeSet *s, ImportGraphNode *n) {
|
||||
ptr_set_remove(s, n);
|
||||
}
|
||||
|
||||
struct ImportGraphNode {
|
||||
Scope * scope;
|
||||
String path;
|
||||
isize file_id;
|
||||
ImportGraphNodeSet pred;
|
||||
ImportGraphNodeSet succ;
|
||||
isize index; // Index in array/queue
|
||||
isize dep_count;
|
||||
};
|
||||
|
||||
ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) {
|
||||
ImportGraphNode *n = gb_alloc_item(a, ImportGraphNode);
|
||||
n->scope = scope;
|
||||
@@ -368,7 +126,6 @@ ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) {
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void import_graph_node_destroy(ImportGraphNode *n, gbAllocator a) {
|
||||
import_graph_node_set_destroy(&n->pred);
|
||||
import_graph_node_set_destroy(&n->succ);
|
||||
@@ -409,117 +166,9 @@ GB_COMPARE_PROC(ast_node_cmp) {
|
||||
return token_pos_cmp(i.pos, j.pos);
|
||||
}
|
||||
|
||||
struct ForeignContext {
|
||||
AstNode * curr_library;
|
||||
ProcCallingConvention default_cc;
|
||||
String link_prefix;
|
||||
bool in_export;
|
||||
};
|
||||
|
||||
struct CheckerContext {
|
||||
Scope * file_scope;
|
||||
Scope * scope;
|
||||
DeclInfo * decl;
|
||||
u32 stmt_state_flags;
|
||||
bool in_defer; // TODO(bill): Actually handle correctly
|
||||
String proc_name;
|
||||
Type * type_hint;
|
||||
DeclInfo * curr_proc_decl;
|
||||
ForeignContext foreign_context;
|
||||
|
||||
bool collect_delayed_decls;
|
||||
bool allow_polymorphic_types;
|
||||
bool no_polymorphic_errors;
|
||||
Scope * polymorphic_scope;
|
||||
};
|
||||
|
||||
|
||||
// CheckerInfo stores all the symbol information for a type-checked program
|
||||
struct CheckerInfo {
|
||||
Map<TypeAndValue> types; // Key: AstNode * | Expression -> Type (and value)
|
||||
Map<ExprInfo> untyped; // Key: AstNode * | Expression -> ExprInfo
|
||||
Map<AstFile *> files; // Key: String (full path)
|
||||
Map<Entity *> foreigns; // Key: String
|
||||
Array<Entity *> definitions;
|
||||
Array<Entity *> entities;
|
||||
Array<DeclInfo *> variable_init_order;
|
||||
|
||||
Map<Array<Entity *> > gen_procs; // Key: AstNode * | Identifier -> Entity
|
||||
Map<Array<Entity *> > gen_types; // Key: Type *
|
||||
|
||||
Map<isize> type_info_map; // Key: Type *
|
||||
isize type_info_count;
|
||||
|
||||
Scope * init_scope;
|
||||
Entity * entry_point;
|
||||
PtrSet<Entity *> minimum_dependency_set;
|
||||
};
|
||||
|
||||
struct Checker {
|
||||
Parser * parser;
|
||||
CheckerInfo info;
|
||||
gbMutex mutex;
|
||||
|
||||
AstFile * curr_ast_file;
|
||||
Scope * global_scope;
|
||||
// NOTE(bill): Procedures to check
|
||||
Array<ProcedureInfo> procs;
|
||||
Map<Scope *> file_scopes; // Key: String (fullpath)
|
||||
Array<ImportGraphNode *> file_order;
|
||||
|
||||
gbAllocator allocator;
|
||||
gbArena arena;
|
||||
gbArena tmp_arena;
|
||||
gbAllocator tmp_allocator;
|
||||
|
||||
CheckerContext context;
|
||||
|
||||
Array<Type *> proc_stack;
|
||||
bool done_preload;
|
||||
|
||||
PtrSet<AstFile *> checked_files;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
HashKey hash_node (AstNode *node) { return hash_pointer(node); }
|
||||
HashKey hash_ast_file (AstFile *file) { return hash_pointer(file); }
|
||||
HashKey hash_entity (Entity *e) { return hash_pointer(e); }
|
||||
HashKey hash_type (Type *t) { return hash_pointer(t); }
|
||||
HashKey hash_decl_info(DeclInfo *decl) { return hash_pointer(decl); }
|
||||
|
||||
// CheckerInfo API
|
||||
TypeAndValue type_and_value_of_expr (CheckerInfo *i, AstNode *expr);
|
||||
Type * type_of_expr (CheckerInfo *i, AstNode *expr);
|
||||
Entity * entity_of_ident (CheckerInfo *i, AstNode *identifier);
|
||||
Entity * implicit_entity_of_node(CheckerInfo *i, AstNode *clause);
|
||||
Scope * scope_of_node (CheckerInfo *i, AstNode *node);
|
||||
DeclInfo * decl_info_of_ident (CheckerInfo *i, AstNode *ident);
|
||||
DeclInfo * decl_info_of_entity (CheckerInfo *i, Entity * e);
|
||||
AstFile * ast_file_of_filename (CheckerInfo *i, String filename);
|
||||
// IMPORTANT: Only to use once checking is done
|
||||
isize type_info_index (CheckerInfo *i, Type * type, bool error_on_failure = true);
|
||||
|
||||
|
||||
Entity *current_scope_lookup_entity(Scope *s, String name);
|
||||
Entity *scope_lookup_entity (Scope *s, String name);
|
||||
void scope_lookup_parent_entity (Scope *s, String name, Scope **scope_, Entity **entity_);
|
||||
Entity *scope_insert_entity (Scope *s, Entity *entity);
|
||||
|
||||
|
||||
ExprInfo *check_get_expr_info (CheckerInfo *i, AstNode *expr);
|
||||
void check_set_expr_info (CheckerInfo *i, AstNode *expr, ExprInfo info);
|
||||
void check_remove_expr_info (CheckerInfo *i, AstNode *expr);
|
||||
void add_untyped (CheckerInfo *i, AstNode *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value);
|
||||
void add_type_and_value (CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value);
|
||||
void add_entity_use (Checker *c, AstNode *identifier, Entity *entity);
|
||||
void add_implicit_entity (Checker *c, AstNode *node, Entity *e);
|
||||
void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d);
|
||||
|
||||
void check_add_import_decl(Checker *c, AstNodeImportDecl *id);
|
||||
void check_add_export_decl(Checker *c, AstNodeExportDecl *ed);
|
||||
void check_add_foreign_import_decl(Checker *c, AstNode *decl);
|
||||
|
||||
|
||||
void init_declaration_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
|
||||
@@ -771,6 +420,9 @@ Entity *scope_lookup_entity(Scope *s, String name) {
|
||||
|
||||
Entity *scope_insert_entity(Scope *s, Entity *entity) {
|
||||
String name = entity->token.string;
|
||||
if (name == "") {
|
||||
return nullptr;
|
||||
}
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_get(&s->elements, key);
|
||||
|
||||
@@ -1742,30 +1394,6 @@ void init_preload(Checker *c) {
|
||||
|
||||
|
||||
|
||||
bool check_arity_match(Checker *c, AstNodeValueDecl *vd, bool is_global = false);
|
||||
void check_collect_entities(Checker *c, Array<AstNode *> nodes);
|
||||
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws);
|
||||
void check_delayed_file_import_entity(Checker *c, AstNode *decl);
|
||||
|
||||
struct AttributeContext {
|
||||
String link_name;
|
||||
String link_prefix;
|
||||
isize init_expr_list_count;
|
||||
String thread_local_model;
|
||||
};
|
||||
|
||||
AttributeContext make_attribute_context(String link_prefix) {
|
||||
AttributeContext ac = {};
|
||||
ac.link_prefix = link_prefix;
|
||||
return ac;
|
||||
}
|
||||
|
||||
#define DECL_ATTRIBUTE_PROC(_name) bool _name(Checker *c, AstNode *elem, String name, ExactValue value, AttributeContext *ac)
|
||||
typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc);
|
||||
|
||||
|
||||
void check_decl_attributes(Checker *c, Array<AstNode *> attributes, DeclAttributeProc *proc, AttributeContext *ac);
|
||||
|
||||
DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
|
||||
if (name == "default_calling_convention") {
|
||||
if (value.kind == ExactValue_String) {
|
||||
|
||||
+400
@@ -0,0 +1,400 @@
|
||||
// checker.hpp
|
||||
|
||||
struct Type;
|
||||
struct Entity;
|
||||
struct Scope;
|
||||
struct DeclInfo;
|
||||
struct AstFile;
|
||||
|
||||
enum AddressingMode {
|
||||
Addressing_Invalid, // invalid addressing mode
|
||||
Addressing_NoValue, // no value (void in C)
|
||||
Addressing_Value, // computed value (rvalue)
|
||||
Addressing_Immutable, // immutable computed value (const rvalue)
|
||||
Addressing_Variable, // addressable variable (lvalue)
|
||||
Addressing_Constant, // constant
|
||||
Addressing_Type, // type
|
||||
Addressing_Builtin, // built-in procedure
|
||||
Addressing_ProcGroup, // procedure group (overloaded procedure)
|
||||
Addressing_MapIndex, // map index expression -
|
||||
// lhs: acts like a Variable
|
||||
// rhs: acts like OptionalOk
|
||||
Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check)
|
||||
};
|
||||
|
||||
struct TypeAndValue {
|
||||
AddressingMode mode;
|
||||
Type * type;
|
||||
ExactValue value;
|
||||
};
|
||||
|
||||
|
||||
// ExprInfo stores information used for "untyped" expressions
|
||||
struct ExprInfo {
|
||||
bool is_lhs; // Debug info
|
||||
AddressingMode mode;
|
||||
Type * type; // Type_Basic
|
||||
ExactValue value;
|
||||
};
|
||||
|
||||
gb_inline ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue value) {
|
||||
ExprInfo ei = {is_lhs, mode, type, value};
|
||||
return ei;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
enum ExprKind {
|
||||
Expr_Expr,
|
||||
Expr_Stmt,
|
||||
};
|
||||
|
||||
// Statements and Declarations
|
||||
enum StmtFlag {
|
||||
Stmt_BreakAllowed = 1<<0,
|
||||
Stmt_ContinueAllowed = 1<<1,
|
||||
Stmt_FallthroughAllowed = 1<<2,
|
||||
|
||||
Stmt_CheckScopeDecls = 1<<5,
|
||||
};
|
||||
|
||||
struct BuiltinProc {
|
||||
String name;
|
||||
isize arg_count;
|
||||
bool variadic;
|
||||
ExprKind kind;
|
||||
};
|
||||
enum BuiltinProcId {
|
||||
BuiltinProc_Invalid,
|
||||
|
||||
BuiltinProc_len,
|
||||
BuiltinProc_cap,
|
||||
|
||||
// BuiltinProc_new,
|
||||
BuiltinProc_make,
|
||||
// BuiltinProc_free,
|
||||
|
||||
// BuiltinProc_reserve,
|
||||
// BuiltinProc_clear,
|
||||
// BuiltinProc_append,
|
||||
// BuiltinProc_delete,
|
||||
|
||||
BuiltinProc_size_of,
|
||||
BuiltinProc_align_of,
|
||||
BuiltinProc_offset_of,
|
||||
BuiltinProc_type_of,
|
||||
BuiltinProc_type_info_of,
|
||||
|
||||
BuiltinProc_compile_assert,
|
||||
|
||||
BuiltinProc_swizzle,
|
||||
|
||||
BuiltinProc_complex,
|
||||
BuiltinProc_real,
|
||||
BuiltinProc_imag,
|
||||
BuiltinProc_conj,
|
||||
|
||||
// BuiltinProc_slice_ptr,
|
||||
// BuiltinProc_slice_to_bytes,
|
||||
|
||||
BuiltinProc_expand_to_tuple,
|
||||
|
||||
BuiltinProc_min,
|
||||
BuiltinProc_max,
|
||||
BuiltinProc_abs,
|
||||
BuiltinProc_clamp,
|
||||
|
||||
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
|
||||
|
||||
BuiltinProc_COUNT,
|
||||
};
|
||||
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT(""), 0, false, Expr_Stmt},
|
||||
|
||||
{STR_LIT("len"), 1, false, Expr_Expr},
|
||||
{STR_LIT("cap"), 1, false, Expr_Expr},
|
||||
|
||||
// {STR_LIT("new"), 1, false, Expr_Expr},
|
||||
{STR_LIT("make"), 1, true, Expr_Expr},
|
||||
// {STR_LIT("free"), 1, false, Expr_Stmt},
|
||||
|
||||
// {STR_LIT("reserve"), 2, false, Expr_Stmt},
|
||||
// {STR_LIT("clear"), 1, false, Expr_Stmt},
|
||||
// {STR_LIT("append"), 1, true, Expr_Expr},
|
||||
// {STR_LIT("delete"), 2, false, Expr_Stmt},
|
||||
|
||||
{STR_LIT("size_of"), 1, false, Expr_Expr},
|
||||
{STR_LIT("align_of"), 1, false, Expr_Expr},
|
||||
{STR_LIT("offset_of"), 2, false, Expr_Expr},
|
||||
{STR_LIT("type_of"), 1, false, Expr_Expr},
|
||||
{STR_LIT("type_info_of"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("compile_assert"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("swizzle"), 1, true, Expr_Expr},
|
||||
|
||||
{STR_LIT("complex"), 2, false, Expr_Expr},
|
||||
{STR_LIT("real"), 1, false, Expr_Expr},
|
||||
{STR_LIT("imag"), 1, false, Expr_Expr},
|
||||
{STR_LIT("conj"), 1, false, Expr_Expr},
|
||||
|
||||
// {STR_LIT("slice_ptr"), 2, true, Expr_Expr},
|
||||
// {STR_LIT("slice_to_bytes"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("expand_to_tuple"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("min"), 2, false, Expr_Expr},
|
||||
{STR_LIT("max"), 2, false, Expr_Expr},
|
||||
{STR_LIT("abs"), 1, false, Expr_Expr},
|
||||
{STR_LIT("clamp"), 3, false, Expr_Expr},
|
||||
|
||||
{STR_LIT(""), 0, true, Expr_Expr}, // DIRECTIVE
|
||||
};
|
||||
|
||||
|
||||
// Operand is used as an intermediate value whilst checking
|
||||
// Operands store an addressing mode, the expression being evaluated,
|
||||
// its type and node, and other specific information for certain
|
||||
// addressing modes
|
||||
// Its zero-value is a valid "invalid operand"
|
||||
struct Operand {
|
||||
AddressingMode mode;
|
||||
Type * type;
|
||||
ExactValue value;
|
||||
AstNode * expr;
|
||||
BuiltinProcId builtin_id;
|
||||
Entity * proc_group;
|
||||
};
|
||||
|
||||
|
||||
struct BlockLabel {
|
||||
String name;
|
||||
AstNode *label; // AstNode_Label;
|
||||
};
|
||||
|
||||
// DeclInfo is used to store information of certain declarations to allow for "any order" usage
|
||||
struct DeclInfo {
|
||||
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
|
||||
Scope * scope;
|
||||
|
||||
Entity ** entities;
|
||||
isize entity_count;
|
||||
|
||||
AstNode * type_expr;
|
||||
AstNode * init_expr;
|
||||
Array<AstNode *> init_expr_list;
|
||||
Array<AstNode *> attributes;
|
||||
AstNode * proc_lit; // AstNode_ProcLit
|
||||
Type * gen_proc_type; // Precalculated
|
||||
|
||||
PtrSet<Entity *> deps;
|
||||
Array<BlockLabel> labels;
|
||||
};
|
||||
|
||||
// ProcedureInfo stores the information needed for checking a procedure
|
||||
|
||||
|
||||
struct ProcedureInfo {
|
||||
AstFile * file;
|
||||
Token token;
|
||||
DeclInfo * decl;
|
||||
Type * type; // Type_Procedure
|
||||
AstNode * body; // AstNode_BlockStmt
|
||||
u64 tags;
|
||||
bool generated_from_polymorphic;
|
||||
};
|
||||
|
||||
|
||||
struct Scope {
|
||||
AstNode * node;
|
||||
Scope * parent;
|
||||
Scope * prev, *next;
|
||||
Scope * first_child;
|
||||
Scope * last_child;
|
||||
Map<Entity *> elements; // Key: String
|
||||
PtrSet<Entity *> implicit;
|
||||
|
||||
Array<Scope *> shared;
|
||||
Array<AstNode *> delayed_file_decls;
|
||||
PtrSet<Scope *> imported;
|
||||
PtrSet<Scope *> exported; // NOTE(bhall): Contains 'using import' too
|
||||
bool is_proc;
|
||||
bool is_global;
|
||||
bool is_file;
|
||||
bool is_init;
|
||||
bool is_struct;
|
||||
bool has_been_imported; // This is only applicable to file scopes
|
||||
AstFile * file;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct EntityGraphNode;
|
||||
typedef PtrSet<EntityGraphNode *> EntityGraphNodeSet;
|
||||
|
||||
struct EntityGraphNode {
|
||||
Entity * entity; // Procedure, Variable, Constant
|
||||
EntityGraphNodeSet pred;
|
||||
EntityGraphNodeSet succ;
|
||||
isize index; // Index in array/queue
|
||||
isize dep_count;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct ImportGraphNode;
|
||||
typedef PtrSet<ImportGraphNode *> ImportGraphNodeSet;
|
||||
|
||||
|
||||
struct ImportGraphNode {
|
||||
Scope * scope;
|
||||
String path;
|
||||
isize file_id;
|
||||
ImportGraphNodeSet pred;
|
||||
ImportGraphNodeSet succ;
|
||||
isize index; // Index in array/queue
|
||||
isize dep_count;
|
||||
};
|
||||
|
||||
|
||||
struct ForeignContext {
|
||||
AstNode * curr_library;
|
||||
ProcCallingConvention default_cc;
|
||||
String link_prefix;
|
||||
bool in_export;
|
||||
};
|
||||
|
||||
struct CheckerContext {
|
||||
Scope * file_scope;
|
||||
Scope * scope;
|
||||
DeclInfo * decl;
|
||||
u32 stmt_state_flags;
|
||||
bool in_defer; // TODO(bill): Actually handle correctly
|
||||
String proc_name;
|
||||
Type * type_hint;
|
||||
DeclInfo * curr_proc_decl;
|
||||
ForeignContext foreign_context;
|
||||
|
||||
bool collect_delayed_decls;
|
||||
bool allow_polymorphic_types;
|
||||
bool no_polymorphic_errors;
|
||||
Scope * polymorphic_scope;
|
||||
};
|
||||
|
||||
|
||||
// CheckerInfo stores all the symbol information for a type-checked program
|
||||
struct CheckerInfo {
|
||||
Map<TypeAndValue> types; // Key: AstNode * | Expression -> Type (and value)
|
||||
Map<ExprInfo> untyped; // Key: AstNode * | Expression -> ExprInfo
|
||||
Map<AstFile *> files; // Key: String (full path)
|
||||
Map<Entity *> foreigns; // Key: String
|
||||
Array<Entity *> definitions;
|
||||
Array<Entity *> entities;
|
||||
Array<DeclInfo *> variable_init_order;
|
||||
|
||||
Map<Array<Entity *> > gen_procs; // Key: AstNode * | Identifier -> Entity
|
||||
Map<Array<Entity *> > gen_types; // Key: Type *
|
||||
|
||||
Map<isize> type_info_map; // Key: Type *
|
||||
isize type_info_count;
|
||||
|
||||
Scope * init_scope;
|
||||
Entity * entry_point;
|
||||
PtrSet<Entity *> minimum_dependency_set;
|
||||
};
|
||||
|
||||
struct Checker {
|
||||
Parser * parser;
|
||||
CheckerInfo info;
|
||||
gbMutex mutex;
|
||||
|
||||
AstFile * curr_ast_file;
|
||||
Scope * global_scope;
|
||||
// NOTE(bill): Procedures to check
|
||||
Array<ProcedureInfo> procs;
|
||||
Map<Scope *> file_scopes; // Key: String (fullpath)
|
||||
Array<ImportGraphNode *> file_order;
|
||||
|
||||
gbAllocator allocator;
|
||||
gbArena arena;
|
||||
gbArena tmp_arena;
|
||||
gbAllocator tmp_allocator;
|
||||
|
||||
CheckerContext context;
|
||||
|
||||
Array<Type *> proc_stack;
|
||||
bool done_preload;
|
||||
|
||||
PtrSet<AstFile *> checked_files;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
HashKey hash_node (AstNode *node) { return hash_pointer(node); }
|
||||
HashKey hash_ast_file (AstFile *file) { return hash_pointer(file); }
|
||||
HashKey hash_entity (Entity *e) { return hash_pointer(e); }
|
||||
HashKey hash_type (Type *t) { return hash_pointer(t); }
|
||||
HashKey hash_decl_info(DeclInfo *decl) { return hash_pointer(decl); }
|
||||
|
||||
|
||||
|
||||
// CheckerInfo API
|
||||
TypeAndValue type_and_value_of_expr (CheckerInfo *i, AstNode *expr);
|
||||
Type * type_of_expr (CheckerInfo *i, AstNode *expr);
|
||||
Entity * entity_of_ident (CheckerInfo *i, AstNode *identifier);
|
||||
Entity * implicit_entity_of_node(CheckerInfo *i, AstNode *clause);
|
||||
Scope * scope_of_node (CheckerInfo *i, AstNode *node);
|
||||
DeclInfo * decl_info_of_ident (CheckerInfo *i, AstNode *ident);
|
||||
DeclInfo * decl_info_of_entity (CheckerInfo *i, Entity * e);
|
||||
AstFile * ast_file_of_filename (CheckerInfo *i, String filename);
|
||||
// IMPORTANT: Only to use once checking is done
|
||||
isize type_info_index (CheckerInfo *i, Type * type, bool error_on_failure = true);
|
||||
|
||||
|
||||
Entity *current_scope_lookup_entity(Scope *s, String name);
|
||||
Entity *scope_lookup_entity (Scope *s, String name);
|
||||
void scope_lookup_parent_entity (Scope *s, String name, Scope **scope_, Entity **entity_);
|
||||
Entity *scope_insert_entity (Scope *s, Entity *entity);
|
||||
|
||||
|
||||
ExprInfo *check_get_expr_info (CheckerInfo *i, AstNode *expr);
|
||||
void check_set_expr_info (CheckerInfo *i, AstNode *expr, ExprInfo info);
|
||||
void check_remove_expr_info (CheckerInfo *i, AstNode *expr);
|
||||
void add_untyped (CheckerInfo *i, AstNode *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value);
|
||||
void add_type_and_value (CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value);
|
||||
void add_entity_use (Checker *c, AstNode *identifier, Entity *entity);
|
||||
void add_implicit_entity (Checker *c, AstNode *node, Entity *e);
|
||||
void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d);
|
||||
|
||||
void check_add_import_decl(Checker *c, AstNodeImportDecl *id);
|
||||
void check_add_export_decl(Checker *c, AstNodeExportDecl *ed);
|
||||
void check_add_foreign_import_decl(Checker *c, AstNode *decl);
|
||||
|
||||
|
||||
|
||||
bool check_arity_match(Checker *c, AstNodeValueDecl *vd, bool is_global = false);
|
||||
void check_collect_entities(Checker *c, Array<AstNode *> nodes);
|
||||
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws);
|
||||
void check_delayed_file_import_entity(Checker *c, AstNode *decl);
|
||||
|
||||
|
||||
struct AttributeContext {
|
||||
String link_name;
|
||||
String link_prefix;
|
||||
isize init_expr_list_count;
|
||||
String thread_local_model;
|
||||
};
|
||||
|
||||
AttributeContext make_attribute_context(String link_prefix) {
|
||||
AttributeContext ac = {};
|
||||
ac.link_prefix = link_prefix;
|
||||
return ac;
|
||||
}
|
||||
|
||||
#define DECL_ATTRIBUTE_PROC(_name) bool _name(Checker *c, AstNode *elem, String name, ExactValue value, AttributeContext *ac)
|
||||
typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc);
|
||||
|
||||
void check_decl_attributes(Checker *c, Array<AstNode *> attributes, DeclAttributeProc *proc, AttributeContext *ac);
|
||||
+9
-8
@@ -37,14 +37,15 @@ enum EntityFlag {
|
||||
EntityFlag_Using = 1<<2,
|
||||
EntityFlag_Field = 1<<3,
|
||||
EntityFlag_Param = 1<<4,
|
||||
EntityFlag_ArrayElem = 1<<5,
|
||||
EntityFlag_Ellipsis = 1<<6,
|
||||
EntityFlag_NoAlias = 1<<7,
|
||||
EntityFlag_TypeField = 1<<8,
|
||||
EntityFlag_Value = 1<<9,
|
||||
EntityFlag_Sret = 1<<10,
|
||||
EntityFlag_BitFieldValue = 1<<11,
|
||||
EntityFlag_PolyConst = 1<<12,
|
||||
EntityFlag_Result = 1<<5,
|
||||
EntityFlag_ArrayElem = 1<<6,
|
||||
EntityFlag_Ellipsis = 1<<7,
|
||||
EntityFlag_NoAlias = 1<<8,
|
||||
EntityFlag_TypeField = 1<<9,
|
||||
EntityFlag_Value = 1<<10,
|
||||
EntityFlag_Sret = 1<<11,
|
||||
EntityFlag_BitFieldValue = 1<<12,
|
||||
EntityFlag_PolyConst = 1<<13,
|
||||
|
||||
EntityFlag_CVarArg = 1<<20,
|
||||
};
|
||||
|
||||
+43
-78
@@ -6657,63 +6657,14 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
isize return_count = proc->type->Proc.result_count;
|
||||
isize res_count = rs->results.count;
|
||||
|
||||
if (res_count > 0 &&
|
||||
rs->results[0]->kind == AstNode_FieldValue) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
Array<irValue *> results;
|
||||
array_init_count(&results, proc->module->tmp_allocator, return_count);
|
||||
|
||||
for_array(arg_index, rs->results) {
|
||||
AstNode *arg = rs->results[arg_index];
|
||||
ast_node(fv, FieldValue, arg);
|
||||
GB_ASSERT(fv->field->kind == AstNode_Ident);
|
||||
String name = fv->field->Ident.token.string;
|
||||
isize index = lookup_procedure_result(&proc->type->Proc, name);
|
||||
GB_ASSERT(index >= 0);
|
||||
irValue *expr = ir_build_expr(proc, fv->value);
|
||||
results[index] = expr;
|
||||
}
|
||||
for (isize i = 0; i < return_count; i++) {
|
||||
Entity *e = tuple->variables[i];
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
if (results[i] == nullptr) {
|
||||
if (e->Variable.default_value.kind != ExactValue_Invalid) {
|
||||
results[i] = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value);
|
||||
} else {
|
||||
results[i] = ir_value_nil(proc->module->allocator, e->type);
|
||||
}
|
||||
} else {
|
||||
results[i] = ir_emit_conv(proc, results[i], e->type);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.count == 1) {
|
||||
v = results[0];
|
||||
} else {
|
||||
GB_ASSERT(results.count == return_count);
|
||||
|
||||
Type *ret_type = proc->type->Proc.results;
|
||||
v = ir_add_local_generated(proc, ret_type);
|
||||
for_array(i, results) {
|
||||
irValue *field = ir_emit_struct_ep(proc, v, cast(i32)i);
|
||||
irValue *res = results[i];
|
||||
ir_emit_store(proc, field, res);
|
||||
}
|
||||
|
||||
v = ir_emit_load(proc, v);
|
||||
}
|
||||
} else if (return_count == 0) {
|
||||
if (return_count == 0) {
|
||||
// No return values
|
||||
} else if (return_count == 1) {
|
||||
Entity *e = tuple->variables[0];
|
||||
if (res_count == 0) {
|
||||
if (e->Variable.default_value.kind != ExactValue_Invalid) {
|
||||
v = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value);
|
||||
} else {
|
||||
v = ir_value_nil(proc->module->allocator, e->type);
|
||||
}
|
||||
irValue **found = map_get(&proc->module->values, hash_entity(e));
|
||||
GB_ASSERT(found);
|
||||
v = ir_emit_load(proc, *found);
|
||||
} else {
|
||||
v = ir_build_expr(proc, rs->results[0]);
|
||||
v = ir_emit_conv(proc, v, e->type);
|
||||
@@ -6725,34 +6676,29 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
Array<irValue *> results;
|
||||
array_init(&results, proc->module->tmp_allocator, return_count);
|
||||
|
||||
isize total_index = 0;
|
||||
isize res_index = 0;
|
||||
for (; res_index < res_count; res_index++) {
|
||||
irValue *res = ir_build_expr(proc, rs->results[res_index]);
|
||||
Type *t = ir_type(res);
|
||||
if (t->kind == Type_Tuple) {
|
||||
for_array(i, t->Tuple.variables) {
|
||||
Entity *e = t->Tuple.variables[i];
|
||||
irValue *v = ir_emit_struct_ev(proc, res, cast(i32)i);
|
||||
array_add(&results, v);
|
||||
total_index++;
|
||||
if (res_count != 0) {
|
||||
for (isize res_index = 0; res_index < res_count; res_index++) {
|
||||
irValue *res = ir_build_expr(proc, rs->results[res_index]);
|
||||
Type *t = ir_type(res);
|
||||
if (t->kind == Type_Tuple) {
|
||||
for_array(i, t->Tuple.variables) {
|
||||
Entity *e = t->Tuple.variables[i];
|
||||
irValue *v = ir_emit_struct_ev(proc, res, cast(i32)i);
|
||||
array_add(&results, v);
|
||||
}
|
||||
} else {
|
||||
array_add(&results, res);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
for (isize res_index = 0; res_index < return_count; res_index++) {
|
||||
Entity *e = tuple->variables[res_index];
|
||||
irValue **found = map_get(&proc->module->values, hash_entity(e));
|
||||
GB_ASSERT(found);
|
||||
irValue *res = ir_emit_load(proc, *found);
|
||||
array_add(&results, res);
|
||||
total_index++;
|
||||
}
|
||||
}
|
||||
while (total_index < return_count) {
|
||||
Entity *e = tuple->variables[total_index];
|
||||
irValue *res = nullptr;
|
||||
if (e->Variable.default_value.kind != ExactValue_Invalid) {
|
||||
res = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value);
|
||||
} else {
|
||||
res = ir_value_nil(proc->module->allocator, e->type);
|
||||
}
|
||||
array_add(&results, res);
|
||||
total_index++;
|
||||
}
|
||||
|
||||
GB_ASSERT(results.count == return_count);
|
||||
|
||||
@@ -6766,7 +6712,6 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
|
||||
}
|
||||
|
||||
v = ir_emit_load(proc, v);
|
||||
|
||||
}
|
||||
|
||||
ir_emit_return(proc, v);
|
||||
@@ -7441,6 +7386,26 @@ void ir_begin_procedure_body(irProcedure *proc) {
|
||||
}
|
||||
}
|
||||
|
||||
if (proc->type->Proc.has_named_results) {
|
||||
GB_ASSERT(proc->type->Proc.result_count > 0);
|
||||
TypeTuple *results = &proc->type->Proc.results->Tuple;
|
||||
for_array(i, results->variables) {
|
||||
Entity *e = results->variables[i];
|
||||
if (e->kind != Entity_Variable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e->token.string != "") {
|
||||
GB_ASSERT(!is_blank_ident(e->token));
|
||||
irValue *res = ir_add_local(proc, e, e->identifier, true);
|
||||
if (e->Variable.default_value.kind != ExactValue_Invalid) {
|
||||
irValue *c = ir_value_constant(a, e->type, e->Variable.default_value);
|
||||
ir_emit_store(proc, res, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (proc->type->Proc.calling_convention == ProcCC_Odin) {
|
||||
Entity *e = make_entity_param(a, nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false);
|
||||
|
||||
+7
-4
@@ -1,10 +1,15 @@
|
||||
// #define NO_ARRAY_BOUNDS_CHECK
|
||||
// #define NO_POINTER_ARITHMETIC
|
||||
|
||||
#include "common.cpp"
|
||||
#include "timings.cpp"
|
||||
#include "build_settings.cpp"
|
||||
#include "tokenizer.cpp"
|
||||
#include "exact_value.cpp"
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "checker.hpp"
|
||||
|
||||
#include "parser.cpp"
|
||||
#include "docs.cpp"
|
||||
#include "checker.cpp"
|
||||
@@ -37,6 +42,7 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||
// gb_printf_err("%.*s\n", cast(int)cmd_len, cmd_line);
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
|
||||
|
||||
@@ -54,7 +60,6 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
return exit_code;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||
@@ -386,12 +391,10 @@ bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_CrossCompile: {
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
cross_compile_target = value.value_string;
|
||||
#ifdef GB_SYSTEM_UNIX
|
||||
#ifdef GB_ARCH_64_BIT
|
||||
#if defined(GB_SYSTEM_UNIX) && defined(GB_ARCH_64_BIT)
|
||||
if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) {
|
||||
|
||||
} else
|
||||
#endif
|
||||
#endif
|
||||
{
|
||||
gb_printf_err("Unsupported cross compilation target '%.*s'\n", LIT(cross_compile_target));
|
||||
|
||||
+10
-546
@@ -1,543 +1,3 @@
|
||||
struct AstNode;
|
||||
struct Scope;
|
||||
struct Entity;
|
||||
struct DeclInfo;
|
||||
|
||||
enum ParseFileError {
|
||||
ParseFile_None,
|
||||
|
||||
ParseFile_WrongExtension,
|
||||
ParseFile_InvalidFile,
|
||||
ParseFile_EmptyFile,
|
||||
ParseFile_Permission,
|
||||
ParseFile_NotFound,
|
||||
ParseFile_InvalidToken,
|
||||
|
||||
ParseFile_Count,
|
||||
};
|
||||
|
||||
struct CommentGroup {
|
||||
Array<Token> list; // Token_Comment
|
||||
};
|
||||
|
||||
|
||||
enum ImportedFileKind {
|
||||
ImportedFile_Normal,
|
||||
ImportedFile_Shared,
|
||||
ImportedFile_Init,
|
||||
};
|
||||
|
||||
struct ImportedFile {
|
||||
ImportedFileKind kind;
|
||||
String path;
|
||||
String rel_path;
|
||||
TokenPos pos; // import
|
||||
isize index;
|
||||
};
|
||||
|
||||
|
||||
struct AstFile {
|
||||
isize id;
|
||||
String fullpath;
|
||||
gbArena arena;
|
||||
Tokenizer tokenizer;
|
||||
Array<Token> tokens;
|
||||
isize curr_token_index;
|
||||
Token curr_token;
|
||||
Token prev_token; // previous non-comment
|
||||
|
||||
// >= 0: In Expression
|
||||
// < 0: In Control Clause
|
||||
// NOTE(bill): Used to prevent type literals in control clauses
|
||||
isize expr_level;
|
||||
bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases
|
||||
bool in_foreign_block;
|
||||
bool allow_type;
|
||||
isize when_level;
|
||||
|
||||
Array<AstNode *> decls;
|
||||
ImportedFileKind file_kind;
|
||||
bool is_global_scope;
|
||||
Array<AstNode *> imports_and_exports; // 'import' 'using import' 'export'
|
||||
|
||||
|
||||
AstNode * curr_proc;
|
||||
isize scope_level;
|
||||
Scope * scope; // NOTE(bill): Created in checker
|
||||
DeclInfo * decl_info; // NOTE(bill): Created in checker
|
||||
|
||||
|
||||
CommentGroup lead_comment; // Comment (block) before the decl
|
||||
CommentGroup line_comment; // Comment after the semicolon
|
||||
CommentGroup docs; // current docs
|
||||
Array<CommentGroup> comments; // All the comments!
|
||||
|
||||
|
||||
#define PARSER_MAX_FIX_COUNT 6
|
||||
isize fix_count;
|
||||
TokenPos fix_prev_pos;
|
||||
};
|
||||
|
||||
|
||||
struct Parser {
|
||||
String init_fullpath;
|
||||
Array<AstFile *> files;
|
||||
Array<ImportedFile> imports;
|
||||
isize total_token_count;
|
||||
isize total_line_count;
|
||||
gbMutex file_add_mutex;
|
||||
gbMutex file_decl_mutex;
|
||||
};
|
||||
|
||||
enum ProcInlining {
|
||||
ProcInlining_none = 0,
|
||||
ProcInlining_inline = 1,
|
||||
ProcInlining_no_inline = 2,
|
||||
};
|
||||
|
||||
enum ProcTag {
|
||||
ProcTag_bounds_check = 1<<0,
|
||||
ProcTag_no_bounds_check = 1<<1,
|
||||
ProcTag_require_results = 1<<4,
|
||||
};
|
||||
|
||||
enum ProcCallingConvention {
|
||||
ProcCC_Invalid = 0,
|
||||
ProcCC_Odin,
|
||||
ProcCC_Contextless,
|
||||
ProcCC_CDecl,
|
||||
ProcCC_StdCall,
|
||||
ProcCC_FastCall,
|
||||
|
||||
// TODO(bill): Add extra calling conventions
|
||||
// ProcCC_VectorCall,
|
||||
// ProcCC_ClrCall,
|
||||
|
||||
ProcCC_ForeignBlockDefault = -1,
|
||||
};
|
||||
|
||||
enum StmtStateFlag {
|
||||
StmtStateFlag_bounds_check = 1<<0,
|
||||
StmtStateFlag_no_bounds_check = 1<<1,
|
||||
};
|
||||
|
||||
enum FieldFlag {
|
||||
FieldFlag_NONE = 0,
|
||||
FieldFlag_ellipsis = 1<<0,
|
||||
FieldFlag_using = 1<<1,
|
||||
FieldFlag_no_alias = 1<<2,
|
||||
FieldFlag_c_vararg = 1<<3,
|
||||
FieldFlag_in = 1<<4,
|
||||
|
||||
// FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_in,
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg,
|
||||
FieldFlag_Struct = FieldFlag_using,
|
||||
};
|
||||
|
||||
enum StmtAllowFlag {
|
||||
StmtAllowFlag_None = 0,
|
||||
StmtAllowFlag_In = 1<<0,
|
||||
StmtAllowFlag_Label = 1<<1,
|
||||
};
|
||||
|
||||
|
||||
|
||||
Array<AstNode *> make_ast_node_array(AstFile *f, isize init_capacity = 8) {
|
||||
Array<AstNode *> a;
|
||||
array_init(&a, heap_allocator(), init_capacity);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): This massive define is so it is possible to create a discriminated union (and extra debug info)
|
||||
// for the AstNode. I personally prefer discriminated unions over subtype polymorphism as I can preallocate
|
||||
// all the nodes and even memcpy in a different kind of node
|
||||
#define AST_NODE_KINDS \
|
||||
AST_NODE_KIND(Ident, "identifier", struct { \
|
||||
Token token; \
|
||||
Entity *entity; \
|
||||
}) \
|
||||
AST_NODE_KIND(Implicit, "implicit", Token) \
|
||||
AST_NODE_KIND(Undef, "undef", Token) \
|
||||
AST_NODE_KIND(BasicLit, "basic literal", struct { \
|
||||
Token token; \
|
||||
}) \
|
||||
AST_NODE_KIND(BasicDirective, "basic directive", struct { \
|
||||
Token token; \
|
||||
String name; \
|
||||
}) \
|
||||
AST_NODE_KIND(Ellipsis, "ellipsis", struct { \
|
||||
Token token; \
|
||||
AstNode *expr; \
|
||||
}) \
|
||||
AST_NODE_KIND(ProcGroup, "procedure group", struct { \
|
||||
Token token; \
|
||||
Token open; \
|
||||
Token close; \
|
||||
Array<AstNode *> args; \
|
||||
}) \
|
||||
AST_NODE_KIND(ProcLit, "procedure literal", struct { \
|
||||
AstNode *type; \
|
||||
AstNode *body; \
|
||||
u64 tags; \
|
||||
ProcInlining inlining; \
|
||||
}) \
|
||||
AST_NODE_KIND(CompoundLit, "compound literal", struct { \
|
||||
AstNode *type; \
|
||||
Array<AstNode *> elems; \
|
||||
Token open, close; \
|
||||
}) \
|
||||
AST_NODE_KIND(_ExprBegin, "", i32) \
|
||||
AST_NODE_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \
|
||||
AST_NODE_KIND(TagExpr, "tag expression", struct { Token token, name; AstNode *expr; }) \
|
||||
AST_NODE_KIND(RunExpr, "run expression", struct { Token token, name; AstNode *expr; }) \
|
||||
AST_NODE_KIND(UnaryExpr, "unary expression", struct { Token op; AstNode *expr; }) \
|
||||
AST_NODE_KIND(BinaryExpr, "binary expression", struct { Token op; AstNode *left, *right; } ) \
|
||||
AST_NODE_KIND(ParenExpr, "parentheses expression", struct { AstNode *expr; Token open, close; }) \
|
||||
AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \
|
||||
AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \
|
||||
AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \
|
||||
AST_NODE_KIND(SliceExpr, "slice expression", struct { \
|
||||
AstNode *expr; \
|
||||
Token open, close; \
|
||||
Token interval; \
|
||||
AstNode *low, *high; \
|
||||
}) \
|
||||
AST_NODE_KIND(CallExpr, "call expression", struct { \
|
||||
AstNode * proc; \
|
||||
Array<AstNode *> args; \
|
||||
Token open; \
|
||||
Token close; \
|
||||
Token ellipsis; \
|
||||
}) \
|
||||
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
|
||||
AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \
|
||||
AST_NODE_KIND(TypeAssertion, "type assertion", struct { AstNode *expr; Token dot; AstNode *type; }) \
|
||||
AST_NODE_KIND(TypeCast, "type cast", struct { Token token; AstNode *type, *expr; }) \
|
||||
AST_NODE_KIND(_ExprEnd, "", i32) \
|
||||
AST_NODE_KIND(_StmtBegin, "", i32) \
|
||||
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
|
||||
AST_NODE_KIND(EmptyStmt, "empty statement", struct { Token token; }) \
|
||||
AST_NODE_KIND(ExprStmt, "expression statement", struct { AstNode *expr; } ) \
|
||||
AST_NODE_KIND(TagStmt, "tag statement", struct { \
|
||||
Token token; \
|
||||
Token name; \
|
||||
AstNode *stmt; \
|
||||
}) \
|
||||
AST_NODE_KIND(AssignStmt, "assign statement", struct { \
|
||||
Token op; \
|
||||
Array<AstNode *> lhs, rhs; \
|
||||
}) \
|
||||
AST_NODE_KIND(IncDecStmt, "increment decrement statement", struct { \
|
||||
Token op; \
|
||||
AstNode *expr; \
|
||||
}) \
|
||||
AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
AST_NODE_KIND(BlockStmt, "block statement", struct { \
|
||||
Array<AstNode *> stmts; \
|
||||
Token open, close; \
|
||||
}) \
|
||||
AST_NODE_KIND(IfStmt, "if statement", struct { \
|
||||
Token token; \
|
||||
AstNode *init; \
|
||||
AstNode *cond; \
|
||||
AstNode *body; \
|
||||
AstNode *else_stmt; \
|
||||
}) \
|
||||
AST_NODE_KIND(WhenStmt, "when statement", struct { \
|
||||
Token token; \
|
||||
AstNode *cond; \
|
||||
AstNode *body; \
|
||||
AstNode *else_stmt; \
|
||||
bool is_cond_determined; \
|
||||
bool determined_cond; \
|
||||
}) \
|
||||
AST_NODE_KIND(ReturnStmt, "return statement", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> results; \
|
||||
}) \
|
||||
AST_NODE_KIND(ForStmt, "for statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *init; \
|
||||
AstNode *cond; \
|
||||
AstNode *post; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(RangeStmt, "range statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *val0; \
|
||||
AstNode *val1; \
|
||||
Token in_token; \
|
||||
AstNode *expr; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(CaseClause, "case clause", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> list; \
|
||||
Array<AstNode *> stmts; \
|
||||
Entity *implicit_entity; \
|
||||
}) \
|
||||
AST_NODE_KIND(SwitchStmt, "switch statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *init; \
|
||||
AstNode *tag; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(TypeSwitchStmt, "type switch statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *tag; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> list; \
|
||||
}) \
|
||||
AST_NODE_KIND(UsingInStmt, "using in statement", struct { \
|
||||
Token using_token; \
|
||||
Array<AstNode *> list; \
|
||||
Token in_token; \
|
||||
AstNode *expr; \
|
||||
}) \
|
||||
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
|
||||
Token string; \
|
||||
AstNode *operand; \
|
||||
}) \
|
||||
AST_NODE_KIND(AsmStmt, "assembly statement", struct { \
|
||||
Token token; \
|
||||
bool is_volatile; \
|
||||
Token open, close; \
|
||||
Token code_string; \
|
||||
AstNode *output_list; \
|
||||
AstNode *input_list; \
|
||||
AstNode *clobber_list; \
|
||||
isize output_count, input_count, clobber_count; \
|
||||
}) \
|
||||
AST_NODE_KIND(PushContext, "context <- statement", struct { \
|
||||
Token token; \
|
||||
AstNode *expr; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(_ComplexStmtEnd, "", i32) \
|
||||
AST_NODE_KIND(_StmtEnd, "", i32) \
|
||||
AST_NODE_KIND(_DeclBegin, "", i32) \
|
||||
AST_NODE_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \
|
||||
AST_NODE_KIND(ForeignBlockDecl, "foreign block declaration", struct { \
|
||||
Token token; \
|
||||
AstNode * foreign_library; \
|
||||
Token open, close; \
|
||||
Array<AstNode *> decls; \
|
||||
Array<AstNode *> attributes; \
|
||||
bool been_handled; \
|
||||
CommentGroup docs; \
|
||||
}) \
|
||||
AST_NODE_KIND(Label, "label", struct { \
|
||||
Token token; \
|
||||
AstNode *name; \
|
||||
}) \
|
||||
AST_NODE_KIND(ValueDecl, "value declaration", struct { \
|
||||
Array<AstNode *> names; \
|
||||
AstNode * type; \
|
||||
Array<AstNode *> values; \
|
||||
bool is_using; \
|
||||
bool is_mutable; \
|
||||
bool been_handled; \
|
||||
Array<AstNode *> attributes; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(ImportDecl, "import declaration", struct { \
|
||||
AstFile *file; \
|
||||
Token token; \
|
||||
Token relpath; \
|
||||
String fullpath; \
|
||||
Token import_name; \
|
||||
bool is_using; \
|
||||
bool been_handled; \
|
||||
Array<AstNode *> using_in_list; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(ExportDecl, "export declaration", struct { \
|
||||
AstFile *file; \
|
||||
Token token; \
|
||||
Token relpath; \
|
||||
String fullpath; \
|
||||
bool been_handled; \
|
||||
Array<AstNode *> using_in_list; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(ForeignImportDecl, "foreign import declaration", struct { \
|
||||
Token token; \
|
||||
Token filepath; \
|
||||
Token library_name; \
|
||||
String base_dir; \
|
||||
String collection_name; \
|
||||
String fullpath; \
|
||||
bool been_handled; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(_DeclEnd, "", i32) \
|
||||
AST_NODE_KIND(Attribute, "attribute", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
Array<AstNode *> elems; \
|
||||
Token open, close; \
|
||||
}) \
|
||||
AST_NODE_KIND(Field, "field", struct { \
|
||||
Array<AstNode *> names; \
|
||||
AstNode * type; \
|
||||
AstNode * default_value; \
|
||||
u32 flags; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(FieldList, "field list", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> list; \
|
||||
}) \
|
||||
AST_NODE_KIND(UnionField, "union field", struct { \
|
||||
AstNode *name; \
|
||||
AstNode *list; \
|
||||
}) \
|
||||
AST_NODE_KIND(_TypeBegin, "", i32) \
|
||||
AST_NODE_KIND(TypeType, "type", struct { \
|
||||
Token token; \
|
||||
AstNode *specialization; \
|
||||
}) \
|
||||
AST_NODE_KIND(HelperType, "helper type", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
}) \
|
||||
AST_NODE_KIND(AliasType, "alias type", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
}) \
|
||||
AST_NODE_KIND(PolyType, "polymorphic type", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
AstNode *specialization; \
|
||||
}) \
|
||||
AST_NODE_KIND(ProcType, "procedure type", struct { \
|
||||
Token token; \
|
||||
AstNode *params; \
|
||||
AstNode *results; \
|
||||
u64 tags; \
|
||||
ProcCallingConvention calling_convention; \
|
||||
bool generic; \
|
||||
}) \
|
||||
AST_NODE_KIND(PointerType, "pointer type", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
}) \
|
||||
AST_NODE_KIND(ArrayType, "array type", struct { \
|
||||
Token token; \
|
||||
AstNode *count; \
|
||||
AstNode *elem; \
|
||||
}) \
|
||||
AST_NODE_KIND(DynamicArrayType, "dynamic array type", struct { \
|
||||
Token token; \
|
||||
AstNode *elem; \
|
||||
}) \
|
||||
AST_NODE_KIND(StructType, "struct type", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> fields; \
|
||||
isize field_count; \
|
||||
AstNode * polymorphic_params; \
|
||||
bool is_packed; \
|
||||
bool is_raw_union; \
|
||||
AstNode * align; \
|
||||
}) \
|
||||
AST_NODE_KIND(UnionType, "union type", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> variants; \
|
||||
AstNode * align; \
|
||||
}) \
|
||||
AST_NODE_KIND(EnumType, "enum type", struct { \
|
||||
Token token; \
|
||||
AstNode * base_type; \
|
||||
Array<AstNode *> fields; /* FieldValue */ \
|
||||
}) \
|
||||
AST_NODE_KIND(BitFieldType, "bit field type", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> fields; /* FieldValue with : */ \
|
||||
AstNode * align; \
|
||||
}) \
|
||||
AST_NODE_KIND(MapType, "map type", struct { \
|
||||
Token token; \
|
||||
AstNode *count; \
|
||||
AstNode *key; \
|
||||
AstNode *value; \
|
||||
}) \
|
||||
AST_NODE_KIND(_TypeEnd, "", i32)
|
||||
|
||||
enum AstNodeKind {
|
||||
AstNode_Invalid,
|
||||
#define AST_NODE_KIND(_kind_name_, ...) GB_JOIN2(AstNode_, _kind_name_),
|
||||
AST_NODE_KINDS
|
||||
#undef AST_NODE_KIND
|
||||
AstNode_Count,
|
||||
};
|
||||
|
||||
String const ast_node_strings[] = {
|
||||
{cast(u8 *)"invalid node", gb_size_of("invalid node")},
|
||||
#define AST_NODE_KIND(_kind_name_, name, ...) {cast(u8 *)name, gb_size_of(name)-1},
|
||||
AST_NODE_KINDS
|
||||
#undef AST_NODE_KIND
|
||||
};
|
||||
|
||||
#define AST_NODE_KIND(_kind_name_, name, ...) typedef __VA_ARGS__ GB_JOIN2(AstNode, _kind_name_);
|
||||
AST_NODE_KINDS
|
||||
#undef AST_NODE_KIND
|
||||
|
||||
struct AstNode {
|
||||
AstNodeKind kind;
|
||||
u32 stmt_state_flags;
|
||||
AstFile * file;
|
||||
Scope * scope;
|
||||
|
||||
union {
|
||||
#define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_;
|
||||
AST_NODE_KINDS
|
||||
#undef AST_NODE_KIND
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_))
|
||||
#define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_);
|
||||
#ifndef case_end
|
||||
#define case_end } break;
|
||||
#endif
|
||||
|
||||
|
||||
gb_inline bool is_ast_node_expr(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_stmt(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__StmtBegin+1, AstNode__StmtEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_complex_stmt(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__ComplexStmtBegin+1, AstNode__ComplexStmtEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_decl(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__DeclBegin+1, AstNode__DeclEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_type(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_when_stmt(AstNode *node) {
|
||||
return node->kind == AstNode_WhenStmt;
|
||||
}
|
||||
|
||||
|
||||
Token ast_node_token(AstNode *node) {
|
||||
switch (node->kind) {
|
||||
case AstNode_Ident: return node->Ident.token;
|
||||
@@ -3135,7 +2595,7 @@ AstNode *parse_results(AstFile *f) {
|
||||
|
||||
AstNode *list = nullptr;
|
||||
expect_token(f, Token_OpenParen);
|
||||
list = parse_field_list(f, nullptr, 0, Token_CloseParen, true, false);
|
||||
list = parse_field_list(f, nullptr, FieldFlag_Results, Token_CloseParen, true, false);
|
||||
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||
return list;
|
||||
}
|
||||
@@ -3547,6 +3007,10 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
||||
Array<AstNode *> names = {};
|
||||
AstNode *type = list[i].node;
|
||||
Token token = blank_token;
|
||||
if (allowed_flags&FieldFlag_Results) {
|
||||
// NOTE(bill): Make this nothing and not `_`
|
||||
token.string = str_lit("");
|
||||
}
|
||||
|
||||
array_init_count(&names, heap_allocator(), 1);
|
||||
token.pos = ast_node_token(type).pos;
|
||||
@@ -3725,11 +3189,11 @@ AstNode *parse_return_stmt(AstFile *f) {
|
||||
|
||||
while (f->curr_token.kind != Token_Semicolon) {
|
||||
AstNode *arg = parse_expr(f, false);
|
||||
if (f->curr_token.kind == Token_Eq) {
|
||||
Token eq = expect_token(f, Token_Eq);
|
||||
AstNode *value = parse_value(f);
|
||||
arg = ast_field_value(f, arg, value, eq);
|
||||
}
|
||||
// if (f->curr_token.kind == Token_Eq) {
|
||||
// Token eq = expect_token(f, Token_Eq);
|
||||
// AstNode *value = parse_value(f);
|
||||
// arg = ast_field_value(f, arg, value, eq);
|
||||
// }
|
||||
|
||||
array_add(&results, arg);
|
||||
if (f->curr_token.kind != Token_Comma ||
|
||||
|
||||
+545
@@ -0,0 +1,545 @@
|
||||
struct AstNode;
|
||||
struct Scope;
|
||||
struct Type;
|
||||
struct Entity;
|
||||
struct DeclInfo;
|
||||
|
||||
|
||||
enum ParseFileError {
|
||||
ParseFile_None,
|
||||
|
||||
ParseFile_WrongExtension,
|
||||
ParseFile_InvalidFile,
|
||||
ParseFile_EmptyFile,
|
||||
ParseFile_Permission,
|
||||
ParseFile_NotFound,
|
||||
ParseFile_InvalidToken,
|
||||
|
||||
ParseFile_Count,
|
||||
};
|
||||
|
||||
struct CommentGroup {
|
||||
Array<Token> list; // Token_Comment
|
||||
};
|
||||
|
||||
|
||||
enum ImportedFileKind {
|
||||
ImportedFile_Normal,
|
||||
ImportedFile_Shared,
|
||||
ImportedFile_Init,
|
||||
};
|
||||
|
||||
struct ImportedFile {
|
||||
ImportedFileKind kind;
|
||||
String path;
|
||||
String rel_path;
|
||||
TokenPos pos; // import
|
||||
isize index;
|
||||
};
|
||||
|
||||
|
||||
struct AstFile {
|
||||
isize id;
|
||||
String fullpath;
|
||||
gbArena arena;
|
||||
Tokenizer tokenizer;
|
||||
Array<Token> tokens;
|
||||
isize curr_token_index;
|
||||
Token curr_token;
|
||||
Token prev_token; // previous non-comment
|
||||
|
||||
// >= 0: In Expression
|
||||
// < 0: In Control Clause
|
||||
// NOTE(bill): Used to prevent type literals in control clauses
|
||||
isize expr_level;
|
||||
bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases
|
||||
bool in_foreign_block;
|
||||
bool allow_type;
|
||||
isize when_level;
|
||||
|
||||
Array<AstNode *> decls;
|
||||
ImportedFileKind file_kind;
|
||||
bool is_global_scope;
|
||||
Array<AstNode *> imports_and_exports; // 'import' 'using import' 'export'
|
||||
|
||||
|
||||
AstNode * curr_proc;
|
||||
isize scope_level;
|
||||
Scope * scope; // NOTE(bill): Created in checker
|
||||
DeclInfo * decl_info; // NOTE(bill): Created in checker
|
||||
|
||||
|
||||
CommentGroup lead_comment; // Comment (block) before the decl
|
||||
CommentGroup line_comment; // Comment after the semicolon
|
||||
CommentGroup docs; // current docs
|
||||
Array<CommentGroup> comments; // All the comments!
|
||||
|
||||
|
||||
#define PARSER_MAX_FIX_COUNT 6
|
||||
isize fix_count;
|
||||
TokenPos fix_prev_pos;
|
||||
};
|
||||
|
||||
|
||||
struct Parser {
|
||||
String init_fullpath;
|
||||
Array<AstFile *> files;
|
||||
Array<ImportedFile> imports;
|
||||
isize total_token_count;
|
||||
isize total_line_count;
|
||||
gbMutex file_add_mutex;
|
||||
gbMutex file_decl_mutex;
|
||||
};
|
||||
|
||||
enum ProcInlining {
|
||||
ProcInlining_none = 0,
|
||||
ProcInlining_inline = 1,
|
||||
ProcInlining_no_inline = 2,
|
||||
};
|
||||
|
||||
enum ProcTag {
|
||||
ProcTag_bounds_check = 1<<0,
|
||||
ProcTag_no_bounds_check = 1<<1,
|
||||
ProcTag_require_results = 1<<4,
|
||||
};
|
||||
|
||||
enum ProcCallingConvention {
|
||||
ProcCC_Invalid = 0,
|
||||
ProcCC_Odin,
|
||||
ProcCC_Contextless,
|
||||
ProcCC_CDecl,
|
||||
ProcCC_StdCall,
|
||||
ProcCC_FastCall,
|
||||
|
||||
// TODO(bill): Add extra calling conventions
|
||||
// ProcCC_VectorCall,
|
||||
// ProcCC_ClrCall,
|
||||
|
||||
ProcCC_ForeignBlockDefault = -1,
|
||||
};
|
||||
|
||||
enum StmtStateFlag {
|
||||
StmtStateFlag_bounds_check = 1<<0,
|
||||
StmtStateFlag_no_bounds_check = 1<<1,
|
||||
};
|
||||
|
||||
enum FieldFlag {
|
||||
FieldFlag_NONE = 0,
|
||||
FieldFlag_ellipsis = 1<<0,
|
||||
FieldFlag_using = 1<<1,
|
||||
FieldFlag_no_alias = 1<<2,
|
||||
FieldFlag_c_vararg = 1<<3,
|
||||
|
||||
FieldFlag_in = 1<<5,
|
||||
|
||||
|
||||
FieldFlag_Results = 1<<16,
|
||||
|
||||
// FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_in,
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg,
|
||||
FieldFlag_Struct = FieldFlag_using,
|
||||
};
|
||||
|
||||
enum StmtAllowFlag {
|
||||
StmtAllowFlag_None = 0,
|
||||
StmtAllowFlag_In = 1<<0,
|
||||
StmtAllowFlag_Label = 1<<1,
|
||||
};
|
||||
|
||||
|
||||
|
||||
Array<AstNode *> make_ast_node_array(AstFile *f, isize init_capacity = 8) {
|
||||
Array<AstNode *> a;
|
||||
array_init(&a, heap_allocator(), init_capacity);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): This massive define is so it is possible to create a discriminated union (and extra debug info)
|
||||
// for the AstNode. I personally prefer discriminated unions over subtype polymorphism as I can preallocate
|
||||
// all the nodes and even memcpy in a different kind of node
|
||||
#define AST_NODE_KINDS \
|
||||
AST_NODE_KIND(Ident, "identifier", struct { \
|
||||
Token token; \
|
||||
Entity *entity; \
|
||||
}) \
|
||||
AST_NODE_KIND(Implicit, "implicit", Token) \
|
||||
AST_NODE_KIND(Undef, "undef", Token) \
|
||||
AST_NODE_KIND(BasicLit, "basic literal", struct { \
|
||||
Token token; \
|
||||
}) \
|
||||
AST_NODE_KIND(BasicDirective, "basic directive", struct { \
|
||||
Token token; \
|
||||
String name; \
|
||||
}) \
|
||||
AST_NODE_KIND(Ellipsis, "ellipsis", struct { \
|
||||
Token token; \
|
||||
AstNode *expr; \
|
||||
}) \
|
||||
AST_NODE_KIND(ProcGroup, "procedure group", struct { \
|
||||
Token token; \
|
||||
Token open; \
|
||||
Token close; \
|
||||
Array<AstNode *> args; \
|
||||
}) \
|
||||
AST_NODE_KIND(ProcLit, "procedure literal", struct { \
|
||||
AstNode *type; \
|
||||
AstNode *body; \
|
||||
u64 tags; \
|
||||
ProcInlining inlining; \
|
||||
}) \
|
||||
AST_NODE_KIND(CompoundLit, "compound literal", struct { \
|
||||
AstNode *type; \
|
||||
Array<AstNode *> elems; \
|
||||
Token open, close; \
|
||||
}) \
|
||||
AST_NODE_KIND(_ExprBegin, "", i32) \
|
||||
AST_NODE_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \
|
||||
AST_NODE_KIND(TagExpr, "tag expression", struct { Token token, name; AstNode *expr; }) \
|
||||
AST_NODE_KIND(RunExpr, "run expression", struct { Token token, name; AstNode *expr; }) \
|
||||
AST_NODE_KIND(UnaryExpr, "unary expression", struct { Token op; AstNode *expr; }) \
|
||||
AST_NODE_KIND(BinaryExpr, "binary expression", struct { Token op; AstNode *left, *right; } ) \
|
||||
AST_NODE_KIND(ParenExpr, "parentheses expression", struct { AstNode *expr; Token open, close; }) \
|
||||
AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \
|
||||
AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \
|
||||
AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \
|
||||
AST_NODE_KIND(SliceExpr, "slice expression", struct { \
|
||||
AstNode *expr; \
|
||||
Token open, close; \
|
||||
Token interval; \
|
||||
AstNode *low, *high; \
|
||||
}) \
|
||||
AST_NODE_KIND(CallExpr, "call expression", struct { \
|
||||
AstNode * proc; \
|
||||
Array<AstNode *> args; \
|
||||
Token open; \
|
||||
Token close; \
|
||||
Token ellipsis; \
|
||||
}) \
|
||||
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
|
||||
AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \
|
||||
AST_NODE_KIND(TypeAssertion, "type assertion", struct { AstNode *expr; Token dot; AstNode *type; }) \
|
||||
AST_NODE_KIND(TypeCast, "type cast", struct { Token token; AstNode *type, *expr; }) \
|
||||
AST_NODE_KIND(_ExprEnd, "", i32) \
|
||||
AST_NODE_KIND(_StmtBegin, "", i32) \
|
||||
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
|
||||
AST_NODE_KIND(EmptyStmt, "empty statement", struct { Token token; }) \
|
||||
AST_NODE_KIND(ExprStmt, "expression statement", struct { AstNode *expr; } ) \
|
||||
AST_NODE_KIND(TagStmt, "tag statement", struct { \
|
||||
Token token; \
|
||||
Token name; \
|
||||
AstNode *stmt; \
|
||||
}) \
|
||||
AST_NODE_KIND(AssignStmt, "assign statement", struct { \
|
||||
Token op; \
|
||||
Array<AstNode *> lhs, rhs; \
|
||||
}) \
|
||||
AST_NODE_KIND(IncDecStmt, "increment decrement statement", struct { \
|
||||
Token op; \
|
||||
AstNode *expr; \
|
||||
}) \
|
||||
AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
AST_NODE_KIND(BlockStmt, "block statement", struct { \
|
||||
Array<AstNode *> stmts; \
|
||||
Token open, close; \
|
||||
}) \
|
||||
AST_NODE_KIND(IfStmt, "if statement", struct { \
|
||||
Token token; \
|
||||
AstNode *init; \
|
||||
AstNode *cond; \
|
||||
AstNode *body; \
|
||||
AstNode *else_stmt; \
|
||||
}) \
|
||||
AST_NODE_KIND(WhenStmt, "when statement", struct { \
|
||||
Token token; \
|
||||
AstNode *cond; \
|
||||
AstNode *body; \
|
||||
AstNode *else_stmt; \
|
||||
bool is_cond_determined; \
|
||||
bool determined_cond; \
|
||||
}) \
|
||||
AST_NODE_KIND(ReturnStmt, "return statement", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> results; \
|
||||
}) \
|
||||
AST_NODE_KIND(ForStmt, "for statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *init; \
|
||||
AstNode *cond; \
|
||||
AstNode *post; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(RangeStmt, "range statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *val0; \
|
||||
AstNode *val1; \
|
||||
Token in_token; \
|
||||
AstNode *expr; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(CaseClause, "case clause", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> list; \
|
||||
Array<AstNode *> stmts; \
|
||||
Entity *implicit_entity; \
|
||||
}) \
|
||||
AST_NODE_KIND(SwitchStmt, "switch statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *init; \
|
||||
AstNode *tag; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(TypeSwitchStmt, "type switch statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *tag; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> list; \
|
||||
}) \
|
||||
AST_NODE_KIND(UsingInStmt, "using in statement", struct { \
|
||||
Token using_token; \
|
||||
Array<AstNode *> list; \
|
||||
Token in_token; \
|
||||
AstNode *expr; \
|
||||
}) \
|
||||
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
|
||||
Token string; \
|
||||
AstNode *operand; \
|
||||
}) \
|
||||
AST_NODE_KIND(AsmStmt, "assembly statement", struct { \
|
||||
Token token; \
|
||||
bool is_volatile; \
|
||||
Token open, close; \
|
||||
Token code_string; \
|
||||
AstNode *output_list; \
|
||||
AstNode *input_list; \
|
||||
AstNode *clobber_list; \
|
||||
isize output_count, input_count, clobber_count; \
|
||||
}) \
|
||||
AST_NODE_KIND(PushContext, "context <- statement", struct { \
|
||||
Token token; \
|
||||
AstNode *expr; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(_ComplexStmtEnd, "", i32) \
|
||||
AST_NODE_KIND(_StmtEnd, "", i32) \
|
||||
AST_NODE_KIND(_DeclBegin, "", i32) \
|
||||
AST_NODE_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \
|
||||
AST_NODE_KIND(ForeignBlockDecl, "foreign block declaration", struct { \
|
||||
Token token; \
|
||||
AstNode * foreign_library; \
|
||||
Token open, close; \
|
||||
Array<AstNode *> decls; \
|
||||
Array<AstNode *> attributes; \
|
||||
bool been_handled; \
|
||||
CommentGroup docs; \
|
||||
}) \
|
||||
AST_NODE_KIND(Label, "label", struct { \
|
||||
Token token; \
|
||||
AstNode *name; \
|
||||
}) \
|
||||
AST_NODE_KIND(ValueDecl, "value declaration", struct { \
|
||||
Array<AstNode *> names; \
|
||||
AstNode * type; \
|
||||
Array<AstNode *> values; \
|
||||
bool is_using; \
|
||||
bool is_mutable; \
|
||||
bool been_handled; \
|
||||
Array<AstNode *> attributes; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(ImportDecl, "import declaration", struct { \
|
||||
AstFile *file; \
|
||||
Token token; \
|
||||
Token relpath; \
|
||||
String fullpath; \
|
||||
Token import_name; \
|
||||
bool is_using; \
|
||||
bool been_handled; \
|
||||
Array<AstNode *> using_in_list; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(ExportDecl, "export declaration", struct { \
|
||||
AstFile *file; \
|
||||
Token token; \
|
||||
Token relpath; \
|
||||
String fullpath; \
|
||||
bool been_handled; \
|
||||
Array<AstNode *> using_in_list; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(ForeignImportDecl, "foreign import declaration", struct { \
|
||||
Token token; \
|
||||
Token filepath; \
|
||||
Token library_name; \
|
||||
String base_dir; \
|
||||
String collection_name; \
|
||||
String fullpath; \
|
||||
bool been_handled; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(_DeclEnd, "", i32) \
|
||||
AST_NODE_KIND(Attribute, "attribute", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
Array<AstNode *> elems; \
|
||||
Token open, close; \
|
||||
}) \
|
||||
AST_NODE_KIND(Field, "field", struct { \
|
||||
Array<AstNode *> names; \
|
||||
AstNode * type; \
|
||||
AstNode * default_value; \
|
||||
u32 flags; \
|
||||
CommentGroup docs; \
|
||||
CommentGroup comment; \
|
||||
}) \
|
||||
AST_NODE_KIND(FieldList, "field list", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> list; \
|
||||
}) \
|
||||
AST_NODE_KIND(UnionField, "union field", struct { \
|
||||
AstNode *name; \
|
||||
AstNode *list; \
|
||||
}) \
|
||||
AST_NODE_KIND(_TypeBegin, "", i32) \
|
||||
AST_NODE_KIND(TypeType, "type", struct { \
|
||||
Token token; \
|
||||
AstNode *specialization; \
|
||||
}) \
|
||||
AST_NODE_KIND(HelperType, "helper type", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
}) \
|
||||
AST_NODE_KIND(AliasType, "alias type", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
}) \
|
||||
AST_NODE_KIND(PolyType, "polymorphic type", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
AstNode *specialization; \
|
||||
}) \
|
||||
AST_NODE_KIND(ProcType, "procedure type", struct { \
|
||||
Token token; \
|
||||
AstNode *params; \
|
||||
AstNode *results; \
|
||||
u64 tags; \
|
||||
ProcCallingConvention calling_convention; \
|
||||
bool generic; \
|
||||
}) \
|
||||
AST_NODE_KIND(PointerType, "pointer type", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
}) \
|
||||
AST_NODE_KIND(ArrayType, "array type", struct { \
|
||||
Token token; \
|
||||
AstNode *count; \
|
||||
AstNode *elem; \
|
||||
}) \
|
||||
AST_NODE_KIND(DynamicArrayType, "dynamic array type", struct { \
|
||||
Token token; \
|
||||
AstNode *elem; \
|
||||
}) \
|
||||
AST_NODE_KIND(StructType, "struct type", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> fields; \
|
||||
isize field_count; \
|
||||
AstNode * polymorphic_params; \
|
||||
bool is_packed; \
|
||||
bool is_raw_union; \
|
||||
AstNode * align; \
|
||||
}) \
|
||||
AST_NODE_KIND(UnionType, "union type", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> variants; \
|
||||
AstNode * align; \
|
||||
}) \
|
||||
AST_NODE_KIND(EnumType, "enum type", struct { \
|
||||
Token token; \
|
||||
AstNode * base_type; \
|
||||
Array<AstNode *> fields; /* FieldValue */ \
|
||||
}) \
|
||||
AST_NODE_KIND(BitFieldType, "bit field type", struct { \
|
||||
Token token; \
|
||||
Array<AstNode *> fields; /* FieldValue with : */ \
|
||||
AstNode * align; \
|
||||
}) \
|
||||
AST_NODE_KIND(MapType, "map type", struct { \
|
||||
Token token; \
|
||||
AstNode *count; \
|
||||
AstNode *key; \
|
||||
AstNode *value; \
|
||||
}) \
|
||||
AST_NODE_KIND(_TypeEnd, "", i32)
|
||||
|
||||
enum AstNodeKind {
|
||||
AstNode_Invalid,
|
||||
#define AST_NODE_KIND(_kind_name_, ...) GB_JOIN2(AstNode_, _kind_name_),
|
||||
AST_NODE_KINDS
|
||||
#undef AST_NODE_KIND
|
||||
AstNode_Count,
|
||||
};
|
||||
|
||||
String const ast_node_strings[] = {
|
||||
{cast(u8 *)"invalid node", gb_size_of("invalid node")},
|
||||
#define AST_NODE_KIND(_kind_name_, name, ...) {cast(u8 *)name, gb_size_of(name)-1},
|
||||
AST_NODE_KINDS
|
||||
#undef AST_NODE_KIND
|
||||
};
|
||||
|
||||
#define AST_NODE_KIND(_kind_name_, name, ...) typedef __VA_ARGS__ GB_JOIN2(AstNode, _kind_name_);
|
||||
AST_NODE_KINDS
|
||||
#undef AST_NODE_KIND
|
||||
|
||||
struct AstNode {
|
||||
AstNodeKind kind;
|
||||
u32 stmt_state_flags;
|
||||
AstFile * file;
|
||||
Scope * scope;
|
||||
|
||||
union {
|
||||
#define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_;
|
||||
AST_NODE_KINDS
|
||||
#undef AST_NODE_KIND
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_))
|
||||
#define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_);
|
||||
#ifndef case_end
|
||||
#define case_end } break;
|
||||
#endif
|
||||
|
||||
|
||||
gb_inline bool is_ast_node_expr(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_stmt(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__StmtBegin+1, AstNode__StmtEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_complex_stmt(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__ComplexStmtBegin+1, AstNode__ComplexStmtEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_decl(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__DeclBegin+1, AstNode__DeclEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_type(AstNode *node) {
|
||||
return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1);
|
||||
}
|
||||
gb_inline bool is_ast_node_when_stmt(AstNode *node) {
|
||||
return node->kind == AstNode_WhenStmt;
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@ struct TypeStruct {
|
||||
bool is_polymorphic; \
|
||||
bool is_poly_specialized; \
|
||||
bool has_proc_default_values; \
|
||||
bool has_named_results; \
|
||||
isize specialization_count; \
|
||||
ProcCallingConvention calling_convention; \
|
||||
}) \
|
||||
|
||||
Reference in New Issue
Block a user