Basic allowance for := and ::

This commit is contained in:
Ginger Bill
2017-06-28 22:38:04 +01:00
parent 647e2cafd7
commit 9ca2246bac
5 changed files with 477 additions and 26 deletions
+28 -24
View File
@@ -2,12 +2,12 @@ import "fmt.odin";
proc general_stuff() {
// Complex numbers
var a = 3 + 4i;
var b: complex64 = 3 + 4i;
var c: complex128 = 3 + 4i;
var d = complex(2, 3);
a := 3 + 4i;
b: complex64 = 3 + 4i;
c: complex128 = 3 + 4i;
d := complex(2, 3);
var e = a / conj(a);
e := a / conj(a);
fmt.println("(3+4i)/(3-4i) =", e);
fmt.println(real(e), "+", imag(e), "i");
@@ -15,7 +15,8 @@ proc general_stuff() {
// C-style variadic procedures
foreign __llvm_core {
// The variadic part allows for extra type checking too which C does not provide
proc c_printf(fmt: ^u8, #c_vararg args: ..any) -> i32 #link_name "printf";
proc c_printf(fmt: ^u8, #c_vararg args: ..any) -> i32
#link_name "printf";
}
@@ -27,6 +28,9 @@ proc general_stuff() {
var foo = Foo{123, 0.513, "A string"};
var x, y, z = expand_to_tuple(foo);
fmt.println(x, y, z);
compile_assert(type_of(x) == int);
compile_assert(type_of(y) == f32);
compile_assert(type_of(z) == string);
// By default, all variables are zeroed
@@ -51,7 +55,6 @@ proc foreign_blocks() {
// See sys/windows.odin
}
proc default_arguments() {
proc hello(a: int = 9, b: int = 9) {
fmt.printf("a is %d; b is %d\n", a, b);
@@ -73,9 +76,9 @@ proc named_arguments() {
};
using Colour;
proc make_character(name, catch_phrase: string, favorite_color, least_favorite_color: Colour) {
proc make_character(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
fmt.println();
fmt.printf("My name is %v and I like %v. %v\n", name, favorite_color, catch_phrase);
fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase);
}
make_character("Frank", "¡Ay, caramba!", Blue, Green);
@@ -88,24 +91,24 @@ proc named_arguments() {
// Named arguments help to disambiguate this problem
make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
least_favorite_color = Green, favorite_color = Blue);
least_favourite_colour = Green, favourite_colour = Blue);
// The named arguments can be specifed in any order.
make_character(favorite_color = Octarine, catch_phrase = "U wot m8!",
least_favorite_color = Green, name = "Dennis");
make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
least_favourite_colour = Green, name = "Dennis");
// NOTE: You cannot mix named arguments with normal values
/*
make_character("Dennis",
favorite_color = Octarine, catch_phrase = "U wot m8!",
least_favorite_color = Green);
favourite_colour = Octarine, catch_phrase = "U wot m8!",
least_favourite_colour = Green);
*/
// Named arguments can also aid with default arguments
proc numerous_things(s : string, a = 1, b = 2, c = 3.14,
proc numerous_things(s: string, a = 1, b = 2, c = 3.14,
d = "The Best String!", e = false, f = 10.3/3.1, g = false) {
var g_str = g ? "true" : "false";
fmt.printf("How many?! %s: %v\n", s, g_str);
@@ -340,21 +343,22 @@ proc explicit_parametric_polymorphic_procedures() {
// An alternative to `union`s
for entity in entities {
match e in entity.derived {
case Rock: fmt.println("Rock", e.portable_id);
case Door: fmt.println("Door", e.portable_id);
case Monster: fmt.println("Monster", e.portable_id);
case Rock: fmt.println("Rock", e.portable_id, e.x, e.y);
case Door: fmt.println("Door", e.portable_id, e.x, e.y);
case Monster: fmt.println("Monster", e.portable_id, e.x, e.y);
}
}
}
proc main() {
general_stuff();
foreign_blocks();
default_arguments();
named_arguments();
default_return_values();
call_location();
explicit_parametric_polymorphic_procedures();
// foreign_blocks();
// default_arguments();
// named_arguments();
// default_return_values();
// call_location();
// explicit_parametric_polymorphic_procedures();
// Command line argument(s)!
// -opt=0,1,2,3
+147
View File
@@ -1666,6 +1666,153 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
c->context = prev_context;
case_end;
case_ast_node(vd, ValueDecl, node);
if (vd->is_mutable) {
Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
isize entity_count = 0;
if (vd->flags & VarDeclFlag_thread_local) {
vd->flags &= ~VarDeclFlag_thread_local;
error(node, "`thread_local` may only be applied to a variable declaration");
}
for_array(i, vd->names) {
AstNode *name = vd->names[i];
Entity *entity = NULL;
if (name->kind != AstNode_Ident) {
error(name, "A variable declaration must be an identifier");
} else {
Token token = name->Ident;
String str = token.string;
Entity *found = NULL;
// NOTE(bill): Ignore assignments to `_`
if (str != "_") {
found = current_scope_lookup_entity(c->context.scope, str);
}
if (found == NULL) {
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL, false);
entity->identifier = name;
AstNode *fl = c->context.curr_foreign_library;
if (fl != NULL) {
GB_ASSERT(fl->kind == AstNode_Ident);
entity->Variable.is_foreign = true;
entity->Variable.foreign_library_ident = fl;
}
} else {
TokenPos pos = found->token.pos;
error(token,
"Redeclaration of `%.*s` in this scope\n"
"\tat %.*s(%td:%td)",
LIT(str), LIT(pos.file), pos.line, pos.column);
entity = found;
}
}
if (entity == NULL) {
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
}
entity->parent_proc_decl = c->context.curr_proc_decl;
entities[entity_count++] = entity;
}
Type *init_type = NULL;
if (vd->type) {
init_type = check_type(c, vd->type, NULL);
if (init_type == NULL) {
init_type = t_invalid;
}
}
for (isize i = 0; i < entity_count; i++) {
Entity *e = entities[i];
GB_ASSERT(e != NULL);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
continue;
}
e->flags |= EntityFlag_Visited;
if (e->type == NULL) {
e->type = init_type;
}
}
check_arity_match(c, vd);
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
for (isize i = 0; i < entity_count; i++) {
Entity *e = entities[i];
if (e->Variable.is_foreign) {
if (vd->values.count > 0) {
error(e->token, "A foreign variable declaration cannot have a default value");
}
init_entity_foreign_library(c, e);
String name = e->token.string;
auto *fp = &c->info.foreigns;
HashKey key = hash_string(name);
Entity **found = map_get(fp, key);
if (found) {
Entity *f = *found;
TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type);
if (!are_types_identical(this_type, other_type)) {
error(e->token,
"Foreign entity `%.*s` previously declared elsewhere with a different type\n"
"\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
}
} else {
map_set(fp, key, e);
}
}
add_entity(c, c->context.scope, e->identifier, e);
}
if ((vd->flags & VarDeclFlag_using) != 0) {
Token token = ast_node_token(node);
if (vd->type != NULL && entity_count > 1) {
error(token, "`using` can only be applied to one variable of the same type");
// TODO(bill): Should a `continue` happen here?
}
for (isize entity_index = 0; entity_index < entity_count; entity_index++) {
Entity *e = entities[entity_index];
if (e == NULL) {
continue;
}
if (e->kind != Entity_Variable) {
continue;
}
bool is_immutable = e->Variable.is_immutable;
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope *scope = scope_of_node(&c->info, t->Record.node);
for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
uvar->Variable.is_immutable = is_immutable;
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
return;
}
}
}
} else {
// NOTE(bill): skip the rest to remove extra errors
error(token, "`using` can only be applied to variables of type struct or raw_union");
return;
}
}
}
}
case_end;
case_ast_node(gd, GenDecl, node);
GB_ASSERT(!c->context.scope->is_file);
+150
View File
@@ -1387,6 +1387,7 @@ void init_preload(Checker *c) {
bool check_arity_match(Checker *c, AstNodeValueSpec *s);
bool check_arity_match(Checker *c, AstNodeValueDecl *vd);
void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_scope);
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope);
@@ -1537,6 +1538,37 @@ bool check_arity_match(Checker *c, AstNodeValueSpec *spec) {
return true;
}
bool check_arity_match(Checker *c, AstNodeValueDecl *vd) {
isize lhs = vd->names.count;
isize rhs = vd->values.count;
if (rhs == 0) {
if (vd->type == NULL) {
error(vd->names[0], "Missing type or initial expression");
return false;
}
} else if (lhs < rhs) {
if (lhs < vd->values.count) {
AstNode *n = vd->values[lhs];
gbString str = expr_to_string(n);
error(n, "Extra initial expression `%s`", str);
gb_string_free(str);
} else {
error(vd->names[0], "Extra initial expression");
}
return false;
} else if (lhs > rhs && rhs != 1) {
AstNode *n = vd->names[rhs];
gbString str = expr_to_string(n);
error(n, "Missing expression for `%s`", str);
gb_string_free(str);
return false;
}
return true;
}
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope) {
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, ws->cond);
@@ -1595,6 +1627,124 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
}
case_end;
case_ast_node(vd, ValueDecl, decl);
if (vd->is_mutable) {
if (!c->context.scope->is_file) {
// NOTE(bill): local scope -> handle later and in order
break;
}
// NOTE(bill): You need to store the entity information here unline a constant declaration
isize entity_cap = vd->names.count;
isize entity_count = 0;
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_cap);
DeclInfo *di = NULL;
if (vd->values.count > 0) {
di = make_declaration_info(heap_allocator(), c->context.scope, c->context.decl);
di->entities = entities;
di->type_expr = vd->type;
di->init_expr = vd->values[0];
if (vd->flags & VarDeclFlag_thread_local) {
error(decl, "#thread_local variable declarations cannot have initialization values");
}
}
for_array(i, vd->names) {
AstNode *name = vd->names[i];
AstNode *value = NULL;
if (i < vd->values.count) {
value = vd->values[i];
}
if (name->kind != AstNode_Ident) {
error(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
continue;
}
Entity *e = make_entity_variable(c->allocator, c->context.scope, name->Ident, NULL, false);
e->Variable.is_thread_local = (vd->flags & VarDeclFlag_thread_local) != 0;
e->identifier = name;
if (vd->flags & VarDeclFlag_using) {
vd->flags &= ~VarDeclFlag_using; // NOTE(bill): This error will be only caught once
error(name, "`using` is not allowed at the file scope");
}
AstNode *fl = c->context.curr_foreign_library;
if (fl != NULL) {
GB_ASSERT(fl->kind == AstNode_Ident);
e->Variable.is_foreign = true;
e->Variable.foreign_library_ident = fl;
}
entities[entity_count++] = e;
DeclInfo *d = di;
if (d == NULL) {
AstNode *init_expr = value;
d = make_declaration_info(heap_allocator(), e->scope, c->context.decl);
d->type_expr = vd->type;
d->init_expr = init_expr;
}
add_entity_and_decl_info(c, name, e, d);
}
if (di != NULL) {
di->entity_count = entity_count;
}
check_arity_match(c, vd);
} else {
for_array(i, vd->names) {
AstNode *name = vd->names[i];
if (name->kind != AstNode_Ident) {
error(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
continue;
}
AstNode *init = unparen_expr(vd->values[i]);
AstNode *fl = c->context.curr_foreign_library;
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);
Entity *e = NULL;
if (is_ast_node_type(init)) {
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
d->type_expr = init;
d->init_expr = init;
} else if (init->kind == AstNode_ProcLit) {
ast_node(pl, ProcLit, init);
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, pl->tags);
if (fl != NULL) {
GB_ASSERT(fl->kind == AstNode_Ident);
e->Procedure.foreign_library_ident = fl;
pl->tags |= ProcTag_foreign;
}
GB_PANIC("TODO(bill): Constant procedure literals");
d->proc_decl = init;
d->type_expr = pl->type;
} else {
e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, empty_exact_value);
d->type_expr = vd->type;
d->init_expr = init;
}
e->identifier = name;
if (fl != NULL && e->kind != Entity_Procedure) {
error(name, "Only procedures and variables are allowed to be in a foreign block");
// continue;
}
add_entity_and_decl_info(c, name, e, d);
}
check_arity_match(c, vd);
}
case_end;
case_ast_node(gd, GenDecl, decl);
AstNodeValueSpec empty_spec = {};
AstNodeValueSpec *last_spec = NULL;
+77
View File
@@ -5949,6 +5949,83 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
ir_build_assign_op(proc, addr, v_one, op);
case_end;
case_ast_node(vd, ValueDecl, node);
if (vd->is_mutable) {
irModule *m = proc->module;
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
if (vd->values.count == 0) { // declared and zero-initialized
for_array(i, vd->names) {
AstNode *name = vd->names[i];
if (!ir_is_blank_ident(name)) {
ir_add_local_for_identifier(proc, name, true);
}
}
} else { // Tuple(s)
Array<irAddr> lvals = {};
Array<irValue *> inits = {};
array_init(&lvals, m->tmp_allocator, vd->names.count);
array_init(&inits, m->tmp_allocator, vd->names.count);
for_array(i, vd->names) {
AstNode *name = vd->names[i];
irAddr lval = ir_addr(NULL);
if (!ir_is_blank_ident(name)) {
ir_add_local_for_identifier(proc, name, false);
lval = ir_build_addr(proc, name);
}
array_add(&lvals, lval);
}
for_array(i, vd->values) {
irValue *init = ir_build_expr(proc, vd->values[i]);
Type *t = ir_type(init);
if (t->kind == Type_Tuple) {
for (isize i = 0; i < t->Tuple.variable_count; i++) {
Entity *e = t->Tuple.variables[i];
irValue *v = ir_emit_struct_ev(proc, init, i);
array_add(&inits, v);
}
} else {
array_add(&inits, init);
}
}
for_array(i, inits) {
ir_addr_store(proc, lvals[i], inits[i]);
}
}
gb_temp_arena_memory_end(tmp);
} else {
for_array(i, vd->names) {
AstNode *ident = vd->names[i];
GB_ASSERT(ident->kind == AstNode_Ident);
Entity *e = entity_of_ident(proc->module->info, ident);
GB_ASSERT(e != NULL);
if (e->kind == Entity_TypeName) {
// NOTE(bill): Generate a new name
// parent_proc.name-guid
String ts_name = e->token.string;
isize name_len = proc->name.len + 1 + ts_name.len + 1 + 10 + 1;
u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
i32 guid = cast(i32)proc->module->members.entries.count;
name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(ts_name), guid);
String name = make_string(name_text, name_len-1);
irValue *value = ir_value_type_name(proc->module->allocator,
name, e->type);
map_set(&proc->module->entity_names, hash_entity(e), name);
ir_gen_global_type_name(proc->module, e, name);
} else if (e->kind == Entity_Procedure) {
GB_PANIC("TODO(bill): Procedure values");
}
}
}
case_end;
case_ast_node(gd, GenDecl, node);
for_array(i, gd->specs) {
AstNode *spec = gd->specs[i];
+75 -2
View File
@@ -337,6 +337,15 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
u64 flags; \
CommentGroup docs; \
}) \
AST_NODE_KIND(ValueDecl, "value declaration", struct { \
Array<AstNode *> names; \
AstNode * type; \
Array<AstNode *> values; \
u64 flags; \
bool is_mutable; \
CommentGroup docs; \
CommentGroup comment; \
}) \
AST_NODE_KIND(ValueSpec, "value specification", struct { \
Array<AstNode *> names; \
AstNode * type; \
@@ -577,6 +586,7 @@ Token ast_node_token(AstNode *node) {
case AstNode_Label: return node->Label.token;
case AstNode_GenDecl: return node->GenDecl.token;
case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names[0]);
case AstNode_ValueSpec: return ast_node_token(node->ValueSpec.names[0]);
case AstNode_ImportSpec: return node->ImportSpec.import_name;
case AstNode_TypeSpec: return ast_node_token(node->TypeSpec.name);
@@ -817,6 +827,11 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
case AstNode_GenDecl:
n->GenDecl.specs = clone_ast_node_array(a, n->GenDecl.specs);
break;
case AstNode_ValueDecl:
n->ValueDecl.names = clone_ast_node_array(a, n->ValueDecl.names);
n->ValueDecl.type = clone_ast_node(a, n->ValueDecl.type);
n->ValueDecl.values = clone_ast_node_array(a, n->ValueDecl.values);
break;
case AstNode_ValueSpec:
n->ValueSpec.names = clone_ast_node_array(a, n->ValueSpec.names);
n->ValueSpec.type = clone_ast_node(a, n->ValueSpec.type);
@@ -1531,6 +1546,18 @@ AstNode *ast_gen_decl(AstFile *f, Token token, Token open, Token close, Array<As
return result;
}
AstNode *ast_value_decl(AstFile *f, Array<AstNode *> names, AstNode *type, Array<AstNode *> values, bool is_mutable,
CommentGroup docs, CommentGroup comment) {
AstNode *result = make_ast_node(f, AstNode_ValueDecl);
result->ValueDecl.names = names;
result->ValueDecl.type = type;
result->ValueDecl.values = values;
result->ValueDecl.is_mutable = is_mutable;
result->ValueDecl.docs = docs;
result->ValueDecl.comment = comment;
return result;
}
AstNode *ast_value_spec(AstFile *f, Array<AstNode *> names, AstNode *type, Array<AstNode *> values,
CommentGroup docs, CommentGroup comment) {
AstNode *result = make_ast_node(f, AstNode_ValueSpec);
@@ -3081,7 +3108,52 @@ AstNode *parse_decl(AstFile *f) {
return parse_gen_decl(f, token, func);
}
AstNode *parse_value_decl(AstFile *f, Array<AstNode *> names, CommentGroup docs) {
bool is_mutable = true;
AstNode *type = NULL;
Array<AstNode *> values = {};
Token colon = expect_token_after(f, Token_Colon, "identifier list");
type = parse_type_attempt(f);
if (f->curr_token.kind == Token_Eq ||
f->curr_token.kind == Token_Colon) {
Token sep = f->curr_token; next_token(f);
is_mutable = sep.kind != Token_Colon;
values = parse_rhs_expr_list(f);
if (values.count > names.count) {
syntax_error(f->curr_token, "Too many values on the right hand side of the declaration");
} else if (values.count < names.count && !is_mutable) {
syntax_error(f->curr_token, "All constant declarations must be defined");
} else if (values.count == 0) {
syntax_error(f->curr_token, "Expected an expression for this declaration");
}
}
if (is_mutable) {
if (type == NULL && values.count == 0) {
syntax_error(f->curr_token, "Missing variable type or initialization");
return ast_bad_decl(f, f->curr_token, f->curr_token);
}
} else {
if (type == NULL && values.count == 0 && names.count > 0) {
syntax_error(f->curr_token, "Missing constant value");
return ast_bad_decl(f, f->curr_token, f->curr_token);
}
}
if (values.data == NULL) {
values = make_ast_node_array(f);
}
if (f->expr_level >= 0) {
expect_semicolon(f, NULL);
}
return ast_value_decl(f, names, type, values, is_mutable, docs, f->line_comment);
}
AstNode *parse_simple_stmt(AstFile *f, StmtAllowFlag flags) {
Token token = f->curr_token;
@@ -3091,6 +3163,8 @@ AstNode *parse_simple_stmt(AstFile *f, StmtAllowFlag flags) {
return parse_decl(f);
}
CommentGroup docs = f->lead_comment;
Array<AstNode *> lhs = parse_lhs_expr_list(f);
token = f->curr_token;
switch (token.kind) {
@@ -3163,8 +3237,7 @@ AstNode *parse_simple_stmt(AstFile *f, StmtAllowFlag flags) {
} break;
}
}
// return parse_value_decl(f, lhs);
return parse_value_decl(f, lhs, docs);
}
if (lhs.count > 1) {