Commit 1f8e3871ee

Andrew Kelley <superjoe30@gmail.com>
2015-12-26 23:05:27
parse if maybe expression
1 parent 5943f99
doc/langref.md
@@ -102,7 +102,11 @@ BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExp
 
 ReturnExpression : token(Return) option(Expression)
 
-IfExpression : token(If) Expression Block option(Else | ElseIf)
+IfExpression : IfVarExpression | IfBoolExpression
+
+IfBoolExpression : token(If) option((token) Expression Block option(Else | ElseIf)
+
+IfVarExpression : token(If) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression Block Option(Else | ElseIf)
 
 ElseIf : token(Else) IfExpression
 
example/guess_number/main.zig
@@ -12,17 +12,16 @@ fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     const answer = rand_int(&rand_state, 0, 100) + 1;
 
     while true {
-        Buffer line = readline("\nGuess a number between 1 and 100: ");
+        line = readline("\nGuess a number between 1 and 100: ");
 
-        (const err, const guess) = parse_number(line);
-        if err == Error.None {
-            if guess == answer {
-                print_str("You win!\n");
-                return 0;
-            } else if guess < answer {
+        if const guess ?= parse_number(line) {
+            if (guess > answer) {
+                print_str("Guess lower.\n");
+            } else if (guess < answer) {
                 print_str("Guess higher.\n");
             } else {
-                print_str("Guess lower.\n");
+                print_str("You win!\n");
+                return 0;
             }
         } else {
             print_str("Invalid number format.\n");
src/analyze.cpp
@@ -45,7 +45,8 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeUse:
         case NodeTypeVoid:
         case NodeTypeBoolLiteral:
-        case NodeTypeIfExpr:
+        case NodeTypeIfBoolExpr:
+        case NodeTypeIfVarExpr:
         case NodeTypeLabel:
         case NodeTypeGoto:
         case NodeTypeBreak:
@@ -528,7 +529,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
         case NodeTypeSymbol:
         case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
-        case NodeTypeIfExpr:
+        case NodeTypeIfBoolExpr:
+        case NodeTypeIfVarExpr:
         case NodeTypeWhileExpr:
         case NodeTypeLabel:
         case NodeTypeGoto:
@@ -598,7 +600,8 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
         case NodeTypeSymbol:
         case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
-        case NodeTypeIfExpr:
+        case NodeTypeIfBoolExpr:
+        case NodeTypeIfVarExpr:
         case NodeTypeWhileExpr:
         case NodeTypeLabel:
         case NodeTypeGoto:
@@ -1394,6 +1397,39 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor
     return g->builtin_types.entry_unreachable;
 }
 
+static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_bool_expr.condition);
+
+    TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,
+            node->data.if_bool_expr.then_block);
+
+    TypeTableEntry *else_type;
+    if (node->data.if_bool_expr.else_node) {
+        else_type = analyze_expression(g, import, context, expected_type, node->data.if_bool_expr.else_node);
+    } else {
+        else_type = g->builtin_types.entry_void;
+        else_type = resolve_type_compatibility(g, context, node, expected_type, else_type);
+    }
+
+
+    if (expected_type) {
+        return (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
+    } else {
+        return resolve_peer_type_compatibility(g, context, node,
+                node->data.if_bool_expr.then_block, node->data.if_bool_expr.else_node,
+                then_type, else_type);
+    }
+}
+
+static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    assert(node->type == NodeTypeIfVarExpr);
+    zig_panic("TODO analyze_if_var_expr");
+}
+
 static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -1651,31 +1687,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                     }
             }
             break;
