| author | |
| committer | |
| log | a3c97081ca37a6f78732c9a5f2244cef5a11cb07 |
| tree | aec69a113eacaa084039364b65ef803d695e0dd5 |
| parent | 9b9fd5ad23e37d1e2b37b05b168abcfd53f4629d |
add null literal
fix number literal / maybe interactions14 files changed, 287 insertions(+), 23 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -120,6 +120,7 @@ set(ZIG_STD_SRC |
| 120 | 120 | "${CMAKE_SOURCE_DIR}/std/builtin.zig" |
| 121 | 121 | "${CMAKE_SOURCE_DIR}/std/std.zig" |
| 122 | 122 | "${CMAKE_SOURCE_DIR}/std/syscall.zig" |
| 123 | "${CMAKE_SOURCE_DIR}/std/errno.zig" | |
| 123 | 124 | "${CMAKE_SOURCE_DIR}/std/rand.zig" |
| 124 | 125 | ) |
| 125 | 126 |
doc/langref.md+9-6| ... | ... | @@ -98,7 +98,9 @@ AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token |
| 98 | 98 | |
| 99 | 99 | AsmClobbers: token(Colon) list(token(String), token(Comma)) |
| 100 | 100 | |
| 101 | AssignmentExpression : BoolOrExpression AssignmentOperator BoolOrExpression | BoolOrExpression | |
| 101 | UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression | |
| 102 | ||
| 103 | AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression | |
| 102 | 104 | |
| 103 | 105 | 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) |
| 104 | 106 | |
| ... | ... | @@ -166,7 +168,7 @@ Goto: token(Goto) token(Symbol) |
| 166 | 168 | |
| 167 | 169 | GroupedExpression : token(LParen) Expression token(RParen) |
| 168 | 170 | |
| 169 | KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | |
| 171 | KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null) | |
| 170 | 172 | ``` |
| 171 | 173 | |
| 172 | 174 | ## Operator Precedence |
| ... | ... | @@ -184,6 +186,7 @@ as |
| 184 | 186 | == != < > <= >= |
| 185 | 187 | && |
| 186 | 188 | || |
| 189 | ?? | |
| 187 | 190 | = *= /= %= += -= <<= >>= &= ^= |= &&= ||= |
| 188 | 191 | ``` |
| 189 | 192 | |
| ... | ... | @@ -192,7 +195,7 @@ as |
| 192 | 195 | ### Characters and Strings |
| 193 | 196 | |
| 194 | 197 | | Example | Characters | Escapes | Null Term | Type |
| 195 | --------------------------------------------------------------------------------- | |
| 198 | ----------------|----------|-------------|----------------|-----------|---------- | |
| 196 | 199 | Byte | 'H' | All ASCII | Byte | No | u8 |
| 197 | 200 | UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5; u8] |
| 198 | 201 | UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | *const u8 |
| ... | ... | @@ -200,7 +203,7 @@ as |
| 200 | 203 | ### Byte Escapes |
| 201 | 204 | |
| 202 | 205 | | Name |
| 203 | ----------------------------------------------- | |
| 206 | ------|---------------------------------------- | |
| 204 | 207 | \x7F | 8-bit character code (exactly 2 digits) |
| 205 | 208 | \n | Newline |
| 206 | 209 | \r | Carriage return |
| ... | ... | @@ -213,13 +216,13 @@ as |
| 213 | 216 | ### Unicode Escapes |
| 214 | 217 | |
| 215 | 218 | | Name |
| 216 | ---------------------------------------------------------- | |
| 219 | ----------|----------------------------------------------- | |
| 217 | 220 | \u{7FFF} | 24-bit Unicode character code (up to 6 digits) |
| 218 | 221 | |
| 219 | 222 | ### Numbers |
| 220 | 223 | |
| 221 | 224 | Number literals | Example | Exponentiation |
| 222 | -------------------------------------------------- | |
| 225 | --------------------|-------------|--------------- | |
| 223 | 226 | Decimal integer | 98222 | N/A |
| 224 | 227 | Hex integer | 0xff | N/A |
| 225 | 228 | Octal integer | 0o77 | N/A |
doc/vim/syntax/zig.vim+10| ... | ... | @@ -14,6 +14,7 @@ syn keyword zigStatement goto break return continue asm |
| 14 | 14 | syn keyword zigConditional if else match |
| 15 | 15 | syn keyword zigRepeat while for |
| 16 | 16 | |
| 17 | syn keyword zigConstant null | |
| 17 | 18 | syn keyword zigKeyword fn unreachable use void |
| 18 | 19 | syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string |
| 19 | 20 | |
| ... | ... | @@ -28,6 +29,12 @@ syn match zigHexNumber display "\<0x[a-fA-F0-9_]\+\%([iu]\%(size\|8\|16\|32\|64\ |
| 28 | 29 | syn match zigOctNumber display "\<0o[0-7_]\+\%([iu]\%(size\|8\|16\|32\|64\)\)\=" |
| 29 | 30 | syn match zigBinNumber display "\<0b[01_]\+\%([iu]\%(size\|8\|16\|32\|64\)\)\=" |
| 30 | 31 | |
| 32 | ||
| 33 | syn match zigCharacterInvalid display contained /b\?'\zs[\n\r\t']\ze'/ | |
| 34 | syn match zigCharacterInvalidUnicode display contained /b'\zs[^[:cntrl:][:graph:][:alnum:][:space:]]\ze'/ | |
| 35 | syn match zigCharacter /b'\([^\\]\|\\\(.\|x\x\{2}\)\)'/ contains=zigEscape,zigEscapeError,zigCharacterInvalid,zigCharacterInvalidUnicode | |
| 36 | syn match zigCharacter /'\([^\\]\|\\\(.\|x\x\{2}\|u\x\{4}\|U\x\{8}\|u{\x\{1,6}}\)\)'/ contains=zigEscape,zigEscapeUnicode,zigEscapeError,zigCharacterInvalid | |
| 37 | ||
| 31 | 38 | syn match zigShebang /\%^#![^[].*/ |
| 32 | 39 | |
| 33 | 40 | syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell |
| ... | ... | @@ -64,6 +71,9 @@ hi def link zigCommentBlockDoc zigCommentLineDoc |
| 64 | 71 | hi def link zigTodo Todo |
| 65 | 72 | hi def link zigStringContinuation Special |
| 66 | 73 | hi def link zigString String |
| 74 | hi def link zigCharacterInvalid Error | |
| 75 | hi def link zigCharacterInvalidUnicode zigCharacterInvalid | |
| 76 | hi def link zigCharacter Character | |
| 67 | 77 | hi def link zigEscape Special |
| 68 | 78 | hi def link zigEscapeUnicode zigEscape |
| 69 | 79 | hi def link zigEscapeError Error |
example/guess_number/main.zig+8-6| ... | ... | @@ -14,7 +14,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 14 | 14 | var err : isize; |
| 15 | 15 | if ({err = os_get_random_bytes(&seed as &u8, #sizeof(u32)); err != #sizeof(u32)}) { |
| 16 | 16 | // TODO full error message |
| 17 | fprint_str(stderr_fileno, "unable to get random bytes"); | |
| 17 | fprint_str(stderr_fileno, "unable to get random bytes\n"); | |
| 18 | 18 | return 1; |
| 19 | 19 | } |
| 20 | 20 | |
| ... | ... | @@ -27,11 +27,14 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 27 | 27 | print_u64(answer); |
| 28 | 28 | print_str("\n"); |
| 29 | 29 | |
| 30 | return 0; | |
| 31 | ||
| 32 | /* | |
| 33 | 30 | while (true) { |
| 34 | const line = readline("\nGuess a number between 1 and 100: "); | |
| 31 | print_str("\nGuess a number between 1 and 100: "); | |
| 32 | var line_buf : [20]u8; | |
| 33 | const line = readline(line_buf) ?? { | |
| 34 | // TODO full error message | |
| 35 | fprint_str(stderr_fileno, "unable to read input\n"); | |
| 36 | return 1; | |
| 37 | }; | |
| 35 | 38 | |
| 36 | 39 | if (const guess ?= parse_u64(line)) { |
| 37 | 40 | if (guess > answer) { |
| ... | ... | @@ -46,5 +49,4 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 46 | 49 | print_str("Invalid number format.\n"); |
| 47 | 50 | } |
| 48 | 51 | } |
| 49 | */ | |
| 50 | 52 | } |
example/maybe_type/main.zig+16| ... | ... | @@ -15,5 +15,21 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 15 | 15 | print_str("x is none\n"); |
| 16 | 16 | } |
| 17 | 17 | |
| 18 | const next_x : ?i32 = null; | |
| 19 | ||
| 20 | const z = next_x ?? 1234; | |
| 21 | ||
| 22 | if (z != 1234) { | |
| 23 | print_str("BAD\n"); | |
| 24 | } | |
| 25 | ||
| 26 | const final_x : ?i32 = 13; | |
| 27 | ||
| 28 | const num = final_x ?? unreachable; | |
| 29 | ||
| 30 | if (num != 13) { | |
| 31 | print_str("BAD\n"); | |
| 32 | } | |
| 33 | ||
| 18 | 34 | return 0; |
| 19 | 35 | } |
src/analyze.cpp+60-3| ... | ... | @@ -48,6 +48,7 @@ static AstNode *first_executing_node(AstNode *node) { |
| 48 | 48 | case NodeTypeUse: |
| 49 | 49 | case NodeTypeVoid: |
| 50 | 50 | case NodeTypeBoolLiteral: |
| 51 | case NodeTypeNullLiteral: | |
| 51 | 52 | case NodeTypeIfBoolExpr: |
| 52 | 53 | case NodeTypeIfVarExpr: |
| 53 | 54 | case NodeTypeLabel: |
| ... | ... | @@ -358,6 +359,7 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context, |
| 358 | 359 | case BinOpTypeSub: |
| 359 | 360 | case BinOpTypeMult: |
| 360 | 361 | case BinOpTypeDiv: |
| 362 | case BinOpTypeUnwrapMaybe: | |
| 361 | 363 | return g->builtin_types.entry_invalid; |
| 362 | 364 | case BinOpTypeInvalid: |
| 363 | 365 | case BinOpTypeAssign: |
| ... | ... | @@ -388,6 +390,8 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context, |
| 388 | 390 | case NodeTypeBoolLiteral: |
| 389 | 391 | out_number_literal->data.x_uint = node->data.bool_literal ? 1 : 0; |
| 390 | 392 | return node->codegen_node->expr_node.type_entry; |
| 393 | case NodeTypeNullLiteral: | |
| 394 | return node->codegen_node->expr_node.type_entry; | |
| 391 | 395 | case NodeTypeBinOpExpr: |
| 392 | 396 | return eval_const_expr_bin_op(g, context, node, out_number_literal); |
| 393 | 397 | case NodeTypeCompilerFnType: |
| ... | ... | @@ -877,6 +881,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 877 | 881 | case NodeTypeUnreachable: |
| 878 | 882 | case NodeTypeVoid: |
| 879 | 883 | case NodeTypeBoolLiteral: |
| 884 | case NodeTypeNullLiteral: | |
| 880 | 885 | case NodeTypeSymbol: |
| 881 | 886 | case NodeTypeCastExpr: |
| 882 | 887 | case NodeTypePrefixOpExpr: |
| ... | ... | @@ -951,6 +956,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 951 | 956 | case NodeTypeUnreachable: |
| 952 | 957 | case NodeTypeVoid: |
| 953 | 958 | case NodeTypeBoolLiteral: |
| 959 | case NodeTypeNullLiteral: | |
| 954 | 960 | case NodeTypeSymbol: |
| 955 | 961 | case NodeTypeCastExpr: |
| 956 | 962 | case NodeTypePrefixOpExpr: |
| ... | ... | @@ -1018,7 +1024,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, |
| 1018 | 1024 | return false; |
| 1019 | 1025 | } |
| 1020 | 1026 | case TypeTableEntryIdMaybe: |
| 1021 | return num_lit_fits_in_other_type(g, literal_type, other_type->data.maybe.child_type); | |
| 1027 | return false; | |
| 1022 | 1028 | } |
| 1023 | 1029 | zig_unreachable(); |
| 1024 | 1030 | } |
| ... | ... | @@ -1133,6 +1139,9 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont |
| 1133 | 1139 | if (actual_type->id == TypeTableEntryIdNumberLiteral && |
| 1134 | 1140 | num_lit_fits_in_other_type(g, actual_type, expected_type)) |
| 1135 | 1141 | { |
| 1142 | assert(!node->codegen_node->data.num_lit_node.resolved_type || | |
| 1143 | node->codegen_node->data.num_lit_node.resolved_type == expected_type); | |
| 1144 | node->codegen_node->data.num_lit_node.resolved_type = expected_type; | |
| 1136 | 1145 | return expected_type; |
| 1137 | 1146 | } |
| 1138 | 1147 | |
| ... | ... | @@ -1419,6 +1428,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { |
| 1419 | 1428 | case BinOpTypeMult: |
| 1420 | 1429 | case BinOpTypeDiv: |
| 1421 | 1430 | case BinOpTypeMod: |
| 1431 | case BinOpTypeUnwrapMaybe: | |
| 1422 | 1432 | zig_unreachable(); |
| 1423 | 1433 | } |
| 1424 | 1434 | zig_unreachable(); |
| ... | ... | @@ -1619,6 +1629,24 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 1619 | 1629 | |
| 1620 | 1630 | return resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type); |
| 1621 | 1631 | } |
| 1632 | case BinOpTypeUnwrapMaybe: | |
| 1633 | { | |
| 1634 | AstNode *op1 = node->data.bin_op_expr.op1; | |
| 1635 | AstNode *op2 = node->data.bin_op_expr.op2; | |
| 1636 | TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1); | |
| 1637 | ||
| 1638 | if (lhs_type->id == TypeTableEntryIdInvalid) { | |
| 1639 | return lhs_type; | |
| 1640 | } else if (lhs_type->id == TypeTableEntryIdMaybe) { | |
| 1641 | TypeTableEntry *child_type = lhs_type->data.maybe.child_type; | |
| 1642 | analyze_expression(g, import, context, child_type, op2); | |
| 1643 | return child_type; | |
| 1644 | } else { | |
| 1645 | add_node_error(g, op1, | |
| 1646 | buf_sprintf("expected maybe type, got '%s'", | |
| 1647 | buf_ptr(&lhs_type->name))); | |
| 1648 | } | |
| 1649 | } | |
| 1622 | 1650 | case BinOpTypeInvalid: |
| 1623 | 1651 | zig_unreachable(); |
| 1624 | 1652 | } |
| ... | ... | @@ -1695,6 +1723,27 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE |
| 1695 | 1723 | return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false); |
| 1696 | 1724 | } |
| 1697 | 1725 | |
| 1726 | static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *import, | |
| 1727 | BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) | |
| 1728 | { | |
| 1729 | assert(node->type == NodeTypeNullLiteral); | |
| 1730 | ||
| 1731 | if (expected_type) { | |
| 1732 | assert(expected_type->id == TypeTableEntryIdMaybe); | |
| 1733 | ||
| 1734 | assert(node->codegen_node); | |
| 1735 | node->codegen_node->data.struct_val_expr_node.type_entry = expected_type; | |
| 1736 | node->codegen_node->data.struct_val_expr_node.source_node = node; | |
| 1737 | block_context->struct_val_expr_alloca_list.append(&node->codegen_node->data.struct_val_expr_node); | |
| 1738 | ||
| 1739 | return expected_type; | |
| 1740 | } else { | |
| 1741 | add_node_error(g, node, | |
| 1742 | buf_sprintf("unable to determine null type")); | |
| 1743 | return g->builtin_types.entry_invalid; | |
| 1744 | } | |
| 1745 | } | |
| 1746 | ||
| 1698 | 1747 | static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, |
| 1699 | 1748 | BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) |
| 1700 | 1749 | { |
| ... | ... | @@ -1706,8 +1755,11 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry |
| 1706 | 1755 | } else if (expected_type) { |
| 1707 | 1756 | NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; |
| 1708 | 1757 | assert(!codegen_num_lit->resolved_type); |
| 1709 | codegen_num_lit->resolved_type = resolve_type_compatibility(g, block_context, node, expected_type, num_lit_type); | |
| 1710 | return codegen_num_lit->resolved_type; | |
| 1758 | TypeTableEntry *after_implicit_cast_resolved_type = | |
| 1759 | resolve_type_compatibility(g, block_context, node, expected_type, num_lit_type); | |
| 1760 | assert(codegen_num_lit->resolved_type || | |
| 1761 | after_implicit_cast_resolved_type->id == TypeTableEntryIdInvalid); | |
| 1762 | return after_implicit_cast_resolved_type; | |
| 1711 | 1763 | } else { |
| 1712 | 1764 | return num_lit_type; |
| 1713 | 1765 | } |
| ... | ... | @@ -2174,6 +2226,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 2174 | 2226 | return_type = g->builtin_types.entry_bool; |
| 2175 | 2227 | break; |
| 2176 | 2228 | |
| 2229 | case NodeTypeNullLiteral: | |
| 2230 | return_type = analyze_null_literal_expr(g, import, context, expected_type, node); | |
| 2231 | break; | |
| 2232 | ||
| 2177 | 2233 | case NodeTypeSymbol: |
| 2178 | 2234 | { |
| 2179 | 2235 | return_type = analyze_variable_name(g, import, context, node, &node->data.symbol); |
| ... | ... | @@ -2491,6 +2547,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 2491 | 2547 | case NodeTypeUnreachable: |
| 2492 | 2548 | case NodeTypeVoid: |
| 2493 | 2549 | case NodeTypeBoolLiteral: |
| 2550 | case NodeTypeNullLiteral: | |
| 2494 | 2551 | case NodeTypeSymbol: |
| 2495 | 2552 | case NodeTypeCastExpr: |
| 2496 | 2553 | case NodeTypePrefixOpExpr: |
src/codegen.cpp+86-4| ... | ... | @@ -613,6 +613,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, |
| 613 | 613 | case BinOpTypeAssign: |
| 614 | 614 | case BinOpTypeAssignBoolAnd: |
| 615 | 615 | case BinOpTypeAssignBoolOr: |
| 616 | case BinOpTypeUnwrapMaybe: | |
| 616 | 617 | zig_unreachable(); |
| 617 | 618 | } |
| 618 | 619 | zig_unreachable(); |
| ... | ... | @@ -814,6 +815,70 @@ static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { |
| 814 | 815 | return gen_assign_raw(g, node, node->data.bin_op_expr.bin_op, target_ref, value, op1_type, op2_type); |
| 815 | 816 | } |
| 816 | 817 | |
| 818 | static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref) { | |
| 819 | add_debug_source_node(g, node); | |
| 820 | LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, ""); | |
| 821 | // TODO if it's a struct we might not want to load the pointer | |
| 822 | return LLVMBuildLoad(g->builder, maybe_field_ptr, ""); | |
| 823 | } | |
| 824 | ||
| 825 | static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) { | |
| 826 | assert(node->type == NodeTypeBinOpExpr); | |
| 827 | assert(node->data.bin_op_expr.bin_op == BinOpTypeUnwrapMaybe); | |
| 828 | ||
| 829 | AstNode *op1_node = node->data.bin_op_expr.op1; | |
| 830 | AstNode *op2_node = node->data.bin_op_expr.op2; | |
| 831 | ||
| 832 | LLVMValueRef maybe_struct_ref = gen_expr(g, op1_node); | |
| 833 | ||
| 834 | add_debug_source_node(g, node); | |
| 835 | LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, ""); | |
| 836 | LLVMValueRef cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); | |
| 837 | ||
| 838 | LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNonNull"); | |
| 839 | LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNull"); | |
| 840 | LLVMBasicBlockRef end_block; | |
| 841 | ||
| 842 | bool non_null_reachable = get_expr_type(op1_node)->id != TypeTableEntryIdUnreachable; | |
| 843 | bool null_reachable = get_expr_type(op2_node)->id != TypeTableEntryIdUnreachable; | |
| 844 | bool end_reachable = non_null_reachable || null_reachable; | |
| 845 | if (end_reachable) { | |
| 846 | end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd"); | |
| 847 | } | |
| 848 | ||
| 849 | LLVMBuildCondBr(g->builder, cond_value, non_null_block, null_block); | |
| 850 | ||
| 851 | LLVMPositionBuilderAtEnd(g->builder, non_null_block); | |
| 852 | LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref); | |
| 853 | if (non_null_reachable) { | |
| 854 | add_debug_source_node(g, node); | |
| 855 | LLVMBuildBr(g->builder, end_block); | |
| 856 | } | |
| 857 | ||
| 858 | LLVMPositionBuilderAtEnd(g->builder, null_block); | |
| 859 | LLVMValueRef null_result = gen_expr(g, op2_node); | |
| 860 | if (null_reachable) { | |
| 861 | add_debug_source_node(g, node); | |
| 862 | LLVMBuildBr(g->builder, end_block); | |
| 863 | } | |
| 864 | ||
| 865 | if (end_reachable) { | |
| 866 | LLVMPositionBuilderAtEnd(g->builder, end_block); | |
| 867 | if (null_reachable) { | |
| 868 | add_debug_source_node(g, node); | |
| 869 | LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), ""); | |
| 870 | LLVMValueRef incoming_values[2] = {non_null_result, null_result}; | |
| 871 | LLVMBasicBlockRef incoming_blocks[2] = {non_null_block, null_block}; | |
| 872 | LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); | |
| 873 | return phi; | |
| 874 | } else { | |
| 875 | return non_null_result; | |
| 876 | } | |
| 877 | } | |
| 878 | ||
| 879 | return nullptr; | |
| 880 | } | |
| 881 | ||
| 817 | 882 | static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
| 818 | 883 | switch (node->data.bin_op_expr.bin_op) { |
| 819 | 884 | case BinOpTypeInvalid: |
| ... | ... | @@ -843,6 +908,8 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
| 843 | 908 | case BinOpTypeCmpLessOrEq: |
| 844 | 909 | case BinOpTypeCmpGreaterOrEq: |
| 845 | 910 | return gen_cmp_expr(g, node); |
| 911 | case BinOpTypeUnwrapMaybe: | |
| 912 | return gen_unwrap_maybe_expr(g, node); | |
| 846 | 913 | case BinOpTypeBinOr: |
| 847 | 914 | case BinOpTypeBinXor: |
| 848 | 915 | case BinOpTypeBinAnd: |
| ... | ... | @@ -1124,6 +1191,22 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { |
| 1124 | 1191 | return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, ""); |
| 1125 | 1192 | } |
| 1126 | 1193 | |
| 1194 | static LLVMValueRef gen_null_literal(CodeGen *g, AstNode *node) { | |
| 1195 | assert(node->type == NodeTypeNullLiteral); | |
| 1196 | ||
| 1197 | TypeTableEntry *type_entry = get_expr_type(node); | |
| 1198 | assert(type_entry->id == TypeTableEntryIdMaybe); | |
| 1199 | ||
| 1200 | LLVMValueRef tmp_struct_ptr = node->codegen_node->data.struct_val_expr_node.ptr; | |
| 1201 | ||
| 1202 | add_debug_source_node(g, node); | |
| 1203 | LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, ""); | |
| 1204 | LLVMValueRef null_value = LLVMConstNull(LLVMInt1Type()); | |
| 1205 | LLVMBuildStore(g->builder, null_value, field_ptr); | |
| 1206 | ||
| 1207 | return tmp_struct_ptr; | |
| 1208 | } | |
| 1209 | ||
| 1127 | 1210 | static LLVMValueRef gen_struct_val_expr(CodeGen *g, AstNode *node) { |
| 1128 | 1211 | assert(node->type == NodeTypeStructValueExpr); |
| 1129 | 1212 | |
| ... | ... | @@ -1242,10 +1325,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa |
| 1242 | 1325 | LLVMValueRef value; |
| 1243 | 1326 | if (unwrap_maybe) { |
| 1244 | 1327 | assert(var_decl->expr); |
| 1245 | add_debug_source_node(g, source_node); | |
| 1246 | LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, *init_value, 0, ""); | |
| 1247 | // TODO if it's a struct we might not want to load the pointer | |
| 1248 | value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); | |
| 1328 | value = gen_unwrap_maybe(g, source_node, *init_value); | |
| 1249 | 1329 | } else { |
| 1250 | 1330 | value = *init_value; |
| 1251 | 1331 | } |
| ... | ... | @@ -1375,6 +1455,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1375 | 1455 | return LLVMConstAllOnes(LLVMInt1Type()); |
| 1376 | 1456 | else |
| 1377 | 1457 | return LLVMConstNull(LLVMInt1Type()); |
| 1458 | case NodeTypeNullLiteral: | |
| 1459 | return gen_null_literal(g, node); | |
| 1378 | 1460 | case NodeTypeIfBoolExpr: |
| 1379 | 1461 | return gen_if_bool_expr(g, node); |
| 1380 | 1462 | case NodeTypeIfVarExpr: |
src/parser.cpp+40-3| ... | ... | @@ -48,6 +48,7 @@ static const char *bin_op_str(BinOpType bin_op) { |
| 48 | 48 | case BinOpTypeAssignBitOr: return "|="; |
| 49 | 49 | case BinOpTypeAssignBoolAnd: return "&&="; |
| 50 | 50 | case BinOpTypeAssignBoolOr: return "||="; |
| 51 | case BinOpTypeUnwrapMaybe: return "??"; | |
| 51 | 52 | } |
| 52 | 53 | zig_unreachable(); |
| 53 | 54 | } |
| ... | ... | @@ -117,6 +118,8 @@ const char *node_type_str(NodeType node_type) { |
| 117 | 118 | return "Void"; |
| 118 | 119 | case NodeTypeBoolLiteral: |
| 119 | 120 | return "BoolLiteral"; |
| 121 | case NodeTypeNullLiteral: | |
| 122 | return "NullLiteral"; | |
| 120 | 123 | case NodeTypeIfBoolExpr: |
| 121 | 124 | return "IfBoolExpr"; |
| 122 | 125 | case NodeTypeIfVarExpr: |
| ... | ... | @@ -349,6 +352,9 @@ void ast_print(AstNode *node, int indent) { |
| 349 | 352 | case NodeTypeBoolLiteral: |
| 350 | 353 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false"); |
| 351 | 354 | break; |
| 355 | case NodeTypeNullLiteral: | |
| 356 | fprintf(stderr, "%s\n", node_type_str(node->type)); | |
| 357 | break; | |
| 352 | 358 | case NodeTypeIfBoolExpr: |
| 353 | 359 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 354 | 360 | if (node->data.if_bool_expr.condition) |
| ... | ... | @@ -1280,6 +1286,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { |
| 1280 | 1286 | |
| 1281 | 1287 | /* |
| 1282 | 1288 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType |
| 1289 | KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null) | |
| 1283 | 1290 | */ |
| 1284 | 1291 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1285 | 1292 | Token *token = &pc->tokens->at(*token_index); |
| ... | ... | @@ -1317,6 +1324,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 1317 | 1324 | node->data.bool_literal = false; |
| 1318 | 1325 | *token_index += 1; |
| 1319 | 1326 | return node; |
| 1327 | } else if (token->id == TokenIdKeywordNull) { | |
| 1328 | AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token); | |
| 1329 | *token_index += 1; | |
| 1330 | return node; | |
| 1320 | 1331 | } else if (token->id == TokenIdSymbol) { |
| 1321 | 1332 | Token *next_token = &pc->tokens->at(*token_index + 1); |
| 1322 | 1333 | |
| ... | ... | @@ -2045,19 +2056,45 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool manda |
| 2045 | 2056 | } |
| 2046 | 2057 | |
| 2047 | 2058 | /* |
| 2048 | AssignmentExpression : BoolOrExpression AssignmentOperator BoolOrExpression | BoolOrExpression | |
| 2059 | UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression | |
| 2049 | 2060 | */ |
| 2050 | static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) { | |
| 2061 | static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory) { | |
| 2051 | 2062 | AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); |
| 2052 | 2063 | if (!lhs) |
| 2053 | 2064 | return nullptr; |
| 2054 | 2065 | |
| 2066 | Token *token = &pc->tokens->at(*token_index); | |
| 2067 | ||
| 2068 | if (token->id != TokenIdDoubleQuestion) { | |
| 2069 | return lhs; | |
| 2070 | } | |
| 2071 | ||
| 2072 | *token_index += 1; | |
| 2073 | ||
| 2074 | AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true); | |
| 2075 | ||
| 2076 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); | |
| 2077 | node->data.bin_op_expr.op1 = lhs; | |
| 2078 | node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; | |
| 2079 | node->data.bin_op_expr.op2 = rhs; | |
| 2080 | ||
| 2081 | return node; | |
| 2082 | } | |
| 2083 | ||
| 2084 | /* | |
| 2085 | AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression | |
| 2086 | */ | |
| 2087 | static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) { | |
| 2088 | AstNode *lhs = ast_parse_unwrap_maybe_expr(pc, token_index, mandatory); | |
| 2089 | if (!lhs) | |
| 2090 | return nullptr; | |
| 2091 | ||
| 2055 | 2092 | Token *token = &pc->tokens->at(*token_index); |
| 2056 | 2093 | BinOpType ass_op = ast_parse_ass_op(pc, token_index, false); |
| 2057 | 2094 | if (ass_op == BinOpTypeInvalid) |
| 2058 | 2095 | return lhs; |
| 2059 | 2096 | |
| 2060 | AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true); | |
| 2097 | AstNode *rhs = ast_parse_unwrap_maybe_expr(pc, token_index, true); | |
| 2061 | 2098 | |
| 2062 | 2099 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); |
| 2063 | 2100 | node->data.bin_op_expr.op1 = lhs; |
src/parser.hpp+2| ... | ... | @@ -45,6 +45,7 @@ enum NodeType { |
| 45 | 45 | NodeTypeUse, |
| 46 | 46 | NodeTypeVoid, |
| 47 | 47 | NodeTypeBoolLiteral, |
| 48 | NodeTypeNullLiteral, | |
| 48 | 49 | NodeTypeIfBoolExpr, |
| 49 | 50 | NodeTypeIfVarExpr, |
| 50 | 51 | NodeTypeWhileExpr, |
| ... | ... | @@ -161,6 +162,7 @@ enum BinOpType { |
| 161 | 162 | BinOpTypeMult, |
| 162 | 163 | BinOpTypeDiv, |
| 163 | 164 | BinOpTypeMod, |
| 165 | BinOpTypeUnwrapMaybe, | |
| 164 | 166 | }; |
| 165 | 167 | |
| 166 | 168 | struct AstNodeBinOpExpr { |
src/tokenizer.cpp+9| ... | ... | @@ -241,6 +241,8 @@ static void end_token(Tokenize *t) { |
| 241 | 241 | t->cur_tok->id = TokenIdKeywordContinue; |
| 242 | 242 | } else if (mem_eql_str(token_mem, token_len, "break")) { |
| 243 | 243 | t->cur_tok->id = TokenIdKeywordBreak; |
| 244 | } else if (mem_eql_str(token_mem, token_len, "null")) { | |
| 245 | t->cur_tok->id = TokenIdKeywordNull; | |
| 244 | 246 | } |
| 245 | 247 | |
| 246 | 248 | t->cur_tok = nullptr; |
| ... | ... | @@ -418,6 +420,11 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 418 | 420 | break; |
| 419 | 421 | case TokenizeStateSawQuestionMark: |
| 420 | 422 | switch (c) { |
| 423 | case '?': | |
| 424 | t.cur_tok->id = TokenIdDoubleQuestion; | |
| 425 | end_token(&t); | |
| 426 | t.state = TokenizeStateStart; | |
| 427 | break; | |
| 421 | 428 | case '=': |
| 422 | 429 | t.cur_tok->id = TokenIdMaybeAssign; |
| 423 | 430 | end_token(&t); |
| ... | ... | @@ -1002,6 +1009,7 @@ static const char * token_name(Token *token) { |
| 1002 | 1009 | case TokenIdKeywordWhile: return "While"; |
| 1003 | 1010 | case TokenIdKeywordContinue: return "Continue"; |
| 1004 | 1011 | case TokenIdKeywordBreak: return "Break"; |
| 1012 | case TokenIdKeywordNull: return "Null"; | |
| 1005 | 1013 | case TokenIdLParen: return "LParen"; |
| 1006 | 1014 | case TokenIdRParen: return "RParen"; |
| 1007 | 1015 | case TokenIdComma: return "Comma"; |
| ... | ... | @@ -1052,6 +1060,7 @@ static const char * token_name(Token *token) { |
| 1052 | 1060 | case TokenIdDot: return "Dot"; |
| 1053 | 1061 | case TokenIdEllipsis: return "Ellipsis"; |
| 1054 | 1062 | case TokenIdMaybe: return "Maybe"; |
| 1063 | case TokenIdDoubleQuestion: return "DoubleQuestion"; | |
| 1055 | 1064 | case TokenIdMaybeAssign: return "MaybeAssign"; |
| 1056 | 1065 | } |
| 1057 | 1066 | return "(invalid token)"; |
src/tokenizer.hpp+2| ... | ... | @@ -35,6 +35,7 @@ enum TokenId { |
| 35 | 35 | TokenIdKeywordWhile, |
| 36 | 36 | TokenIdKeywordContinue, |
| 37 | 37 | TokenIdKeywordBreak, |
| 38 | TokenIdKeywordNull, | |
| 38 | 39 | TokenIdLParen, |
| 39 | 40 | TokenIdRParen, |
| 40 | 41 | TokenIdComma, |
| ... | ... | @@ -85,6 +86,7 @@ enum TokenId { |
| 85 | 86 | TokenIdDot, |
| 86 | 87 | TokenIdEllipsis, |
| 87 | 88 | TokenIdMaybe, |
| 89 | TokenIdDoubleQuestion, | |
| 88 | 90 | TokenIdMaybeAssign, |
| 89 | 91 | }; |
| 90 | 92 |
std/std.zig+21| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | use "syscall.zig"; |
| 2 | 2 | |
| 3 | const stdin_fileno : isize = 0; | |
| 3 | 4 | const stdout_fileno : isize = 1; |
| 4 | 5 | const stderr_fileno : isize = 2; |
| 5 | 6 | |
| ... | ... | @@ -38,6 +39,26 @@ pub fn print_i64(x: i64) -> isize { |
| 38 | 39 | return write(stdout_fileno, buf.ptr, len); |
| 39 | 40 | } |
| 40 | 41 | |
| 42 | /* | |
| 43 | // TODO error handling | |
| 44 | pub fn readline(buf: []u8) -> ?[]u8 { | |
| 45 | var index = 0; | |
| 46 | while (index < buf.len) { | |
| 47 | // TODO unknown size array indexing operator | |
| 48 | const err = read(stdin_fileno, &buf.ptr[index], 1); | |
| 49 | if (err != 0) { | |
| 50 | return null; | |
| 51 | } | |
| 52 | // TODO unknown size array indexing operator | |
| 53 | if (buf.ptr[index] == '\n') { | |
| 54 | return buf[0...index + 1]; | |
| 55 | } | |
| 56 | index += 1; | |
| 57 | } | |
| 58 | return null; | |
| 59 | } | |
| 60 | */ | |
| 61 | ||
| 41 | 62 | fn digit_to_char(digit: u64) -> u8 { |
| 42 | 63 | '0' + (digit as u8) |
| 43 | 64 | } |
std/syscall.zig+5| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | const SYS_read : usize = 0; | |
| 1 | 2 | const SYS_write : usize = 1; |
| 2 | 3 | const SYS_exit : usize = 60; |
| 3 | 4 | const SYS_getrandom : usize = 318; |
| ... | ... | @@ -16,6 +17,10 @@ fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize { |
| 16 | 17 | : "rcx", "r11") |
| 17 | 18 | } |
| 18 | 19 | |
| 20 | pub fn read(fd: isize, buf: &u8, count: usize) -> isize { | |
| 21 | syscall3(SYS_read, fd as usize, buf as usize, count) as isize | |
| 22 | } | |
| 23 | ||
| 19 | 24 | pub fn write(fd: isize, buf: &const u8, count: usize) -> isize { |
| 20 | 25 | syscall3(SYS_write, fd as usize, buf as usize, count) as isize |
| 21 | 26 | } |
test/run_tests.cpp+18-1| ... | ... | @@ -710,7 +710,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 710 | 710 | |
| 711 | 711 | add_simple_case("maybe type", R"SOURCE( |
| 712 | 712 | use "std.zig"; |
| 713 | pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { | |
| 713 | pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { | |
| 714 | 714 | const x : ?bool = true; |
| 715 | 715 | |
| 716 | 716 | if (const y ?= x) { |
| ... | ... | @@ -722,6 +722,23 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 722 | 722 | } else { |
| 723 | 723 | print_str("x is none\n"); |
| 724 | 724 | } |
| 725 | ||
| 726 | const next_x : ?i32 = null; | |
| 727 | ||
| 728 | const z = next_x ?? 1234; | |
| 729 | ||
| 730 | if (z != 1234) { | |
| 731 | print_str("BAD\n"); | |
| 732 | } | |
| 733 | ||
| 734 | const final_x : ?i32 = 13; | |
| 735 | ||
| 736 | const num = final_x ?? unreachable; | |
| 737 | ||
| 738 | if (num != 13) { | |
| 739 | print_str("BAD\n"); | |
| 740 | } | |
| 741 | ||
| 725 | 742 | return 0; |
| 726 | 743 | } |
| 727 | 744 | )SOURCE", "x is true\n"); |