Commit 179443bd61

Andrew Kelley <superjoe30@gmail.com>
2016-02-01 10:11:46
add ?? prefix operator
1 parent b3459f6
doc/langref.md
@@ -143,7 +143,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",")
 
 StructLiteralField = "." "Symbol" "=" Expression
 
-PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%"
+PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%" | "??"
 
 PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." "Symbol")
 
src/all_types.hpp
@@ -418,6 +418,7 @@ enum PrefixOp {
     PrefixOpMaybe,
     PrefixOpError,
     PrefixOpUnwrapError,
+    PrefixOpUnwrapMaybe,
 };
 
 struct AstNodePrefixOpExpr {
src/analyze.cpp
@@ -4207,6 +4207,20 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                     return g->builtin_types.entry_invalid;
                 }
             }
+        case PrefixOpUnwrapMaybe:
+            {
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+
+                if (type_entry->id == TypeTableEntryIdInvalid) {
+                    return type_entry;
+                } else if (type_entry->id == TypeTableEntryIdMaybe) {
+                    return type_entry->data.maybe.child_type;
+                } else {
+                    add_node_error(g, expr_node,
+                        buf_sprintf("expected maybe type, got '%s'", buf_ptr(&type_entry->name)));
+                    return g->builtin_types.entry_invalid;
+                }
+            }
     }
     zig_unreachable();
 }
src/ast_render.cpp
@@ -53,6 +53,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
         case PrefixOpMaybe: return "?";
         case PrefixOpError: return "%";
         case PrefixOpUnwrapError: return "%%";
+        case PrefixOpUnwrapMaybe: return "??";
     }
 }
 
@@ -696,8 +697,9 @@ static void render_node(AstRender *ar, AstNode *node) {
             if (node->data.fn_call_expr.is_builtin) {
                 fprintf(ar->f, "@");
             }
-            render_node(ar, node->data.fn_call_expr.fn_ref_expr);
             fprintf(ar->f, "(");
+            render_node(ar, node->data.fn_call_expr.fn_ref_expr);
+            fprintf(ar->f, ")(");
             for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
                 AstNode *param = node->data.fn_call_expr.params.at(i);
                 if (i != 0) {
src/codegen.cpp
@@ -85,6 +85,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
 static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
         LLVMValueRef target_ref, LLVMValueRef value,
         TypeTableEntry *op1_type, TypeTableEntry *op2_type);
+static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref);
 
 static TypeTableEntry *get_type_for_type_node(AstNode *node) {
     Expr *expr = get_resolved_expr(node);
@@ -1005,6 +1006,12 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
                     return nullptr;
                 }
             }
+        case PrefixOpUnwrapMaybe:
+            {
+                LLVMValueRef expr_val = gen_expr(g, expr_node);
+                // TODO in debug mode, put a panic here if null
+                return gen_unwrap_maybe(g, expr_node, expr_val);
+            }
     }
     zig_unreachable();
 }
src/parseh.cpp
@@ -249,33 +249,11 @@ static AstNode *create_one_statement_block(Context *c, AstNode *statement) {
     return node;
 }
 
-static AstNode *create_container_init_node(Context *c, AstNode *type_node) {
-    AstNode *node = create_node(c, NodeTypeContainerInitExpr);
-    node->data.container_init_expr.kind = ContainerInitKindArray;
-    node->data.container_init_expr.type = type_node;
-
-    normalize_parent_ptrs(node);
-    return node;
-}
-
-static AstNode *create_bin_op_node(Context *c, AstNode *lhs, BinOpType op, AstNode *rhs) {
-    AstNode *node = create_node(c, NodeTypeBinOpExpr);
-    node->data.bin_op_expr.op1 = lhs;
-    node->data.bin_op_expr.bin_op = op;
-    node->data.bin_op_expr.op2 = rhs;
-
-    normalize_parent_ptrs(node);
-    return node;
-}
-
 static AstNode *create_inline_fn_node(Context *c, Buf *fn_name, Buf *var_name, TypeTableEntry *fn_type) {
     AstNode *node = create_node(c, NodeTypeFnDef);
     node->data.fn_def.fn_proto = create_fn_proto_node(c, fn_name, fn_type);
 
-    AstNode *unreach_type_node = make_type_node(c, c->codegen->builtin_types.entry_unreachable);
-    AstNode *unreach_node = create_container_init_node(c, unreach_type_node);
-    AstNode *unwrap_node = create_bin_op_node(c, create_symbol_node(c, buf_ptr(var_name)),
-            BinOpTypeUnwrapMaybe, unreach_node);
+    AstNode *unwrap_node = create_prefix_node(c, PrefixOpUnwrapMaybe, create_symbol_node(c, buf_ptr(var_name)));
 
     AstNode *fn_call_node = create_node(c, NodeTypeFnCallExpr);
     fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;
src/parser.cpp
@@ -1206,6 +1206,7 @@ static PrefixOp tok_to_prefix_op(Token *token) {
         case TokenIdMaybe: return PrefixOpMaybe;
         case TokenIdPercent: return PrefixOpError;
         case TokenIdPercentPercent: return PrefixOpUnwrapError;
+        case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;
         case TokenIdBoolAnd: return PrefixOpAddressOf;
         default: return PrefixOpInvalid;
     }
test/run_tests.cpp
@@ -2116,7 +2116,7 @@ extern void (*fn_ptr)(void);
             "pub extern var fn_ptr: ?extern fn();",
             R"SOURCE(#attribute("inline")
 pub fn foo() {
-    (fn_ptr ?? (unreachable){})()
+    (??fn_ptr)()
 })SOURCE");
 }