mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
parent
f0a43cfda9
commit
fbbef14013
@ -90,10 +90,12 @@ AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpre
|
|||||||
|
|
||||||
AssignmentOperator : token(Eq) | token(TimesEq) | token(DivEq) | token(ModEq) | token(PlusEq) | token(MinusEq) | token(BitShiftLeftEq) | token(BitShiftRightEq) | token(BitAndEq) | token(BitXorEq) | token(BitOrEq) | token(BoolAndEq) | token(BoolOrEq)
|
AssignmentOperator : token(Eq) | token(TimesEq) | token(DivEq) | token(ModEq) | token(PlusEq) | token(MinusEq) | token(BitShiftLeftEq) | token(BitShiftRightEq) | token(BitAndEq) | token(BitXorEq) | token(BitOrEq) | token(BoolAndEq) | token(BoolOrEq)
|
||||||
|
|
||||||
BlockExpression : IfExpression | Block | WhileExpression
|
BlockExpression : IfExpression | Block | WhileExpression | ForExpression
|
||||||
|
|
||||||
WhileExpression : token(While) token(LParen) Expression token(RParen) Expression
|
WhileExpression : token(While) token(LParen) Expression token(RParen) Expression
|
||||||
|
|
||||||
|
ForExpression : token(For) token(LParen) Symbol token(Comma) Expression option(token(Comma) token(Symbol)) token(RParen) Expression
|
||||||
|
|
||||||
BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExpression
|
BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExpression
|
||||||
|
|
||||||
ReturnExpression : token(Return) option(Expression)
|
ReturnExpression : token(Return) option(Expression)
|
||||||
|
|||||||
@ -138,6 +138,7 @@ enum NodeType {
|
|||||||
NodeTypeIfBoolExpr,
|
NodeTypeIfBoolExpr,
|
||||||
NodeTypeIfVarExpr,
|
NodeTypeIfVarExpr,
|
||||||
NodeTypeWhileExpr,
|
NodeTypeWhileExpr,
|
||||||
|
NodeTypeForExpr,
|
||||||
NodeTypeLabel,
|
NodeTypeLabel,
|
||||||
NodeTypeGoto,
|
NodeTypeGoto,
|
||||||
NodeTypeBreak,
|
NodeTypeBreak,
|
||||||
@ -393,6 +394,21 @@ struct AstNodeWhileExpr {
|
|||||||
bool condition_always_true;
|
bool condition_always_true;
|
||||||
bool contains_break;
|
bool contains_break;
|
||||||
Expr resolved_expr;
|
Expr resolved_expr;
|
||||||
|
BlockContext *block_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AstNodeForExpr {
|
||||||
|
AstNode *elem_node; // always a symbol
|
||||||
|
AstNode *array_expr;
|
||||||
|
AstNode *index_node; // always a symbol, might be null
|
||||||
|
AstNode *body;
|
||||||
|
|
||||||
|
// populated by semantic analyzer
|
||||||
|
bool contains_break;
|
||||||
|
Expr resolved_expr;
|
||||||
|
BlockContext *block_context;
|
||||||
|
VariableTableEntry *elem_var;
|
||||||
|
VariableTableEntry *index_var;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstNodeLabel {
|
struct AstNodeLabel {
|
||||||
@ -605,6 +621,7 @@ struct AstNode {
|
|||||||
AstNodeIfBoolExpr if_bool_expr;
|
AstNodeIfBoolExpr if_bool_expr;
|
||||||
AstNodeIfVarExpr if_var_expr;
|
AstNodeIfVarExpr if_var_expr;
|
||||||
AstNodeWhileExpr while_expr;
|
AstNodeWhileExpr while_expr;
|
||||||
|
AstNodeForExpr for_expr;
|
||||||
AstNodeLabel label;
|
AstNodeLabel label;
|
||||||
AstNodeGoto goto_expr;
|
AstNodeGoto goto_expr;
|
||||||
AstNodeAsmExpr asm_expr;
|
AstNodeAsmExpr asm_expr;
|
||||||
@ -916,7 +933,8 @@ struct VariableTableEntry {
|
|||||||
bool is_ptr; // if true, value_ref is a pointer
|
bool is_ptr; // if true, value_ref is a pointer
|
||||||
AstNode *decl_node;
|
AstNode *decl_node;
|
||||||
LLVMZigDILocalVariable *di_loc_var;
|
LLVMZigDILocalVariable *di_loc_var;
|
||||||
int arg_index;
|
int src_arg_index;
|
||||||
|
int gen_arg_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlockContext {
|
struct BlockContext {
|
||||||
@ -927,8 +945,8 @@ struct BlockContext {
|
|||||||
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
|
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
|
||||||
ZigList<Cast *> cast_expr_alloca_list;
|
ZigList<Cast *> cast_expr_alloca_list;
|
||||||
ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
|
ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
|
||||||
|
ZigList<VariableTableEntry *> variable_list;
|
||||||
AstNode *parent_loop_node;
|
AstNode *parent_loop_node;
|
||||||
AstNode *next_child_parent_loop_node;
|
|
||||||
LLVMZigDIScope *di_scope;
|
LLVMZigDIScope *di_scope;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
180
src/analyze.cpp
180
src/analyze.cpp
@ -60,6 +60,7 @@ static AstNode *first_executing_node(AstNode *node) {
|
|||||||
case NodeTypeStructField:
|
case NodeTypeStructField:
|
||||||
case NodeTypeStructValueField:
|
case NodeTypeStructValueField:
|
||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
|
case NodeTypeForExpr:
|
||||||
case NodeTypeContainerInitExpr:
|
case NodeTypeContainerInitExpr:
|
||||||
case NodeTypeArrayType:
|
case NodeTypeArrayType:
|
||||||
return node;
|
return node;
|
||||||
@ -867,6 +868,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
|
|||||||
case NodeTypeIfBoolExpr:
|
case NodeTypeIfBoolExpr:
|
||||||
case NodeTypeIfVarExpr:
|
case NodeTypeIfVarExpr:
|
||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
|
case NodeTypeForExpr:
|
||||||
case NodeTypeLabel:
|
case NodeTypeLabel:
|
||||||
case NodeTypeGoto:
|
case NodeTypeGoto:
|
||||||
case NodeTypeBreak:
|
case NodeTypeBreak:
|
||||||
@ -1175,12 +1177,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
|
|||||||
context->type_table.init(8);
|
context->type_table.init(8);
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
if (parent->next_child_parent_loop_node) {
|
context->parent_loop_node = parent->parent_loop_node;
|
||||||
context->parent_loop_node = parent->next_child_parent_loop_node;
|
|
||||||
parent->next_child_parent_loop_node = nullptr;
|
|
||||||
} else {
|
|
||||||
context->parent_loop_node = parent->parent_loop_node;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node && node->type == NodeTypeFnDef) {
|
if (node && node->type == NodeTypeFnDef) {
|
||||||
@ -1986,6 +1983,36 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
|
|||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set name to nullptr to make the variable anonymous (not visible to programmer).
|
||||||
|
static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, BlockContext *context,
|
||||||
|
Buf *name, TypeTableEntry *type_entry, bool is_const)
|
||||||
|
{
|
||||||
|
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
|
||||||
|
variable_entry->type = type_entry;
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
buf_init_from_buf(&variable_entry->name, name);
|
||||||
|
VariableTableEntry *existing_var = find_local_variable(context, name);
|
||||||
|
|
||||||
|
if (existing_var) {
|
||||||
|
add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
|
||||||
|
variable_entry->type = g->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->variable_table.put(&variable_entry->name, variable_entry);
|
||||||
|
context->variable_list.append(variable_entry);
|
||||||
|
} else {
|
||||||
|
buf_init_from_str(&variable_entry->name, "_anon");
|
||||||
|
context->variable_list.append(variable_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
variable_entry->is_const = is_const;
|
||||||
|
variable_entry->is_ptr = true;
|
||||||
|
variable_entry->decl_node = source_node;
|
||||||
|
|
||||||
|
return variable_entry;
|
||||||
|
}
|
||||||
|
|
||||||
static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import,
|
static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import,
|
||||||
BlockContext *context, AstNode *source_node,
|
BlockContext *context, AstNode *source_node,
|
||||||
AstNodeVariableDeclaration *variable_declaration,
|
AstNodeVariableDeclaration *variable_declaration,
|
||||||
@ -2037,38 +2064,26 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
|
|||||||
TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
|
TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
|
||||||
assert(type != nullptr); // should have been caught by the parser
|
assert(type != nullptr); // should have been caught by the parser
|
||||||
|
|
||||||
VariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol);
|
VariableTableEntry *var = add_local_var(g, source_node, context,
|
||||||
if (existing_variable) {
|
&variable_declaration->symbol, type, variable_declaration->is_const);
|
||||||
add_node_error(g, source_node,
|
|
||||||
buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol)));
|
|
||||||
} else {
|
|
||||||
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
|
|
||||||
buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
|
|
||||||
variable_entry->type = type;
|
|
||||||
variable_entry->is_const = variable_declaration->is_const;
|
|
||||||
variable_entry->is_ptr = true;
|
|
||||||
variable_entry->decl_node = source_node;
|
|
||||||
context->variable_table.put(&variable_entry->name, variable_entry);
|
|
||||||
|
|
||||||
bool is_pub = (variable_declaration->visib_mod != VisibModPrivate);
|
|
||||||
if (is_pub) {
|
bool is_pub = (variable_declaration->visib_mod != VisibModPrivate);
|
||||||
for (int i = 0; i < import->importers.length; i += 1) {
|
if (is_pub) {
|
||||||
ImporterInfo importer = import->importers.at(i);
|
for (int i = 0; i < import->importers.length; i += 1) {
|
||||||
auto table_entry = importer.import->block_context->variable_table.maybe_get(&variable_entry->name);
|
ImporterInfo importer = import->importers.at(i);
|
||||||
if (table_entry) {
|
auto table_entry = importer.import->block_context->variable_table.maybe_get(&var->name);
|
||||||
add_node_error(g, importer.source_node,
|
if (table_entry) {
|
||||||
buf_sprintf("import of variable '%s' overrides existing definition",
|
add_node_error(g, importer.source_node,
|
||||||
buf_ptr(&variable_entry->name)));
|
buf_sprintf("import of variable '%s' overrides existing definition",
|
||||||
} else {
|
buf_ptr(&var->name)));
|
||||||
importer.import->block_context->variable_table.put(&variable_entry->name, variable_entry);
|
} else {
|
||||||
}
|
importer.import->block_context->variable_table.put(&var->name, var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return variable_entry;
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
|
static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
|
||||||
@ -2172,11 +2187,15 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import,
|
|||||||
|
|
||||||
AstNode *condition_node = node->data.while_expr.condition;
|
AstNode *condition_node = node->data.while_expr.condition;
|
||||||
AstNode *while_body_node = node->data.while_expr.body;
|
AstNode *while_body_node = node->data.while_expr.body;
|
||||||
|
|
||||||
TypeTableEntry *condition_type = analyze_expression(g, import, context,
|
TypeTableEntry *condition_type = analyze_expression(g, import, context,
|
||||||
g->builtin_types.entry_bool, condition_node);
|
g->builtin_types.entry_bool, condition_node);
|
||||||
|
|
||||||
context->next_child_parent_loop_node = node;
|
BlockContext *child_context = new_block_context(node, context);
|
||||||
analyze_expression(g, import, context, g->builtin_types.entry_void, while_body_node);
|
child_context->parent_loop_node = node;
|
||||||
|
node->data.while_expr.block_context = child_context;
|
||||||
|
|
||||||
|
analyze_expression(g, import, child_context, g->builtin_types.entry_void, while_body_node);
|
||||||
|
|
||||||
|
|
||||||
TypeTableEntry *expr_return_type = g->builtin_types.entry_void;
|
TypeTableEntry *expr_return_type = g->builtin_types.entry_void;
|
||||||
@ -2200,6 +2219,54 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import,
|
|||||||
return expr_return_type;
|
return expr_return_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||||
|
TypeTableEntry *expected_type, AstNode *node)
|
||||||
|
{
|
||||||
|
assert(node->type == NodeTypeForExpr);
|
||||||
|
|
||||||
|
AstNode *array_node = node->data.for_expr.array_expr;
|
||||||
|
TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, array_node);
|
||||||
|
TypeTableEntry *child_type;
|
||||||
|
if (array_type->id == TypeTableEntryIdInvalid) {
|
||||||
|
child_type = array_type;
|
||||||
|
} else if (array_type->id == TypeTableEntryIdArray) {
|
||||||
|
child_type = array_type->data.array.child_type;
|
||||||
|
} else if (array_type->id == TypeTableEntryIdStruct &&
|
||||||
|
array_type->data.structure.is_unknown_size_array)
|
||||||
|
{
|
||||||
|
TypeTableEntry *pointer_type = array_type->data.structure.fields[0].type_entry;
|
||||||
|
assert(pointer_type->id == TypeTableEntryIdPointer);
|
||||||
|
child_type = pointer_type->data.pointer.child_type;
|
||||||
|
} else {
|
||||||
|
add_node_error(g, node,
|
||||||
|
buf_sprintf("iteration over non array type '%s'", buf_ptr(&array_type->name)));
|
||||||
|
child_type = g->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockContext *child_context = new_block_context(node, context);
|
||||||
|
node->data.for_expr.block_context = child_context;
|
||||||
|
|
||||||
|
AstNode *elem_var_node = node->data.for_expr.elem_node;
|
||||||
|
Buf *elem_var_name = &elem_var_node->data.symbol_expr.symbol;
|
||||||
|
node->data.for_expr.elem_var = add_local_var(g, elem_var_node, child_context, elem_var_name, child_type, true);
|
||||||
|
|
||||||
|
AstNode *index_var_node = node->data.for_expr.index_node;
|
||||||
|
if (index_var_node) {
|
||||||
|
Buf *index_var_name = &index_var_node->data.symbol_expr.symbol;
|
||||||
|
node->data.for_expr.index_var = add_local_var(g, index_var_node, child_context, index_var_name,
|
||||||
|
g->builtin_types.entry_usize, true);
|
||||||
|
} else {
|
||||||
|
node->data.for_expr.index_var = add_local_var(g, node, child_context, nullptr,
|
||||||
|
g->builtin_types.entry_usize, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode *for_body_node = node->data.for_expr.body;
|
||||||
|
analyze_expression(g, import, child_context, g->builtin_types.entry_void, for_body_node);
|
||||||
|
|
||||||
|
|
||||||
|
return g->builtin_types.entry_void;
|
||||||
|
}
|
||||||
|
|
||||||
static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||||
TypeTableEntry *expected_type, AstNode *node)
|
TypeTableEntry *expected_type, AstNode *node)
|
||||||
{
|
{
|
||||||
@ -3018,6 +3085,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
|||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
return_type = analyze_while_expr(g, import, context, expected_type, node);
|
return_type = analyze_while_expr(g, import, context, expected_type, node);
|
||||||
break;
|
break;
|
||||||
|
case NodeTypeForExpr:
|
||||||
|
return_type = analyze_for_expr(g, import, context, expected_type, node);
|
||||||
|
break;
|
||||||
case NodeTypeArrayType:
|
case NodeTypeArrayType:
|
||||||
return_type = analyze_array_type(g, import, context, expected_type, node);
|
return_type = analyze_array_type(g, import, context, expected_type, node);
|
||||||
break;
|
break;
|
||||||
@ -3081,6 +3151,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
|
|||||||
|
|
||||||
AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
|
AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
|
||||||
bool is_exported = (fn_proto->visib_mod == VisibModExport);
|
bool is_exported = (fn_proto->visib_mod == VisibModExport);
|
||||||
|
int gen_arg_index = 0;
|
||||||
for (int i = 0; i < fn_proto->params.length; i += 1) {
|
for (int i = 0; i < fn_proto->params.length; i += 1) {
|
||||||
AstNode *param_decl_node = fn_proto->params.at(i);
|
AstNode *param_decl_node = fn_proto->params.at(i);
|
||||||
assert(param_decl_node->type == NodeTypeParamDecl);
|
assert(param_decl_node->type == NodeTypeParamDecl);
|
||||||
@ -3099,28 +3170,15 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
|
|||||||
buf_sprintf("byvalue struct parameters not yet supported on exported functions"));
|
buf_sprintf("byvalue struct parameters not yet supported on exported functions"));
|
||||||
}
|
}
|
||||||
|
|
||||||
VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
|
VariableTableEntry *var = add_local_var(g, param_decl_node, context, ¶m_decl->name, type, true);
|
||||||
buf_init_from_buf(&variable_entry->name, ¶m_decl->name);
|
var->src_arg_index = i;
|
||||||
variable_entry->type = type;
|
param_decl_node->data.param_decl.variable = var;
|
||||||
variable_entry->is_const = true;
|
|
||||||
variable_entry->decl_node = param_decl_node;
|
|
||||||
variable_entry->arg_index = i;
|
|
||||||
|
|
||||||
param_decl_node->data.param_decl.variable = variable_entry;
|
if (type->size_in_bits > 0) {
|
||||||
|
var->gen_arg_index = gen_arg_index;
|
||||||
VariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name);
|
gen_arg_index += 1;
|
||||||
if (!existing_entry) {
|
|
||||||
// unique definition
|
|
||||||
context->variable_table.put(&variable_entry->name, variable_entry);
|
|
||||||
} else {
|
} else {
|
||||||
add_node_error(g, node,
|
var->gen_arg_index = -1;
|
||||||
buf_sprintf("redeclaration of parameter '%s'.", buf_ptr(&existing_entry->name)));
|
|
||||||
if (existing_entry->type == variable_entry->type) {
|
|
||||||
// types agree, so the type is probably good enough for the rest of analysis
|
|
||||||
} else {
|
|
||||||
// types disagree. don't trust either one of them.
|
|
||||||
existing_entry->type = g->builtin_types.entry_invalid;;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3187,6 +3245,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
|
|||||||
case NodeTypeIfBoolExpr:
|
case NodeTypeIfBoolExpr:
|
||||||
case NodeTypeIfVarExpr:
|
case NodeTypeIfVarExpr:
|
||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
|
case NodeTypeForExpr:
|
||||||
case NodeTypeLabel:
|
case NodeTypeLabel:
|
||||||
case NodeTypeGoto:
|
case NodeTypeGoto:
|
||||||
case NodeTypeBreak:
|
case NodeTypeBreak:
|
||||||
@ -3281,6 +3340,10 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
|
|||||||
collect_expr_decl_deps(g, import, node->data.while_expr.condition, decl_node);
|
collect_expr_decl_deps(g, import, node->data.while_expr.condition, decl_node);
|
||||||
collect_expr_decl_deps(g, import, node->data.while_expr.body, decl_node);
|
collect_expr_decl_deps(g, import, node->data.while_expr.body, decl_node);
|
||||||
break;
|
break;
|
||||||
|
case NodeTypeForExpr:
|
||||||
|
collect_expr_decl_deps(g, import, node->data.for_expr.array_expr, decl_node);
|
||||||
|
collect_expr_decl_deps(g, import, node->data.for_expr.body, decl_node);
|
||||||
|
break;
|
||||||
case NodeTypeBlock:
|
case NodeTypeBlock:
|
||||||
for (int i = 0; i < node->data.block.statements.length; i += 1) {
|
for (int i = 0; i < node->data.block.statements.length; i += 1) {
|
||||||
AstNode *stmt = node->data.block.statements.at(i);
|
AstNode *stmt = node->data.block.statements.at(i);
|
||||||
@ -3505,6 +3568,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
|
|||||||
case NodeTypeIfBoolExpr:
|
case NodeTypeIfBoolExpr:
|
||||||
case NodeTypeIfVarExpr:
|
case NodeTypeIfVarExpr:
|
||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
|
case NodeTypeForExpr:
|
||||||
case NodeTypeLabel:
|
case NodeTypeLabel:
|
||||||
case NodeTypeGoto:
|
case NodeTypeGoto:
|
||||||
case NodeTypeBreak:
|
case NodeTypeBreak:
|
||||||
@ -3681,6 +3745,8 @@ Expr *get_resolved_expr(AstNode *node) {
|
|||||||
return &node->data.if_var_expr.resolved_expr;
|
return &node->data.if_var_expr.resolved_expr;
|
||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
return &node->data.while_expr.resolved_expr;
|
return &node->data.while_expr.resolved_expr;
|
||||||
|
case NodeTypeForExpr:
|
||||||
|
return &node->data.for_expr.resolved_expr;
|
||||||
case NodeTypeAsmExpr:
|
case NodeTypeAsmExpr:
|
||||||
return &node->data.asm_expr.resolved_expr;
|
return &node->data.asm_expr.resolved_expr;
|
||||||
case NodeTypeContainerInitExpr:
|
case NodeTypeContainerInitExpr:
|
||||||
@ -3743,6 +3809,7 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
|
|||||||
case NodeTypeIfBoolExpr:
|
case NodeTypeIfBoolExpr:
|
||||||
case NodeTypeIfVarExpr:
|
case NodeTypeIfVarExpr:
|
||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
|
case NodeTypeForExpr:
|
||||||
case NodeTypeAsmExpr:
|
case NodeTypeAsmExpr:
|
||||||
case NodeTypeContainerInitExpr:
|
case NodeTypeContainerInitExpr:
|
||||||
case NodeTypeRoot:
|
case NodeTypeRoot:
|
||||||
@ -3793,6 +3860,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
|
|||||||
case NodeTypeIfBoolExpr:
|
case NodeTypeIfBoolExpr:
|
||||||
case NodeTypeIfVarExpr:
|
case NodeTypeIfVarExpr:
|
||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
|
case NodeTypeForExpr:
|
||||||
case NodeTypeAsmExpr:
|
case NodeTypeAsmExpr:
|
||||||
case NodeTypeContainerInitExpr:
|
case NodeTypeContainerInitExpr:
|
||||||
case NodeTypeRoot:
|
case NodeTypeRoot:
|
||||||
|
|||||||
148
src/codegen.cpp
148
src/codegen.cpp
@ -526,41 +526,35 @@ static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) {
|
|||||||
return array_ptr;
|
return array_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
|
static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMValueRef array_ptr,
|
||||||
assert(node->type == NodeTypeArrayAccessExpr);
|
TypeTableEntry *array_type, LLVMValueRef subscript_value)
|
||||||
|
{
|
||||||
AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr;
|
|
||||||
TypeTableEntry *type_entry = get_expr_type(array_expr_node);
|
|
||||||
|
|
||||||
LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node);
|
|
||||||
|
|
||||||
LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript);
|
|
||||||
assert(subscript_value);
|
assert(subscript_value);
|
||||||
|
|
||||||
if (type_entry->size_in_bits == 0) {
|
if (array_type->size_in_bits == 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type_entry->id == TypeTableEntryIdArray) {
|
if (array_type->id == TypeTableEntryIdArray) {
|
||||||
LLVMValueRef indices[] = {
|
LLVMValueRef indices[] = {
|
||||||
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
|
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
|
||||||
subscript_value
|
subscript_value
|
||||||
};
|
};
|
||||||
add_debug_source_node(g, node);
|
add_debug_source_node(g, source_node);
|
||||||
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
|
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
|
||||||
} else if (type_entry->id == TypeTableEntryIdPointer) {
|
} else if (array_type->id == TypeTableEntryIdPointer) {
|
||||||
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
||||||
LLVMValueRef indices[] = {
|
LLVMValueRef indices[] = {
|
||||||
subscript_value
|
subscript_value
|
||||||
};
|
};
|
||||||
add_debug_source_node(g, node);
|
add_debug_source_node(g, source_node);
|
||||||
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, "");
|
return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, "");
|
||||||
} else if (type_entry->id == TypeTableEntryIdStruct) {
|
} else if (array_type->id == TypeTableEntryIdStruct) {
|
||||||
assert(type_entry->data.structure.is_unknown_size_array);
|
assert(array_type->data.structure.is_unknown_size_array);
|
||||||
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
||||||
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
|
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
|
||||||
|
|
||||||
add_debug_source_node(g, node);
|
add_debug_source_node(g, source_node);
|
||||||
LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, 0, "");
|
LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, 0, "");
|
||||||
LLVMValueRef ptr = LLVMBuildLoad(g->builder, ptr_ptr, "");
|
LLVMValueRef ptr = LLVMBuildLoad(g->builder, ptr_ptr, "");
|
||||||
return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, "");
|
return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, "");
|
||||||
@ -569,6 +563,19 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
|
||||||
|
assert(node->type == NodeTypeArrayAccessExpr);
|
||||||
|
|
||||||
|
AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr;
|
||||||
|
TypeTableEntry *array_type = get_expr_type(array_expr_node);
|
||||||
|
|
||||||
|
LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node);
|
||||||
|
|
||||||
|
LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript);
|
||||||
|
|
||||||
|
return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value);
|
||||||
|
}
|
||||||
|
|
||||||
static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **out_type_entry) {
|
static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **out_type_entry) {
|
||||||
assert(node->type == NodeTypeFieldAccessExpr);
|
assert(node->type == NodeTypeFieldAccessExpr);
|
||||||
|
|
||||||
@ -1695,10 +1702,13 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
|
|||||||
assert(node->data.while_expr.condition);
|
assert(node->data.while_expr.condition);
|
||||||
assert(node->data.while_expr.body);
|
assert(node->data.while_expr.body);
|
||||||
|
|
||||||
|
BlockContext *old_block_context = g->cur_block_context;
|
||||||
|
|
||||||
bool condition_always_true = node->data.while_expr.condition_always_true;
|
bool condition_always_true = node->data.while_expr.condition_always_true;
|
||||||
bool contains_break = node->data.while_expr.contains_break;
|
bool contains_break = node->data.while_expr.contains_break;
|
||||||
if (condition_always_true) {
|
if (condition_always_true) {
|
||||||
// generate a forever loop
|
// generate a forever loop
|
||||||
|
g->cur_block_context = node->data.while_expr.block_context;
|
||||||
|
|
||||||
LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
|
LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
|
||||||
LLVMBasicBlockRef end_block = nullptr;
|
LLVMBasicBlockRef end_block = nullptr;
|
||||||
@ -1735,6 +1745,7 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
|
|||||||
LLVMBuildBr(g->builder, cond_block);
|
LLVMBuildBr(g->builder, cond_block);
|
||||||
|
|
||||||
LLVMPositionBuilderAtEnd(g->builder, cond_block);
|
LLVMPositionBuilderAtEnd(g->builder, cond_block);
|
||||||
|
g->cur_block_context = old_block_context;
|
||||||
LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition);
|
LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition);
|
||||||
add_debug_source_node(g, node->data.while_expr.condition);
|
add_debug_source_node(g, node->data.while_expr.condition);
|
||||||
LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);
|
LLVMBuildCondBr(g->builder, cond_val, body_block, end_block);
|
||||||
@ -1742,6 +1753,7 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
|
|||||||
LLVMPositionBuilderAtEnd(g->builder, body_block);
|
LLVMPositionBuilderAtEnd(g->builder, body_block);
|
||||||
g->break_block_stack.append(end_block);
|
g->break_block_stack.append(end_block);
|
||||||
g->continue_block_stack.append(cond_block);
|
g->continue_block_stack.append(cond_block);
|
||||||
|
g->cur_block_context = node->data.while_expr.block_context;
|
||||||
gen_expr(g, node->data.while_expr.body);
|
gen_expr(g, node->data.while_expr.body);
|
||||||
g->break_block_stack.pop();
|
g->break_block_stack.pop();
|
||||||
g->continue_block_stack.pop();
|
g->continue_block_stack.pop();
|
||||||
@ -1753,6 +1765,77 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
|
|||||||
LLVMPositionBuilderAtEnd(g->builder, end_block);
|
LLVMPositionBuilderAtEnd(g->builder, end_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g->cur_block_context = old_block_context;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef gen_for_expr(CodeGen *g, AstNode *node) {
|
||||||
|
assert(node->type == NodeTypeForExpr);
|
||||||
|
assert(node->data.for_expr.array_expr);
|
||||||
|
assert(node->data.for_expr.body);
|
||||||
|
|
||||||
|
VariableTableEntry *elem_var = node->data.for_expr.elem_var;
|
||||||
|
assert(elem_var);
|
||||||
|
|
||||||
|
TypeTableEntry *array_type = get_expr_type(node->data.for_expr.array_expr);
|
||||||
|
|
||||||
|
VariableTableEntry *index_var = node->data.for_expr.index_var;
|
||||||
|
assert(index_var);
|
||||||
|
LLVMValueRef index_ptr = index_var->value_ref;
|
||||||
|
LLVMValueRef one_const = LLVMConstInt(g->builtin_types.entry_usize->type_ref, 1, false);
|
||||||
|
|
||||||
|
BlockContext *old_block_context = g->cur_block_context;
|
||||||
|
|
||||||
|
LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForCond");
|
||||||
|
LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForBody");
|
||||||
|
LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForEnd");
|
||||||
|
|
||||||
|
LLVMValueRef array_val = gen_expr(g, node->data.for_expr.array_expr);
|
||||||
|
add_debug_source_node(g, node);
|
||||||
|
LLVMBuildStore(g->builder, LLVMConstNull(index_var->type->type_ref), index_ptr);
|
||||||
|
LLVMValueRef len_val;
|
||||||
|
TypeTableEntry *child_type;
|
||||||
|
if (array_type->id == TypeTableEntryIdArray) {
|
||||||
|
len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
|
||||||
|
array_type->data.array.len, false);
|
||||||
|
child_type = array_type->data.array.child_type;
|
||||||
|
} else if (array_type->id == TypeTableEntryIdStruct) {
|
||||||
|
assert(array_type->data.structure.is_unknown_size_array);
|
||||||
|
TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry;
|
||||||
|
assert(child_ptr_type->id == TypeTableEntryIdPointer);
|
||||||
|
child_type = child_ptr_type->data.pointer.child_type;
|
||||||
|
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, array_val, 1, "");
|
||||||
|
len_val = LLVMBuildLoad(g->builder, len_field_ptr, "");
|
||||||
|
} else {
|
||||||
|
zig_unreachable();
|
||||||
|
}
|
||||||
|
LLVMBuildBr(g->builder, cond_block);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, cond_block);
|
||||||
|
LLVMValueRef index_val = LLVMBuildLoad(g->builder, index_ptr, "");
|
||||||
|
LLVMValueRef cond = LLVMBuildICmp(g->builder, LLVMIntSLT, index_val, len_val, "");
|
||||||
|
LLVMBuildCondBr(g->builder, cond, body_block, end_block);
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, body_block);
|
||||||
|
LLVMValueRef elem_ptr = gen_array_elem_ptr(g, node, array_val, array_type, index_val);
|
||||||
|
LLVMValueRef elem_val = handle_is_ptr(child_type) ? elem_ptr : LLVMBuildLoad(g->builder, elem_ptr, "");
|
||||||
|
gen_assign_raw(g, node, BinOpTypeAssign, elem_var->value_ref, elem_val,
|
||||||
|
elem_var->type, child_type);
|
||||||
|
g->break_block_stack.append(end_block);
|
||||||
|
g->continue_block_stack.append(cond_block);
|
||||||
|
g->cur_block_context = node->data.for_expr.block_context;
|
||||||
|
gen_expr(g, node->data.for_expr.body);
|
||||||
|
g->break_block_stack.pop();
|
||||||
|
g->continue_block_stack.pop();
|
||||||
|
if (get_expr_type(node->data.for_expr.body)->id != TypeTableEntryIdUnreachable) {
|
||||||
|
add_debug_source_node(g, node);
|
||||||
|
LLVMValueRef new_index_val = LLVMBuildAdd(g->builder, index_val, one_const, "");
|
||||||
|
LLVMBuildStore(g->builder, new_index_val, index_ptr);
|
||||||
|
LLVMBuildBr(g->builder, cond_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMPositionBuilderAtEnd(g->builder, end_block);
|
||||||
|
g->cur_block_context = old_block_context;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1935,6 +2018,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
|
|||||||
return gen_if_var_expr(g, node);
|
return gen_if_var_expr(g, node);
|
||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
return gen_while_expr(g, node);
|
return gen_while_expr(g, node);
|
||||||
|
case NodeTypeForExpr:
|
||||||
|
return gen_for_expr(g, node);
|
||||||
case NodeTypeAsmExpr:
|
case NodeTypeAsmExpr:
|
||||||
return gen_asm_expr(g, node);
|
return gen_asm_expr(g, node);
|
||||||
case NodeTypeNumberLiteral:
|
case NodeTypeNumberLiteral:
|
||||||
@ -2177,22 +2262,6 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
|
|
||||||
fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram);
|
fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram);
|
||||||
|
|
||||||
int non_void_param_count = count_non_void_params(g, &fn_proto->params);
|
|
||||||
assert(non_void_param_count == (int)LLVMCountParams(fn));
|
|
||||||
LLVMValueRef *params = allocate<LLVMValueRef>(non_void_param_count);
|
|
||||||
LLVMGetParams(fn, params);
|
|
||||||
|
|
||||||
int non_void_index = 0;
|
|
||||||
for (int param_i = 0; param_i < fn_proto->params.length; param_i += 1) {
|
|
||||||
AstNode *param_decl = fn_proto->params.at(param_i);
|
|
||||||
assert(param_decl->type == NodeTypeParamDecl);
|
|
||||||
if (is_param_decl_type_void(g, param_decl))
|
|
||||||
continue;
|
|
||||||
VariableTableEntry *parameter_variable = fn_def_node->data.fn_def.block_context->variable_table.get(¶m_decl->data.param_decl.name);
|
|
||||||
parameter_variable->value_ref = params[non_void_index];
|
|
||||||
non_void_index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
AstNode *body_node = fn_def_node->data.fn_def.body;
|
AstNode *body_node = fn_def_node->data.fn_def.body;
|
||||||
build_label_blocks(g, body_node);
|
build_label_blocks(g, body_node);
|
||||||
|
|
||||||
@ -2212,13 +2281,9 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
|
|
||||||
g->cur_block_context = block_context;
|
g->cur_block_context = block_context;
|
||||||
|
|
||||||
auto it = block_context->variable_table.entry_iterator();
|
for (int var_i = 0; var_i < block_context->variable_list.length; var_i += 1) {
|
||||||
for (;;) {
|
VariableTableEntry *var = block_context->variable_list.at(var_i);
|
||||||
auto *entry = it.next();
|
|
||||||
if (!entry)
|
|
||||||
break;
|
|
||||||
|
|
||||||
VariableTableEntry *var = entry->value;
|
|
||||||
if (var->type->size_in_bits == 0) {
|
if (var->type->size_in_bits == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2227,7 +2292,10 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
unsigned arg_no;
|
unsigned arg_no;
|
||||||
if (block_context->node->type == NodeTypeFnDef) {
|
if (block_context->node->type == NodeTypeFnDef) {
|
||||||
tag = LLVMZigTag_DW_arg_variable();
|
tag = LLVMZigTag_DW_arg_variable();
|
||||||
arg_no = var->arg_index + 1;
|
arg_no = var->gen_arg_index + 1;
|
||||||
|
|
||||||
|
var->is_ptr = false;
|
||||||
|
var->value_ref = LLVMGetParam(fn, var->gen_arg_index);
|
||||||
} else {
|
} else {
|
||||||
tag = LLVMZigTag_DW_auto_variable();
|
tag = LLVMZigTag_DW_auto_variable();
|
||||||
arg_no = 0;
|
arg_no = 0;
|
||||||
|
|||||||
@ -121,6 +121,8 @@ const char *node_type_str(NodeType node_type) {
|
|||||||
return "IfVarExpr";
|
return "IfVarExpr";
|
||||||
case NodeTypeWhileExpr:
|
case NodeTypeWhileExpr:
|
||||||
return "WhileExpr";
|
return "WhileExpr";
|
||||||
|
case NodeTypeForExpr:
|
||||||
|
return "ForExpr";
|
||||||
case NodeTypeLabel:
|
case NodeTypeLabel:
|
||||||
return "Label";
|
return "Label";
|
||||||
case NodeTypeGoto:
|
case NodeTypeGoto:
|
||||||
@ -331,6 +333,15 @@ void ast_print(AstNode *node, int indent) {
|
|||||||
ast_print(node->data.while_expr.condition, indent + 2);
|
ast_print(node->data.while_expr.condition, indent + 2);
|
||||||
ast_print(node->data.while_expr.body, indent + 2);
|
ast_print(node->data.while_expr.body, indent + 2);
|
||||||
break;
|
break;
|
||||||
|
case NodeTypeForExpr:
|
||||||
|
fprintf(stderr, "%s\n", node_type_str(node->type));
|
||||||
|
ast_print(node->data.for_expr.elem_node, indent + 2);
|
||||||
|
ast_print(node->data.for_expr.array_expr, indent + 2);
|
||||||
|
if (node->data.for_expr.index_node) {
|
||||||
|
ast_print(node->data.for_expr.index_node, indent + 2);
|
||||||
|
}
|
||||||
|
ast_print(node->data.for_expr.body, indent + 2);
|
||||||
|
break;
|
||||||
case NodeTypeLabel:
|
case NodeTypeLabel:
|
||||||
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name));
|
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name));
|
||||||
break;
|
break;
|
||||||
@ -2114,8 +2125,49 @@ static AstNode *ast_parse_while_expr(ParseContext *pc, int *token_index, bool ma
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AstNode *ast_parse_symbol(ParseContext *pc, int *token_index) {
|
||||||
|
Token *token = ast_eat_token(pc, token_index, TokenIdSymbol);
|
||||||
|
AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
|
||||||
|
ast_buf_from_token(pc, token, &node->data.symbol_expr.symbol);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
BlockExpression : IfExpression | Block | WhileExpression
|
ForExpression : token(For) token(LParen) Symbol token(Comma) Expression option(token(Comma) token(Symbol)) token(RParen) Expression
|
||||||
|
*/
|
||||||
|
static AstNode *ast_parse_for_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
|
Token *token = &pc->tokens->at(*token_index);
|
||||||
|
|
||||||
|
if (token->id != TokenIdKeywordFor) {
|
||||||
|
if (mandatory) {
|
||||||
|
ast_invalid_token_error(pc, token);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*token_index += 1;
|
||||||
|
|
||||||
|
AstNode *node = ast_create_node(pc, NodeTypeForExpr, token);
|
||||||
|
|
||||||
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||||
|
node->data.for_expr.elem_node = ast_parse_symbol(pc, token_index);
|
||||||
|
ast_eat_token(pc, token_index, TokenIdComma);
|
||||||
|
node->data.for_expr.array_expr = ast_parse_expression(pc, token_index, true);
|
||||||
|
|
||||||
|
Token *comma = &pc->tokens->at(*token_index);
|
||||||
|
if (comma->id == TokenIdComma) {
|
||||||
|
*token_index += 1;
|
||||||
|
node->data.for_expr.index_node = ast_parse_symbol(pc, token_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
||||||
|
|
||||||
|
node->data.for_expr.body = ast_parse_expression(pc, token_index, true);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
BlockExpression : IfExpression | Block | WhileExpression | ForExpression
|
||||||
*/
|
*/
|
||||||
static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
|
||||||
Token *token = &pc->tokens->at(*token_index);
|
Token *token = &pc->tokens->at(*token_index);
|
||||||
@ -2132,6 +2184,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma
|
|||||||
if (while_expr)
|
if (while_expr)
|
||||||
return while_expr;
|
return while_expr;
|
||||||
|
|
||||||
|
AstNode *for_expr = ast_parse_for_expr(pc, token_index, false);
|
||||||
|
if (for_expr)
|
||||||
|
return for_expr;
|
||||||
|
|
||||||
if (mandatory)
|
if (mandatory)
|
||||||
ast_invalid_token_error(pc, token);
|
ast_invalid_token_error(pc, token);
|
||||||
|
|
||||||
|
|||||||
@ -231,6 +231,8 @@ static void end_token(Tokenize *t) {
|
|||||||
t->cur_tok->id = TokenIdKeywordStruct;
|
t->cur_tok->id = TokenIdKeywordStruct;
|
||||||
} else if (mem_eql_str(token_mem, token_len, "enum")) {
|
} else if (mem_eql_str(token_mem, token_len, "enum")) {
|
||||||
t->cur_tok->id = TokenIdKeywordEnum;
|
t->cur_tok->id = TokenIdKeywordEnum;
|
||||||
|
} else if (mem_eql_str(token_mem, token_len, "for")) {
|
||||||
|
t->cur_tok->id = TokenIdKeywordFor;
|
||||||
} else if (mem_eql_str(token_mem, token_len, "while")) {
|
} else if (mem_eql_str(token_mem, token_len, "while")) {
|
||||||
t->cur_tok->id = TokenIdKeywordWhile;
|
t->cur_tok->id = TokenIdKeywordWhile;
|
||||||
} else if (mem_eql_str(token_mem, token_len, "continue")) {
|
} else if (mem_eql_str(token_mem, token_len, "continue")) {
|
||||||
@ -1028,6 +1030,7 @@ const char * token_name(TokenId id) {
|
|||||||
case TokenIdKeywordStruct: return "struct";
|
case TokenIdKeywordStruct: return "struct";
|
||||||
case TokenIdKeywordEnum: return "enum";
|
case TokenIdKeywordEnum: return "enum";
|
||||||
case TokenIdKeywordWhile: return "while";
|
case TokenIdKeywordWhile: return "while";
|
||||||
|
case TokenIdKeywordFor: return "for";
|
||||||
case TokenIdKeywordContinue: return "continue";
|
case TokenIdKeywordContinue: return "continue";
|
||||||
case TokenIdKeywordBreak: return "break";
|
case TokenIdKeywordBreak: return "break";
|
||||||
case TokenIdKeywordNull: return "null";
|
case TokenIdKeywordNull: return "null";
|
||||||
|
|||||||
@ -31,6 +31,7 @@ enum TokenId {
|
|||||||
TokenIdKeywordStruct,
|
TokenIdKeywordStruct,
|
||||||
TokenIdKeywordEnum,
|
TokenIdKeywordEnum,
|
||||||
TokenIdKeywordWhile,
|
TokenIdKeywordWhile,
|
||||||
|
TokenIdKeywordFor,
|
||||||
TokenIdKeywordContinue,
|
TokenIdKeywordContinue,
|
||||||
TokenIdKeywordBreak,
|
TokenIdKeywordBreak,
|
||||||
TokenIdKeywordNull,
|
TokenIdKeywordNull,
|
||||||
|
|||||||
@ -25,12 +25,9 @@ fn strlen(ptr: &u8) usize => {
|
|||||||
|
|
||||||
fn call_main() unreachable => {
|
fn call_main() unreachable => {
|
||||||
var args: [argc][]u8;
|
var args: [argc][]u8;
|
||||||
var i : @typeof(argc) = 0;
|
for (arg, args, i) {
|
||||||
// TODO for in loop over the array
|
|
||||||
while (i < argc) {
|
|
||||||
const ptr = argv[i];
|
const ptr = argv[i];
|
||||||
args[i] = ptr[0...strlen(ptr)];
|
args[i] = ptr[0...strlen(ptr)];
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
exit(main(args))
|
exit(main(args))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1136,6 +1136,32 @@ pub fn main(args: [][]u8) i32 => {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
)SOURCE", "hello\nthis\nis\nmy\nthing\n");
|
)SOURCE", "hello\nthis\nis\nmy\nthing\n");
|
||||||
|
|
||||||
|
add_simple_case("for loops", R"SOURCE(
|
||||||
|
import "std.zig";
|
||||||
|
|
||||||
|
pub fn main(args: [][]u8) i32 => {
|
||||||
|
const array = []u8 {9, 8, 7, 6};
|
||||||
|
for (item, array) {
|
||||||
|
print_u64(item);
|
||||||
|
print_str("\n");
|
||||||
|
}
|
||||||
|
for (item, array, index) {
|
||||||
|
print_u64(index);
|
||||||
|
print_str("\n");
|
||||||
|
}
|
||||||
|
const unknown_size: []u8 = array;
|
||||||
|
for (item, unknown_size) {
|
||||||
|
print_u64(item);
|
||||||
|
print_str("\n");
|
||||||
|
}
|
||||||
|
for (item, unknown_size, index) {
|
||||||
|
print_u64(index);
|
||||||
|
print_str("\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
)SOURCE", "9\n8\n7\n6\n0\n1\n2\n3\n9\n8\n7\n6\n0\n1\n2\n3\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1226,7 +1252,7 @@ fn b() => {}
|
|||||||
add_compile_fail_case("parameter redeclaration", R"SOURCE(
|
add_compile_fail_case("parameter redeclaration", R"SOURCE(
|
||||||
fn f(a : i32, a : i32) => {
|
fn f(a : i32, a : i32) => {
|
||||||
}
|
}
|
||||||
)SOURCE", 1, ".tmp_source.zig:2:1: error: redeclaration of parameter 'a'");
|
)SOURCE", 1, ".tmp_source.zig:2:15: error: redeclaration of variable 'a'");
|
||||||
|
|
||||||
add_compile_fail_case("local variable redeclaration", R"SOURCE(
|
add_compile_fail_case("local variable redeclaration", R"SOURCE(
|
||||||
fn f() => {
|
fn f() => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user