"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/onyx",
- "args": ["progs/test.onyx"],
+ "args": ["progs/basic.onyx"],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
typedef struct AstArgument AstArgument;
typedef struct AstUse AstUse;
+typedef struct AstType AstType;
+typedef struct AstBasicType AstBasicType;
+typedef struct AstPointerType AstPointerType;
+
typedef enum AstKind {
Ast_Kind_Error,
Ast_Kind_Program,
Ast_Kind_Binary_Op,
Ast_Kind_Type,
+ Ast_Kind_Basic_Type,
+ Ast_Kind_Pointer_Type,
+
Ast_Kind_Literal,
Ast_Kind_Param,
Ast_Kind_Argument,
Binary_Op_Greater_Equal = 10,
} BinaryOp;
+
+// Base Nodes
+
// NOTE: AstNode and AstTyped need to be EXACTLY the same for all
// arguments existing in AstNode. I do this to avoid a nested
// "inheiritance" where you would have to say node.base.base.next
u32 flags;
OnyxToken *token;
AstNode *next;
- Type *type;
-};
-
-struct AstBinOp {
- AstTyped base;
- BinaryOp operation;
-
- AstTyped *left, *right;
-};
-
-struct AstUnaryOp {
- AstTyped base;
-
- UnaryOp operation;
-
- AstTyped *expr;
-};
-
-struct AstAssign {
- AstNode base;
-
- AstTyped* lval;
- AstTyped* expr;
-};
-
-struct AstNumLit {
- AstTyped base;
-
- union { i32 i; i64 l; f32 f; f64 d; } value;
-};
-
-struct AstLocal {
- AstTyped base;
-
- AstLocal *prev_local;
-};
-
-struct AstReturn {
- AstNode base;
-
- AstTyped* expr;
-};
-
-struct AstLocalGroup {
- AstNode base;
-
- AstLocalGroup *prev_group;
- AstLocal *last_local;
-};
-
-struct AstBlock {
- AstNode base;
-
- AstNode *body;
- AstLocalGroup *locals;
+ // NOTE: 'type_node' is filled out by the parser.
+ // For a type such as '^^i32', the tree would look something like
+ //
+ // Typed Thing -> AstPointerType -> AstPointerType -> AstNode (symbol node)
+ //
+ // The symbol node will be filled out during symbol resolution.
+ // It will end up pointing to an AstBasicType that corresponds to
+ // the underlying type.
+ //
+ // 'type' is filled out afterwards. If it is NULL, the Type* is built
+ // using the type_node. This can then be used to typecheck this node.
+ AstType *type_node;
+ Type *type;
};
+// Expression Nodes
+struct AstBinOp { AstTyped base; BinaryOp operation; AstTyped *left, *right; };
+struct AstUnaryOp { AstTyped base; UnaryOp operation; AstTyped *expr; };
+struct AstAssign { AstNode base; AstTyped* lval; AstTyped* expr; };
+struct AstNumLit { AstTyped base; union { i32 i; i64 l; f32 f; f64 d; } value; };
+struct AstLocal { AstTyped base; AstLocal *prev_local; };
+struct AstReturn { AstNode base; AstTyped* expr; };
+struct AstCall { AstTyped base; AstArgument *arguments; AstNode *callee; };
+struct AstArgument { AstTyped base; AstTyped *value; };
+
+// Structure Nodes
+struct AstLocalGroup { AstNode base; AstLocalGroup *prev_group; AstLocal *last_local; };
+struct AstBlock { AstNode base; AstNode *body; AstLocalGroup *locals; };
+struct AstWhile { AstNode base; AstTyped *cond; AstBlock *body; };
struct AstIf {
AstNode base;
-
AstTyped *cond;
union {
} true_block, false_block;
};
-struct AstWhile {
- AstNode base;
-
- AstTyped *cond;
- AstBlock *body;
-};
-
-struct AstFunction {
- AstTyped base;
-
- AstBlock *body;
- AstLocal *params;
-};
-
-struct AstForeign {
- AstNode base;
-
- OnyxToken *mod_token, *name_token;
- AstNode *import;
-};
-
-struct AstGlobal {
- AstTyped base;
-
- AstTyped *initial_value;
-};
-
-struct AstCall {
- AstTyped base;
-
- AstArgument *arguments; // NOTE: Expressions that form the actual param list
- // They will be chained down using the "next" property
- // unless this becomes used by something else
- AstNode *callee; // NOTE: Function definition node
-};
-
-struct AstArgument {
- AstTyped base;
-
- AstTyped *value;
-};
+// Type Nodes
+// NOTE: This node is very similar to an AstNode, just
+// without the 'next' member. This is because types
+// can't be in expressions so a 'next' thing
+// doesn't make sense.
+struct AstType { AstKind kind; u32 flags; char* name; };
+struct AstBasicType { AstType base; Type* type; };
+struct AstPointerType { AstType base; AstType* elem; };
+
+// Top level nodes
+struct AstFunction { AstTyped base; AstBlock *body; AstLocal *params; };
+struct AstForeign { AstNode base; OnyxToken *mod_token, *name_token; AstNode *import; };
+struct AstGlobal { AstTyped base; AstTyped *initial_value; };
+struct AstUse { AstNode base; OnyxToken *filename; };
typedef enum OnyxIntrinsic {
ONYX_INTRINSIC_UNDEFINED,
OnyxIntrinsic intrinsic;
};
-struct AstUse {
- AstNode base;
-
- OnyxToken *filename;
-};
-
typedef struct OnyxProgram {
bh_arr(AstGlobal *) globals;
bh_arr(AstFunction *) functions;
bh_arr(AstForeign *) foreigns;
} OnyxProgram;
+
+
+
+
+// NOTE: Basic internal types constructed in the parser
+extern AstBasicType basic_type_void;
+extern AstBasicType basic_type_bool;
+extern AstBasicType basic_type_i8;
+extern AstBasicType basic_type_u8;
+extern AstBasicType basic_type_i16;
+extern AstBasicType basic_type_u16;
+extern AstBasicType basic_type_i32;
+extern AstBasicType basic_type_u32;
+extern AstBasicType basic_type_i64;
+extern AstBasicType basic_type_u64;
+extern AstBasicType basic_type_f32;
+extern AstBasicType basic_type_f64;
+extern AstBasicType basic_type_rawptr;
+
#endif // #ifndef ONYXASTNODES_H
OnyxToken *prev_token;
OnyxToken *curr_token;
- // NOTE: Identifiers currently is only used to resolve type names
- // at parse time, since these are the only symbols we know.
- bh_table(AstNode *) identifiers;
OnyxMessages *msgs;
bh_allocator allocator;
AstLocalGroup* curr_local_group;
// NOTE: Used in type checking phase
- TypeInfo* expected_return_type;
+ Type* expected_return_type;
// NOTE: All symbols a given point that we can resolve
bh_table(SemPassSymbol *) symbols;
#define TYPE_KINDS \
TYPE_KIND(Basic, TypeBasic) \
- TYPE_KIND(Pointer, struct { Type *elem; })
+ TYPE_KIND(Pointer, struct { TypeBasic base; Type *elem; })
typedef enum TypeKind {
Type_Kind_Invalid,
extern Type basic_types[];
+struct AstType;
+b32 types_are_compatible(Type* t1, Type* t2);
+Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node);
#endif // #ifndef ONYX_TYPES
--- /dev/null
+use "progs/intrinsics"
+
+test :: proc (a: bool) -> i32 {
+ return 0;
+}
+
+export main :: proc {
+ a : i32 = 0;
+}
static void check_if(OnyxSemPassState* state, AstIf* ifnode);
static void check_while(OnyxSemPassState* state, AstWhile* whilenode);
static void check_call(OnyxSemPassState* state, AstCall* call);
+static void check_binaryop(OnyxSemPassState* state, AstBinaryOp* binop);
static void check_expression(OnyxSemPassState* state, AstTyped* expr);
static void check_global(OnyxSemPassState* state, AstGlobal* global);
return;
}
- if ((assign->lval->flags & Ast_Flag_Const) != 0 && assign->lval->type->is_known) {
+ if (assign->lval->type == NULL) {
+ assign->lval->type = type_build_from_ast(state->node_allocator, assign->lval->type_node);
+ }
+
+ if ((assign->lval->flags & Ast_Flag_Const) != 0 && assign->lval->type != NULL) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_ASSIGN_CONST,
assign->base.token->pos,
check_expression(state, assign->expr);
- if (!assign->lval->type->is_known) {
+ if (assign->lval->type == NULL) {
assign->lval->type = assign->expr->type;
} else {
- if (assign->lval->type != assign->expr->type) {
+ if (!types_are_compatible(assign->lval->type, assign->expr->type)) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH,
assign->base.token->pos,
- assign->lval->type->name, assign->expr->type->name);
+ "TEMP", "TEMP");
return;
}
}
if (retnode->expr) {
check_expression(state, retnode->expr);
- if (retnode->expr->type != state->expected_return_type) {
+ if (!types_are_compatible(retnode->expr->type, state->expected_return_type)) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_FUNCTION_RETURN_MISMATCH,
retnode->expr->token->pos,
- retnode->expr->type->name, state->expected_return_type->name);
+ "TEMP", "TEMP");
}
} else {
- if (state->expected_return_type->size > 0) {
+ if (state->expected_return_type->Basic.size > 0) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_LITERAL,
retnode->base.token->pos,
static void check_if(OnyxSemPassState* state, AstIf* ifnode) {
check_expression(state, ifnode->cond);
- if (ifnode->cond->type != &builtin_types[TYPE_INFO_KIND_BOOL]) {
+
+ if (ifnode->cond->type->kind != Type_Kind_Basic
+ || ifnode->cond->type->Basic.kind != Basic_Kind_Bool) {
+
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_LITERAL,
ifnode->cond->token->pos,
return;
}
- if (ifnode->true_block.as_if) check_statement(state, (AstNode *) ifnode->true_block.as_block);
+ if (ifnode->true_block.as_if) check_statement(state, (AstNode *) ifnode->true_block.as_block);
if (ifnode->false_block.as_if) check_statement(state, (AstNode *) ifnode->false_block.as_block);
}
static void check_while(OnyxSemPassState* state, AstWhile* whilenode) {
check_expression(state, whilenode->cond);
- if (whilenode->cond->type != &builtin_types[TYPE_INFO_KIND_BOOL]) {
+
+ if (whilenode->cond->type->kind != Type_Kind_Basic
+ || whilenode->cond->type->Basic.kind != Basic_Kind_Bool) {
+
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_LITERAL,
whilenode->cond->token->pos,
onyx_token_null_toggle(callee->base.token);
}
+ if (callee->base.type == NULL) {
+ callee->base.type = type_build_from_ast(state->node_allocator, callee->base.type_node);
+ }
call->base.type = callee->base.type;
AstLocal* formal_param = callee->params;
while (formal_param != NULL && actual_param != NULL) {
check_expression(state, (AstTyped *) actual_param);
- if (formal_param->base.type != actual_param->base.type) {
+ if (formal_param->base.type == NULL) {
+ formal_param->base.type = type_build_from_ast(state->node_allocator, formal_param->base.type_node);
+ }
+
+ if (!types_are_compatible(formal_param->base.type, actual_param->base.type)) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_FUNCTION_PARAM_TYPE_MISMATCH,
actual_param->value->token->pos,
callee->base.token->text, callee->base.token->length,
- formal_param->base.type->name, arg_pos,
- actual_param->base.type->name);
+ "TEMP", arg_pos,
+ "TEMP");
return;
}
}
}
-static void check_expression(OnyxSemPassState* state, AstTyped* expr) {
- switch (expr->kind) {
- case Ast_Kind_Binary_Op:
- expr->type = &builtin_types[TYPE_INFO_KIND_UNKNOWN];
+static void check_binaryop(OnyxSemPassState* state, AstBinaryOp* binop) {
+ check_expression(state, binop->left);
+ check_expression(state, binop->right);
- check_expression(state, ((AstBinaryOp *) expr)->left);
- check_expression(state, ((AstBinaryOp *) expr)->right);
+ if (binop->left->type == NULL) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE,
+ binop->base.token->pos,
+ NULL, 0);
+ return;
+ }
- if (((AstBinaryOp *) expr)->left->type == NULL) {
- onyx_message_add(state->msgs,
- ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE,
- expr->token->pos,
- NULL, 0);
- return;
- }
+ if (binop->right->type == NULL) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE,
+ binop->base.token->pos,
+ NULL, 0);
+ return;
+ }
- if (((AstBinaryOp *) expr)->right->type == NULL) {
- onyx_message_add(state->msgs,
- ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE,
- expr->token->pos,
- NULL, 0);
- return;
- }
+ if (binop->left->type->kind == Type_Kind_Pointer
+ || binop->right->type->kind == Type_Kind_Pointer) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_LITERAL,
+ binop->base.token->pos,
+ "binary operations are not supported for pointers (yet).");
+ return;
+ }
- if (((AstBinaryOp *) expr)->left->type != ((AstBinaryOp *) expr)->right->type) {
- onyx_message_add(state->msgs,
- ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE,
- expr->token->pos,
- ((AstBinaryOp *) expr)->left->type->name,
- ((AstBinaryOp *) expr)->right->type->name);
- return;
- }
+ if (!types_are_compatible(binop->left->type, binop->right->type)) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE,
+ binop->base.token->pos,
+ "TEMP", "TEMP");
+ return;
+ }
- if (((AstBinaryOp *) expr)->operation >= Binary_Op_Equal
- && ((AstBinaryOp *) expr)->operation <= Binary_Op_Greater_Equal) {
- expr->type = &builtin_types[TYPE_INFO_KIND_BOOL];
- } else {
- expr->type = ((AstBinaryOp *) expr)->left->type;
- }
+ if (binop->operation >= Binary_Op_Equal
+ && binop->operation <= Binary_Op_Greater_Equal) {
+ binop->base.type = &basic_types[Basic_Kind_Bool];
+ } else {
+ binop->base.type = binop->left->type;
+ }
+}
+static void check_expression(OnyxSemPassState* state, AstTyped* expr) {
+ if (expr->type == NULL) {
+ expr->type = type_build_from_ast(state->node_allocator, expr->type_node);
+ }
+
+ switch (expr->kind) {
+ case Ast_Kind_Binary_Op:
+ check_binaryop(state, (AstBinaryOp *) expr);
break;
case Ast_Kind_Unary_Op:
case Ast_Kind_Local:
case Ast_Kind_Param:
- if (!expr->type->is_known) {
+ if (expr->type == NULL) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_LITERAL,
expr->token->pos,
break;
case Ast_Kind_Global:
- if (!expr->type->is_known) {
+ if (expr->type == NULL) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_LITERAL,
expr->token->pos,
case Ast_Kind_Literal:
// NOTE: Literal types should have been decided
// in the parser (for now).
- assert(expr->type->is_known);
+ assert(expr->type != NULL);
break;
default:
if (global->initial_value) {
check_expression(state, global->initial_value);
- if (global->base.type->is_known) {
- if (global->base.type != global->initial_value->type) {
+ if (global->base.type == NULL) {
+ global->base.type = type_build_from_ast(state->node_allocator, global->base.type_node);
+ }
+
+ if (global->base.type != NULL) {
+ if (!types_are_compatible(global->base.type, global->initial_value->type)) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_GLOBAL_TYPE_MISMATCH,
global->base.token->pos,
global->base.token->text, global->base.token->length,
- global->base.type->name, global->initial_value->type->name);
+ "TEMP", "TEMP");
return;
}
} else {
}
} else {
- if (!global->base.type || !global->base.type->is_known) {
+ if (global->base.type == NULL) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_LITERAL,
global->base.token->pos,
check_statement_chain(state, block->body);
forll(AstLocal, local, block->locals->last_local, prev_local) {
- if (!local->base.type->is_known) {
+ if (local->base.type == NULL) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE,
local->base.token->pos,
static void check_function(OnyxSemPassState* state, AstFunction* func) {
for (AstLocal *param = func->params; param != NULL; param = (AstLocal *) param->base.next) {
- if (!param->base.type->is_known) {
+ if (param->base.type == NULL) {
+ param->base.type = type_build_from_ast(state->node_allocator, param->base.type_node);
+ }
+
+ if (param->base.type == NULL) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_LITERAL,
param->base.token->pos,
return;
}
- if (param->base.type->size == 0) {
+ if (param->base.type->Basic.size == 0) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_LITERAL,
param->base.token->pos,
}
}
+ if (func->base.type == NULL) {
+ func->base.type = type_build_from_ast(state->node_allocator, func->base.type_node);
+ }
+
state->expected_return_type = func->base.type;
if (func->body) {
check_block(state, func->body);
void onyx_message_print(OnyxMessages* msgs) {
OnyxMessage* msg = msgs->first;
- i32 msg_count = 3;
+ i32 msg_count = 1000;
while (msg && msg_count-- > 0) {
if (msg->pos.filename) {
// NOTE: The one weird define you need to know before read the code below
#define make_node(nclass, kind) onyx_ast_node_new(parser->allocator, sizeof(nclass), kind)
-struct TypeInfo builtin_types[] = {
- { TYPE_INFO_KIND_UNKNOWN, 0, "unknown" },
- { TYPE_INFO_KIND_VOID, 0, "void", 0, 0, 0, 0, 1 },
-
- { TYPE_INFO_KIND_BOOL, 1, "bool", 0, 1, 0, 1, 1 },
-
- { TYPE_INFO_KIND_UINT32, 4, "u32", 1, 1, 0, 0, 1 },
- { TYPE_INFO_KIND_UINT64, 8, "u64", 1, 1, 0, 0, 1 },
-
- { TYPE_INFO_KIND_INT32, 4, "i32", 1, 0, 0, 0, 1 },
- { TYPE_INFO_KIND_INT64, 8, "i64", 1, 0, 0, 0, 1 },
-
- { TYPE_INFO_KIND_FLOAT32, 4, "f32", 0, 0, 1, 0, 1 },
- { TYPE_INFO_KIND_FLOAT64, 8, "f64", 0, 0, 1, 0, 1},
- { TYPE_INFO_KIND_SOFT_FLOAT, 8, "sf64", 0, 0, 1, 0, 1 },
-
- { 0xffffffff } // Sentinel
-};
-
static AstNode error_node = { Ast_Kind_Error, 0, NULL, NULL };
+AstBasicType basic_type_void = { { Ast_Kind_Basic_Type, 0, "void" }, &basic_types[Basic_Kind_Void] };
+AstBasicType basic_type_bool = { { Ast_Kind_Basic_Type, 0, "bool" }, &basic_types[Basic_Kind_Bool] };
+AstBasicType basic_type_i8 = { { Ast_Kind_Basic_Type, 0, "i8" }, &basic_types[Basic_Kind_I8] };
+AstBasicType basic_type_u8 = { { Ast_Kind_Basic_Type, 0, "u8" }, &basic_types[Basic_Kind_U8] };
+AstBasicType basic_type_i16 = { { Ast_Kind_Basic_Type, 0, "i16" }, &basic_types[Basic_Kind_I16] };
+AstBasicType basic_type_u16 = { { Ast_Kind_Basic_Type, 0, "u16" }, &basic_types[Basic_Kind_U16] };
+AstBasicType basic_type_i32 = { { Ast_Kind_Basic_Type, 0, "i32" }, &basic_types[Basic_Kind_I32] };
+AstBasicType basic_type_u32 = { { Ast_Kind_Basic_Type, 0, "u32" }, &basic_types[Basic_Kind_U32] };
+AstBasicType basic_type_i64 = { { Ast_Kind_Basic_Type, 0, "i64" }, &basic_types[Basic_Kind_I64] };
+AstBasicType basic_type_u64 = { { Ast_Kind_Basic_Type, 0, "u64" }, &basic_types[Basic_Kind_U64] };
+AstBasicType basic_type_f32 = { { Ast_Kind_Basic_Type, 0, "f32" }, &basic_types[Basic_Kind_F32] };
+AstBasicType basic_type_f64 = { { Ast_Kind_Basic_Type, 0, "f64" }, &basic_types[Basic_Kind_F64] };
+AstBasicType basic_type_rawptr = { { Ast_Kind_Basic_Type, 0, "rawptr" }, &basic_types[Basic_Kind_Rawptr] };
+
// NOTE: Forward declarations
static void parser_next_token(OnyxParser* parser);
static void parser_prev_token(OnyxParser* parser);
static AstReturn* parse_return_statement(OnyxParser* parser);
static AstBlock* parse_block(OnyxParser* parser);
static AstNode* parse_statement(OnyxParser* parser);
-static AstNode* parse_type(OnyxParser* parser);
+static AstType* parse_type(OnyxParser* parser);
static AstLocal* parse_function_params(OnyxParser* parser);
static AstFunction* parse_function_definition(OnyxParser* parser);
static AstNode* parse_top_level_statement(OnyxParser* parser);
lit_node->base.flags |= Ast_Flag_Comptime;
lit_node->value.l = 0ll;
+ AstType* type;
onyx_token_null_toggle(lit_node->base.token);
-
- TypeInfo* type;
char* tok = lit_node->base.token->text;
// NOTE: charset_contains() behaves more like string_contains()
// so I'm using it in this case
if (charset_contains(tok, '.')) {
if (tok[lit_node->base.token->length - 1] == 'f') {
- type = &builtin_types[TYPE_INFO_KIND_FLOAT32];
+ type = (AstType *) &basic_type_f32;
lit_node->value.f = strtof(tok, NULL);
} else {
- type = &builtin_types[TYPE_INFO_KIND_FLOAT64];
+ type = (AstType *) &basic_type_f64;
lit_node->value.d = strtod(tok, NULL);
}
} else {
i64 value = strtoll(tok, NULL, 0);
if (bh_abs(value) < ((u64) 1 << 32)) {
- type = &builtin_types[TYPE_INFO_KIND_INT32];
+ type = (AstType *) &basic_type_i32;
} else {
- type = &builtin_types[TYPE_INFO_KIND_INT64];
+ type = (AstType *) &basic_type_i64;
}
lit_node->value.l = value;
}
- lit_node->base.type = type;
+ lit_node->base.type_node = type;
onyx_token_null_toggle(lit_node->base.token);
return lit_node;
}
case Token_Type_Literal_True:
{
AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_Literal);
- bool_node->base.type = &builtin_types[TYPE_INFO_KIND_BOOL];
+ bool_node->base.type_node = (AstType *) &basic_type_i8;
bool_node->base.token = expect(parser, Token_Type_Literal_True);
bool_node->value.i = 1;
retval = (AstTyped *) bool_node;
case Token_Type_Literal_False:
{
AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_Literal);
- bool_node->base.type = &builtin_types[TYPE_INFO_KIND_BOOL];
+ bool_node->base.type_node = (AstType *) &basic_type_i8;
bool_node->base.token = expect(parser, Token_Type_Literal_False);
bool_node->value.i = 0;
retval = (AstTyped *) bool_node;
parser_next_token(parser);
AstUnaryOp* cast_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
- cast_node->base.type = parse_type(parser);
+ cast_node->base.type_node = parse_type(parser);
cast_node->operation = Unary_Op_Cast;
cast_node->expr = retval;
retval = (AstTyped *) cast_node;
case ':':
{
parser_next_token(parser);
- TypeInfo* type = &builtin_types[TYPE_INFO_KIND_UNKNOWN];
+ AstType* type_node = NULL;
// NOTE: var: type
if (parser->curr_token->type == Token_Type_Symbol) {
- type = parse_type(parser);
+ type_node = parse_type(parser);
}
AstLocal* local = make_node(AstLocal, Ast_Kind_Local);
local->base.token = symbol;
- local->base.type = type;
+ local->base.type_node = type_node;
local->base.flags |= Ast_Flag_Lval; // NOTE: DELETE
*ret = (AstNode *) local;
return block;
}
-static TypeInfo* parse_type(OnyxParser* parser) {
- TypeInfo* type_info = &builtin_types[TYPE_INFO_KIND_UNKNOWN];
+static AstType* parse_type(OnyxParser* parser) {
+ AstType* root = NULL;
+ AstType** next_insertion = &root;
- OnyxToken* symbol = expect(parser, Token_Type_Symbol);
- if (symbol == NULL) return type_info;
+ while (1) {
+ if (parser->curr_token->type == '^') {
+ AstPointerType* new = make_node(AstPointerType, Ast_Kind_Pointer_Type);
+ *next_insertion = (AstType *) new;
+ next_insertion = &new->elem;
+ }
- onyx_token_null_toggle(symbol);
+ else if (parser->curr_token->type == Token_Type_Symbol) {
+ AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
+ symbol_node->token = expect(parser, Token_Type_Symbol);
+ *next_insertion = (AstType *) symbol_node;
+ next_insertion = NULL;
+ }
- if (!bh_table_has(AstNode*, parser->identifiers, symbol->text)) {
- onyx_message_add(parser->msgs, ONYX_MESSAGE_TYPE_UNKNOWN_TYPE, symbol->pos, symbol->text);
- } else {
- AstTyped* type_info_node = bh_table_get(AstTyped*, parser->identifiers, symbol->text);
+ else {
+ onyx_token_null_toggle(parser->curr_token);
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
+ parser->curr_token->pos,
+ parser->curr_token->text);
+ onyx_token_null_toggle(parser->curr_token);
- if (type_info_node->kind == Ast_Kind_Type) {
- type_info = type_info_node->type;
+ parser_next_token(parser);
+ break;
}
+
+ if (next_insertion == NULL) break;
}
- onyx_token_null_toggle(symbol);
- return type_info;
+ return root;
}
static AstLocal* parse_function_params(OnyxParser* parser) {
curr_param = make_node(AstLocal, Ast_Kind_Param);
curr_param->base.token = symbol;
curr_param->base.flags |= Ast_Flag_Const;
- curr_param->base.type = parse_type(parser);
+ curr_param->base.type_node = parse_type(parser);
if (first_param == NULL) first_param = curr_param;
if (parser->curr_token->type == Token_Type_Right_Arrow) {
expect(parser, Token_Type_Right_Arrow);
- TypeInfo* return_type = parse_type(parser);
- func_def->base.type = return_type;
+ AstType* return_type = parse_type(parser);
+ func_def->base.type_node = return_type;
} else {
- func_def->base.type = &builtin_types[TYPE_INFO_KIND_VOID];
+ func_def->base.type_node = (AstType *) &basic_type_void;
}
func_def->body = parse_block(parser);
foreign->import = (AstNode *) parse_function_definition(parser);
} else {
- TypeInfo* type = parse_type(parser);
+ AstType* type = parse_type(parser);
AstGlobal* global = make_node(AstGlobal, Ast_Kind_Global);
- global->base.type = type;
+ global->base.type_node = type;
global->base.flags |= Ast_Flag_Lval;
foreign->import = (AstNode *) global;
// Global constant with initial value
AstGlobal* global = make_node(AstGlobal, Ast_Kind_Global);
global->initial_value = parse_expression(parser);
- global->base.type = &builtin_types[TYPE_INFO_KIND_UNKNOWN];
global->base.flags |= Ast_Flag_Const;
global->base.flags |= Ast_Flag_Lval;
global->base.flags |= Ast_Flag_Comptime;
expect(parser, ':');
- TypeInfo* type = &builtin_types[TYPE_INFO_KIND_UNKNOWN];
+ AstType* type = NULL;
if (parser->curr_token->type == Token_Type_Symbol) {
type = parse_type(parser);
AstNode* node = parse_top_level_constant_symbol(parser);
if (node->kind == Ast_Kind_Global) {
- ((AstGlobal *) node)->base.type = type;
+ ((AstGlobal *) node)->base.type_node = type;
}
if (node->kind == Ast_Kind_Foreign) {
global->base.token = symbol;
global->base.flags |= Ast_Flag_Lval;
global->initial_value = parse_expression(parser);
- global->base.type = type;
+ global->base.type_node = type;
return (AstNode *) global;
OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs) {
OnyxParser parser;
- bh_table_init(bh_heap_allocator(), parser.identifiers, 61);
-
- TypeInfo* it = &builtin_types[0];
- while (it->kind != 0xffffffff) {
- AstTyped* tmp = onyx_ast_node_new(alloc, sizeof(AstTyped), Ast_Kind_Type);
- tmp->type = it;
- bh_table_put(AstNode*, parser.identifiers, (char *)it->name, tmp);
- it++;
- }
-
parser.allocator = alloc;
parser.tokenizer = tokenizer;
parser.curr_token = tokenizer->tokens;
}
void onyx_parser_free(OnyxParser* parser) {
- bh_table_free(parser->identifiers);
}
bh_arr(AstNode *) onyx_parse(OnyxParser *parser) {
#include "onyxsempass.h"
static void symbol_introduce(OnyxSemPassState* state, AstNode* symbol);
+static void symbol_basic_type_introduce(OnyxSemPassState* state, AstBasicType* basic_type);
static b32 symbol_unique_introduce(OnyxSemPassState* state, AstNode* symbol);
static void symbol_remove(OnyxSemPassState* state, AstNode* symbol);
static AstNode* symbol_resolve(OnyxSemPassState* state, AstNode* symbol);
static void local_group_leave(OnyxSemPassState* state);
static void symres_local(OnyxSemPassState* state, AstLocal** local);
static void symres_call(OnyxSemPassState* state, AstCall* call);
-static void symres_expression(OnyxSemPassState* state, AstNode** expr);
+static void symres_expression(OnyxSemPassState* state, AstTyped** expr);
static void symres_assignment(OnyxSemPassState* state, AstAssign* assign);
static void symres_return(OnyxSemPassState* state, AstReturn* ret);
static void symres_if(OnyxSemPassState* state, AstIf* ifnode);
static b32 symres_statement(OnyxSemPassState* state, AstNode* stmt);
static void symres_block(OnyxSemPassState* state, AstBlock* block);
static void symres_function(OnyxSemPassState* state, AstFunction* func);
+static AstType* symres_type(OnyxSemPassState* state, AstType* type);
static void symbol_introduce(OnyxSemPassState* state, AstNode* symbol) {
onyx_token_null_toggle(symbol->token);
state->curr_local_group = state->curr_local_group->prev_group;
}
+static void symbol_basic_type_introduce(OnyxSemPassState* state, AstBasicType* basic_type) {
+ SemPassSymbol* sp_sym = bh_alloc_item(state->allocator, SemPassSymbol);
+ sp_sym->node = (AstNode *) basic_type;
+ sp_sym->shadowed = NULL;
+ bh_table_put(SemPassSymbol *, state->symbols, basic_type->base.name, sp_sym);
+}
+
static b32 symbol_unique_introduce(OnyxSemPassState* state, AstNode* symbol) {
onyx_token_null_toggle(symbol->token);
return 1;
}
+static AstType* symres_type(OnyxSemPassState* state, AstType* type) {
+ if (type == NULL) return NULL;
+
+ if (type->kind == Ast_Kind_Symbol) {
+ return (AstType *) symbol_resolve(state, (AstNode *) type);
+ }
+
+ // NOTE: Already resolved
+ if (type->kind == Ast_Kind_Basic_Type) return type;
+
+ if (type->kind == Ast_Kind_Pointer_Type) {
+ ((AstPointerType *) type)->elem = symres_type(state, ((AstPointerType *) type)->elem);
+ return type;
+ }
+
+ assert(("Bad type node", 0));
+ return NULL;
+}
+
static void symres_local(OnyxSemPassState* state, AstLocal** local) {
+ (*local)->base.type_node = symres_type(state, (*local)->base.type_node);
symbol_introduce(state, (AstNode *) *local);
}
symres_statement_chain(state, (AstNode *) call->arguments, (AstNode **) &call->arguments);
}
-static void symres_expression(OnyxSemPassState* state, AstNode** expr) {
+static void symres_unaryop(OnyxSemPassState* state, AstUnaryOp** unaryop) {
+ if ((*unaryop)->operation == Unary_Op_Cast) {
+ (*unaryop)->base.type_node = symres_type(state, (*unaryop)->base.type_node);
+ }
+
+ symres_expression(state, &(*unaryop)->expr);
+}
+
+static void symres_expression(OnyxSemPassState* state, AstTyped** expr) {
switch ((*expr)->kind) {
case Ast_Kind_Binary_Op:
- symres_expression(state, (AstNode **) &((AstBinaryOp *)(*expr))->left);
- symres_expression(state, (AstNode **) &((AstBinaryOp *)(*expr))->right);
+ symres_expression(state, &((AstBinaryOp *)(*expr))->left);
+ symres_expression(state, &((AstBinaryOp *)(*expr))->right);
break;
case Ast_Kind_Unary_Op:
- symres_expression(state, (AstNode **) &((AstUnaryOp *)(*expr))->expr);
+ symres_unaryop(state, (AstUnaryOp **) expr);
break;
case Ast_Kind_Call: symres_call(state, (AstCall *) *expr); break;
case Ast_Kind_Block: symres_block(state, (AstBlock *) *expr); break;
case Ast_Kind_Symbol:
- *expr = symbol_resolve(state, *expr);
+ *expr = (AstTyped *) symbol_resolve(state, (AstNode *) *expr);
break;
// NOTE: This is a good case, since it means the symbol is already resolved
case Ast_Kind_Local: break;
- case Ast_Kind_Literal: break;
+ case Ast_Kind_Literal:
+ (*expr)->type_node = symres_type(state, (*expr)->type_node);
+ break;
default:
DEBUG_HERE;
if (lval == NULL) return;
assign->lval = lval;
- symres_expression(state, (AstNode **) &assign->expr);
+ symres_expression(state, &assign->expr);
}
static void symres_return(OnyxSemPassState* state, AstReturn* ret) {
if (ret->expr)
- symres_expression(state, (AstNode **) &ret->expr);
+ symres_expression(state, &ret->expr);
}
static void symres_if(OnyxSemPassState* state, AstIf* ifnode) {
- symres_expression(state, (AstNode **) &ifnode->cond);
+ symres_expression(state, &ifnode->cond);
if (ifnode->true_block.as_if != NULL) {
if (ifnode->true_block.as_if->base.kind == Ast_Kind_Block)
symres_block(state, ifnode->true_block.as_block);
}
static void symres_while(OnyxSemPassState* state, AstWhile* whilenode) {
- symres_expression(state, (AstNode **) &whilenode->cond);
+ symres_expression(state, &whilenode->cond);
symres_block(state, whilenode->body);
}
case Ast_Kind_If: symres_if(state, (AstIf *) stmt); return 0;
case Ast_Kind_While: symres_while(state, (AstWhile *) stmt); return 0;
case Ast_Kind_Call: symres_call(state, (AstCall *) stmt); return 0;
- case Ast_Kind_Argument: symres_expression(state, (AstNode **) &((AstArgument *)stmt)->value); return 0;
+ case Ast_Kind_Argument: symres_expression(state, (AstTyped **) &((AstArgument *)stmt)->value); return 0;
case Ast_Kind_Block: symres_block(state, (AstBlock *) stmt); return 0;
default: return 0;
static void symres_function(OnyxSemPassState* state, AstFunction* func) {
for (AstLocal *param = func->params; param != NULL; param = (AstLocal *) param->base.next) {
+ param->base.type_node = symres_type(state, param->base.type_node);
+
symbol_introduce(state, (AstNode *) param);
}
+ if (func->base.type_node != NULL) {
+ if (func->base.type_node->kind == Ast_Kind_Symbol) {
+ func->base.type_node = symres_type(state, func->base.type_node);
+ }
+ }
+
symres_block(state, func->body);
for (AstLocal *param = func->params; param != NULL; param = (AstLocal *) param->base.next) {
void onyx_resolve_symbols(OnyxSemPassState* state, OnyxProgram* program) {
- // NOTE: First, introduce all global symbols
+ // NOTE: Add types to global scope
+ symbol_basic_type_introduce(state, &basic_type_void);
+ symbol_basic_type_introduce(state, &basic_type_bool);
+ symbol_basic_type_introduce(state, &basic_type_i8);
+ symbol_basic_type_introduce(state, &basic_type_u8);
+ symbol_basic_type_introduce(state, &basic_type_i16);
+ symbol_basic_type_introduce(state, &basic_type_u16);
+ symbol_basic_type_introduce(state, &basic_type_i32);
+ symbol_basic_type_introduce(state, &basic_type_u32);
+ symbol_basic_type_introduce(state, &basic_type_i64);
+ symbol_basic_type_introduce(state, &basic_type_u64);
+ symbol_basic_type_introduce(state, &basic_type_f32);
+ symbol_basic_type_introduce(state, &basic_type_f64);
+ symbol_basic_type_introduce(state, &basic_type_rawptr);
+
+ // NOTE: Introduce all global symbols
bh_arr_each(AstGlobal *, global, program->globals)
if (!symbol_unique_introduce(state, (AstNode *) *global)) return;
#include "onyxtypes.h"
+#include "onyxastnodes.h"
+// NOTE: These have to be in the same order as Basiuc
Type basic_types[] = {
{ Type_Kind_Basic, { Basic_Kind_Void, 0, 0, "void" } },
return 0;
}
+
+Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
+ if (type_node == NULL) return NULL;
+
+ switch (type_node->kind) {
+ case Ast_Kind_Pointer_Type: {
+ TypePointer* ptr_type = bh_alloc_item(alloc, TypePointer);
+
+ ptr_type->base.flags |= Basic_Flag_Pointer;
+ ptr_type->base.size = 4;
+ ptr_type->elem = type_build_from_ast(alloc, ((AstPointerType *) type_node)->elem);
+
+ return (Type *) ptr_type;
+ }
+
+ case Ast_Kind_Basic_Type:
+ return ((AstBasicType *) type_node)->type;
+
+ default:
+ assert(("Node is not a type node", 0));
+ return NULL;
+ }
+}
}
}
-static WasmType onyx_type_to_wasm_type(TypeInfo* type) {
- if (type->is_bool) return WASM_TYPE_INT32;
- else if (type->is_int) {
- if (type->size == 4) return WASM_TYPE_INT32;
- if (type->size == 8) return WASM_TYPE_INT64;
+static WasmType onyx_type_to_wasm_type(Type* type) {
+ if (type->kind == Type_Kind_Pointer) {
+ return WASM_TYPE_INT32;
}
- else if (type->is_float) {
- if (type->size == 4) return WASM_TYPE_FLOAT32;
- if (type->size == 8) return WASM_TYPE_FLOAT64;
+
+ if (type->kind == Type_Kind_Basic) {
+ TypeBasic* basic = &type->Basic;
+ if (basic->flags & Basic_Flag_Boolean) return WASM_TYPE_INT32;
+ if (basic->flags & Basic_Flag_Integer) {
+ if (basic->size <= 4) return WASM_TYPE_INT32;
+ if (basic->size == 8) return WASM_TYPE_INT64;
+ }
+ if (basic->flags & Basic_Flag_Float) {
+ if (basic->size <= 4) return WASM_TYPE_FLOAT32;
+ if (basic->size == 8) return WASM_TYPE_FLOAT64;;
+ }
+ if (basic->size == 0) return WASM_TYPE_VOID;
}
return WASM_TYPE_VOID;
// Unsigned instructions are always right after
// the signed equivalent
if (is_sign_significant) {
- if (binop->left->type->is_unsigned) {
+ if (binop->left->type->Basic.flags & Basic_Flag_Unsigned) {
binop_instr = (WasmInstructionType) ((i32) binop_instr + 1);
}
}
switch (unop->operation) {
case Unary_Op_Negate:
{
- TypeInfoKind type_kind = unop->base.type->kind;
+ TypeBasic* type = &unop->base.type->Basic;
- if (type_kind == TYPE_INFO_KIND_INT32) {
+ if (type->kind == Basic_Kind_I32
+ || type->kind == Basic_Kind_I16
+ || type->kind == Basic_Kind_I8) {
WID(WI_I32_CONST, 0x00);
compile_expression(mod, &code, unop->expr);
WI(WI_I32_SUB);
- } else if (type_kind == TYPE_INFO_KIND_INT64) {
+ }
+ else if (type->kind == Basic_Kind_I64) {
WID(WI_I64_CONST, 0x00);
compile_expression(mod, &code, unop->expr);
WI(WI_I64_SUB);
- } else {
+ }
+ else {
compile_expression(mod, &code, unop->expr);
- if (type_kind == TYPE_INFO_KIND_FLOAT32)
+ if (type->kind == Basic_Kind_F32)
WI(WI_F32_NEG);
- if (type_kind == TYPE_INFO_KIND_FLOAT64)
+ if (type->kind == Basic_Kind_F64)
WI(WI_F64_NEG);
}
compile_expression(mod, &code, cast->expr);
- TypeInfo* from = cast->expr->type;
- TypeInfo* to = cast->base.type;
+ Type* from = cast->expr->type;
+ Type* to = cast->base.type;
i32 fromidx = 0, toidx = 0;
- if (from->is_int) {
- if (from->size == 4 && !from->is_unsigned) fromidx = 0;
- else if (from->size == 4 && from->is_unsigned) fromidx = 1;
- else if (from->size == 8 && !from->is_unsigned) fromidx = 2;
- else if (from->size == 8 && from->is_unsigned) fromidx = 3;
- } else if (from->is_float) {
- if (from->size == 4) fromidx = 4;
- else if (from->size == 8) fromidx = 5;
+ if (from->Basic.flags & Basic_Flag_Numeric) {
+ b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+ if (from->Basic.size == 4 && !unsign) fromidx = 0;
+ else if (from->Basic.size == 4 && unsign) fromidx = 1;
+ else if (from->Basic.size == 8 && !unsign) fromidx = 2;
+ else if (from->Basic.size == 8 && unsign) fromidx = 3;
+ }
+ else if (from->Basic.flags & Basic_Flag_Float) {
+ if (from->Basic.size == 4) fromidx = 4;
+ else if (from->Basic.size == 8) fromidx = 5;
}
- if (to->is_int) {
- if (to->size == 4 && !to->is_unsigned) toidx = 0;
- else if (to->size == 4 && to->is_unsigned) toidx = 1;
- else if (to->size == 8 && !to->is_unsigned) toidx = 2;
- else if (to->size == 8 && to->is_unsigned) toidx = 3;
- } else if (to->is_float) {
- if (to->size == 4) toidx = 4;
- else if (to->size == 8) toidx = 5;
+ if (to->Basic.flags & Basic_Flag_Numeric) {
+ b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+ if (to->Basic.size == 4 && !unsign) toidx = 0;
+ else if (to->Basic.size == 4 && unsign) toidx = 1;
+ else if (to->Basic.size == 8 && !unsign) toidx = 2;
+ else if (to->Basic.size == 8 && unsign) toidx = 3;
+ }
+ else if (to->Basic.flags & Basic_Flag_Float) {
+ if (to->Basic.size == 4) toidx = 4;
+ else if (to->Basic.size == 8) toidx = 5;
}
WasmInstructionType cast_op = cast_map[fromidx][toidx];