diff --git a/doc/langref.md b/doc/langref.md index 02ac26f7b9..10ed66ac80 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -60,10 +60,12 @@ ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen) ParamDecl : token(Symbol) token(Colon) Type | token(Ellipsis) -Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType +Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType | MaybeType PointerType : token(Ampersand) option(token(Const)) Type +MaybeType : token(Question) Type + ArrayType : token(LBracket) Type token(Semicolon) Expression token(RBracket) Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) @@ -96,7 +98,7 @@ AssignmentOperator : token(Eq) | token(TimesEq) | token(DivEq) | token(ModEq) | BlockExpression : IfExpression | Block | WhileExpression -WhileExpression : token(While) Expression Block +WhileExpression : token(While) token(LParen) Expression token(RParen) Expression BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExpression @@ -104,13 +106,11 @@ ReturnExpression : token(Return) option(Expression) IfExpression : IfVarExpression | IfBoolExpression -IfBoolExpression : token(If) option((token) Expression Block option(Else | ElseIf) +IfBoolExpression : token(If) token(LParen) Expression token(RParen) Expression option(Else) -IfVarExpression : token(If) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression Block Option(Else | ElseIf) +IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression token(RParen) Expression Option(Else) -ElseIf : token(Else) IfExpression - -Else : token(Else) Block +Else : token(Else) Expression BoolAndExpression : ComparisonExpression token(BoolAnd) BoolAndExpression | ComparisonExpression diff --git a/example/maybe_type/main.zig b/example/maybe_type/main.zig new file mode 100644 index 0000000000..46327e2863 --- /dev/null +++ b/example/maybe_type/main.zig @@ -0,0 +1,19 @@ +export executable "maybe_type"; + +use "std.zig"; + +fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { + const x : ?bool = true; + + if (const y ?= x) { + if (y) { + print_str("x is true\n"); + } else { + print_str("x is false\n"); + } + } else { + print_str("x is none\n"); + } + + return 0; +} diff --git a/src/analyze.cpp b/src/analyze.cpp index 2be84ffa5b..7801cac201 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -132,6 +132,26 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool } } +static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { + if (child_type->maybe_parent) { + return child_type->maybe_parent; + } else { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe); + // TODO entry->type_ref + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); + // TODO entry->size_in_bits + // TODO entry->align_in_bits + assert(child_type->di_type); + // TODO entry->di_type + entry->data.maybe.child_type = child_type; + + g->type_table.put(&entry->name, entry); + child_type->maybe_parent = entry; + return entry; + } +} + static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) { auto existing_entry = child_type->arrays_by_size.maybe_get(array_size); if (existing_entry) { @@ -208,6 +228,20 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { } return type_node->entry; } + case AstNodeTypeTypeMaybe: + { + resolve_type(g, node->data.type.child_type); + TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry; + assert(child_type); + if (child_type->id == TypeTableEntryIdUnreachable) { + add_node_error(g, node, + buf_create_from_str("maybe unreachable type not allowed")); + } else if (child_type->id == TypeTableEntryIdInvalid) { + return child_type; + } + type_node->entry = get_maybe_type(g, child_type); + return type_node->entry; + } } zig_unreachable(); } diff --git a/src/analyze.hpp b/src/analyze.hpp index b53e7849a7..cbb593dc65 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -96,6 +96,7 @@ struct TypeTableEntry { TypeTableEntry *pointer_const_parent; TypeTableEntry *pointer_mut_parent; HashMap arrays_by_size; + TypeTableEntry *maybe_parent; }; diff --git a/src/parser.cpp b/src/parser.cpp index 610ed061fa..ecbb2d2e3b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -225,6 +225,12 @@ void ast_print(AstNode *node, int indent) { ast_print(node->data.type.array_size, indent + 2); break; } + case AstNodeTypeTypeMaybe: + { + fprintf(stderr, "MaybeType\n"); + ast_print(node->data.type.child_type, indent + 2); + break; + } } break; case NodeTypeReturnExpr: @@ -920,7 +926,7 @@ static void ast_parse_type_assume_amp(ParseContext *pc, int *token_index, AstNod } /* -Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType +Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType | MaybeType PointerType : token(Ampersand) option(token(Const)) Type ArrayType : token(LBracket) Type token(Semicolon) token(Number) token(RBracket) */ @@ -941,6 +947,9 @@ static AstNode *ast_parse_type(ParseContext *pc, int *token_index) { ast_buf_from_token(pc, token, &node->data.type.primitive_name); } else if (token->id == TokenIdAmpersand) { ast_parse_type_assume_amp(pc, token_index, node); + } else if (token->id == TokenIdMaybe) { + node->data.type.type = AstNodeTypeTypeMaybe; + node->data.type.child_type = ast_parse_type(pc, token_index); } else if (token->id == TokenIdBoolAnd) { // Pretend that we got 2 ampersand tokens node->data.type.type = AstNodeTypeTypePointer; @@ -1636,10 +1645,9 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool } /* -ElseIf : token(Else) IfExpression -Else : token(Else) Block +Else : token(Else) Expression */ -static AstNode *ast_parse_else_or_else_if(ParseContext *pc, int *token_index, bool mandatory) { +static AstNode *ast_parse_else(ParseContext *pc, int *token_index, bool mandatory) { Token *else_token = &pc->tokens->at(*token_index); if (else_token->id != TokenIdKeywordElse) { @@ -1651,17 +1659,13 @@ static AstNode *ast_parse_else_or_else_if(ParseContext *pc, int *token_index, bo } *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); + return ast_parse_expression(pc, token_index, true); } /* 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) +IfBoolExpression : token(If) token(LParen) Expression token(RParen) Expression option(Else) +IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression token(RParen) Expression Option(Else) */ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *if_tok = &pc->tokens->at(*token_index); @@ -1674,6 +1678,8 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda } *token_index += 1; + ast_eat_token(pc, token_index, TokenIdLParen); + Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdKeywordConst || token->id == TokenIdKeywordVar) { AstNode *node = ast_create_node(pc, NodeTypeIfVarExpr, if_tok); @@ -1695,14 +1701,16 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda } 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); + ast_eat_token(pc, token_index, TokenIdRParen); + node->data.if_var_expr.then_block = ast_parse_expression(pc, token_index, true); + node->data.if_var_expr.else_node = ast_parse_else(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); + ast_eat_token(pc, token_index, TokenIdRParen); + node->data.if_bool_expr.then_block = ast_parse_expression(pc, token_index, true); + node->data.if_bool_expr.else_node = ast_parse_else(pc, token_index, false); return node; } } @@ -1795,7 +1803,7 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool } /* -WhileExpression : token(While) Expression Block +WhileExpression : token(While) token(LParen) Expression token(RParen) Expression */ static AstNode *ast_parse_while_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1811,8 +1819,13 @@ static AstNode *ast_parse_while_expr(ParseContext *pc, int *token_index, bool ma AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, token); + ast_eat_token(pc, token_index, TokenIdLParen); node->data.while_expr.condition = ast_parse_expression(pc, token_index, true); - node->data.while_expr.body = ast_parse_block(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdRParen); + + node->data.while_expr.body = ast_parse_expression(pc, token_index, true); + + return node; } diff --git a/src/parser.hpp b/src/parser.hpp index 2e4b62dc07..4f1db6b4de 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -95,6 +95,7 @@ enum AstNodeTypeType { AstNodeTypeTypePrimitive, AstNodeTypeTypePointer, AstNodeTypeTypeArray, + AstNodeTypeTypeMaybe, }; struct AstNodeType { diff --git a/test/run_tests.cpp b/test/run_tests.cpp index f1e8555903..aada4354b3 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -184,17 +184,17 @@ static void add_compiling_test_cases(void) { use "std.zig"; pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - if 1 != 0 { + if (1 != 0) { print_str("1 is true\n"); } else { print_str("1 is false\n"); } - if 0 != 0 { + if (0 != 0) { print_str("0 is true\n"); - } else if 1 - 1 != 0 { + } else if (1 - 1 != 0) { print_str("1 - 1 is true\n"); } - if !(0 != 0) { + if (!(0 != 0)) { print_str("!0 is true\n"); } return 0; @@ -209,7 +209,7 @@ static void add_compiling_test_cases(void) { } pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - if add(22, 11) == 33 { + if (add(22, 11) == 33) { print_str("pass\n"); } return 0; @@ -220,7 +220,7 @@ static void add_compiling_test_cases(void) { use "std.zig"; fn loop(a : i32) { - if a == 0 { + if (a == 0) { goto done; } print_str("loop\n"); @@ -304,7 +304,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { var i = 0 as i32; loop_start: - if i == 3 { + if (i == 3) { goto done; } print_str("loop\n"); @@ -323,7 +323,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { var i : i32 = 0; loop_start: - if i == 5 { + if (i == 5) { goto loop_end; } array[i] = i + 1; @@ -335,7 +335,7 @@ loop_end: i = 0; var accumulator = 0 as i32; loop_2_start: - if i == 5 { + if (i == 5) { goto loop_2_end; } @@ -345,7 +345,7 @@ loop_2_start: goto loop_2_start; loop_2_end: - if accumulator == 15 { + if (accumulator == 15) { print_str("OK\n"); } @@ -368,18 +368,18 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { - if false || false || false { print_str("BAD 1\n"); } - if true && true && false { print_str("BAD 2\n"); } - if 1 | 2 | 4 != 7 { print_str("BAD 3\n"); } - if 3 ^ 6 ^ 8 != 13 { print_str("BAD 4\n"); } - if 7 & 14 & 28 != 4 { print_str("BAD 5\n"); } - if 9 << 1 << 2 != 9 << 3 { print_str("BAD 6\n"); } - if 90 >> 1 >> 2 != 90 >> 3 { print_str("BAD 7\n"); } - if 100 - 1 + 1000 != 1099 { print_str("BAD 8\n"); } - if 5 * 4 / 2 % 3 != 1 { print_str("BAD 9\n"); } - if 5 as i32 as i32 != 5 { print_str("BAD 10\n"); } - if !!false { print_str("BAD 11\n"); } - if 7 != --7 { print_str("BAD 12\n"); } + if (false || false || false) { print_str("BAD 1\n"); } + if (true && true && false) { print_str("BAD 2\n"); } + if (1 | 2 | 4 != 7) { print_str("BAD 3\n"); } + if (3 ^ 6 ^ 8 != 13) { print_str("BAD 4\n"); } + if (7 & 14 & 28 != 4) { print_str("BAD 5\n"); } + if (9 << 1 << 2 != 9 << 3) { print_str("BAD 6\n"); } + if (90 >> 1 >> 2 != 90 >> 3) { print_str("BAD 7\n"); } + if (100 - 1 + 1000 != 1099) { print_str("BAD 8\n"); } + if (5 * 4 / 2 % 3 != 1) { print_str("BAD 9\n"); } + if (5 as i32 as i32 != 5) { print_str("BAD 10\n"); } + if (!!false) { print_str("BAD 11\n"); } + if (7 != --7) { print_str("BAD 12\n"); } print_str("OK\n"); return 0; @@ -390,17 +390,17 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { - if true || { print_str("BAD 1\n"); false } { + if (true || { print_str("BAD 1\n"); false }) { print_str("OK 1\n"); } - if false || { print_str("OK 2\n"); false } { + if (false || { print_str("OK 2\n"); false }) { print_str("BAD 2\n"); } - if true && { print_str("OK 3\n"); false } { + if (true && { print_str("OK 3\n"); false }) { print_str("BAD 3\n"); } - if false && { print_str("BAD 4\n"); false } { + if (false && { print_str("BAD 4\n"); false }) { } else { print_str("OK 4\n"); } @@ -414,18 +414,18 @@ use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { var i : i32 = 0; - i += 5; if i != 5 { print_str("BAD +=\n"); } - i -= 2; if i != 3 { print_str("BAD -=\n"); } - i *= 20; if i != 60 { print_str("BAD *=\n"); } - i /= 3; if i != 20 { print_str("BAD /=\n"); } - i %= 11; if i != 9 { print_str("BAD %=\n"); } - i <<= 1; if i != 18 { print_str("BAD <<=\n"); } - i >>= 2; if i != 4 { print_str("BAD >>=\n"); } + i += 5; if (i != 5) { print_str("BAD +=\n"); } + i -= 2; if (i != 3) { print_str("BAD -=\n"); } + i *= 20; if (i != 60) { print_str("BAD *=\n"); } + i /= 3; if (i != 20) { print_str("BAD /=\n"); } + i %= 11; if (i != 9) { print_str("BAD %=\n"); } + i <<= 1; if (i != 18) { print_str("BAD <<=\n"); } + i >>= 2; if (i != 4) { print_str("BAD >>=\n"); } i = 6; - i &= 5; if i != 4 { print_str("BAD &=\n"); } - i ^= 6; if i != 2 { print_str("BAD ^=\n"); } + i &= 5; if (i != 4) { print_str("BAD &=\n"); } + i ^= 6; if (i != 2) { print_str("BAD ^=\n"); } i = 6; - i |= 3; if i != 7 { print_str("BAD |=\n"); } + i |= 3; if (i != 7) { print_str("BAD |=\n"); } print_str("OK\n"); return 0; @@ -570,7 +570,7 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { foo.b = foo.a == 1; test_foo(foo); test_mutation(&foo); - if foo.c != 100 { + if (foo.c != 100) { print_str("BAD\n"); } test_point_to_self(); @@ -585,7 +585,7 @@ struct Foo { c : f32, } fn test_foo(foo : Foo) { - if !foo.b { + if (!foo.b) { print_str("BAD\n"); } } @@ -610,7 +610,7 @@ fn test_point_to_self() { root.next = &node; - if node.next.next.next.val.x != 1 { + if (node.next.next.next.val.x != 1) { print_str("BAD\n"); } } @@ -620,15 +620,15 @@ fn test_byval_assign() { foo1.a = 1234; - if foo2.a != 0 { print_str("BAD\n"); } + if (foo2.a != 0) { print_str("BAD\n"); } foo2 = foo1; - if foo2.a != 1234 { print_str("BAD - byval assignment failed\n"); } + if (foo2.a != 1234) { print_str("BAD - byval assignment failed\n"); } } fn test_initializer() { const val = Val { .x = 42 }; - if val.x != 42 { print_str("BAD\n"); } + if (val.x != 42) { print_str("BAD\n"); } } )SOURCE", "OK\n"); @@ -639,9 +639,9 @@ const g1 : i32 = 1233 + 1; var g2 : i32; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { - if g2 != 0 { print_str("BAD\n"); } + if (g2 != 0) { print_str("BAD\n"); } g2 = g1; - if g2 != 1234 { print_str("BAD\n"); } + if (g2 != 1234) { print_str("BAD\n"); } print_str("OK\n"); return 0; } @@ -651,7 +651,7 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { var i : i32 = 0; - while i < 4 { + while (i < 4) { print_str("loop\n"); i += 1; } @@ -663,10 +663,10 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { var i : i32 = 0; - while true { + while (true) { print_str("loop\n"); i += 1; - if i < 4 { + if (i < 4) { continue; } break; @@ -871,8 +871,8 @@ fn f() { add_compile_fail_case("missing else clause", R"SOURCE( fn f() { - const x : i32 = if true { 1 }; - const y = if true { 1 as i32 }; + const x : i32 = if (true) { 1 }; + const y = if (true) { 1 as i32 }; } )SOURCE", 2, ".tmp_source.zig:3:21: error: expected type 'i32', got 'void'", ".tmp_source.zig:4:15: error: incompatible types: 'i32' and 'void'");