mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00: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{
|
is_digit :: inline proc(r: rune) -> bool{
|
||||||
return '0' <= r && r <= '9';
|
return '0' <= r && r <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
result := 0;
|
new_offset = offset;
|
||||||
i := 0;
|
n := len(s[new_offset..]);
|
||||||
for i < len(s[offset..]) {
|
for new_offset < n {
|
||||||
c := rune(s[offset+i]);
|
c := rune(s[new_offset]);
|
||||||
if !is_digit(c) do break;
|
if !is_digit(c) do break;
|
||||||
i += 1;
|
new_offset += 1;
|
||||||
|
|
||||||
result *= 10;
|
result *= 10;
|
||||||
result += int(c)-'0';
|
result += int(c)-'0';
|
||||||
}
|
}
|
||||||
|
ok = new_offset > offset;
|
||||||
return result, offset+i, i != 0;
|
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) {
|
parse_arg_number :: proc(format: string) -> (int, int, bool) {
|
||||||
if len(format) < 3 do return 0, 1, false;
|
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;
|
return arg_index, offset, false;
|
||||||
}
|
}
|
||||||
fi.reordered = true;
|
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 {
|
if ok && 0 <= index && index < arg_count {
|
||||||
return index, offset+width, true;
|
return index, offset+width, true;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
|
|||||||
return nil, true;
|
return nil, true;
|
||||||
}
|
}
|
||||||
|
|
||||||
data := make([]byte, int(length));
|
data = make([]byte, int(length));
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return nil, false;
|
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":
|
case "0", "f", "F", "false", "FALSE", "False":
|
||||||
return false, true;
|
return false, true;
|
||||||
}
|
}
|
||||||
return ok = false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_digit_value :: proc(r: rune) -> int {
|
_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) {
|
is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
|
||||||
neg := false;
|
|
||||||
if is_signed {
|
if is_signed {
|
||||||
switch bit_size {
|
switch bit_size {
|
||||||
case 8:
|
case 8:
|
||||||
|
|||||||
+21
-1
@@ -648,15 +648,35 @@ using_in :: proc() {
|
|||||||
println(f);
|
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() {
|
main :: proc() {
|
||||||
general_stuff();
|
|
||||||
when false {
|
when false {
|
||||||
|
general_stuff();
|
||||||
default_struct_values();
|
default_struct_values();
|
||||||
union_type();
|
union_type();
|
||||||
parametric_polymorphism();
|
parametric_polymorphism();
|
||||||
threading_example();
|
threading_example();
|
||||||
array_programming();
|
array_programming();
|
||||||
using_in();
|
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));
|
error(op, "Operator '%.*s' is only allowed with numeric or pointer expressions", LIT(op.string));
|
||||||
return false;
|
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)) {
|
if (is_type_pointer(type)) {
|
||||||
o->type = t_int;
|
o->type = t_int;
|
||||||
}
|
}
|
||||||
@@ -1118,6 +1124,7 @@ bool check_binary_op(Checker *c, Operand *o, Token op) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case Token_Mul:
|
case Token_Mul:
|
||||||
case Token_Quo:
|
case Token_Quo:
|
||||||
@@ -1644,6 +1651,12 @@ Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offs
|
|||||||
return operand;
|
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 *base_ptr = base_type(ptr->type); GB_ASSERT(base_ptr->kind == Type_Pointer);
|
||||||
Type *elem = base_ptr->Pointer.elem;
|
Type *elem = base_ptr->Pointer.elem;
|
||||||
i64 elem_size = type_size_of(c->allocator, 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;
|
return operand;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+50
-109
@@ -1165,135 +1165,76 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool first_is_field_value = false;
|
// bool first_is_field_value = false;
|
||||||
if (rs->results.count > 0) {
|
// if (rs->results.count > 0) {
|
||||||
bool fail = false;
|
// bool fail = false;
|
||||||
first_is_field_value = (rs->results[0]->kind == AstNode_FieldValue);
|
// first_is_field_value = (rs->results[0]->kind == AstNode_FieldValue);
|
||||||
for_array(i, rs->results) {
|
// for_array(i, rs->results) {
|
||||||
AstNode *arg = rs->results[i];
|
// AstNode *arg = rs->results[i];
|
||||||
bool mix = false;
|
// bool mix = false;
|
||||||
if (first_is_field_value) {
|
// if (first_is_field_value) {
|
||||||
mix = arg->kind != AstNode_FieldValue;
|
// mix = arg->kind != AstNode_FieldValue;
|
||||||
} else {
|
// } else {
|
||||||
mix = arg->kind == AstNode_FieldValue;
|
// mix = arg->kind == AstNode_FieldValue;
|
||||||
}
|
// }
|
||||||
if (mix) {
|
// if (mix) {
|
||||||
error(arg, "Mixture of 'field = value' and value elements in a procedure all is not allowed");
|
// error(arg, "Mixture of 'field = value' and value elements in a procedure all is not allowed");
|
||||||
fail = true;
|
// fail = true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (fail) {
|
// if (fail) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
Type *proc_type = c->proc_stack[c->proc_stack.count-1];
|
Type *proc_type = c->proc_stack[c->proc_stack.count-1];
|
||||||
TypeProc *pt = &proc_type->Proc;
|
TypeProc *pt = &proc_type->Proc;
|
||||||
isize result_count = 0;
|
isize result_count = 0;
|
||||||
|
bool has_named_results = pt->has_named_results;
|
||||||
if (pt->results) {
|
if (pt->results) {
|
||||||
result_count = proc_type->Proc.results->Tuple.variables.count;
|
result_count = proc_type->Proc.results->Tuple.variables.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
isize result_count_excluding_defaults = result_count;
|
// isize result_count_excluding_defaults = result_count;
|
||||||
for (isize i = result_count-1; i >= 0; i--) {
|
// for (isize i = result_count-1; i >= 0; i--) {
|
||||||
Entity *e = pt->results->Tuple.variables[i];
|
// Entity *e = pt->results->Tuple.variables[i];
|
||||||
if (e->kind == Entity_TypeName) {
|
// if (e->kind == Entity_TypeName) {
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
GB_ASSERT(e->kind == Entity_Variable);
|
// GB_ASSERT(e->kind == Entity_Variable);
|
||||||
if (e->Variable.default_value.kind != ExactValue_Invalid ||
|
// if (e->Variable.default_value.kind != ExactValue_Invalid ||
|
||||||
e->Variable.default_is_nil) {
|
// e->Variable.default_is_nil) {
|
||||||
result_count_excluding_defaults--;
|
// result_count_excluding_defaults--;
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
Array<Operand> operands = {};
|
Array<Operand> operands = {};
|
||||||
defer (array_free(&operands));
|
defer (array_free(&operands));
|
||||||
|
|
||||||
if (first_is_field_value) {
|
// if (first_is_field_value) {
|
||||||
array_init_count(&operands, heap_allocator(), rs->results.count);
|
// array_init_count(&operands, heap_allocator(), rs->results.count);
|
||||||
for_array(i, rs->results) {
|
// for_array(i, rs->results) {
|
||||||
AstNode *arg = rs->results[i];
|
// AstNode *arg = rs->results[i];
|
||||||
ast_node(fv, FieldValue, arg);
|
// ast_node(fv, FieldValue, arg);
|
||||||
check_expr(c, &operands[i], fv->value);
|
// check_expr(c, &operands[i], fv->value);
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
array_init(&operands, heap_allocator(), 2*rs->results.count);
|
array_init(&operands, heap_allocator(), 2*rs->results.count);
|
||||||
check_unpack_arguments(c, nullptr, -1, &operands, rs->results, false);
|
check_unpack_arguments(c, nullptr, -1, &operands, rs->results, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
if (result_count == 0 && rs->results.count > 0) {
|
||||||
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) {
|
|
||||||
error(rs->results[0], "No return values expected");
|
error(rs->results[0], "No return values expected");
|
||||||
} else if (operands.count > result_count) {
|
} else if (has_named_results && operands.count == 0) {
|
||||||
if (result_count_excluding_defaults < result_count) {
|
// Okay
|
||||||
error(node, "Expected a maximum of %td return values, got %td", result_count, operands.count);
|
} else if (operands.count != result_count) {
|
||||||
} else {
|
error(node, "Expected %td return values, got %td", result_count, operands.count);
|
||||||
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 {
|
} else {
|
||||||
isize max_count = rs->results.count;
|
isize max_count = rs->results.count;
|
||||||
for (isize i = 0; i < max_count; i++) {
|
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;
|
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);
|
Entity *param = make_entity_param(c->allocator, scope, token, type, false, false);
|
||||||
|
param->flags |= EntityFlag_Result;
|
||||||
param->Variable.default_value = value;
|
param->Variable.default_value = value;
|
||||||
param->Variable.default_is_nil = default_is_nil;
|
param->Variable.default_is_nil = default_is_nil;
|
||||||
array_add(&variables, param);
|
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;
|
ProcCallingConvention cc = pt->calling_convention;
|
||||||
if (cc == ProcCC_ForeignBlockDefault) {
|
if (cc == ProcCC_ForeignBlockDefault) {
|
||||||
cc = ProcCC_CDecl;
|
cc = ProcCC_CDecl;
|
||||||
|
|||||||
+4
-376
@@ -1,150 +1,6 @@
|
|||||||
#include "entity.cpp"
|
#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"
|
#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) {
|
bool is_operand_value(Operand o) {
|
||||||
switch (o.mode) {
|
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;
|
gb_global Scope *universal_scope = nullptr;
|
||||||
|
|
||||||
void scope_reset(Scope *scope) {
|
void scope_reset(Scope *scope) {
|
||||||
@@ -257,7 +40,7 @@ i32 is_scope_an_ancestor(Scope *parent, Scope *child) {
|
|||||||
isize i = 0;
|
isize i = 0;
|
||||||
while (child != nullptr) {
|
while (child != nullptr) {
|
||||||
if (parent == child) {
|
if (parent == child) {
|
||||||
return true;
|
return i;
|
||||||
}
|
}
|
||||||
child = child->parent;
|
child = child->parent;
|
||||||
i++;
|
i++;
|
||||||
@@ -265,18 +48,6 @@ i32 is_scope_an_ancestor(Scope *parent, Scope *child) {
|
|||||||
return -1;
|
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) {
|
void entity_graph_node_set_destroy(EntityGraphNodeSet *s) {
|
||||||
if (s->hashes.data != nullptr) {
|
if (s->hashes.data != nullptr) {
|
||||||
ptr_set_destroy(s);
|
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) {
|
void import_graph_node_set_destroy(ImportGraphNodeSet *s) {
|
||||||
if (s->hashes.data != nullptr) {
|
if (s->hashes.data != nullptr) {
|
||||||
ptr_set_destroy(s);
|
ptr_set_destroy(s);
|
||||||
@@ -350,16 +118,6 @@ void import_graph_node_set_remove(ImportGraphNodeSet *s, ImportGraphNode *n) {
|
|||||||
ptr_set_remove(s, 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 *import_graph_node_create(gbAllocator a, Scope *scope) {
|
||||||
ImportGraphNode *n = gb_alloc_item(a, ImportGraphNode);
|
ImportGraphNode *n = gb_alloc_item(a, ImportGraphNode);
|
||||||
n->scope = scope;
|
n->scope = scope;
|
||||||
@@ -368,7 +126,6 @@ ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void import_graph_node_destroy(ImportGraphNode *n, gbAllocator a) {
|
void import_graph_node_destroy(ImportGraphNode *n, gbAllocator a) {
|
||||||
import_graph_node_set_destroy(&n->pred);
|
import_graph_node_set_destroy(&n->pred);
|
||||||
import_graph_node_set_destroy(&n->succ);
|
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);
|
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) {
|
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) {
|
Entity *scope_insert_entity(Scope *s, Entity *entity) {
|
||||||
String name = entity->token.string;
|
String name = entity->token.string;
|
||||||
|
if (name == "") {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
HashKey key = hash_string(name);
|
HashKey key = hash_string(name);
|
||||||
Entity **found = map_get(&s->elements, key);
|
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) {
|
DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
|
||||||
if (name == "default_calling_convention") {
|
if (name == "default_calling_convention") {
|
||||||
if (value.kind == ExactValue_String) {
|
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_Using = 1<<2,
|
||||||
EntityFlag_Field = 1<<3,
|
EntityFlag_Field = 1<<3,
|
||||||
EntityFlag_Param = 1<<4,
|
EntityFlag_Param = 1<<4,
|
||||||
EntityFlag_ArrayElem = 1<<5,
|
EntityFlag_Result = 1<<5,
|
||||||
EntityFlag_Ellipsis = 1<<6,
|
EntityFlag_ArrayElem = 1<<6,
|
||||||
EntityFlag_NoAlias = 1<<7,
|
EntityFlag_Ellipsis = 1<<7,
|
||||||
EntityFlag_TypeField = 1<<8,
|
EntityFlag_NoAlias = 1<<8,
|
||||||
EntityFlag_Value = 1<<9,
|
EntityFlag_TypeField = 1<<9,
|
||||||
EntityFlag_Sret = 1<<10,
|
EntityFlag_Value = 1<<10,
|
||||||
EntityFlag_BitFieldValue = 1<<11,
|
EntityFlag_Sret = 1<<11,
|
||||||
EntityFlag_PolyConst = 1<<12,
|
EntityFlag_BitFieldValue = 1<<12,
|
||||||
|
EntityFlag_PolyConst = 1<<13,
|
||||||
|
|
||||||
EntityFlag_CVarArg = 1<<20,
|
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 return_count = proc->type->Proc.result_count;
|
||||||
isize res_count = rs->results.count;
|
isize res_count = rs->results.count;
|
||||||
|
|
||||||
if (res_count > 0 &&
|
if (return_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) {
|
|
||||||
// No return values
|
// No return values
|
||||||
} else if (return_count == 1) {
|
} else if (return_count == 1) {
|
||||||
Entity *e = tuple->variables[0];
|
Entity *e = tuple->variables[0];
|
||||||
if (res_count == 0) {
|
if (res_count == 0) {
|
||||||
if (e->Variable.default_value.kind != ExactValue_Invalid) {
|
irValue **found = map_get(&proc->module->values, hash_entity(e));
|
||||||
v = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value);
|
GB_ASSERT(found);
|
||||||
} else {
|
v = ir_emit_load(proc, *found);
|
||||||
v = ir_value_nil(proc->module->allocator, e->type);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
v = ir_build_expr(proc, rs->results[0]);
|
v = ir_build_expr(proc, rs->results[0]);
|
||||||
v = ir_emit_conv(proc, v, e->type);
|
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<irValue *> results;
|
||||||
array_init(&results, proc->module->tmp_allocator, return_count);
|
array_init(&results, proc->module->tmp_allocator, return_count);
|
||||||
|
|
||||||
isize total_index = 0;
|
if (res_count != 0) {
|
||||||
isize res_index = 0;
|
for (isize res_index = 0; res_index < res_count; res_index++) {
|
||||||
for (; res_index < res_count; res_index++) {
|
irValue *res = ir_build_expr(proc, rs->results[res_index]);
|
||||||
irValue *res = ir_build_expr(proc, rs->results[res_index]);
|
Type *t = ir_type(res);
|
||||||
Type *t = ir_type(res);
|
if (t->kind == Type_Tuple) {
|
||||||
if (t->kind == Type_Tuple) {
|
for_array(i, t->Tuple.variables) {
|
||||||
for_array(i, t->Tuple.variables) {
|
Entity *e = t->Tuple.variables[i];
|
||||||
Entity *e = t->Tuple.variables[i];
|
irValue *v = ir_emit_struct_ev(proc, res, cast(i32)i);
|
||||||
irValue *v = ir_emit_struct_ev(proc, res, cast(i32)i);
|
array_add(&results, v);
|
||||||
array_add(&results, v);
|
}
|
||||||
total_index++;
|
} 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);
|
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);
|
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);
|
v = ir_emit_load(proc, v);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ir_emit_return(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) {
|
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);
|
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_ARRAY_BOUNDS_CHECK
|
||||||
|
// #define NO_POINTER_ARITHMETIC
|
||||||
|
|
||||||
#include "common.cpp"
|
#include "common.cpp"
|
||||||
#include "timings.cpp"
|
#include "timings.cpp"
|
||||||
#include "build_settings.cpp"
|
#include "build_settings.cpp"
|
||||||
#include "tokenizer.cpp"
|
#include "tokenizer.cpp"
|
||||||
#include "exact_value.cpp"
|
#include "exact_value.cpp"
|
||||||
|
|
||||||
|
#include "parser.hpp"
|
||||||
|
#include "checker.hpp"
|
||||||
|
|
||||||
#include "parser.cpp"
|
#include "parser.cpp"
|
||||||
#include "docs.cpp"
|
#include "docs.cpp"
|
||||||
#include "checker.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);
|
// gb_printf_err("%.*s\n", cast(int)cmd_len, cmd_line);
|
||||||
|
|
||||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
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));
|
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;
|
exit_code = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_temp_arena_memory_end(tmp);
|
|
||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||||
@@ -386,12 +391,10 @@ bool parse_build_flags(Array<String> args) {
|
|||||||
case BuildFlag_CrossCompile: {
|
case BuildFlag_CrossCompile: {
|
||||||
GB_ASSERT(value.kind == ExactValue_String);
|
GB_ASSERT(value.kind == ExactValue_String);
|
||||||
cross_compile_target = value.value_string;
|
cross_compile_target = value.value_string;
|
||||||
#ifdef GB_SYSTEM_UNIX
|
#if defined(GB_SYSTEM_UNIX) && defined(GB_ARCH_64_BIT)
|
||||||
#ifdef GB_ARCH_64_BIT
|
|
||||||
if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) {
|
if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) {
|
||||||
|
|
||||||
} else
|
} else
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
gb_printf_err("Unsupported cross compilation target '%.*s'\n", LIT(cross_compile_target));
|
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) {
|
Token ast_node_token(AstNode *node) {
|
||||||
switch (node->kind) {
|
switch (node->kind) {
|
||||||
case AstNode_Ident: return node->Ident.token;
|
case AstNode_Ident: return node->Ident.token;
|
||||||
@@ -3135,7 +2595,7 @@ AstNode *parse_results(AstFile *f) {
|
|||||||
|
|
||||||
AstNode *list = nullptr;
|
AstNode *list = nullptr;
|
||||||
expect_token(f, Token_OpenParen);
|
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");
|
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -3547,6 +3007,10 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
|||||||
Array<AstNode *> names = {};
|
Array<AstNode *> names = {};
|
||||||
AstNode *type = list[i].node;
|
AstNode *type = list[i].node;
|
||||||
Token token = blank_token;
|
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);
|
array_init_count(&names, heap_allocator(), 1);
|
||||||
token.pos = ast_node_token(type).pos;
|
token.pos = ast_node_token(type).pos;
|
||||||
@@ -3725,11 +3189,11 @@ AstNode *parse_return_stmt(AstFile *f) {
|
|||||||
|
|
||||||
while (f->curr_token.kind != Token_Semicolon) {
|
while (f->curr_token.kind != Token_Semicolon) {
|
||||||
AstNode *arg = parse_expr(f, false);
|
AstNode *arg = parse_expr(f, false);
|
||||||
if (f->curr_token.kind == Token_Eq) {
|
// if (f->curr_token.kind == Token_Eq) {
|
||||||
Token eq = expect_token(f, Token_Eq);
|
// Token eq = expect_token(f, Token_Eq);
|
||||||
AstNode *value = parse_value(f);
|
// AstNode *value = parse_value(f);
|
||||||
arg = ast_field_value(f, arg, value, eq);
|
// arg = ast_field_value(f, arg, value, eq);
|
||||||
}
|
// }
|
||||||
|
|
||||||
array_add(&results, arg);
|
array_add(&results, arg);
|
||||||
if (f->curr_token.kind != Token_Comma ||
|
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_polymorphic; \
|
||||||
bool is_poly_specialized; \
|
bool is_poly_specialized; \
|
||||||
bool has_proc_default_values; \
|
bool has_proc_default_values; \
|
||||||
|
bool has_named_results; \
|
||||||
isize specialization_count; \
|
isize specialization_count; \
|
||||||
ProcCallingConvention calling_convention; \
|
ProcCallingConvention calling_convention; \
|
||||||
}) \
|
}) \
|
||||||
|
|||||||
Reference in New Issue
Block a user