authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-01 21:19:38-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-01 21:19:38-07:00
log08a2311efd8b388cd431feb6000741f4a62da613
tree528ba0056e9a54583c91b0787b9d5ffcdcc11de7
parent1ed926c3216016d73e83377a295869d8579e2fde

support if conditionals


10 files changed, 400 insertions(+), 64 deletions(-)

README.md+19-3
......@@ -44,7 +44,11 @@ make
4444
4545 * variable declarations and assignment expressions
4646 * Type checking
47 * loops
48 * labels and goto
4749 * inline assembly and syscalls
50 * conditional compilation and ability to check target platform and architecture
51 * main function with command line arguments
4852 * running code at compile time
4953 * print! macro that takes var args
5054 * panic! macro that prints a stack trace to stderr in debug mode and calls
......@@ -104,14 +108,26 @@ Type : token(Symbol) | PointerType | token(Unreachable)
104108
105109PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
106110
107Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
111Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
108112
109Expression : BoolOrExpression | ReturnExpression
113Statement : NonBlockExpression token(Semicolon) | BlockExpression
114
115Expression : BlockExpression | NonBlockExpression
116
117NonBlockExpression : BoolOrExpression | ReturnExpression
118
119BlockExpression : IfExpression | Block
110120
111121BoolOrExpression : BoolAndExpression token(BoolOr) BoolAndExpression | BoolAndExpression
112122
113123ReturnExpression : token(Return) option(Expression)
114124
125IfExpression : token(If) Expression Block option(Else | ElseIf)
126
127ElseIf : token(Else) IfExpression
128
129Else : token(Else) Block
130
115131BoolAndExpression : ComparisonExpression token(BoolAnd) ComparisonExpression | ComparisonExpression
116132
117133ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
......@@ -144,7 +160,7 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)
144160
145161PrefixOp : token(Not) | token(Dash) | token(Tilde)
146162
147PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | Block | token(Symbol)
163PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)
148164
149165GroupedExpression : token(LParen) Expression token(RParen)
150166```
doc/vim/syntax/zig.vim+2-2
......@@ -7,8 +7,8 @@ if exists("b:current_syntax")
77 finish
88endif
99
10syn keyword zigKeyword fn return mut const extern unreachable export pub as use
11syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void
10syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void
11syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128
1212
1313syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
1414syn region zigCommentLineDoc start="//\%(//\@!\|!\)" end="$" contains=zigTodo,@Spell
src/analyze.cpp+105-7
......@@ -274,6 +274,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
274274 case NodeTypeSymbol:
275275 case NodeTypeCastExpr:
276276 case NodeTypePrefixOpExpr:
277 case NodeTypeIfExpr:
277278 zig_unreachable();
278279 }
279280}
......@@ -302,7 +303,9 @@ static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry *
302303 add_node_error(g, node, buf_sprintf("type mismatch. expected %s. got %s", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
303304}
304305
305static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) {
306static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
307 TypeTableEntry *expected_type, AstNode *node)
308{
306309 TypeTableEntry *return_type = nullptr;
307310 switch (node->type) {
308311 case NodeTypeBlock:
......@@ -348,10 +351,64 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
348351
349352 case NodeTypeBinOpExpr:
350353 {
351 // TODO: think about expected types
352 analyze_expression(g, import, context, expected_type, node->data.bin_op_expr.op1);
353 analyze_expression(g, import, context, expected_type, node->data.bin_op_expr.op2);
354 return_type = expected_type;
354 switch (node->data.bin_op_expr.bin_op) {
355 case BinOpTypeBoolOr:
356 case BinOpTypeBoolAnd:
357 analyze_expression(g, import, context, g->builtin_types.entry_bool,
358 node->data.bin_op_expr.op1);
359 analyze_expression(g, import, context, g->builtin_types.entry_bool,
360 node->data.bin_op_expr.op2);
361 return_type = g->builtin_types.entry_bool;
362 break;
363 case BinOpTypeCmpEq:
364 case BinOpTypeCmpNotEq:
365 case BinOpTypeCmpLessThan:
366 case BinOpTypeCmpGreaterThan:
367 case BinOpTypeCmpLessOrEq:
368 case BinOpTypeCmpGreaterOrEq:
369 // TODO think how should type checking for these work?
370 analyze_expression(g, import, context, g->builtin_types.entry_i32,
371 node->data.bin_op_expr.op1);
372 analyze_expression(g, import, context, g->builtin_types.entry_i32,
373 node->data.bin_op_expr.op2);
374 return_type = g->builtin_types.entry_bool;
375 break;
376 case BinOpTypeBinOr:
377 zig_panic("TODO bin or type");
378 break;
379 case BinOpTypeBinXor:
380 zig_panic("TODO bin xor type");
381 break;
382 case BinOpTypeBinAnd:
383 zig_panic("TODO bin and type");
384 break;
385 case BinOpTypeBitShiftLeft:
386 zig_panic("TODO bit shift left type");
387 break;
388 case BinOpTypeBitShiftRight:
389 zig_panic("TODO bit shift right type");
390 break;
391 case BinOpTypeAdd:
392 case BinOpTypeSub:
393 // TODO think how should type checking for these work?
394 analyze_expression(g, import, context, g->builtin_types.entry_i32,
395 node->data.bin_op_expr.op1);
396 analyze_expression(g, import, context, g->builtin_types.entry_i32,
397 node->data.bin_op_expr.op2);
398 return_type = g->builtin_types.entry_i32;
399 break;
400 case BinOpTypeMult:
401 zig_panic("TODO mult type");
402 break;
403 case BinOpTypeDiv:
404 zig_panic("TODO div type");
405 break;
406 case BinOpTypeMod:
407 zig_panic("TODO modulus type");
408 break;
409 case BinOpTypeInvalid:
410 zig_unreachable();
411 }
355412 break;
356413 }
357414
......@@ -426,11 +483,46 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
426483
427484 case NodeTypeSymbol:
428485 // look up symbol in symbol table
429 zig_panic("TODO");
486 zig_panic("TODO analyze_expression symbol");
430487
431488 case NodeTypeCastExpr:
489 zig_panic("TODO analyze_expression cast expr");
490 break;
491
432492 case NodeTypePrefixOpExpr:
433 zig_panic("TODO");
493 switch (node->data.prefix_op_expr.prefix_op) {
494 case PrefixOpBoolNot:
495 analyze_expression(g, import, context, g->builtin_types.entry_bool,
496 node->data.prefix_op_expr.primary_expr);
497 return_type = g->builtin_types.entry_bool;
498 break;
499 case PrefixOpBinNot:
500 zig_panic("TODO type check bin not");
501 break;
502 case PrefixOpNegation:
503 zig_panic("TODO type check negation");
504 break;
505 case PrefixOpInvalid:
506 zig_unreachable();
507 }
508 break;
509 case NodeTypeIfExpr:
510 {
511 analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_expr.condition);
512
513 TypeTableEntry *else_type;
514 if (node->data.if_expr.else_node) {
515 else_type = analyze_expression(g, import, context, expected_type, node->data.if_expr.else_node);
516 } else {
517 else_type = g->builtin_types.entry_void;
518 }
519 TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,
520 node->data.if_expr.then_block);
521
522 check_type_compatibility(g, node, expected_type, else_type);
523 return_type = then_type;
524 break;
525 }
434526 case NodeTypeDirective:
435527 case NodeTypeFnDecl:
436528 case NodeTypeFnProto:
......@@ -445,6 +537,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
445537 }
446538 assert(return_type);
447539 check_type_compatibility(g, node, expected_type, return_type);
540
541 assert(!node->codegen_node);
542 node->codegen_node = allocate<CodeGenNode>(1);
543 node->codegen_node->data.expr_node.type_entry = return_type;
544
448545 return return_type;
449546}
450547
......@@ -509,6 +606,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
509606 case NodeTypeSymbol:
510607 case NodeTypeCastExpr:
511608 case NodeTypePrefixOpExpr:
609 case NodeTypeIfExpr:
512610 zig_unreachable();
513611 }
514612}
src/codegen.cpp+111-32
......@@ -120,6 +120,10 @@ static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) {
120120 zig_unreachable();
121121}
122122
123static TypeTableEntry *get_expr_type(AstNode *node) {
124 return node->codegen_node->data.expr_node.type_entry;
125}
126
123127static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
124128 assert(node->type == NodeTypeFnCallExpr);
125129
......@@ -283,6 +287,7 @@ static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
283287
284288 LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
285289
290 LLVMBasicBlockRef orig_block = LLVMGetInsertBlock(g->builder);
286291 // block for when val1 == true
287292 LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndTrue");
288293 // 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) {
297302 LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
298303 add_debug_source_node(g, node);
299304 LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
305 LLVMBuildBr(g->builder, false_block);
300306
301307 LLVMPositionBuilderAtEnd(g->builder, false_block);
302308 add_debug_source_node(g, node);
303309 LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
304310 LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
305311 LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
306 LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), true_block};
312 LLVMBasicBlockRef incoming_blocks[2] = {orig_block, true_block};
307313 LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
308314
309315 return phi;
......@@ -314,6 +320,8 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
314320
315321 LLVMValueRef val1 = gen_expr(g, expr_node->data.bin_op_expr.op1);
316322
323 LLVMBasicBlockRef orig_block = LLVMGetInsertBlock(g->builder);
324
317325 // block for when val1 == false
318326 LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrFalse");
319327 // 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) {
328336 LLVMValueRef val2 = gen_expr(g, expr_node->data.bin_op_expr.op2);
329337 add_debug_source_node(g, expr_node);
330338 LLVMValueRef val2_i1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
339 LLVMBuildBr(g->builder, true_block);
331340
332341 LLVMPositionBuilderAtEnd(g->builder, true_block);
333342 add_debug_source_node(g, expr_node);
334343 LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
335344 LLVMValueRef one_i1 = LLVMConstAllOnes(LLVMInt1Type());
336345 LLVMValueRef incoming_values[2] = {one_i1, val2_i1};
337 LLVMBasicBlockRef incoming_blocks[2] = {LLVMGetInsertBlock(g->builder), false_block};
346 LLVMBasicBlockRef incoming_blocks[2] = {orig_block, false_block};
338347 LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
339348
340349 return phi;
......@@ -383,9 +392,91 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
383392 return LLVMBuildRetVoid(g->builder);
384393 }
385394}
386/*
387Expression : BoolOrExpression | ReturnExpression
388*/
395
396static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
397 assert(node->type == NodeTypeIfExpr);
398 assert(node->data.if_expr.condition);
399 assert(node->data.if_expr.then_block);
400
401 LLVMValueRef cond_value = gen_expr(g, node->data.if_expr.condition);
402
403 TypeTableEntry *then_type = get_expr_type(node->data.if_expr.then_block);
404 bool use_expr_value = (then_type != g->builtin_types.entry_unreachable &&
405 then_type != g->builtin_types.entry_void);
406
407 if (node->data.if_expr.else_node) {
408 LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
409 LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
410 LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
411
412 LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
413
414 LLVMPositionBuilderAtEnd(g->builder, then_block);
415 LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block);
416 LLVMBuildBr(g->builder, endif_block);
417
418 LLVMPositionBuilderAtEnd(g->builder, else_block);
419 LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node);
420 LLVMBuildBr(g->builder, endif_block);
421
422 LLVMPositionBuilderAtEnd(g->builder, endif_block);
423 if (use_expr_value) {
424 LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
425 LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
426 LLVMBasicBlockRef incoming_blocks[2] = {then_block, else_block};
427 LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
428
429 return phi;
430 }
431
432 return nullptr;
433 }
434
435 assert(!use_expr_value);
436
437 LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
438 LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
439
440 LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block);
441
442 LLVMPositionBuilderAtEnd(g->builder, then_block);
443 gen_expr(g, node->data.if_expr.then_block);
444 LLVMBuildBr(g->builder, endif_block);
445
446 LLVMPositionBuilderAtEnd(g->builder, endif_block);
447 return nullptr;
448}
449
450static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
451 assert(block_node->type == NodeTypeBlock);
452
453 ImportTableEntry *import = g->cur_fn->import_entry;
454
455 LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(),
456 import->di_file, block_node->line + 1, block_node->column + 1);
457 g->block_scopes.append(LLVMZigLexicalBlockToScope(di_block));
458
459 add_debug_source_node(g, block_node);
460
461 LLVMValueRef return_value;
462 for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
463 AstNode *statement_node = block_node->data.block.statements.at(i);
464 return_value = gen_expr(g, statement_node);
465 }
466
467 if (implicit_return_type) {
468 if (implicit_return_type == g->builtin_types.entry_void) {
469 LLVMBuildRetVoid(g->builder);
470 } else if (implicit_return_type != g->builtin_types.entry_unreachable) {
471 LLVMBuildRet(g->builder, return_value);
472 }
473 }
474
475 g->block_scopes.pop();
476
477 return return_value;
478}
479
389480static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
390481 switch (node->type) {
391482 case NodeTypeBinOpExpr:
......@@ -403,6 +494,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
403494 return LLVMBuildUnreachable(g->builder);
404495 case NodeTypeVoid:
405496 return nullptr;
497 case NodeTypeIfExpr:
498 return gen_if_expr(g, node);
406499 case NodeTypeNumberLiteral:
407500 {
408501 Buf *number_str = &node->data.number;
......@@ -427,6 +520,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
427520 Buf *name = &node->data.symbol;
428521 return get_variable_value(g, name);
429522 }
523 case NodeTypeBlock:
524 return gen_block(g, node, nullptr);
430525 case NodeTypeRoot:
431526 case NodeTypeRootExportDecl:
432527 case NodeTypeFnProto:
......@@ -434,7 +529,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
434529 case NodeTypeFnDecl:
435530 case NodeTypeParamDecl:
436531 case NodeTypeType:
437 case NodeTypeBlock:
438532 case NodeTypeExternBlock:
439533 case NodeTypeDirective:
440534 case NodeTypeUse:
......@@ -443,30 +537,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
443537 zig_unreachable();
444538}
445539
446static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node, TypeTableEntry *implicit_return_type) {
447 assert(block_node->type == NodeTypeBlock);
448
449 LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(),
450 import->di_file, block_node->line + 1, block_node->column + 1);
451 g->block_scopes.append(LLVMZigLexicalBlockToScope(di_block));
452
453 add_debug_source_node(g, block_node);
454
455 LLVMValueRef return_value;
456 for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
457 AstNode *statement_node = block_node->data.block.statements.at(i);
458 return_value = gen_expr(g, statement_node);
459 }
460
461 if (implicit_return_type == g->builtin_types.entry_void) {
462 LLVMBuildRetVoid(g->builder);
463 } else if (implicit_return_type != g->builtin_types.entry_unreachable) {
464 LLVMBuildRet(g->builder, return_value);
465 }
466
467 g->block_scopes.pop();
468}
469
470540static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto,
471541 LLVMZigDIFile *di_file)
472542{
......@@ -558,7 +628,7 @@ static void do_code_gen(CodeGen *g) {
558628 LLVMGetParams(fn, codegen_fn_def->params);
559629
560630 TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
561 gen_block(g, import, fn_def_node->data.fn_def.body, implicit_return_type);
631 gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);
562632
563633 g->block_scopes.pop();
564634 }
......@@ -585,6 +655,15 @@ static void define_primitive_types(CodeGen *g) {
585655 buf_init_from_str(&entry->name, "(invalid)");
586656 g->builtin_types.entry_invalid = entry;
587657 }
658 {
659 TypeTableEntry *entry = allocate<TypeTableEntry>(1);
660 entry->type_ref = LLVMInt1Type();
661 buf_init_from_str(&entry->name, "bool");
662 entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), 1, 8,
663 LLVMZigEncoding_DW_ATE_unsigned());
664 g->type_table.put(&entry->name, entry);
665 g->builtin_types.entry_bool = entry;
666 }
588667 {
589668 TypeTableEntry *entry = allocate<TypeTableEntry>(1);
590669 entry->type_ref = LLVMInt8Type();
......@@ -803,7 +882,7 @@ static Buf *to_c_type(CodeGen *g, AstNode *type_node) {
803882 g->c_stdint_used = true;
804883 return buf_create_from_str("int32_t");
805884 } else {
806 zig_panic("TODO");
885 zig_panic("TODO to_c_type");
807886 }
808887}
809888
src/parser.cpp+117-20
......@@ -91,6 +91,8 @@ const char *node_type_str(NodeType node_type) {
9191 return "Use";
9292 case NodeTypeVoid:
9393 return "Void";
94 case NodeTypeIfExpr:
95 return "IfExpr";
9496 }
9597 zig_unreachable();
9698}
......@@ -236,7 +238,15 @@ void ast_print(AstNode *node, int indent) {
236238 fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));
237239 break;
238240 case NodeTypeVoid:
239 fprintf(stderr, "Void\n");
241 fprintf(stderr, "%s\n", node_type_str(node->type));
242 break;
243 case NodeTypeIfExpr:
244 fprintf(stderr, "%s\n", node_type_str(node->type));
245 if (node->data.if_expr.condition)
246 ast_print(node->data.if_expr.condition, indent + 2);
247 ast_print(node->data.if_expr.then_block, indent + 2);
248 if (node->data.if_expr.else_node)
249 ast_print(node->data.if_expr.else_node, indent + 2);
240250 break;
241251 }
242252}
......@@ -353,6 +363,7 @@ static void ast_invalid_token_error(ParseContext *pc, Token *token) {
353363
354364static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory);
355365static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory);
366static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory);
356367
357368
358369static 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
558569}
559570
560571/*
561PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | Block | token(Symbol)
572PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)
562573*/
563574static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
564575 Token *token = &pc->tokens->at(*token_index);
......@@ -588,11 +599,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
588599 return node;
589600 }
590601
591 AstNode *block_node = ast_parse_block(pc, token_index, false);
592 if (block_node) {
593 return block_node;
594 }
595
596602 AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
597603 if (grouped_expr_node) {
598604 return grouped_expr_node;
......@@ -975,6 +981,50 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool
975981 return node;
976982}
977983
984/*
985ElseIf : token(Else) IfExpression
986Else : token(Else) Block
987*/
988static AstNode *ast_parse_else_or_else_if(ParseContext *pc, int *token_index, bool mandatory) {
989 Token *else_token = &pc->tokens->at(*token_index);
990
991 if (else_token->id != TokenIdKeywordElse) {
992 if (mandatory) {
993 ast_invalid_token_error(pc, else_token);
994 } else {
995 return nullptr;
996 }
997 }
998 *token_index += 1;
999
1000 AstNode *if_expr = ast_parse_if_expr(pc, token_index, false);
1001 if (if_expr)
1002 return if_expr;
1003
1004 return ast_parse_block(pc, token_index, true);
1005}
1006
1007/*
1008IfExpression : token(If) Expression Block option(Else | ElseIf)
1009*/
1010static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) {
1011 Token *if_tok = &pc->tokens->at(*token_index);
1012 if (if_tok->id != TokenIdKeywordIf) {
1013 if (mandatory) {
1014 ast_invalid_token_error(pc, if_tok);
1015 } else {
1016 return nullptr;
1017 }
1018 }
1019 *token_index += 1;
1020
1021 AstNode *node = ast_create_node(pc, NodeTypeIfExpr, if_tok);
1022 node->data.if_expr.condition = ast_parse_expression(pc, token_index, true);
1023 node->data.if_expr.then_block = ast_parse_block(pc, token_index, true);
1024 node->data.if_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false);
1025 return node;
1026}
1027
9781028/*
9791029ReturnExpression : token(Return) option(Expression)
9801030*/
......@@ -1016,27 +1066,68 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool
10161066}
10171067
10181068/*
1019Expression : BoolOrExpression | ReturnExpression
1069BlockExpression : IfExpression | Block
10201070*/
1021static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
1071static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
10221072 Token *token = &pc->tokens->at(*token_index);
10231073
1024 AstNode *return_expr = ast_parse_return_expr(pc, token_index, false);
1025 if (return_expr)
1026 return return_expr;
1074 AstNode *if_expr = ast_parse_if_expr(pc, token_index, false);
1075 if (if_expr)
1076 return if_expr;
1077
1078 AstNode *block = ast_parse_block(pc, token_index, false);
1079 if (block)
1080 return block;
1081
1082 if (mandatory)
1083 ast_invalid_token_error(pc, token);
1084
1085 return nullptr;
1086}
1087
1088/*
1089NonBlockExpression : BoolOrExpression | ReturnExpression
1090*/
1091static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
1092 Token *token = &pc->tokens->at(*token_index);
10271093
10281094 AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false);
10291095 if (bool_or_expr)
10301096 return bool_or_expr;
10311097
1032 if (!mandatory)
1033 return nullptr;
1098 AstNode *return_expr = ast_parse_return_expr(pc, token_index, false);
1099 if (return_expr)
1100 return return_expr;
10341101
1035 ast_invalid_token_error(pc, token);
1102 if (mandatory)
1103 ast_invalid_token_error(pc, token);
1104
1105 return nullptr;
1106}
1107
1108/*
1109Expression : BlockExpression | NonBlockExpression
1110*/
1111static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
1112 Token *token = &pc->tokens->at(*token_index);
1113
1114 AstNode *block_expr = ast_parse_block_expr(pc, token_index, false);
1115 if (block_expr)
1116 return block_expr;
1117
1118 AstNode *non_block_expr = ast_parse_non_block_expr(pc, token_index, false);
1119 if (non_block_expr)
1120 return non_block_expr;
1121
1122 if (mandatory)
1123 ast_invalid_token_error(pc, token);
1124
1125 return nullptr;
10361126}
10371127
10381128/*
1039Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
1129Statement : NonBlockExpression token(Semicolon) | BlockExpression
1130Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
10401131*/
10411132static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {
10421133 Token *last_token = &pc->tokens->at(*token_index);
......@@ -1058,16 +1149,22 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
10581149 // {2;} -> {2;void}
10591150 // {;2} -> {void;2}
10601151 for (;;) {
1061 AstNode *expression_node = ast_parse_expression(pc, token_index, false);
1062 if (!expression_node) {
1063 expression_node = ast_create_node(pc, NodeTypeVoid, last_token);
1152 AstNode *statement_node = ast_parse_block_expr(pc, token_index, false);
1153 bool semicolon_expected = !statement_node;
1154 if (!statement_node) {
1155 statement_node = ast_parse_non_block_expr(pc, token_index, false);
1156 if (!statement_node) {
1157 statement_node = ast_create_node(pc, NodeTypeVoid, last_token);
1158 }
10641159 }
1065 node->data.block.statements.append(expression_node);
1160 node->data.block.statements.append(statement_node);
10661161
10671162 last_token = &pc->tokens->at(*token_index);
10681163 if (last_token->id == TokenIdRBrace) {
10691164 *token_index += 1;
10701165 return node;
1166 } else if (!semicolon_expected) {
1167 continue;
10711168 } else if (last_token->id == TokenIdSemicolon) {
10721169 *token_index += 1;
10731170 } else {
src/parser.hpp+8
......@@ -39,6 +39,7 @@ enum NodeType {
3939 NodeTypeFnCallExpr,
4040 NodeTypeUse,
4141 NodeTypeVoid,
42 NodeTypeIfExpr,
4243};
4344
4445struct AstNodeRoot {
......@@ -167,6 +168,12 @@ struct AstNodeUse {
167168 ZigList<AstNode *> *directives;
168169};
169170
171struct AstNodeIfExpr {
172 AstNode *condition;
173 AstNode *then_block;
174 AstNode *else_node; // null, block node, or other if expr node
175};
176
170177struct AstNode {
171178 enum NodeType type;
172179 int line;
......@@ -190,6 +197,7 @@ struct AstNode {
190197 AstNodePrefixOpExpr prefix_op_expr;
191198 AstNodeFnCallExpr fn_call_expr;
192199 AstNodeUse use;
200 AstNodeIfExpr if_expr;
193201 Buf number;
194202 Buf string;
195203 Buf symbol;
src/semantic_info.hpp+6
......@@ -63,6 +63,7 @@ struct CodeGen {
6363 HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
6464
6565 struct {
66 TypeTableEntry *entry_bool;
6667 TypeTableEntry *entry_u8;
6768 TypeTableEntry *entry_i32;
6869 TypeTableEntry *entry_string_literal;
......@@ -111,10 +112,15 @@ struct FnDefNode {
111112 LLVMValueRef *params;
112113};
113114
115struct ExprNode {
116 TypeTableEntry *type_entry;
117};
118
114119struct CodeGenNode {
115120 union {
116121 TypeNode type_node; // for NodeTypeType
117122 FnDefNode fn_def_node; // for NodeTypeFnDef
123 ExprNode expr_node; // for all the expression nodes
118124 } data;
119125};
120126
src/tokenizer.cpp+6
......@@ -183,6 +183,10 @@ static void end_token(Tokenize *t) {
183183 t->cur_tok->id = TokenIdKeywordUse;
184184 } else if (mem_eql_str(token_mem, token_len, "void")) {
185185 t->cur_tok->id = TokenIdKeywordVoid;
186 } else if (mem_eql_str(token_mem, token_len, "if")) {
187 t->cur_tok->id = TokenIdKeywordIf;
188 } else if (mem_eql_str(token_mem, token_len, "else")) {
189 t->cur_tok->id = TokenIdKeywordElse;
186190 }
187191
188192 t->cur_tok = nullptr;
......@@ -577,6 +581,8 @@ static const char * token_name(Token *token) {
577581 case TokenIdKeywordAs: return "As";
578582 case TokenIdKeywordUse: return "Use";
579583 case TokenIdKeywordVoid: return "Void";
584 case TokenIdKeywordIf: return "If";
585 case TokenIdKeywordElse: return "Else";
580586 case TokenIdLParen: return "LParen";
581587 case TokenIdRParen: return "RParen";
582588 case TokenIdComma: return "Comma";
src/tokenizer.hpp+2
......@@ -24,6 +24,8 @@ enum TokenId {
2424 TokenIdKeywordAs,
2525 TokenIdKeywordUse,
2626 TokenIdKeywordVoid,
27 TokenIdKeywordIf,
28 TokenIdKeywordElse,
2729 TokenIdLParen,
2830 TokenIdRParen,
2931 TokenIdComma,
test/run_tests.cpp+24
......@@ -189,6 +189,30 @@ static void add_compiling_test_cases(void) {
189189 )SOURCE");
190190 }
191191
192 add_simple_case("if statements", R"SOURCE(
193 #link("c")
194 extern {
195 fn puts(s: *const u8) -> i32;
196 fn exit(code: i32) -> unreachable;
197 }
198
199 export fn _start() -> unreachable {
200 if 1 != 0 {
201 puts("1 is true");
202 } else {
203 puts("1 is false");
204 }
205 if 0 != 0 {
206 puts("0 is true");
207 } else if 1 - 1 != 0 {
208 puts("1 - 1 is true");
209 }
210 if !(0 != 0) {
211 puts("!0 is true");
212 }
213 exit(0);
214 }
215 )SOURCE", "1 is true\n!0 is true\n");
192216}
193217
194218static void add_compile_failure_test_cases(void) {