println(x); // Doesn't work
}
+[X] Initialization statements on control statements should be better. For example, this should
+ be possible:
+
+ if x, y := foo(); x != y {
+ // ...
+ }
+
[X] macros are not allowed at the expression level. This is not necessarily a bug, but does
bring many complications to the table about how resolve this. Current solution is to
turn expression macros (macros that specify a return value) into a `do` expression.
-// CLEANUP: FINISH THIS EXAMPLE WHEN OVERLOADS ARE BETTER
-
// Overloaded procedures, if you're not familiar with the concept, means that a
// given procedure has multiple implementations and which one is used depends on
+// There going to be examples in this sections that I do not want to be part of the
+// code, but I want the syntax highlighting to apply to them so I'm just going to
+// `#if false` this section out so the examples compiled.
+#if false {
+
+// If you are unfamiliar, polymorphic procedures allow you to write code that applies
+// to all types, instead of to only a particular type. For example, you will probably
+// want something like a max function that returns the larger of its two arguments.
+// You might start by writing it for just integer types as so:
+
+max :: (a: i32, b: i32) -> i32 {
+ return a if a > b else b;
+}
+
+// This will work as advertised, but if you try to call it with something other than
+// i32's, you will get a compile time error, even though the function body doesn't do
+// any thing that only applies to i32's. The function body just assumes that '>' is
+// defined for a and b.
+
+// To write max in a polymorphic way, you can do it in this way:
+
+max :: (a: $Type, b: Type) -> Type {
+ return a if a > b else b;
+}
+
+// In this example, 'Type' is a polymorphic variable, meaning that it is solved for
+// whenever 'max' is called. You'll notice that the first 'Type' has a dollar sign in
+// front of it. The dollar sign appears exactly once in front the polymorphic variable
+// name, as it specifies where the variable should be solved for. In this example, 'Type'
+// will be given to be the same type as 'a', and then 'b' and the return type should be
+// of the same type.
+
+// As another example, take 'array.push':
+
+push :: (arr: ^[..] $T, value: T) {
+ // ...
+}
+
+// The dollar sign in front of the first T means the argument will provide the type for T,
+// then the value is expected to be the same type. If the dollar sign would have been on
+// the second T like so,
+
+push :: (arr: ^[..] T, value: $T) {
+ // ...
+}
+
+// then the type of value would be used for T. This wouldn't change much in practice, for a
+// correct program. However, if your program has an error, then the second example would
+// report an error saying that the first argument is of the wrong type, instead of the more
+// likely error that the second argument is of the wrong type.
+
+// One final example is a single compose function:
+
+compose :: (a: $A, f: (A) -> $B, g: (B) -> $C) -> C {
+ return g(f(a));
+}
+
+}
+
#load "core/std"
use package core
AstNode_base;
Scope *scope;
- AstLocal *local;
- AstBinaryOp *assignment;
+ AstNode* initialization;
AstTyped *cond;
AstNode_base;
Scope *scope;
- AstLocal *local;
- AstBinaryOp *assignment;
+ AstNode* initialization;
AstTyped *expr;
}
CheckStatus check_if(AstIfWhile* ifnode) {
- if (ifnode->assignment != NULL) CHECK(statement, (AstNode **) &ifnode->assignment);
+ if (ifnode->initialization != NULL) CHECK(statement_chain, &ifnode->initialization);
if (ifnode->kind == Ast_Kind_Static_If) {
if ((ifnode->flags & Ast_Flag_Static_If_Resolved) == 0) {
if (static_if_resolution(ifnode)) {
if (ifnode->true_stmt != NULL) CHECK(statement, (AstNode **) &ifnode->true_stmt);
-
+
} else {
if (ifnode->false_stmt != NULL) CHECK(statement, (AstNode **) &ifnode->false_stmt);
}
}
CheckStatus check_while(AstIfWhile* whilenode) {
- if (whilenode->assignment != NULL) CHECK(statement, (AstNode **) &whilenode->assignment);
+ if (whilenode->initialization != NULL) CHECK(statement_chain, &whilenode->initialization);
CHECK(expression, &whilenode->cond);
}
CheckStatus check_switch(AstSwitch* switchnode) {
- if (switchnode->assignment != NULL) CHECK(statement, (AstNode **) &switchnode->assignment);
+ if (switchnode->initialization != NULL) CHECK(statement_chain, &switchnode->initialization);
CHECK(expression, &switchnode->expr);
Type* resolved_expr_type = resolve_expression_type(switchnode->expr);
case Ast_Kind_If:
case Ast_Kind_While:
- C(AstIfWhile, local);
- C(AstIfWhile, assignment);
-
- if (((AstIfWhile *) nn)->assignment)
- ((AstIfWhile *) nn)->assignment->left = (AstTyped *) ((AstIfWhile *) nn)->local;
+ ((AstIfWhile *) nn)->initialization = ast_clone_list(a, ((AstIfWhile *) node)->initialization);
C(AstIfWhile, cond);
//fallthrough
AstSwitch* dw = (AstSwitch *) nn;
AstSwitch* sw = (AstSwitch *) node;
- dw->local = (AstLocal *) ast_clone(a, sw->local);
- dw->assignment = (AstBinaryOp *) ast_clone(a, sw->assignment);
- if (dw->assignment)
- dw->assignment->left = (AstTyped *) sw->local;
+ ((AstSwitch *) nn)->initialization = ast_clone_list(a, ((AstSwitch *) node)->initialization);
dw->expr = (AstTyped *) ast_clone(a, sw->expr);
dw->default_case = (AstBlock *) ast_clone(a, sw->default_case);
if_node->token = expect_token(parser, Token_Type_Keyword_If);
AstIfWhile* root_if = if_node;
+ AstTyped* cond;
+ AstNode* initialization_or_cond=NULL;
+ b32 had_initialization = 0;
+ if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
+ had_initialization = 1;
- // CLEANUP: All of the control blocks use this same kind of logic, and it
- // is underpowered for what I think should be possible. Factor it out and
- // make it better?
- if (peek_token(1)->type == ':') {
- OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
- if_node->local = make_local(parser->allocator, local_sym, NULL);
-
- expect_token(parser, ':');
-
- AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- assignment->operation = Binary_Op_Assign;
- assignment->token = expect_token(parser, '=');
- assignment->left = (AstTyped *) if_node->local;
- assignment->right = parse_expression(parser, 0);
+ } else {
+ // NOTE: Assignment is allowed here because instead of not parsing correctly,
+ // an error is reported in the typechecking, saying that assignment isn't allowed
+ // here, which is better than an unexpected token error.
+ initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
+ }
- if_node->assignment = assignment;
+ if (had_initialization || parser->curr->type == ';') {
expect_token(parser, ';');
+ cond = parse_expression(parser, 1);
+ } else {
+ cond = (AstTyped *) initialization_or_cond;
+ initialization_or_cond = NULL;
}
- // NOTE: Assignment is allowed here because instead of not parsing correctly,
- // an error is reported in the typechecking, saying that assignment isn't allowed
- // here, which is better than an unexpected token error.
- AstTyped* cond = parse_expression(parser, 1);
AstBlock* true_stmt = parse_block(parser, 1);
+ if_node->initialization = initialization_or_cond;
if_node->cond = cond;
if (true_stmt != NULL)
if_node->true_stmt = true_stmt;
AstIfWhile* while_node = make_node(AstIfWhile, Ast_Kind_While);
while_node->token = while_token;
- // CLEANUP: See above in parse_if_stmt
- if (peek_token(1)->type == ':') {
- OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
- while_node->local = make_local(parser->allocator, local_sym, NULL);
+ AstTyped* cond;
+ AstNode* initialization_or_cond=NULL;
+ b32 had_initialization = 0;
+ if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
+ had_initialization = 1;
- expect_token(parser, ':');
-
- AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- assignment->operation = Binary_Op_Assign;
- assignment->token = expect_token(parser, '=');
- assignment->left = (AstTyped *) while_node->local;
- assignment->right = parse_expression(parser, 0);
+ } else {
+ // NOTE: Assignment is allowed here because instead of not parsing correctly,
+ // an error is reported in the typechecking, saying that assignment isn't allowed
+ // here, which is better than an unexpected token error.
+ initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
+ }
- while_node->assignment = assignment;
+ if (had_initialization || parser->curr->type == ';') {
expect_token(parser, ';');
+ cond = parse_expression(parser, 1);
+ } else {
+ cond = (AstTyped *) initialization_or_cond;
+ initialization_or_cond = NULL;
}
- while_node->cond = parse_expression(parser, 1);
+ while_node->initialization = initialization_or_cond;
+ while_node->cond = cond;
while_node->true_stmt = parse_block(parser, 1);
if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
bh_arr_new(global_heap_allocator, switch_node->cases, 4);
- // CLEANUP: See above in parse_if_stmt
- if (peek_token(1)->type == ':') {
- OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
- switch_node->local = make_local(parser->allocator, local_sym, NULL);
-
- expect_token(parser, ':');
+ AstTyped* expr;
+ AstNode* initialization_or_expr=NULL;
+ b32 had_initialization = 0;
+ if (parse_possible_symbol_declaration(parser, &initialization_or_expr)) {
+ had_initialization = 1;
- AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- assignment->operation = Binary_Op_Assign;
- assignment->token = expect_token(parser, '=');
- assignment->left = (AstTyped *) switch_node->local;
- assignment->right = parse_expression(parser, 0);
+ } else {
+ // NOTE: Assignment is allowed here because instead of not parsing correctly,
+ // an error is reported in the typechecking, saying that assignment isn't allowed
+ // here, which is better than an unexpected token error.
+ initialization_or_expr = (AstNode *) parse_compound_expression(parser, 1);
+ }
- switch_node->assignment = assignment;
+ if (had_initialization || parser->curr->type == ';') {
expect_token(parser, ';');
+ expr = parse_expression(parser, 1);
+
+ } else {
+ expr = (AstTyped *) initialization_or_expr;
+ initialization_or_expr = NULL;
}
- switch_node->expr = parse_expression(parser, 1);
+ switch_node->initialization = initialization_or_expr;
+ switch_node->expr = expr;
+
expect_token(parser, '{');
while (consume_token_if_next(parser, Token_Type_Keyword_Case)) {
if (parser->curr->type == ':') {
AstBinding* binding = parse_top_level_binding(parser, symbol);
if (parser->hit_unexpected_token) return 2;
-
+
ENTITY_SUBMIT(binding);
return 2;
}
i32 symbol_res = parse_possible_symbol_declaration(parser, &retval);
if (symbol_res == 2) needs_semicolon = 0;
if (symbol_res != 0) break;
-
+
// fallthrough
}
if (parser->curr->type != '=')
memres->type_node = parse_type(parser);
-
+
if (consume_token_if_next(parser, '='))
memres->initial_value = parse_expression(parser, 1);
-
-
+
+
ENTITY_SUBMIT(memres);
AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
AstBinding* binding = parse_top_level_binding(parser, binding_name);
if (binding) ENTITY_SUBMIT(binding);
-
+
consume_token_if_next(parser, ';');
} else {
}
global_node->type_node = parse_type(parser);
-
+
ENTITY_SUBMIT(global_node);
return (AstTyped *) global_node;
if (static_if_resolution(ifnode)) {
if (ifnode->true_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
-
+
} else {
if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
}
} else {
- if (ifnode->assignment != NULL) {
+ if (ifnode->initialization != NULL) {
ifnode->scope = scope_create(context.ast_alloc, curr_scope, ifnode->token->pos);
scope_enter(ifnode->scope);
- SYMRES(local, &ifnode->local);
-
- SYMRES(statement, (AstNode **) &ifnode->assignment, NULL);
+ SYMRES(statement_chain, &ifnode->initialization);
}
SYMRES(expression, &ifnode->cond);
if (ifnode->true_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
- if (ifnode->assignment != NULL) scope_leave();
+ if (ifnode->initialization != NULL) scope_leave();
}
return Symres_Success;
}
static SymresStatus symres_while(AstIfWhile* whilenode) {
- if (whilenode->assignment != NULL) {
+ if (whilenode->initialization != NULL) {
whilenode->scope = scope_create(context.ast_alloc, curr_scope, whilenode->token->pos);
scope_enter(whilenode->scope);
- SYMRES(local, &whilenode->local);
-
- SYMRES(statement, (AstNode **) &whilenode->assignment, NULL);
+ SYMRES(statement_chain, &whilenode->initialization);
}
SYMRES(expression, &whilenode->cond);
if (whilenode->true_stmt) SYMRES(block, whilenode->true_stmt);
if (whilenode->false_stmt) SYMRES(block, whilenode->false_stmt);
- if (whilenode->assignment != NULL) scope_leave();
+ if (whilenode->initialization != NULL) scope_leave();
return Symres_Success;
}
}
static SymresStatus symres_switch(AstSwitch* switchnode) {
- if (switchnode->assignment != NULL) {
+ if (switchnode->initialization != NULL) {
switchnode->scope = scope_create(context.ast_alloc, curr_scope, switchnode->token->pos);
scope_enter(switchnode->scope);
- symbol_introduce(curr_scope, switchnode->local->token, (AstNode *) switchnode->local);
-
- SYMRES(statement, (AstNode **) &switchnode->assignment, NULL);
+ SYMRES(statement_chain, &switchnode->initialization);
}
SYMRES(expression, &switchnode->expr);
bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
bh_arr_each(AstTyped *, value, sc->values)
SYMRES(expression, value);
-
+
SYMRES(block, sc->block);
}
if (switchnode->default_case)
SYMRES(block, switchnode->default_case);
- if (switchnode->assignment != NULL) scope_leave();
+ if (switchnode->initialization != NULL) scope_leave();
+
return Symres_Success;
}
EMIT_FUNC(if, AstIfWhile* if_node) {
bh_arr(WasmInstruction) code = *pcode;
- if (if_node->assignment != NULL) {
- bh_imap_put(&mod->local_map, (u64) if_node->local, local_allocate(mod->local_alloc, (AstTyped *) if_node->local));
+ if (if_node->initialization != NULL) {
+ // bh_imap_put(&mod->local_map, (u64) if_node->local, local_allocate(mod->local_alloc, (AstTyped *) if_node->local));
- emit_assignment(mod, &code, if_node->assignment);
+ forll (AstNode, stmt, if_node->initialization, next) {
+ emit_statement(mod, &code, stmt);
+ }
}
if (if_node->kind == Ast_Kind_Static_If) {
emit_leave_structured_block(mod, &code);
- if (if_node->assignment != NULL) {
- local_free(mod->local_alloc, (AstTyped *) if_node->local);
- }
-
-
*pcode = code;
}
EMIT_FUNC(while, AstIfWhile* while_node) {
bh_arr(WasmInstruction) code = *pcode;
- if (while_node->assignment != NULL) {
- bh_imap_put(&mod->local_map, (u64) while_node->local, local_allocate(mod->local_alloc, (AstTyped *) while_node->local));
-
- emit_assignment(mod, &code, while_node->assignment);
+ if (while_node->initialization != NULL) {
+ forll (AstNode, stmt, while_node->initialization, next) {
+ emit_statement(mod, &code, stmt);
+ }
}
if (while_node->false_stmt == NULL) {
emit_leave_structured_block(mod, &code);
}
- if (while_node->assignment != NULL)
- local_free(mod->local_alloc, (AstTyped *) while_node->local);
-
*pcode = code;
}
bh_imap block_map;
bh_imap_init(&block_map, global_heap_allocator, bh_arr_length(switch_node->cases));
- if (switch_node->assignment != NULL) {
- bh_imap_put(&mod->local_map, (u64) switch_node->local, local_allocate(mod->local_alloc, (AstTyped *) switch_node->local));
-
- emit_assignment(mod, &code, switch_node->assignment);
+ if (switch_node->initialization != NULL) {
+ forll (AstNode, stmt, switch_node->initialization, next) {
+ emit_statement(mod, &code, stmt);
+ }
}
emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
emit_leave_structured_block(mod, &code);
- if (switch_node->assignment != NULL)
- local_free(mod->local_alloc, (AstTyped *) switch_node->local);
-
bh_imap_free(&block_map);
*pcode = code;
}