Commit a030b60aeb

Andrew Kelley <superjoe30@gmail.com>
2015-12-24 22:37:43
add while loop
1 parent 2e74889
doc/langref.md
@@ -94,7 +94,9 @@ AssignmentExpression : BoolOrExpression AssignmentOperator BoolOrExpression | Bo
 
 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
+BlockExpression : IfExpression | Block | WhileExpression
+
+WhileExpression : token(While) Expression Block
 
 BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExpression
 
src/analyze.cpp
@@ -54,6 +54,7 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeStructField:
         case NodeTypeStructValueExpr:
         case NodeTypeStructValueField:
+        case NodeTypeWhileExpr:
             return node;
     }
     zig_panic("unreachable");
@@ -526,6 +527,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
         case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
         case NodeTypeIfExpr:
+        case NodeTypeWhileExpr:
         case NodeTypeLabel:
         case NodeTypeGoto:
         case NodeTypeAsmExpr:
@@ -593,6 +595,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
         case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
         case NodeTypeIfExpr:
+        case NodeTypeWhileExpr:
         case NodeTypeLabel:
         case NodeTypeGoto:
         case NodeTypeAsmExpr:
@@ -1349,6 +1352,14 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp
     return type_entry;
 }
 
+static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.while_expr.condition);
+    analyze_expression(g, import, context, g->builtin_types.entry_void, node->data.while_expr.body);
+    return g->builtin_types.entry_void;
+}
+
 static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -1625,6 +1636,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                 }
                 break;
             }
+        case NodeTypeWhileExpr:
+            return_type = analyze_while_expr(g, import, context, expected_type, node);
+            break;
         case NodeTypeStructValueExpr:
             return_type = analyze_struct_val_expr(g, import, context, expected_type, node);
             break;
@@ -1775,6 +1789,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
         case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
         case NodeTypeIfExpr:
+        case NodeTypeWhileExpr:
         case NodeTypeLabel:
         case NodeTypeGoto:
         case NodeTypeAsmExpr:
src/codegen.cpp
@@ -1006,6 +1006,31 @@ static LLVMValueRef gen_struct_val_expr(CodeGen *g, AstNode *node) {
     return tmp_struct_ptr;
 }
 
+static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeWhileExpr);
+    assert(node->data.while_expr.condition);
+    assert(node->data.while_expr.body);
+
+    LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileCond");
+    LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
+    LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
+
+    add_debug_source_node(g, node);
+    LLVMBuildBr(g->builder, cond_block);
+
+    LLVMPositionBuilderAtEnd(g->builder, cond_block);
+    LLVMValueRef cond_val = gen_expr(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);
+
+    LLVMPositionBuilderAtEnd(g->builder, body_block);
+    gen_expr(g, node->data.while_expr.body);
+    LLVMBuildBr(g->builder, cond_block);
+
+    LLVMPositionBuilderAtEnd(g->builder, end_block);
+    return nullptr;
+}
+
 static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
     switch (node->type) {
         case NodeTypeBinOpExpr:
@@ -1067,6 +1092,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
                 return LLVMConstNull(LLVMInt1Type());
         case NodeTypeIfExpr:
             return gen_if_expr(g, node);
+        case NodeTypeWhileExpr:
+            return gen_while_expr(g, node);
         case NodeTypeAsmExpr:
             return gen_asm_expr(g, node);
         case NodeTypeNumberLiteral:
src/parser.cpp
@@ -116,6 +116,8 @@ const char *node_type_str(NodeType node_type) {
             return "BoolLiteral";
         case NodeTypeIfExpr:
             return "IfExpr";
+        case NodeTypeWhileExpr:
+            return "WhileExpr";
         case NodeTypeLabel:
             return "Label";
         case NodeTypeGoto:
@@ -323,6 +325,11 @@ void ast_print(AstNode *node, int indent) {
             if (node->data.if_expr.else_node)
                 ast_print(node->data.if_expr.else_node, indent + 2);
             break;
+        case NodeTypeWhileExpr:
+            fprintf(stderr, "%s\n", node_type_str(node->type));
+            ast_print(node->data.while_expr.condition, indent + 2);
+            ast_print(node->data.while_expr.body, indent + 2);
+            break;
         case NodeTypeLabel:
             fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name));
             break;
@@ -1727,7 +1734,30 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool
 }
 
 /*
-BlockExpression : IfExpression | Block
+WhileExpression : token(While) Expression Block
+*/
+static AstNode *ast_parse_while_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *token = &pc->tokens->at(*token_index);
+
+    if (token->id != TokenIdKeywordWhile) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, token);
+        } else {
+            return nullptr;
+        }
+    }
+    *token_index += 1;
+
+    AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, token);
+
+    node->data.while_expr.condition = ast_parse_expression(pc, token_index, true);
+    node->data.while_expr.body = ast_parse_block(pc, token_index, true);
+
+    return node;
+}
+
+/*
+BlockExpression : IfExpression | Block | WhileExpression
 */
 static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