-        case NodeTypeIfExpr:
-            {
-                analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_expr.condition);
-
-                TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,
-                        node->data.if_expr.then_block);
-
-                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;
-                    else_type = resolve_type_compatibility(g, context, node, expected_type, else_type);
-                }
-
-
-                if (expected_type) {
-                    return_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
-                } else {
-                    return_type = resolve_peer_type_compatibility(g, context, node,
-                            node->data.if_expr.then_block, node->data.if_expr.else_node,
-                            then_type, else_type);
-                }
-                break;
-            }
+        case NodeTypeIfBoolExpr:
+            return_type = analyze_if_bool_expr(g, import, context, expected_type, node);
+            break;
+        case NodeTypeIfVarExpr:
+            return_type = analyze_if_var_expr(g, import, context, expected_type, node);
+            break;
         case NodeTypeWhileExpr:
             return_type = analyze_while_expr(g, import, context, expected_type, node);
             break;
@@ -1828,7 +1845,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
         case NodeTypeSymbol:
         case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
-        case NodeTypeIfExpr:
+        case NodeTypeIfBoolExpr:
+        case NodeTypeIfVarExpr:
         case NodeTypeWhileExpr:
         case NodeTypeLabel:
         case NodeTypeGoto:
src/codegen.cpp
@@ -769,18 +769,18 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
     }
 }
 
-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);
+static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeIfBoolExpr);
+    assert(node->data.if_bool_expr.condition);
+    assert(node->data.if_bool_expr.then_block);
 
-    LLVMValueRef cond_value = gen_expr(g, node->data.if_expr.condition);
+    LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
 
-    TypeTableEntry *then_type = get_expr_type(node->data.if_expr.then_block);
+    TypeTableEntry *then_type = get_expr_type(node->data.if_bool_expr.then_block);
     bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable &&
                            then_type->id != TypeTableEntryIdVoid);
 
-    if (node->data.if_expr.else_node) {
+    if (node->data.if_bool_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");
@@ -788,13 +788,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
         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);
-        if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable)
+        LLVMValueRef then_expr_result = gen_expr(g, node->data.if_bool_expr.then_block);
+        if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable)
             LLVMBuildBr(g->builder, endif_block);
 
         LLVMPositionBuilderAtEnd(g->builder, else_block);
-        LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node);
-        if (get_expr_type(node->data.if_expr.else_node)->id != TypeTableEntryIdUnreachable)
+        LLVMValueRef else_expr_result = gen_expr(g, node->data.if_bool_expr.else_node);
+        if (get_expr_type(node->data.if_bool_expr.else_node)->id != TypeTableEntryIdUnreachable)
             LLVMBuildBr(g->builder, endif_block);
 
         LLVMPositionBuilderAtEnd(g->builder, endif_block);
@@ -818,14 +818,19 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
     LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block);
 
     LLVMPositionBuilderAtEnd(g->builder, then_block);
-    gen_expr(g, node->data.if_expr.then_block);
-    if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable)
+    gen_expr(g, node->data.if_bool_expr.then_block);
+    if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable)
         LLVMBuildBr(g->builder, endif_block);
 
     LLVMPositionBuilderAtEnd(g->builder, endif_block);
     return nullptr;
 }
 
+static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeIfVarExpr);
+    zig_panic("TODO gen_if_var_expr");
+}
+
 static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
     assert(block_node->type == NodeTypeBlock);
 
@@ -1112,8 +1117,10 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
                 return LLVMConstAllOnes(LLVMInt1Type());
             else
                 return LLVMConstNull(LLVMInt1Type());
-        case NodeTypeIfExpr:
-            return gen_if_expr(g, node);
+        case NodeTypeIfBoolExpr:
+            return gen_if_bool_expr(g, node);
+        case NodeTypeIfVarExpr:
+            return gen_if_var_expr(g, node);
         case NodeTypeWhileExpr:
             return gen_while_expr(g, node);
         case NodeTypeAsmExpr:
