mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-18 03:42:23 -07:00
Basic allowance for := and ::
This commit is contained in:
+28
-24
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user