@@ -1740,6 +1770,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma
     if (block)
         return block;
 
+    AstNode *while_expr = ast_parse_while_expr(pc, token_index, false);
+    if (while_expr)
+        return while_expr;
+
     if (mandatory)
         ast_invalid_token_error(pc, token);
 
src/parser.hpp
@@ -45,6 +45,7 @@ enum NodeType {
     NodeTypeVoid,
     NodeTypeBoolLiteral,
     NodeTypeIfExpr,
+    NodeTypeWhileExpr,
     NodeTypeLabel,
     NodeTypeGoto,
     NodeTypeAsmExpr,
@@ -220,6 +221,11 @@ struct AstNodeIfExpr {
     AstNode *else_node; // null, block node, or other if expr node
 };
 
+struct AstNodeWhileExpr {
+    AstNode *condition;
+    AstNode *body;
+};
+
 struct AstNodeLabel {
     Buf name;
 };
@@ -334,6 +340,7 @@ struct AstNode {
         AstNodeArrayAccessExpr array_access_expr;
         AstNodeUse use;
         AstNodeIfExpr if_expr;
+        AstNodeWhileExpr while_expr;
         AstNodeLabel label;
         AstNodeGoto go_to;
         AstNodeAsmExpr asm_expr;
src/tokenizer.cpp
@@ -233,6 +233,12 @@ static void end_token(Tokenize *t) {
         t->cur_tok->id = TokenIdKeywordAsm;
     } else if (mem_eql_str(token_mem, token_len, "struct")) {
         t->cur_tok->id = TokenIdKeywordStruct;
+    } else if (mem_eql_str(token_mem, token_len, "while")) {
+        t->cur_tok->id = TokenIdKeywordWhile;
+    } else if (mem_eql_str(token_mem, token_len, "continue")) {
+        t->cur_tok->id = TokenIdKeywordContinue;
+    } else if (mem_eql_str(token_mem, token_len, "break")) {
+        t->cur_tok->id = TokenIdKeywordBreak;
     }
 
     t->cur_tok = nullptr;
@@ -955,6 +961,9 @@ static const char * token_name(Token *token) {
         case TokenIdKeywordVolatile: return "Volatile";
         case TokenIdKeywordAsm: return "Asm";
         case TokenIdKeywordStruct: return "Struct";
+        case TokenIdKeywordWhile: return "While";
+        case TokenIdKeywordContinue: return "Continue";
+        case TokenIdKeywordBreak: return "Break";
         case TokenIdLParen: return "LParen";
         case TokenIdRParen: return "RParen";
         case TokenIdComma: return "Comma";
src/tokenizer.hpp
@@ -32,6 +32,9 @@ enum TokenId {
     TokenIdKeywordAsm,
     TokenIdKeywordVolatile,
     TokenIdKeywordStruct,
+    TokenIdKeywordWhile,
+    TokenIdKeywordContinue,
+    TokenIdKeywordBreak,
     TokenIdLParen,
     TokenIdRParen,
     TokenIdComma,