Commit 08a2311efd
Changed files (10)
doc/vim/syntax/zig.vim
@@ -7,8 +7,8 @@ if exists("b:current_syntax")
finish
endif
-syn keyword zigKeyword fn return mut const extern unreachable export pub as use
-syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void
+syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void
+syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128
syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
syn region zigCommentLineDoc start="//\%(//\@!\|!\)" end="$" contains=zigTodo,@Spell
src/analyze.cpp
@@ -274,6 +274,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:
+ case NodeTypeIfExpr:
zig_unreachable();
}
}
@@ -302,7 +303,9 @@ static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry *
add_node_error(g, node, buf_sprintf("type mismatch. expected %s. got %s", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
}
-static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) {
+static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+ TypeTableEntry *expected_type, AstNode *node)
+{
TypeTableEntry *return_type = nullptr;
switch (node->type) {
case NodeTypeBlock:
@@ -348,10 +351,64 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
case NodeTypeBinOpExpr:
{
- // TODO: think about expected types
- analyze_expression(g, import, context, expected_type, node->data.bin_op_expr.op1);
- analyze_expression(g, import, context, expected_type, node->data.bin_op_expr.op2);
- return_type = expected_type;
+ switch (node->data.bin_op_expr.bin_op) {
+ case BinOpTypeBoolOr:
+ case BinOpTypeBoolAnd:
+ analyze_expression(g, import, context, g->builtin_types.entry_bool,
+ node->data.bin_op_expr.op1);
+ analyze_expression(g, import, context, g->builtin_types.entry_bool,
+ node->data.bin_op_expr.op2);
+ return_type = g->builtin_types.entry_bool;
+ break;
+ case BinOpTypeCmpEq:
+ case BinOpTypeCmpNotEq:
+ case BinOpTypeCmpLessThan:
+ case BinOpTypeCmpGreaterThan:
+ case BinOpTypeCmpLessOrEq:
+ case BinOpTypeCmpGreaterOrEq:
+ // TODO think how should type checking for these work?
+ analyze_expression(g, import, context, g->builtin_types.entry_i32,
+ node->data.bin_op_expr.op1);
+ analyze_expression(g, import, context, g->builtin_types.entry_i32,
+ node->data.bin_op_expr.op2);
+ return_type = g->builtin_types.entry_bool;
+ break;
+ case BinOpTypeBinOr:
+ zig_panic("TODO bin or type");
+ break;
+ case BinOpTypeBinXor:
+ zig_panic("TODO bin xor type");
+ break;
+ case BinOpTypeBinAnd:
+ zig_panic("TODO bin and type");
+ break;
+ case BinOpTypeBitShiftLeft:
+ zig_panic("TODO bit shift left type");
+ break;
+ case BinOpTypeBitShiftRight:
+ zig_panic("TODO bit shift right type");
+ break;
+ case BinOpTypeAdd:
+ case BinOpTypeSub:
+ // TODO think how should type checking for these work?
+ analyze_expression(g, import, context, g->builtin_types.entry_i32,
+ node->data.bin_op_expr.op1);
+ analyze_expression(g, import, context, g->builtin_types.entry_i32,
+ node->data.bin_op_expr.op2);
+ return_type = g->builtin_types.entry_i32;
+ break;
+ case BinOpTypeMult:
+ zig_panic("TODO mult type");
+ break;
+ case BinOpTypeDiv:
+ zig_panic("TODO div type");
+ break;
+ case BinOpTypeMod:
+ zig_panic("TODO modulus type");
+ break;
+ case BinOpTypeInvalid:
+ zig_unreachable();
+ }
break;
}
@@ -426,11 +483,46 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
case NodeTypeSymbol:
// look up symbol in symbol table
- zig_panic("TODO");
+ zig_panic("TODO analyze_expression symbol");
case NodeTypeCastExpr:
+ zig_panic("TODO analyze_expression cast expr");
+ break;
+
case NodeTypePrefixOpExpr:
- zig_panic("TODO");
+ switch (node->data.prefix_op_expr.prefix_op) {
+ case PrefixOpBoolNot:
+ analyze_expression(g, import, context, g->builtin_types.entry_bool,
+ node->data.prefix_op_expr.primary_expr);
+ return_type = g->builtin_types.entry_bool;
+ break;
+ case PrefixOpBinNot:
+ zig_panic("TODO type check bin not");
+ break;
+ case PrefixOpNegation:
+ zig_panic("TODO type check negation");
+ break;
+ case PrefixOpInvalid:
+ zig_unreachable();
+ }
+ break;
+ case NodeTypeIfExpr:
+ {
+ analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_expr.condition);
+
+ TypeTableEntry *else_type;
+ if (node->data.if_expr.else_node) {
+ else_type = analyze_expression(g, import, context, expected_type, node->data.if_expr.else_node);
+ } else {
+ else_type = g->builtin_types.entry_void;
+ }
+ TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,
+ node->data.if_expr.then_block);
+
+ check_type_compatibility(g, node, expected_type, else_type);
+ return_type = then_type;
+ break;
+ }
case NodeTypeDirective:
case NodeTypeFnDecl:
case NodeTypeFnProto:
@@ -445,6 +537,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
}
assert(return_type);
check_type_compatibility(g, node, expected_type, return_type);
+
+ assert(!node->codegen_node);
+ node->codegen_node = allocate<CodeGenNode>(1);
+ node->codegen_node->data.expr_node.type_entry = return_type;
+
return return_type;
}
@@ -509,6 +606,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:
+ case NodeTypeIfExpr:
zig_unreachable();
}
}
src/codegen.cpp
@@ -120,6 +120,10 @@ static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) {
zig_unreachable();
}
+static TypeTableEntry *get_expr_type(AstNode *node) {
+ return node->codegen_node->data.expr_node.type_entry;
+}
+
static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
@@ -283,6 +287,7 @@ static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
+ LLVMBasicBlockRef orig_block = LLVMGetInsertBlock(g->builder);
// block for when val1 == true
LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndTrue");
// block for when val1 == false (don't even evaluate the second part)
@@ -297,13 +302,14 @@ static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
add_debug_source_node(g, node);
LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
+ LLVMBuildBr(g->builder, false_block);
LLVMPositionBuilderAtEnd(g->builder, false_block);
add_debug_source_node(g, node);
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
- LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), true_block};
+ LLVMBasicBlockRef incoming_blocks[2] = {orig_block, true_block};
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
return phi;
@@ -314,6 +320,8 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
LLVMValueRef val1 = gen_expr(g, expr_node->data.bin_op_expr.op1);
+ LLVMBasicBlockRef orig_block = LLVMGetInsertBlock(g->builder);
+
// block for when val1 == false
LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrFalse");
// block for when val1 == true (don't even evaluate the second part)
@@ -328,13 +336,14 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
LLVMValueRef val2 = gen_expr(g, expr_node->data.bin_op_expr.op2);
add_debug_source_node(g, expr_node);
LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
+ LLVMBuildBr(g->builder, true_block);
LLVMPositionBuilderAtEnd(g->builder, true_block);
add_debug_source_node(g, expr_node);
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
- LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), false_block};
+ LLVMBasicBlockRef incoming_blocks[2] = {orig_block, false_block};
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
return phi;
@@ -383,9 +392,91 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
return LLVMBuildRetVoid(g->builder);
}
}
-/*
-Expression : BoolOrExpression | ReturnExpression
-*/
+
+static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
+ assert(node->type == NodeTypeIfExpr);
+ assert(node->data.if_expr.condition);
+ assert(node->data.if_expr.then_block);
+
+ LLVMValueRef cond_value = gen_expr(g, node->data.if_expr.condition);
+
+ TypeTableEntry *then_type = get_expr_type(node->data.if_expr.then_block);
+ bool use_expr_value = (then_type != g->builtin_types.entry_unreachable &&
+ then_type != g->builtin_types.entry_void);
+
+ if (node->data.if_expr.else_node) {
+ LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
+ LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
+ LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
+
+ LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, then_block);
+ LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block);
+ LLVMBuildBr(g->builder, endif_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, else_block);
+ LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node);
+ LLVMBuildBr(g->builder, endif_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, endif_block);
+ if (use_expr_value) {
+ LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
+ LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
+ LLVMBasicBlockRef incoming_blocks[2] = {then_block, else_block};
+ LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+
+ return phi;
+ }
+
+ return nullptr;
+ }
+
+ assert(!use_expr_value);
+
+ LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
+ LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
+
+ LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, then_block);
+ gen_expr(g, node->data.if_expr.then_block);
+ LLVMBuildBr(g->builder, endif_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, endif_block);
+ return nullptr;
+}
+
+static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
+ assert(block_node->type == NodeTypeBlock);
+
+ ImportTableEntry *import = g->cur_fn->import_entry;
+
+ LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(),
+ import->di_file, block_node->line + 1, block_node->column + 1);
+ g->block_scopes.append(LLVMZigLexicalBlockToScope(di_block));
+
+ add_debug_source_node(g, block_node);
+
+ LLVMValueRef return_value;
+ for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
+ AstNode *statement_node = block_node->data.block.statements.at(i);
+ return_value = gen_expr(g, statement_node);
+ }
+
+ if (implicit_return_type) {
+ if (implicit_return_type == g->builtin_types.entry_void) {
+ LLVMBuildRetVoid(g->builder);
+ } else if (implicit_return_type != g->builtin_types.entry_unreachable) {
+ LLVMBuildRet(g->builder, return_value);
+ }
+ }
+
+ g->block_scopes.pop();
+
+ return return_value;
+}
+
static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
switch (node->type) {
case NodeTypeBinOpExpr:
@@ -403,6 +494,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
return LLVMBuildUnreachable(g->builder);
case NodeTypeVoid:
return nullptr;
+ case NodeTypeIfExpr:
+ return gen_if_expr(g, node);
case NodeTypeNumberLiteral:
{
Buf *number_str = &node->data.number;
@@ -427,6 +520,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
Buf *name = &node->data.symbol;
return get_variable_value(g, name);
}
+ case NodeTypeBlock:
+ return gen_block(g, node, nullptr);
case NodeTypeRoot:
case NodeTypeRootExportDecl:
case NodeTypeFnProto:
@@ -434,7 +529,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
case NodeTypeFnDecl:
case NodeTypeParamDecl:
case NodeTypeType:
- case NodeTypeBlock:
case NodeTypeExternBlock:
case NodeTypeDirective:
case NodeTypeUse:
@@ -443,30 +537,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
zig_unreachable();
}
-static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node, TypeTableEntry *implicit_return_type) {
- assert(block_node->type == NodeTypeBlock);
-
- LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(),
- import->di_file, block_node->line + 1, block_node->column + 1);
- g->block_scopes.append(LLVMZigLexicalBlockToScope(di_block));
-
- add_debug_source_node(g, block_node);
-
- LLVMValueRef return_value;
- for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
- AstNode *statement_node = block_node->data.block.statements.at(i);
- return_value = gen_expr(g, statement_node);
- }
-
- if (implicit_return_type == g->builtin_types.entry_void) {
- LLVMBuildRetVoid(g->builder);
- } else if (implicit_return_type != g->builtin_types.entry_unreachable) {
- LLVMBuildRet(g->builder, return_value);
- }
-
- g->block_scopes.pop();
-}
-
static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto,
LLVMZigDIFile *di_file)
{
@@ -558,7 +628,7 @@ static void do_code_gen(CodeGen *g) {
LLVMGetParams(fn, codegen_fn_def->params);
TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
- gen_block(g, import, fn_def_node->data.fn_def.body, implicit_return_type);
+ gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);
g->block_scopes.pop();
}
@@ -585,6 +655,15 @@ static void define_primitive_types(CodeGen *g) {
buf_init_from_str(&entry->name, "(invalid)");
g->builtin_types.entry_invalid = entry;
}
+ {
+ TypeTableEntry *entry = allocate<TypeTableEntry>(1);
+ entry->type_ref = LLVMInt1Type();
+ buf_init_from_str(&entry->name, "bool");
+ entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 1, 8,
+ LLVMZigEncoding_DW_ATE_unsigned());
+ g->type_table.put(&entry->name, entry);
+ g->builtin_types.entry_bool = entry;
+ }
{
TypeTableEntry *entry = allocate<TypeTableEntry>(1);
entry->type_ref = LLVMInt8Type();
@@ -803,7 +882,7 @@ static Buf *to_c_type(CodeGen *g, AstNode *type_node) {
g->c_stdint_used = true;
return buf_create_from_str("int32_t");
} else {
- zig_panic("TODO");
+ zig_panic("TODO to_c_type");
}
}
src/parser.cpp
@@ -91,6 +91,8 @@ const char *node_type_str(NodeType node_type) {
return "Use";
case NodeTypeVoid:
return "Void";
+ case NodeTypeIfExpr:
+ return "IfExpr";
}
zig_unreachable();
}
@@ -236,7 +238,15 @@ void ast_print(AstNode *node, int indent) {
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));
break;
case NodeTypeVoid:
- fprintf(stderr, "Void\n");
+ fprintf(stderr, "%s\n", node_type_str(node->type));
+ break;
+ case NodeTypeIfExpr:
+ fprintf(stderr, "%s\n", node_type_str(node->type));
+ if (node->data.if_expr.condition)
+ ast_print(node->data.if_expr.condition, indent + 2);
+ ast_print(node->data.if_expr.then_block, indent + 2);
+ if (node->data.if_expr.else_node)
+ ast_print(node->data.if_expr.else_node, indent + 2);
break;
}
}
@@ -353,6 +363,7 @@ static void ast_invalid_token_error(ParseContext *pc, Token *token) {
static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory);
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory);
+static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory);
static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
@@ -558,7 +569,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool
}
/*
-PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | Block | token(Symbol)
+PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)
*/
static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
@@ -588,11 +599,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
return node;
}
- AstNode *block_node = ast_parse_block(pc, token_index, false);
- if (block_node) {
- return block_node;
- }
-
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
if (grouped_expr_node) {
return grouped_expr_node;
@@ -975,6 +981,50 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool
return node;
}
+/*
+ElseIf : token(Else) IfExpression
+Else : token(Else) Block
+*/
+static AstNode *ast_parse_else_or_else_if(ParseContext *pc, int *token_index, bool mandatory) {
+ Token *else_token = &pc->tokens->at(*token_index);
+
+ if (else_token->id != TokenIdKeywordElse) {
+ if (mandatory) {
+ ast_invalid_token_error(pc, else_token);
+ } else {
+ return nullptr;
+ }
+ }
+ *token_index += 1;
+
+ AstNode *if_expr = ast_parse_if_expr(pc, token_index, false);
+ if (if_expr)
+ return if_expr;
+
+ return ast_parse_block(pc, token_index, true);
+}
+
+/*
+IfExpression : token(If) Expression Block option(Else | ElseIf)
+*/
+static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) {
+ Token *if_tok = &pc->tokens->at(*token_index);
+ if (if_tok->id != TokenIdKeywordIf) {
+ if (mandatory) {
+ ast_invalid_token_error(pc, if_tok);
+ } else {
+ return nullptr;
+ }
+ }
+ *token_index += 1;
+
+ AstNode *node = ast_create_node(pc, NodeTypeIfExpr, if_tok);
+ node->data.if_expr.condition = ast_parse_expression(pc, token_index, true);
+ node->data.if_expr.then_block = ast_parse_block(pc, token_index, true);
+ node->data.if_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false);
+ return node;
+}
+
/*
ReturnExpression : token(Return) option(Expression)
*/
@@ -1016,27 +1066,68 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool
}
/*
-Expression : BoolOrExpression | ReturnExpression
+BlockExpression : IfExpression | Block
*/
-static AstNode *ast_parse_expression(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);
- AstNode *return_expr = ast_parse_return_expr(pc, token_index, false);
- if (return_expr)
- return return_expr;
+ AstNode *if_expr = ast_parse_if_expr(pc, token_index, false);
+ if (if_expr)
+ return if_expr;
+
+ AstNode *block = ast_parse_block(pc, token_index, false);
+ if (block)
+ return block;
+
+ if (mandatory)
+ ast_invalid_token_error(pc, token);
+
+ return nullptr;
+}
+
+/*
+NonBlockExpression : BoolOrExpression | ReturnExpression
+*/
+static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
+ Token *token = &pc->tokens->at(*token_index);
AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false);
if (bool_or_expr)
return bool_or_expr;
- if (!mandatory)
- return nullptr;
+ AstNode *return_expr = ast_parse_return_expr(pc, token_index, false);
+ if (return_expr)
+ return return_expr;
- ast_invalid_token_error(pc, token);
+ if (mandatory)
+ ast_invalid_token_error(pc, token);
+
+ return nullptr;
+}
+
+/*
+Expression : BlockExpression | NonBlockExpression
+*/
+static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
+ Token *token = &pc->tokens->at(*token_index);
+
+ AstNode *block_expr = ast_parse_block_expr(pc, token_index, false);
+ if (block_expr)
+ return block_expr;
+
+ AstNode *non_block_expr = ast_parse_non_block_expr(pc, token_index, false);
+ if (non_block_expr)
+ return non_block_expr;
+
+ if (mandatory)
+ ast_invalid_token_error(pc, token);
+
+ return nullptr;
}
/*
-Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
+Statement : NonBlockExpression token(Semicolon) | BlockExpression
+Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
*/
static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {
Token *last_token = &pc->tokens->at(*token_index);
@@ -1058,16 +1149,22 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
// {2;} -> {2;void}
// {;2} -> {void;2}
for (;;) {
- AstNode *expression_node = ast_parse_expression(pc, token_index, false);
- if (!expression_node) {
- expression_node = ast_create_node(pc, NodeTypeVoid, last_token);
+ AstNode *statement_node = ast_parse_block_expr(pc, token_index, false);
+ bool semicolon_expected = !statement_node;
+ if (!statement_node) {
+ statement_node = ast_parse_non_block_expr(pc, token_index, false);
+ if (!statement_node) {
+ statement_node = ast_create_node(pc, NodeTypeVoid, last_token);
+ }
}
- node->data.block.statements.append(expression_node);
+ node->data.block.statements.append(statement_node);
last_token = &pc->tokens->at(*token_index);
if (last_token->id == TokenIdRBrace) {
*token_index += 1;
return node;
+ } else if (!semicolon_expected) {
+ continue;
} else if (last_token->id == TokenIdSemicolon) {
*token_index += 1;
} else {
src/parser.hpp
@@ -39,6 +39,7 @@ enum NodeType {
NodeTypeFnCallExpr,
NodeTypeUse,
NodeTypeVoid,
+ NodeTypeIfExpr,
};
struct AstNodeRoot {
@@ -167,6 +168,12 @@ struct AstNodeUse {
ZigList<AstNode *> *directives;
};
+struct AstNodeIfExpr {
+ AstNode *condition;
+ AstNode *then_block;
+ AstNode *else_node; // null, block node, or other if expr node
+};
+
struct AstNode {
enum NodeType type;
int line;
@@ -190,6 +197,7 @@ struct AstNode {
AstNodePrefixOpExpr prefix_op_expr;
AstNodeFnCallExpr fn_call_expr;
AstNodeUse use;
+ AstNodeIfExpr if_expr;
Buf number;
Buf string;
Buf symbol;
src/semantic_info.hpp
@@ -63,6 +63,7 @@ struct CodeGen {
HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
struct {
+ TypeTableEntry *entry_bool;
TypeTableEntry *entry_u8;
TypeTableEntry *entry_i32;
TypeTableEntry *entry_string_literal;
@@ -111,10 +112,15 @@ struct FnDefNode {
LLVMValueRef *params;
};
+struct ExprNode {
+ TypeTableEntry *type_entry;
+};
+
struct CodeGenNode {
union {
TypeNode type_node; // for NodeTypeType
FnDefNode fn_def_node; // for NodeTypeFnDef
+ ExprNode expr_node; // for all the expression nodes
} data;
};
src/tokenizer.cpp
@@ -183,6 +183,10 @@ static void end_token(Tokenize *t) {
t->cur_tok->id = TokenIdKeywordUse;
} else if (mem_eql_str(token_mem, token_len, "void")) {
t->cur_tok->id = TokenIdKeywordVoid;
+ } else if (mem_eql_str(token_mem, token_len, "if")) {
+ t->cur_tok->id = TokenIdKeywordIf;
+ } else if (mem_eql_str(token_mem, token_len, "else")) {
+ t->cur_tok->id = TokenIdKeywordElse;
}
t->cur_tok = nullptr;
@@ -577,6 +581,8 @@ static const char * token_name(Token *token) {
case TokenIdKeywordAs: return "As";
case TokenIdKeywordUse: return "Use";
case TokenIdKeywordVoid: return "Void";
+ case TokenIdKeywordIf: return "If";
+ case TokenIdKeywordElse: return "Else";
case TokenIdLParen: return "LParen";
case TokenIdRParen: return "RParen";
case TokenIdComma: return "Comma";
src/tokenizer.hpp
@@ -24,6 +24,8 @@ enum TokenId {
TokenIdKeywordAs,
TokenIdKeywordUse,
TokenIdKeywordVoid,
+ TokenIdKeywordIf,
+ TokenIdKeywordElse,
TokenIdLParen,
TokenIdRParen,
TokenIdComma,
test/run_tests.cpp
@@ -189,6 +189,30 @@ static void add_compiling_test_cases(void) {
)SOURCE");
}
+ add_simple_case("if statements", R"SOURCE(
+ #link("c")
+ extern {
+ fn puts(s: *const u8) -> i32;
+ fn exit(code: i32) -> unreachable;
+ }
+
+ export fn _start() -> unreachable {
+ if 1 != 0 {
+ puts("1 is true");
+ } else {
+ puts("1 is false");
+ }
+ if 0 != 0 {
+ puts("0 is true");
+ } else if 1 - 1 != 0 {
+ puts("1 - 1 is true");
+ }
+ if !(0 != 0) {
+ puts("!0 is true");
+ }
+ exit(0);
+ }
+ )SOURCE", "1 is true\n!0 is true\n");
}
static void add_compile_failure_test_cases(void) {
README.md
@@ -44,7 +44,11 @@ make
* variable declarations and assignment expressions
* Type checking
+ * loops
+ * labels and goto
* inline assembly and syscalls
+ * conditional compilation and ability to check target platform and architecture
+ * main function with command line arguments
* running code at compile time
* print! macro that takes var args
* panic! macro that prints a stack trace to stderr in debug mode and calls
@@ -104,14 +108,26 @@ Type : token(Symbol) | PointerType | token(Unreachable)
PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
-Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
+Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
-Expression : BoolOrExpression | ReturnExpression
+Statement : NonBlockExpression token(Semicolon) | BlockExpression
+
+Expression : BlockExpression | NonBlockExpression
+
+NonBlockExpression : BoolOrExpression | ReturnExpression
+
+BlockExpression : IfExpression | Block
BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
ReturnExpression : token(Return) option(Expression)
+IfExpression : token(If) Expression Block option(Else | ElseIf)
+
+ElseIf : token(Else) IfExpression
+
+Else : token(Else) Block
+
BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
@@ -144,7 +160,7 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)
PrefixOp : token(Not) | token(Dash) | token(Tilde)
-PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | Block | token(Symbol)
+PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)
GroupedExpression : token(LParen) Expression token(RParen)
```