"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/onyx",
- "args": ["progs/basic.onyx"],
+ "args": ["progs/arrays.onyx"],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
//-------------------------------------------------------------------------------------
// Better math functions
//-------------------------------------------------------------------------------------
-#define bh_max(a, b) ((a) > (b) ? (a) : (b))
-#define bh_min(a, b) ((a) < (b) ? (a) : (b))
+#define bh_max(a, b) ((a) > (b) ? (a) : (b))
+#define bh_min(a, b) ((a) < (b) ? (a) : (b))
#define bh_clamp(v, a, b) (bh_min((b), bh_max((a), (v))))
#define bh_abs(x) ((x) < 0 ? -(x) : (x))
-
+static inline u64 log2_dumb(u64 n) {
+ switch (n) {
+ case 1: return 0;
+ case 2: return 1;
+ case 4: return 2;
+ case 8: return 3;
+ case 16: return 4;
+ case 32: return 5;
+ case 64: return 6;
+ case 128: return 7;
+ case 256: return 8;
+ case 512: return 9;
+ case 1024: return 10;
+ case 2048: return 11;
+ case 4096: return 12;
+ case 8192: return 13;
+
+ // Don't need all of them right now
+ default: return 0;
+ }
+}
typedef struct AstNode AstNode;
typedef struct AstTyped AstTyped;
-typedef struct AstUnaryOp AstUnaryOp;
+
typedef struct AstBinOp AstBinaryOp;
-typedef struct AstAssign AstAssign;
+typedef struct AstUnaryOp AstUnaryOp;
typedef struct AstNumLit AstNumLit;
typedef struct AstLocal AstLocal;
-typedef struct AstReturn AstReturn;
typedef struct AstCall AstCall;
typedef struct AstIntrinsicCall AstIntrinsicCall;
typedef struct AstArgument AstArgument;
+typedef struct AstArrayAccess AstArrayAccess;
+
+typedef struct AstAssign AstAssign;
+typedef struct AstReturn AstReturn;
typedef struct AstBlock AstBlock;
typedef struct AstIf AstIf;
Ast_Kind_Argument,
Ast_Kind_Call,
Ast_Kind_Intrinsic_Call,
- Ast_Kind_Assignment,
Ast_Kind_Return,
+ Ast_Kind_Array_Access,
Ast_Kind_If,
Ast_Kind_While,
typedef enum AstFlags {
// Top-level flags
Ast_Flag_Exported = BH_BIT(0),
- Ast_Flag_Lval = BH_BIT(1),
+ Ast_Flag_Foreign = BH_BIT(1),
Ast_Flag_Const = BH_BIT(2),
Ast_Flag_Comptime = BH_BIT(3),
// Function flags
Ast_Flag_Inline = BH_BIT(8),
Ast_Flag_Intrinsic = BH_BIT(9),
- Ast_Flag_Foreign = BH_BIT(10),
+
+ // Expression flags
+ Ast_Flag_Expr_Ignored = BH_BIT(8),
} AstFlags;
typedef enum UnaryOp {
} UnaryOp;
typedef enum BinaryOp {
- Binary_Op_Add = 0,
- Binary_Op_Minus = 1,
- Binary_Op_Multiply = 2,
- Binary_Op_Divide = 3,
- Binary_Op_Modulus = 4,
-
- Binary_Op_Equal = 5,
- Binary_Op_Not_Equal = 6,
- Binary_Op_Less = 7,
- Binary_Op_Less_Equal = 8,
- Binary_Op_Greater = 9,
- Binary_Op_Greater_Equal = 10,
+ Binary_Op_Add = 0,
+ Binary_Op_Minus = 1,
+ Binary_Op_Multiply = 2,
+ Binary_Op_Divide = 3,
+ Binary_Op_Modulus = 4,
+
+ Binary_Op_Equal = 5,
+ Binary_Op_Not_Equal = 6,
+ Binary_Op_Less = 7,
+ Binary_Op_Less_Equal = 8,
+ Binary_Op_Greater = 9,
+ Binary_Op_Greater_Equal = 10,
+
+ Binary_Op_Assign_Start = 11,
+ Binary_Op_Assign = 12,
+ Binary_Op_Assign_Add = 13,
+ Binary_Op_Assign_Minus = 14,
+ Binary_Op_Assign_Multiply = 15,
+ Binary_Op_Assign_Divide = 16,
+ Binary_Op_Assign_Modulus = 17,
+ Binary_Op_Assign_End = 18,
} BinaryOp;
+typedef enum OnyxIntrinsic {
+ ONYX_INTRINSIC_UNDEFINED,
+
+ ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW,
+
+ ONYX_INTRINSIC_I32_CLZ, ONYX_INTRINSIC_I32_CTZ, ONYX_INTRINSIC_I32_POPCNT,
+ ONYX_INTRINSIC_I32_AND, ONYX_INTRINSIC_I32_OR, ONYX_INTRINSIC_I32_XOR,
+ ONYX_INTRINSIC_I32_SHL, ONYX_INTRINSIC_I32_SLR, ONYX_INTRINSIC_I32_SAR,
+ ONYX_INTRINSIC_I32_ROTL, ONYX_INTRINSIC_I32_ROTR,
+
+ ONYX_INTRINSIC_I64_CLZ, ONYX_INTRINSIC_I64_CTZ, ONYX_INTRINSIC_I64_POPCNT,
+ ONYX_INTRINSIC_I64_AND, ONYX_INTRINSIC_I64_OR, ONYX_INTRINSIC_I64_XOR,
+ ONYX_INTRINSIC_I64_SHL, ONYX_INTRINSIC_I64_SLR, ONYX_INTRINSIC_I64_SAR,
+ ONYX_INTRINSIC_I64_ROTL, ONYX_INTRINSIC_I64_ROTR,
+
+ ONYX_INTRINSIC_F32_ABS, ONYX_INTRINSIC_F32_SQRT,
+ ONYX_INTRINSIC_F32_CEIL, ONYX_INTRINSIC_F32_FLOOR,
+ ONYX_INTRINSIC_F32_TRUNC, ONYX_INTRINSIC_F32_NEAREST,
+ ONYX_INTRINSIC_F32_MIN, ONYX_INTRINSIC_F32_MAX,
+ ONYX_INTRINSIC_F32_COPYSIGN,
+
+ ONYX_INTRINSIC_F64_ABS, ONYX_INTRINSIC_F64_SQRT,
+ ONYX_INTRINSIC_F64_CEIL, ONYX_INTRINSIC_F64_FLOOR,
+ ONYX_INTRINSIC_F64_TRUNC, ONYX_INTRINSIC_F64_NEAREST,
+ ONYX_INTRINSIC_F64_MIN, ONYX_INTRINSIC_F64_MAX,
+ ONYX_INTRINSIC_F64_COPYSIGN,
+} OnyxIntrinsic;
+
// Base Nodes
#define AstNode_members { \
// 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 AstIntrinsicCall { AstTyped_base; AstArgument *arguments; OnyxIntrinsic intrinsic; };
struct AstArgument { AstTyped_base; AstTyped *value; };
+struct AstArrayAccess { AstTyped_base; AstTyped *addr; AstTyped *expr; u64 elem_size; };
+
+// Intruction Node
+struct AstReturn { AstNode_base; AstTyped* expr; };
// Structure Nodes
struct AstLocalGroup { AstNode_base; AstLocalGroup *prev_group; AstLocal *last_local; };
};
};
-typedef enum OnyxIntrinsic {
- ONYX_INTRINSIC_UNDEFINED,
-
- ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW,
-
- ONYX_INTRINSIC_I32_CLZ, ONYX_INTRINSIC_I32_CTZ, ONYX_INTRINSIC_I32_POPCNT,
- ONYX_INTRINSIC_I32_AND, ONYX_INTRINSIC_I32_OR, ONYX_INTRINSIC_I32_XOR,
- ONYX_INTRINSIC_I32_SHL, ONYX_INTRINSIC_I32_SLR, ONYX_INTRINSIC_I32_SAR,
- ONYX_INTRINSIC_I32_ROTL, ONYX_INTRINSIC_I32_ROTR,
-
- ONYX_INTRINSIC_I64_CLZ, ONYX_INTRINSIC_I64_CTZ, ONYX_INTRINSIC_I64_POPCNT,
- ONYX_INTRINSIC_I64_AND, ONYX_INTRINSIC_I64_OR, ONYX_INTRINSIC_I64_XOR,
- ONYX_INTRINSIC_I64_SHL, ONYX_INTRINSIC_I64_SLR, ONYX_INTRINSIC_I64_SAR,
- ONYX_INTRINSIC_I64_ROTL, ONYX_INTRINSIC_I64_ROTR,
-
- ONYX_INTRINSIC_F32_ABS, ONYX_INTRINSIC_F32_SQRT,
- ONYX_INTRINSIC_F32_CEIL, ONYX_INTRINSIC_F32_FLOOR,
- ONYX_INTRINSIC_F32_TRUNC, ONYX_INTRINSIC_F32_NEAREST,
- ONYX_INTRINSIC_F32_MIN, ONYX_INTRINSIC_F32_MAX,
- ONYX_INTRINSIC_F32_COPYSIGN,
-
- ONYX_INTRINSIC_F64_ABS, ONYX_INTRINSIC_F64_SQRT,
- ONYX_INTRINSIC_F64_CEIL, ONYX_INTRINSIC_F64_FLOOR,
- ONYX_INTRINSIC_F64_TRUNC, ONYX_INTRINSIC_F64_NEAREST,
- ONYX_INTRINSIC_F64_MIN, ONYX_INTRINSIC_F64_MAX,
- ONYX_INTRINSIC_F64_COPYSIGN,
-} OnyxIntrinsic;
-
-// NOTE: This needs to have 'arguments' in the
-// same position as AstNodeCall
-struct AstIntrinsicCall { AstTyped_base; AstArgument *arguments; OnyxIntrinsic intrinsic; };
-
// NOTE: Simple data structure for storing what comes out of the parser
typedef struct ParserOutput {
bh_arr(AstBinding *) top_level_bindings;
extern AstBasicType basic_type_f64;
extern AstBasicType basic_type_rawptr;
+
+// NOTE: Useful inlined functions
+static inline b32 is_lval(AstNode* node) {
+ return (node->kind == Ast_Kind_Local)
+ || (node->kind == Ast_Kind_Global)
+ || (node->kind == Ast_Kind_Array_Access);
+}
+
+static inline b32 binop_is_assignment(AstBinaryOp* binop) {
+ return (binop->operation >= Binary_Op_Assign_Start
+ && binop->operation <= Binary_Op_Assign_End);
+}
+
#endif // #ifndef ONYXASTNODES_H
--- /dev/null
+use "progs/intrinsics"
+use "progs/print_funcs"
+
+main :: proc #export {
+ print_i32(memory_size());
+
+ arr := 0 as ^i64;
+
+ arr[0] = 10 as i64;
+ arr[1] = 20 as i64;
+ arr[2] = 30 as i64;
+ arr[3] = 40 as i64;
+
+ i := 0;
+ while i < 4 {
+ arr[i] += 1 as i64;
+ arr[i] *= 2 as i64;
+
+ print_i64(arr[i]);
+ i += 1;
+ }
+}
+
wasm_global :: global i32
main :: proc #export {
- a := 16;
+ a : i32 = 16;
print(clz_i32(a));
wasm_global = 5 + global_value;
print(4 + global_value);
b := 1 + foo(2);
- if b == 13 b = 10;
+ if b == 13 b += 10;
print(b);
if b == 13 print(5678);
memory_size :: proc #intrinsic -> i32 ---
-memory_grow :: proc #intrinsic -> i32 ---
+memory_grow :: proc #intrinsic (val: i32) -> i32 ---
clz_i32 :: proc #intrinsic (val: i32) -> i32 ---
ctz_i32 :: proc #intrinsic (val: i32) -> i32 ---
#define BH_DEBUG
#include "onyxsempass.h"
+#include "onyxparser.h"
static b32 check_function(SemState* state, AstFunction* func);
static b32 check_block(SemState* state, AstBlock* block);
static b32 check_statement_chain(SemState* state, AstNode* start);
static b32 check_statement(SemState* state, AstNode* stmt);
-static b32 check_assignment(SemState* state, AstAssign* assign);
static b32 check_return(SemState* state, AstReturn* retnode);
static b32 check_if(SemState* state, AstIf* ifnode);
static b32 check_while(SemState* state, AstWhile* whilenode);
static b32 check_call(SemState* state, AstCall* call);
static b32 check_binaryop(SemState* state, AstBinaryOp* binop);
static b32 check_expression(SemState* state, AstTyped* expr);
+static b32 check_array_access(SemState* state, AstArrayAccess* expr);
static b32 check_global(SemState* state, AstGlobal* global);
-static b32 check_assignment(SemState* state, AstAssign* assign) {
- if (assign->lval->kind == Ast_Kind_Symbol) {
- onyx_message_add(state->msgs,
- ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL,
- assign->lval->token->pos,
- assign->lval->token->text, assign->lval->token->length);
- return 1;
- }
-
- if ((assign->lval->flags & Ast_Flag_Const) != 0 && assign->lval->type != NULL) {
- onyx_message_add(state->msgs,
- ONYX_MESSAGE_TYPE_ASSIGN_CONST,
- assign->token->pos,
- assign->lval->token->text, assign->lval->token->length);
- return 1;
- }
-
- if ((assign->lval->flags & Ast_Flag_Lval) == 0) {
- onyx_message_add(state->msgs,
- ONYX_MESSAGE_TYPE_NOT_LVAL,
- assign->token->pos,
- assign->lval->token->text, assign->lval->token->length);
- return 1;
- }
-
- if (assign->lval->type == NULL) {
- assign->lval->type = type_build_from_ast(state->node_allocator, assign->lval->type_node);
- }
-
- if (check_expression(state, assign->expr)) return 1;
-
- if (assign->lval->type == NULL) {
- assign->lval->type = assign->expr->type;
- } else {
- if (!types_are_compatible(assign->lval->type, assign->expr->type)) {
- onyx_message_add(state->msgs,
- ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH,
- assign->token->pos,
- type_get_name(assign->lval->type),
- type_get_name(assign->expr->type));
- return 1;
- }
- }
-
- return 0;
-}
-
static b32 check_return(SemState* state, AstReturn* retnode) {
if (retnode->expr) {
if (check_expression(state, retnode->expr)) return 1;
if (check_expression(state, binop->left)) return 1;
if (check_expression(state, binop->right)) return 1;
+ if (binop_is_assignment(binop)) {
+ if (!is_lval((AstNode *) binop->left)) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_NOT_LVAL,
+ binop->token->pos,
+ binop->left->token->text, binop->left->token->length);
+ return 1;
+ }
+
+ if ((binop->left->flags & Ast_Flag_Const) != 0 && binop->left->type != NULL) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_ASSIGN_CONST,
+ binop->token->pos,
+ binop->left->token->text, binop->left->token->length);
+ return 1;
+ }
+
+ if (binop->operation == Binary_Op_Assign) {
+ // NOTE: Raw assignment
+ if (binop->left->type == NULL) {
+ binop->left->type = binop->right->type;
+ }
+
+ } else {
+ // NOTE: +=, -=, ...
+
+ AstBinaryOp* binop_node = onyx_ast_node_new(
+ state->node_allocator,
+ sizeof(AstBinaryOp),
+ Ast_Kind_Binary_Op);
+
+ binop_node->token = binop->token;
+ binop_node->left = binop->left;
+ binop_node->right = binop->right;
+ binop_node->type = binop->right->type;
+
+ if (binop->operation == Binary_Op_Assign_Add) binop_node->operation = Binary_Op_Add;
+ else if (binop->operation == Binary_Op_Assign_Minus) binop_node->operation = Binary_Op_Minus;
+ else if (binop->operation == Binary_Op_Assign_Multiply) binop_node->operation = Binary_Op_Multiply;
+ else if (binop->operation == Binary_Op_Assign_Divide) binop_node->operation = Binary_Op_Divide;
+ else if (binop->operation == Binary_Op_Assign_Modulus) binop_node->operation = Binary_Op_Modulus;
+
+ binop->right = (AstTyped *) binop_node;
+ binop->operation = Binary_Op_Assign;
+ }
+
+ } else {
+ if (type_is_pointer(binop->left->type)
+ || type_is_pointer(binop->right->type)) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_LITERAL,
+ binop->token->pos,
+ "binary operations are not supported for pointers (yet).");
+ return 1;
+ }
+ }
+
if (binop->left->type == NULL) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE,
return 1;
}
- if (type_is_pointer(binop->left->type)
- || type_is_pointer(binop->right->type)) {
- onyx_message_add(state->msgs,
- ONYX_MESSAGE_TYPE_LITERAL,
- binop->token->pos,
- "binary operations are not supported for pointers (yet).");
- return 1;
- }
if (!types_are_compatible(binop->left->type, binop->right->type)) {
onyx_message_add(state->msgs,
return 0;
}
+static b32 check_array_access(SemState* state, AstArrayAccess* aa) {
+ check_expression(state, aa->addr);
+ check_expression(state, aa->expr);
+
+ if (!type_is_pointer(aa->addr->type)) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_LITERAL,
+ aa->addr->token->pos,
+ "expected pointer type for left of array access");
+ return 1;
+ }
+
+ if (aa->expr->type->kind != Type_Kind_Basic
+ || (aa->expr->type->Basic.flags & Basic_Flag_Integer) == 0) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_LITERAL,
+ aa->expr->token->pos,
+ "expected integer type for index");
+ return 1;
+ }
+
+ aa->type = aa->addr->type->Pointer.elem;
+ aa->elem_size = aa->type->Basic.size;
+
+ return 0;
+}
+
static b32 check_expression(SemState* state, AstTyped* expr) {
if (expr->kind > Ast_Kind_Type_Start && expr->kind < Ast_Kind_Type_End) {
onyx_message_add(state->msgs,
retval = 1;
break;
- case Ast_Kind_Local:
case Ast_Kind_Param:
if (expr->type == NULL) {
onyx_message_add(state->msgs,
}
break;
+ case Ast_Kind_Local: break;
+
+ case Ast_Kind_Array_Access:
+ retval = check_array_access(state, (AstArrayAccess *) expr);
+ break;
+
case Ast_Kind_Global:
if (expr->type == NULL) {
onyx_message_add(state->msgs,
static b32 check_statement(SemState* state, AstNode* stmt) {
switch (stmt->kind) {
- case Ast_Kind_Assignment: return check_assignment(state, (AstAssign *) stmt);
case Ast_Kind_Return: return check_return(state, (AstReturn *) stmt);
case Ast_Kind_If: return check_if(state, (AstIf *) stmt);
case Ast_Kind_While: return check_while(state, (AstWhile *) stmt);
case Ast_Kind_Call: return check_call(state, (AstCall *) stmt);
case Ast_Kind_Block: return check_block(state, (AstBlock *) stmt);
- default: return 0;
+ default:
+ stmt->flags |= Ast_Flag_Expr_Ignored;
+ return check_expression(state, (AstTyped *) stmt);
}
}
switch (node->kind) {
case Ast_Kind_Function: return check_function(state, (AstFunction *) node);
case Ast_Kind_Block: return check_block(state, (AstBlock *) node);
- case Ast_Kind_Assignment: return check_assignment(state, (AstAssign *) node);
case Ast_Kind_Return: return check_return(state, (AstReturn *) node);
case Ast_Kind_If: return check_if(state, (AstIf *) node);
case Ast_Kind_While: return check_while(state, (AstWhile *) node);
#include "onyxmsgs.h"
#include "onyxutils.h"
-#define MAX_MSGS 5
+#define MAX_MSGS 10
static const char* msg_formats[] = {
"%s",
static AstTyped* parse_expression(OnyxParser* parser);
static AstIf* parse_if_stmt(OnyxParser* parser);
static AstWhile* parse_while_stmt(OnyxParser* parser);
-static b32 parse_symbol_statement(OnyxParser* parser, AstNode** ret);
+static b32 parse_symbol_declaration(OnyxParser* parser, AstNode** ret);
static AstReturn* parse_return_statement(OnyxParser* parser);
static AstBlock* parse_block(OnyxParser* parser);
static AstNode* parse_statement(OnyxParser* parser);
return NULL;
}
+ while (parser->curr->type == '[') {
+ AstArrayAccess* aa_node = make_node(AstArrayAccess, Ast_Kind_Array_Access);
+ aa_node->token = expect_token(parser, '[');
+ aa_node->addr = retval;
+ aa_node->expr = parse_expression(parser);
+
+ expect_token(parser, ']');
+
+ retval = (AstTyped *) aa_node;
+ }
+
while (parser->curr->type == Token_Type_Keyword_Cast) {
consume_token(parser);
retval = (AstTyped *) cast_node;
}
+
return retval;
}
static inline i32 get_precedence(BinaryOp kind) {
switch (kind) {
- case Binary_Op_Equal: return 3;
- case Binary_Op_Not_Equal: return 3;
+ case Binary_Op_Assign: return 2;
+ case Binary_Op_Assign_Add: return 2;
+ case Binary_Op_Assign_Minus: return 2;
+ case Binary_Op_Assign_Multiply: return 2;
+ case Binary_Op_Assign_Divide: return 2;
+ case Binary_Op_Assign_Modulus: return 2;
+
+ case Binary_Op_Equal: return 3;
+ case Binary_Op_Not_Equal: return 3;
+
+ case Binary_Op_Less_Equal: return 4;
+ case Binary_Op_Less: return 4;
+ case Binary_Op_Greater_Equal: return 4;
+ case Binary_Op_Greater: return 4;
- case Binary_Op_Less_Equal: return 4;
- case Binary_Op_Less: return 4;
- case Binary_Op_Greater_Equal: return 4;
- case Binary_Op_Greater: return 4;
+ case Binary_Op_Add: return 5;
+ case Binary_Op_Minus: return 5;
- case Binary_Op_Add: return 5;
- case Binary_Op_Minus: return 5;
+ case Binary_Op_Multiply: return 6;
+ case Binary_Op_Divide: return 6;
- case Binary_Op_Multiply: return 6;
- case Binary_Op_Divide: return 6;
+ case Binary_Op_Modulus: return 7;
- case Binary_Op_Modulus: return 7;
- default: return -1;
+ default: return -1;
}
}
-// <factor> + <factor>
-// <factor> - <factor>
-// <factor> * <factor>
-// <factor> / <factor>
-// <factor> % <factor>
-// <factor> == <factor>
-// <factor> != <factor>
-// <factor> <= <factor>
-// <factor> >= <factor>
-// <factor> < <factor>
-// <factor> > <factor>
+// <expr> + <expr>
+// <expr> - <expr>
+// <expr> * <expr>
+// <expr> / <expr>
+// <expr> % <expr>
+// <expr> == <expr>
+// <expr> != <expr>
+// <expr> <= <expr>
+// <expr> >= <expr>
+// <expr> < <expr>
+// <expr> > <expr>
+// <expr> = <expr>
+// <expr> += <expr>
+// <expr> -= <expr>
+// <expr> *= <expr>
+// <expr> /= <expr>
+// <expr> %= <expr>
// With expected precedence rules
static AstTyped* parse_expression(OnyxParser* parser) {
bh_arr(AstBinaryOp*) tree_stack = NULL;
case '*': bin_op_kind = Binary_Op_Multiply; break;
case '/': bin_op_kind = Binary_Op_Divide; break;
case '%': bin_op_kind = Binary_Op_Modulus; break;
+
+ case '=': bin_op_kind = Binary_Op_Assign; break;
+ case Token_Type_Plus_Equal: bin_op_kind = Binary_Op_Assign_Add; break;
+ case Token_Type_Minus_Equal: bin_op_kind = Binary_Op_Assign_Minus; break;
+ case Token_Type_Star_Equal: bin_op_kind = Binary_Op_Assign_Multiply; break;
+ case Token_Type_Fslash_Equal: bin_op_kind = Binary_Op_Assign_Divide; break;
+ case Token_Type_Percent_Equal: bin_op_kind = Binary_Op_Assign_Modulus; break;
default: goto expression_done;
}
// <symbol> : <type> : <expr>
// <symbol> := <expr>
// <symbol> :: <expr>
-// <symbol> = <expr>
-// <symbol> += <expr>
-// <symbol> -= <expr>
-// <symbol> *= <expr>
-// <symbol> /= <expr>
-// <symbol> %= <expr>
-static b32 parse_symbol_statement(OnyxParser* parser, AstNode** ret) {
+static b32 parse_symbol_declaration(OnyxParser* parser, AstNode** ret) {
if (parser->curr->type != Token_Type_Symbol) return 0;
- OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
-
- switch ((u16) parser->curr->type) {
- // NOTE: Declaration
- case ':':
- {
- consume_token(parser);
- AstType* type_node = NULL;
-
- // NOTE: var: type
- if (parser->curr->type != ':'
- && parser->curr->type != '=') {
- type_node = parse_type(parser);
- }
-
- AstLocal* local = make_node(AstLocal, Ast_Kind_Local);
- local->token = symbol;
- local->type_node = type_node;
- local->flags |= Ast_Flag_Lval; // NOTE: DELETE
- *ret = (AstNode *) local;
-
- if (parser->curr->type == '=' || parser->curr->type == ':') {
- if (parser->curr->type == ':') {
- local->flags |= Ast_Flag_Const;
- }
-
- AstAssign* assignment = make_node(AstAssign, Ast_Kind_Assignment);
- local->next = (AstNode *) assignment;
- assignment->token = parser->curr;
- consume_token(parser);
-
- AstTyped* expr = parse_expression(parser);
- if (expr == NULL) {
- token_toggle_end(parser->curr);
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_EXPECTED_EXPRESSION,
- assignment->token->pos,
- parser->curr->text);
- token_toggle_end(parser->curr);
- return 1;
- }
- assignment->expr = expr;
-
- AstNode* left_symbol = make_node(AstNode, Ast_Kind_Symbol);
- left_symbol->token = symbol;
- assignment->lval = (AstTyped *) left_symbol;
- }
- return 1;
- }
-
- // NOTE: Assignment
- case '=':
- {
- AstAssign* assignment = make_node(AstAssign, Ast_Kind_Assignment);
- assignment->token = parser->curr;
- consume_token(parser);
-
- AstNode* lval = make_node(AstNode, Ast_Kind_Symbol);
- lval->token = symbol;
-
- AstTyped* rval = parse_expression(parser);
- assignment->expr = rval;
- assignment->lval = (AstTyped *) lval;
- *ret = (AstNode *) assignment;
- return 1;
- }
+ if ((parser->curr + 1)->type != ':') return 0;
- case Token_Type_Plus_Equal:
- case Token_Type_Minus_Equal:
- case Token_Type_Star_Equal:
- case Token_Type_Fslash_Equal:
- case Token_Type_Percent_Equal:
- {
- BinaryOp bin_op;
- if (parser->curr->type == Token_Type_Plus_Equal) bin_op = Binary_Op_Add;
- else if (parser->curr->type == Token_Type_Minus_Equal) bin_op = Binary_Op_Minus;
- else if (parser->curr->type == Token_Type_Star_Equal) bin_op = Binary_Op_Multiply;
- else if (parser->curr->type == Token_Type_Fslash_Equal) bin_op = Binary_Op_Divide;
- else if (parser->curr->type == Token_Type_Percent_Equal) bin_op = Binary_Op_Modulus;
-
- AstBinaryOp* bin_op_node = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- bin_op_node->operation = bin_op;
- bin_op_node->token = parser->curr;
-
- consume_token(parser);
- AstTyped* expr = parse_expression(parser);
+ OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+ consume_token(parser);
+ AstType* type_node = NULL;
- AstNode* bin_op_left = make_node(AstNode, Ast_Kind_Symbol);
- bin_op_left->token = symbol;
- bin_op_node->left = (AstTyped *) bin_op_left;
- bin_op_node->right = expr;
+ // NOTE: var: type
+ if (parser->curr->type != ':'
+ && parser->curr->type != '=') {
+ type_node = parse_type(parser);
+ }
- AstAssign* assign_node = make_node(AstAssign, Ast_Kind_Assignment);
- assign_node->token = bin_op_node->token;
+ AstLocal* local = make_node(AstLocal, Ast_Kind_Local);
+ local->token = symbol;
+ local->type_node = type_node;
+ *ret = (AstNode *) local;
- // TODO: Maybe I don't need to make another lval node?
- AstNode* lval = make_node(AstNode, Ast_Kind_Symbol);
- lval->token = symbol;
- assign_node->lval = (AstTyped *) lval;
- assign_node->expr = (AstTyped *) bin_op_node;
+ if (parser->curr->type == '=' || parser->curr->type == ':') {
+ if (parser->curr->type == ':') {
+ local->flags |= Ast_Flag_Const;
+ }
- *ret = (AstNode *) assign_node;
+ AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+ assignment->operation = Binary_Op_Assign;
+ local->next = (AstNode *) assignment;
+ assignment->token = parser->curr;
+ consume_token(parser);
- return 1;
- }
+ AstTyped* expr = parse_expression(parser);
+ if (expr == NULL) {
+ token_toggle_end(parser->curr);
+ onyx_message_add(parser->msgs,
+ ONYX_MESSAGE_TYPE_EXPECTED_EXPRESSION,
+ assignment->token->pos,
+ parser->curr->text);
+ token_toggle_end(parser->curr);
+ return 1;
+ }
+ assignment->right = expr;
- default:
- unconsume_token(parser);
+ AstNode* left_symbol = make_node(AstNode, Ast_Kind_Symbol);
+ left_symbol->token = symbol;
+ assignment->left = (AstTyped *) left_symbol;
}
- return 0;
+ return 1;
}
// 'return' <expr>?
break;
case Token_Type_Symbol:
- if (parse_symbol_statement(parser, &retval)) break;
+ if (parse_symbol_declaration(parser, &retval)) break;
// fallthrough
case '(':
}
global_node->type_node = parse_type(parser);
- global_node->flags |= Ast_Flag_Lval;
-
bh_arr_push(parser->results.nodes_to_process, (AstNode *) global_node);
static void symres_local(SemState* state, AstLocal** local);
static void symres_call(SemState* state, AstCall* call);
static void symres_expression(SemState* state, AstTyped** expr);
-static void symres_assignment(SemState* state, AstAssign* assign);
static void symres_return(SemState* state, AstReturn* ret);
static void symres_if(SemState* state, AstIf* ifnode);
static void symres_while(SemState* state, AstWhile* whilenode);
(*expr)->type_node = symres_type(state, (*expr)->type_node);
break;
+ case Ast_Kind_Array_Access:
+ symres_expression(state, &((AstArrayAccess *)(*expr))->addr);
+ symres_expression(state, &((AstArrayAccess *)(*expr))->expr);
+ break;
+
default:
DEBUG_HERE;
break;
}
}
-static void symres_assignment(SemState* state, AstAssign* assign) {
- AstTyped* lval = (AstTyped *) symbol_resolve(state, assign->lval->token);
- if (lval == NULL) return;
- assign->lval = lval;
-
- symres_expression(state, &assign->expr);
-}
-
static void symres_return(SemState* state, AstReturn* ret) {
if (ret->expr)
symres_expression(state, &ret->expr);
static b32 symres_statement(SemState* state, AstNode* stmt) {
switch (stmt->kind) {
case Ast_Kind_Local: symres_local(state, (AstLocal **) &stmt); return 1;
- case Ast_Kind_Assignment: symres_assignment(state, (AstAssign *) stmt); return 0;
case Ast_Kind_Return: symres_return(state, (AstReturn *) stmt); return 0;
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_Argument: symres_expression(state, (AstTyped **) &((AstArgument *)stmt)->value); return 0;
case Ast_Kind_Block: symres_block(state, (AstBlock *) stmt); return 0;
- default: return 0;
+ case Ast_Kind_Break: return 0;
+ case Ast_Kind_Continue: return 0;
+
+ default: symres_expression(state, (AstTyped **) &stmt); return 0;
}
}
"PARAM",
"ARGUMENT",
"CALL",
- "ASSIGN",
"RETURN",
+ "ARRAY_ACCESS",
"IF",
"WHILE",
return WASM_TYPE_VOID;
}
-#define WI(instr) bh_arr_push(code, ((WasmInstruction){ instr, 0x00 }));
-#define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data }));
+#define WI(instr) bh_arr_push(code, ((WasmInstruction){ instr, 0x00 }))
+#define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data }))
#define COMPILE_FUNC(kind, ...) static void compile_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, __VA_ARGS__)
COMPILE_FUNC(function_body, AstFunction* fd);
COMPILE_FUNC(block, AstBlock* block);
COMPILE_FUNC(statement, AstNode* stmt);
-COMPILE_FUNC(assign_lval, AstTyped* lval);
-COMPILE_FUNC(assignment, AstAssign* assign);
+COMPILE_FUNC(assignment, AstBinaryOp* assign);
COMPILE_FUNC(if, AstIf* if_node);
COMPILE_FUNC(while, AstWhile* while_node);
COMPILE_FUNC(binop, AstBinaryOp* binop);
switch (stmt->kind) {
case Ast_Kind_Return: compile_return(mod, &code, (AstReturn *) stmt); break;
- case Ast_Kind_Assignment: compile_assignment(mod, &code, (AstAssign *) stmt); break;
case Ast_Kind_If: compile_if(mod, &code, (AstIf *) stmt); break;
case Ast_Kind_While: compile_while(mod, &code, (AstWhile *) stmt); break;
case Ast_Kind_Break: compile_structured_jump(mod, &code, 0); break;
case Ast_Kind_Continue: compile_structured_jump(mod, &code, 1); break;
case Ast_Kind_Block: compile_block(mod, &code, (AstBlock *) stmt); break;
-
- case Ast_Kind_Call:
- case Ast_Kind_Intrinsic_Call:
- compile_expression(mod, &code, (AstTyped *) stmt);
- break;
-
-
- default: break;
+ default: compile_expression(mod, &code, (AstTyped *) stmt); break;
}
*pcode = code;
}
-COMPILE_FUNC(assign_lval, AstTyped* lval) {
+COMPILE_FUNC(assignment, AstBinaryOp* assign) {
bh_arr(WasmInstruction) code = *pcode;
- if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
+ AstTyped* lval = assign->left;
+
+ if (lval->kind == Ast_Kind_Local) {
i32 localidx = (i32) bh_imap_get(&mod->local_map, (u64) lval);
+ compile_expression(mod, &code, assign->right);
WID(WI_LOCAL_SET, localidx);
} else if (lval->kind == Ast_Kind_Global) {
i32 globalidx = (i32) bh_imap_get(&mod->global_map, (u64) lval);
+ compile_expression(mod, &code, assign->right);
WID(WI_GLOBAL_SET, globalidx);
+ } else if (lval->kind == Ast_Kind_Array_Access) {
+ AstArrayAccess* aa = (AstArrayAccess *) lval;
+ WID(WI_I32_CONST, aa->elem_size);
+ compile_expression(mod, &code, aa->expr);
+ WI(WI_I32_MUL);
+ compile_expression(mod, &code, aa->addr);
+ WI(WI_I32_ADD);
+
+ compile_expression(mod, &code, assign->right);
+
+ i32 store_size = aa->type->Basic.size;
+ i32 is_integer = (aa->type->Basic.flags & Basic_Flag_Integer)
+ || (aa->type->Basic.flags & Basic_Flag_Pointer);
+
+ if (is_integer) {
+ if (store_size == 1) WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
+ else if (store_size == 2) WID(WI_I32_STORE_16, ((WasmInstructionData) { 1, 0 }));
+ else if (store_size == 4) WID(WI_I32_STORE, ((WasmInstructionData) { 2, 0 }));
+ else if (store_size == 8) WID(WI_I64_STORE, ((WasmInstructionData) { 3, 0 }));
+ } else {
+ if (store_size == 4) WID(WI_F32_STORE, ((WasmInstructionData) { 2, 0 }));
+ else if (store_size == 8) WID(WI_F64_STORE, ((WasmInstructionData) { 3, 0 }));
+ }
+
} else {
assert(("Invalid lval", 0));
}
*pcode = code;
}
-COMPILE_FUNC(assignment, AstAssign* assign) {
- bh_arr(WasmInstruction) code = *pcode;
-
- compile_expression(mod, &code, assign->expr);
- compile_assign_lval(mod, &code, assign->lval);
-
- *pcode = code;
-}
-
// NOTE: These need to be in the same order as
// the OnyxBinaryOp enum
static const WasmInstructionType binop_map[][4] = {
COMPILE_FUNC(binop, AstBinaryOp* binop) {
bh_arr(WasmInstruction) code = *pcode;
+ if (binop_is_assignment(binop)) {
+ compile_assignment(mod, &code, binop);
+ *pcode = code;
+ return;
+ }
+
b32 is_sign_significant = 0;
switch (binop->operation) {
case Ast_Kind_Block: compile_block(mod, &code, (AstBlock *) expr); break;
-
case Ast_Kind_Call:
compile_call(mod, &code, (AstCall *) expr);
break;
compile_intrinsic_call(mod, &code, (AstIntrinsicCall *) expr);
break;
+ case Ast_Kind_Array_Access: {
+ AstArrayAccess* aa = (AstArrayAccess *) expr;
+ WID(WI_I32_CONST, aa->elem_size);
+ compile_expression(mod, &code, aa->expr);
+ WI(WI_I32_MUL);
+ compile_expression(mod, &code, aa->addr);
+ WI(WI_I32_ADD);
+
+ i32 load_size = aa->type->Basic.size;
+ i32 is_integer = (aa->type->Basic.flags & Basic_Flag_Integer)
+ || (aa->type->Basic.flags & Basic_Flag_Pointer);
+ i32 is_unsigned = aa->type->Basic.flags & Basic_Flag_Unsigned;
+
+ WasmInstructionType instr = WI_NOP;
+ i32 alignment = log2_dumb(load_size);
+
+ if (is_integer) {
+ if (load_size == 1) instr = WI_I32_LOAD_8_S;
+ else if (load_size == 2) instr = WI_I32_LOAD_16_S;
+ else if (load_size == 4) instr = WI_I32_LOAD;
+ else if (load_size == 8) instr = WI_I64_LOAD;
+
+ if (alignment < 4 && is_unsigned) instr += 1;
+ } else {
+ if (load_size == 4) instr = WI_F32_LOAD;
+ else if (load_size == 8) instr = WI_F64_LOAD;
+ }
+
+ if (instr != WI_NOP) {
+ WID(instr, ((WasmInstructionData) { alignment, 0 }));
+ } else {
+ DEBUG_HERE;
+ }
+
+ break;
+ }
+
default:
- DEBUG_HERE;
bh_printf("Unhandled case: %d\n", expr->kind);
+ DEBUG_HERE;
assert(0);
}
return buff->length - prev_len;
}
+static i32 output_limits(i32 min, i32 max, bh_buffer* buff) {
+ i32 leb_len, prev_len = buff->length;
+ u8* leb;
+
+ bh_buffer_write_byte(buff, (max >= 0) ? 0x01 : 0x00);
+
+ leb = uint_to_uleb128((u64) min, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ if (max >= 0) {
+ leb = uint_to_uleb128((u64) max, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ }
+
+ return buff->length - prev_len;
+}
+
static i32 output_functype(WasmFuncType* type, bh_buffer* buff) {
i32 prev_len = buff->length;
return buff->length - prev_len;
}
+static i32 output_memorysection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_MEMORY);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) 1, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ output_limits(4, 20, &vec_buff);
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
static i32 output_globalsection(OnyxWasmModule* module, bh_buffer* buff) {
i32 prev_len = buff->length;
bh_buffer_write_byte(buff, WASM_SECTION_ID_GLOBAL);
bh_buffer_append(buff, leb, leb_len);
break;
+ case WI_I32_STORE:
+ case WI_I32_STORE_8:
+ case WI_I32_STORE_16:
+ case WI_I64_STORE:
+ case WI_I64_STORE_8:
+ case WI_I64_STORE_16:
+ case WI_I64_STORE_32:
+ case WI_F32_STORE:
+ case WI_F64_STORE:
+ case WI_I32_LOAD:
+ case WI_I32_LOAD_8_S:
+ case WI_I32_LOAD_8_U:
+ case WI_I32_LOAD_16_S:
+ case WI_I32_LOAD_16_U:
+ case WI_I64_LOAD:
+ case WI_I64_LOAD_8_S:
+ case WI_I64_LOAD_8_U:
+ case WI_I64_LOAD_16_S:
+ case WI_I64_LOAD_16_U:
+ case WI_I64_LOAD_32_S:
+ case WI_I64_LOAD_32_U:
+ case WI_F32_LOAD:
+ case WI_F64_LOAD:
+ leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ break;
+
case WI_I32_CONST:
leb = int_to_leb128((i64) instr->data.i1, &leb_len);
bh_buffer_append(buff, leb, leb_len);
output_typesection(module, &master_buffer);
output_importsection(module, &master_buffer);
output_funcsection(module, &master_buffer);
+ output_memorysection(module, &master_buffer);
output_globalsection(module, &master_buffer);
output_exportsection(module, &master_buffer);
output_startsection(module, &master_buffer);