src/parser.cpp
@@ -114,8 +114,10 @@ const char *node_type_str(NodeType node_type) {
             return "Void";
         case NodeTypeBoolLiteral:
             return "BoolLiteral";
-        case NodeTypeIfExpr:
-            return "IfExpr";
+        case NodeTypeIfBoolExpr:
+            return "IfBoolExpr";
+        case NodeTypeIfVarExpr:
+            return "IfVarExpr";
         case NodeTypeWhileExpr:
             return "WhileExpr";
         case NodeTypeLabel:
@@ -321,14 +323,27 @@ void ast_print(AstNode *node, int indent) {
         case NodeTypeBoolLiteral:
             fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false");
             break;
-        case NodeTypeIfExpr:
+        case NodeTypeIfBoolExpr:
             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);
+            if (node->data.if_bool_expr.condition)
+                ast_print(node->data.if_bool_expr.condition, indent + 2);
+            ast_print(node->data.if_bool_expr.then_block, indent + 2);
+            if (node->data.if_bool_expr.else_node)
+                ast_print(node->data.if_bool_expr.else_node, indent + 2);
             break;
+        case NodeTypeIfVarExpr:
+            {
+                Buf *name_buf = &node->data.if_var_expr.var_decl.symbol;
+                fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf));
+                if (node->data.if_var_expr.var_decl.type)
+                    ast_print(node->data.if_var_expr.var_decl.type, indent + 2);
+                if (node->data.if_var_expr.var_decl.expr)
+                    ast_print(node->data.if_var_expr.var_decl.expr, indent + 2);
+                ast_print(node->data.if_var_expr.then_block, indent + 2);
+                if (node->data.if_var_expr.else_node)
+                    ast_print(node->data.if_var_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);
@@ -1644,7 +1659,9 @@ static AstNode *ast_parse_else_or_else_if(ParseContext *pc, int *token_index, bo
 }
 
 /*
-IfExpression : token(If) Expression Block option(Else | ElseIf)
+IfExpression : IfVarExpression | IfBoolExpression
+IfBoolExpression : token(If) option((token) Expression Block option(Else | ElseIf)
+IfVarExpression : token(If) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(Eq) 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);
@@ -1657,11 +1674,37 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda
     }
     *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;
+    Token *token = &pc->tokens->at(*token_index);
+    if (token->id == TokenIdKeywordConst || token->id == TokenIdKeywordVar) {
+        AstNode *node = ast_create_node(pc, NodeTypeIfVarExpr, if_tok);
+        node->data.if_var_expr.var_decl.is_const = (token->id == TokenIdKeywordConst);
+        *token_index += 1;
+
+        Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol);
+        ast_buf_from_token(pc, name_token, &node->data.if_var_expr.var_decl.symbol);
+
+        Token *eq_or_colon = &pc->tokens->at(*token_index);
+        *token_index += 1;
+        if (eq_or_colon->id == TokenIdMaybeAssign) {
+            node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true);
+        } else if (eq_or_colon->id == TokenIdColon) {
+            node->data.if_var_expr.var_decl.type = ast_parse_type(pc, token_index);
+
+            ast_eat_token(pc, token_index, TokenIdMaybeAssign);
+            node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true);
+        } else {
+            ast_invalid_token_error(pc, eq_or_colon);
+        }
+        node->data.if_var_expr.then_block = ast_parse_block(pc, token_index, true);
+        node->data.if_var_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false);
+        return node;
+    } else {
+        AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_tok);
+        node->data.if_bool_expr.condition = ast_parse_expression(pc, token_index, true);
+        node->data.if_bool_expr.then_block = ast_parse_block(pc, token_index, true);
+        node->data.if_bool_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false);
+        return node;
+    }
 }
 
 /*
src/parser.hpp
@@ -44,7 +44,8 @@ enum NodeType {
     NodeTypeUse,
     NodeTypeVoid,
     NodeTypeBoolLiteral,
-    NodeTypeIfExpr,
+    NodeTypeIfBoolExpr,
+    NodeTypeIfVarExpr,
     NodeTypeWhileExpr,
     NodeTypeLabel,
     NodeTypeGoto,
@@ -217,12 +218,18 @@ struct AstNodeUse {
     ZigList<AstNode *> *directives;
 };
 
-struct AstNodeIfExpr {
+struct AstNodeIfBoolExpr {
     AstNode *condition;
     AstNode *then_block;
     AstNode *else_node; // null, block node, or other if expr node
 };
 
+struct AstNodeIfVarExpr {
+    AstNodeVariableDeclaration var_decl;
+    AstNode *then_block;
+    AstNode *else_node; // null, block node, or other if expr node
+};
+
 struct AstNodeWhileExpr {
     AstNode *condition;
     AstNode *body;
@@ -341,7 +348,8 @@ struct AstNode {
         AstNodeFnCallExpr fn_call_expr;
         AstNodeArrayAccessExpr array_access_expr;
         AstNodeUse use;
-        AstNodeIfExpr if_expr;
+        AstNodeIfBoolExpr if_bool_expr;
+        AstNodeIfVarExpr if_var_expr;
         AstNodeWhileExpr while_expr;
         AstNodeLabel label;
         AstNodeGoto go_to;
src/tokenizer.cpp
@@ -125,6 +125,7 @@ enum TokenizeState {
     TokenizeStateSawGreaterThanGreaterThan,
     TokenizeStateSawDot,
     TokenizeStateSawDotDot,
+    TokenizeStateSawQuestionMark,
     TokenizeStateError,
 };
 
@@ -402,10 +403,28 @@ void tokenize(Buf *buf, Tokenization *out) {
                         begin_token(&t, TokenIdDot);
                         t.state = TokenizeStateSawDot;
                         break;
+                    case '?':
+                        begin_token(&t, TokenIdMaybe);
+                        t.state = TokenizeStateSawQuestionMark;
+                        break;
                     default:
                         tokenize_error(&t, "invalid character: '%c'", c);
                 }
                 break;
+            case TokenizeStateSawQuestionMark:
+                switch (c) {
+                    case '=':
+                        t.cur_tok->id = TokenIdMaybeAssign;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        t.pos -= 1;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        continue;
+                }
+                break;
             case TokenizeStateSawDot:
                 switch (c) {
                     case '.':
@@ -917,6 +936,7 @@ void tokenize(Buf *buf, Tokenization *out) {
         case TokenizeStateSawGreaterThan:
         case TokenizeStateSawGreaterThanGreaterThan:
         case TokenizeStateSawDot:
+        case TokenizeStateSawQuestionMark:
             end_token(&t);
             break;
         case TokenizeStateSawDotDot:
@@ -1012,6 +1032,8 @@ static const char * token_name(Token *token) {
         case TokenIdPercent: return "Percent";
         case TokenIdDot: return "Dot";
         case TokenIdEllipsis: return "Ellipsis";
+        case TokenIdMaybe: return "Maybe";
+        case TokenIdMaybeAssign: return "MaybeAssign";
     }
     return "(invalid token)";
 }
src/tokenizer.hpp
@@ -83,6 +83,8 @@ enum TokenId {
     TokenIdPercent,
     TokenIdDot,
     TokenIdEllipsis,
+    TokenIdMaybe,
+    TokenIdMaybeAssign,
 };
 
 struct Token {
README.md
@@ -44,10 +44,11 @@ compromises backward compatibility.
 
 ### Current Status
 
- * Core language features are lacking such as structs, enums, loops.
+ * Have a look in the examples/ folder to see some code examples.
+ * Some language features available such as loops, inline assembly, expressions,
+   literals, functions.
  * Only Linux x86_64 is supported.
  * Only building for the native target is supported.
- * Have a look in the examples/ folder to see some code examples.
  * Optimized machine code that Zig produces is indistinguishable from
    optimized machine code produced from equivalent C program.
  * Zig can generate dynamic libraries, executables, object files, and C