| 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,6 +120,7 @@ set(ZIG_STD_SRC |
| 120 | "${CMAKE_SOURCE_DIR}/std/builtin.zig" | 120 | "${CMAKE_SOURCE_DIR}/std/builtin.zig" |
| 121 | "${CMAKE_SOURCE_DIR}/std/std.zig" | 121 | "${CMAKE_SOURCE_DIR}/std/std.zig" |
| 122 | "${CMAKE_SOURCE_DIR}/std/syscall.zig" | 122 | "${CMAKE_SOURCE_DIR}/std/syscall.zig" |
| 123 | "${CMAKE_SOURCE_DIR}/std/errno.zig" | ||
| 123 | "${CMAKE_SOURCE_DIR}/std/rand.zig" | 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,7 +98,9 @@ AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token |
| 98 | 98 | ||
| 99 | AsmClobbers: token(Colon) list(token(String), token(Comma)) | 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 | 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) | 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,7 +168,7 @@ Goto: token(Goto) token(Symbol) |
| 166 | 168 | ||
| 167 | GroupedExpression : token(LParen) Expression token(RParen) | 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 | ## Operator Precedence | 174 | ## Operator Precedence |
| ... | @@ -184,6 +186,7 @@ as | ... | @@ -184,6 +186,7 @@ as |
| 184 | == != < > <= >= | 186 | == != < > <= >= |
| 185 | && | 187 | && |
| 186 | || | 188 | || |
| 189 | ?? | ||
| 187 | = *= /= %= += -= <<= >>= &= ^= |= &&= ||= | 190 | = *= /= %= += -= <<= >>= &= ^= |= &&= ||= |
| 188 | ``` | 191 | ``` |
| 189 | 192 | ||
| ... | @@ -192,7 +195,7 @@ as | ... | @@ -192,7 +195,7 @@ as |
| 192 | ### Characters and Strings | 195 | ### Characters and Strings |
| 193 | 196 | ||
| 194 | | Example | Characters | Escapes | Null Term | Type | 197 | | Example | Characters | Escapes | Null Term | Type |
| 195 | --------------------------------------------------------------------------------- | 198 | ----------------|----------|-------------|----------------|-----------|---------- |
| 196 | Byte | 'H' | All ASCII | Byte | No | u8 | 199 | Byte | 'H' | All ASCII | Byte | No | u8 |
| 197 | UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5; u8] | 200 | UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5; u8] |
| 198 | UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | *const u8 | 201 | UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | *const u8 |
| ... | @@ -200,7 +203,7 @@ as | ... | @@ -200,7 +203,7 @@ as |
| 200 | ### Byte Escapes | 203 | ### Byte Escapes |
| 201 | 204 | ||
| 202 | | Name | 205 | | Name |
| 203 | ----------------------------------------------- | 206 | ------|---------------------------------------- |
| 204 | \x7F | 8-bit character code (exactly 2 digits) | 207 | \x7F | 8-bit character code (exactly 2 digits) |
| 205 | \n | Newline | 208 | \n | Newline |
| 206 | \r | Carriage return | 209 | \r | Carriage return |
| ... | @@ -213,13 +216,13 @@ as | ... | @@ -213,13 +216,13 @@ as |
| 213 | ### Unicode Escapes | 216 | ### Unicode Escapes |
| 214 | 217 | ||
| 215 | | Name | 218 | | Name |
| 216 | ---------------------------------------------------------- | 219 | ----------|----------------------------------------------- |
| 217 | \u{7FFF} | 24-bit Unicode character code (up to 6 digits) | 220 | \u{7FFF} | 24-bit Unicode character code (up to 6 digits) |
| 218 | 221 | ||
| 219 | ### Numbers | 222 | ### Numbers |
| 220 | 223 | ||
| 221 | Number literals | Example | Exponentiation | 224 | Number literals | Example | Exponentiation |
| 222 | -------------------------------------------------- | 225 | --------------------|-------------|--------------- |
| 223 | Decimal integer | 98222 | N/A | 226 | Decimal integer | 98222 | N/A |
| 224 | Hex integer | 0xff | N/A | 227 | Hex integer | 0xff | N/A |
| 225 | Octal integer | 0o77 | N/A | 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,6 +14,7 @@ syn keyword zigStatement goto break return continue asm |
| 14 | syn keyword zigConditional if else match | 14 | syn keyword zigConditional if else match |
| 15 | syn keyword zigRepeat while for | 15 | syn keyword zigRepeat while for |
| 16 | 16 | ||
| 17 | syn keyword zigConstant null | ||
| 17 | syn keyword zigKeyword fn unreachable use void | 18 | syn keyword zigKeyword fn unreachable use void |
| 18 | syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string | 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,6 +29,12 @@ syn match zigHexNumber display "\<0x[a-fA-F0-9_]\+\%([iu]\%(size\|8\|16\|32\|64\ |
| 28 | syn match zigOctNumber display "\<0o[0-7_]\+\%([iu]\%(size\|8\|16\|32\|64\)\)\=" | 29 | syn match zigOctNumber display "\<0o[0-7_]\+\%([iu]\%(size\|8\|16\|32\|64\)\)\=" |
| 29 | syn match zigBinNumber display "\<0b[01_]\+\%([iu]\%(size\|8\|16\|32\|64\)\)\=" | 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 | syn match zigShebang /\%^#![^[].*/ | 38 | syn match zigShebang /\%^#![^[].*/ |
| 32 | 39 | ||
| 33 | syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell | 40 | syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell |
| ... | @@ -64,6 +71,9 @@ hi def link zigCommentBlockDoc zigCommentLineDoc | ... | @@ -64,6 +71,9 @@ hi def link zigCommentBlockDoc zigCommentLineDoc |
| 64 | hi def link zigTodo Todo | 71 | hi def link zigTodo Todo |
| 65 | hi def link zigStringContinuation Special | 72 | hi def link zigStringContinuation Special |
| 66 | hi def link zigString String | 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 | hi def link zigEscape Special | 77 | hi def link zigEscape Special |
| 68 | hi def link zigEscapeUnicode zigEscape | 78 | hi def link zigEscapeUnicode zigEscape |
| 69 | hi def link zigEscapeError Error | 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,7 +14,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 14 | var err : isize; | 14 | var err : isize; |
| 15 | if ({err = os_get_random_bytes(&seed as &u8, #sizeof(u32)); err != #sizeof(u32)}) { | 15 | if ({err = os_get_random_bytes(&seed as &u8, #sizeof(u32)); err != #sizeof(u32)}) { |
| 16 | // TODO full error message | 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 | return 1; | 18 | return 1; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| ... | @@ -27,11 +27,14 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { | ... | @@ -27,11 +27,14 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 27 | print_u64(answer); | 27 | print_u64(answer); |
| 28 | print_str("\n"); | 28 | print_str("\n"); |
| 29 | 29 | ||
| 30 | return 0; | ||
| 31 | |||
| 32 | /* | ||
| 33 | while (true) { | 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 | if (const guess ?= parse_u64(line)) { | 39 | if (const guess ?= parse_u64(line)) { |
| 37 | if (guess > answer) { | 40 | if (guess > answer) { |
| ... | @@ -46,5 +49,4 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { | ... | @@ -46,5 +49,4 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 46 | print_str("Invalid number format.\n"); | 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,5 +15,21 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 15 | print_str("x is none\n"); | 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 | return 0; | 34 | return 0; |
| 19 | } | 35 | } |
src/analyze.cpp+60-3| ... | @@ -48,6 +48,7 @@ static AstNode *first_executing_node(AstNode *node) { | ... | @@ -48,6 +48,7 @@ static AstNode *first_executing_node(AstNode *node) { |
| 48 | case NodeTypeUse: | 48 | case NodeTypeUse: |
| 49 | case NodeTypeVoid: | 49 | case NodeTypeVoid: |
| 50 | case NodeTypeBoolLiteral: | 50 | case NodeTypeBoolLiteral: |
| 51 | case NodeTypeNullLiteral: | ||
| 51 | case NodeTypeIfBoolExpr: | 52 | case NodeTypeIfBoolExpr: |
| 52 | case NodeTypeIfVarExpr: | 53 | case NodeTypeIfVarExpr: |
| 53 | case NodeTypeLabel: | 54 | case NodeTypeLabel: |
| ... | @@ -358,6 +359,7 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context, | ... | @@ -358,6 +359,7 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context, |
| 358 | case BinOpTypeSub: | 359 | case BinOpTypeSub: |
| 359 | case BinOpTypeMult: | 360 | case BinOpTypeMult: |
| 360 | case BinOpTypeDiv: | 361 | case BinOpTypeDiv: |
| 362 | case BinOpTypeUnwrapMaybe: | ||
| 361 | return g->builtin_types.entry_invalid; | 363 | return g->builtin_types.entry_invalid; |
| 362 | case BinOpTypeInvalid: | 364 | case BinOpTypeInvalid: |
| 363 | case BinOpTypeAssign: | 365 | case BinOpTypeAssign: |
| ... | @@ -388,6 +390,8 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context, | ... | @@ -388,6 +390,8 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context, |
| 388 | case NodeTypeBoolLiteral: | 390 | case NodeTypeBoolLiteral: |
| 389 | out_number_literal->data.x_uint = node->data.bool_literal ? 1 : 0; | 391 | out_number_literal->data.x_uint = node->data.bool_literal ? 1 : 0; |
| 390 | return node->codegen_node->expr_node.type_entry; | 392 | return node->codegen_node->expr_node.type_entry; |
| 393 | case NodeTypeNullLiteral: | ||
| 394 | return node->codegen_node->expr_node.type_entry; | ||
| 391 | case NodeTypeBinOpExpr: | 395 | case NodeTypeBinOpExpr: |
| 392 | return eval_const_expr_bin_op(g, context, node, out_number_literal); | 396 | return eval_const_expr_bin_op(g, context, node, out_number_literal); |
| 393 | case NodeTypeCompilerFnType: | 397 | case NodeTypeCompilerFnType: |
| ... | @@ -877,6 +881,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -877,6 +881,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 877 | case NodeTypeUnreachable: | 881 | case NodeTypeUnreachable: |
| 878 | case NodeTypeVoid: | 882 | case NodeTypeVoid: |
| 879 | case NodeTypeBoolLiteral: | 883 | case NodeTypeBoolLiteral: |
| 884 | case NodeTypeNullLiteral: | ||
| 880 | case NodeTypeSymbol: | 885 | case NodeTypeSymbol: |
| 881 | case NodeTypeCastExpr: | 886 | case NodeTypeCastExpr: |
| 882 | case NodeTypePrefixOpExpr: | 887 | case NodeTypePrefixOpExpr: |
| ... | @@ -951,6 +956,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { | ... | @@ -951,6 +956,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 951 | case NodeTypeUnreachable: | 956 | case NodeTypeUnreachable: |
| 952 | case NodeTypeVoid: | 957 | case NodeTypeVoid: |
| 953 | case NodeTypeBoolLiteral: | 958 | case NodeTypeBoolLiteral: |
| 959 | case NodeTypeNullLiteral: | ||
| 954 | case NodeTypeSymbol: | 960 | case NodeTypeSymbol: |
| 955 | case NodeTypeCastExpr: | 961 | case NodeTypeCastExpr: |
| 956 | case NodeTypePrefixOpExpr: | 962 | case NodeTypePrefixOpExpr: |
| ... | @@ -1018,7 +1024,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, | ... | @@ -1018,7 +1024,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, |
| 1018 | return false; | 1024 | return false; |
| 1019 | } | 1025 | } |
| 1020 | case TypeTableEntryIdMaybe: | 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 | zig_unreachable(); | 1029 | zig_unreachable(); |
| 1024 | } | 1030 | } |
| ... | @@ -1133,6 +1139,9 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont | ... | @@ -1133,6 +1139,9 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont |
| 1133 | if (actual_type->id == TypeTableEntryIdNumberLiteral && | 1139 | if (actual_type->id == TypeTableEntryIdNumberLiteral && |
| 1134 | num_lit_fits_in_other_type(g, actual_type, expected_type)) | 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 | return expected_type; | 1145 | return expected_type; |
| 1137 | } | 1146 | } |
| 1138 | 1147 | ||
| ... | @@ -1419,6 +1428,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { | ... | @@ -1419,6 +1428,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { |
| 1419 | case BinOpTypeMult: | 1428 | case BinOpTypeMult: |
| 1420 | case BinOpTypeDiv: | 1429 | case BinOpTypeDiv: |
| 1421 | case BinOpTypeMod: | 1430 | case BinOpTypeMod: |
| 1431 | case BinOpTypeUnwrapMaybe: | ||
| 1422 | zig_unreachable(); | 1432 | zig_unreachable(); |
| 1423 | } | 1433 | } |
| 1424 | zig_unreachable(); | 1434 | zig_unreachable(); |
| ... | @@ -1619,6 +1629,24 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -1619,6 +1629,24 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 1619 | 1629 | ||
| 1620 | return resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type); | 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 | case BinOpTypeInvalid: | 1650 | case BinOpTypeInvalid: |
| 1623 | zig_unreachable(); | 1651 | zig_unreachable(); |
| 1624 | } | 1652 | } |
| ... | @@ -1695,6 +1723,27 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE | ... | @@ -1695,6 +1723,27 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE |
| 1695 | return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false); | 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 | static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, | 1747 | static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, |
| 1699 | BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) | 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,8 +1755,11 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry |
| 1706 | } else if (expected_type) { | 1755 | } else if (expected_type) { |
| 1707 | NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; | 1756 | NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; |
| 1708 | assert(!codegen_num_lit->resolved_type); | 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); | 1758 | TypeTableEntry *after_implicit_cast_resolved_type = |
| 1710 | return codegen_num_lit->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 | } else { | 1763 | } else { |
| 1712 | return num_lit_type; | 1764 | return num_lit_type; |
| 1713 | } | 1765 | } |
| ... | @@ -2174,6 +2226,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -2174,6 +2226,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 2174 | return_type = g->builtin_types.entry_bool; | 2226 | return_type = g->builtin_types.entry_bool; |
| 2175 | break; | 2227 | break; |
| 2176 | 2228 | ||
| 2229 | case NodeTypeNullLiteral: | ||
| 2230 | return_type = analyze_null_literal_expr(g, import, context, expected_type, node); | ||
| 2231 | break; | ||
| 2232 | |||
| 2177 | case NodeTypeSymbol: | 2233 | case NodeTypeSymbol: |
| 2178 | { | 2234 | { |
| 2179 | return_type = analyze_variable_name(g, import, context, node, &node->data.symbol); | 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,6 +2547,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 2491 | case NodeTypeUnreachable: | 2547 | case NodeTypeUnreachable: |
| 2492 | case NodeTypeVoid: | 2548 | case NodeTypeVoid: |
| 2493 | case NodeTypeBoolLiteral: | 2549 | case NodeTypeBoolLiteral: |
| 2550 | case NodeTypeNullLiteral: | ||
| 2494 | case NodeTypeSymbol: | 2551 | case NodeTypeSymbol: |
| 2495 | case NodeTypeCastExpr: | 2552 | case NodeTypeCastExpr: |
| 2496 | case NodeTypePrefixOpExpr: | 2553 | case NodeTypePrefixOpExpr: |
src/codegen.cpp+86-4| ... | @@ -613,6 +613,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, | ... | @@ -613,6 +613,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, |
| 613 | case BinOpTypeAssign: | 613 | case BinOpTypeAssign: |
| 614 | case BinOpTypeAssignBoolAnd: | 614 | case BinOpTypeAssignBoolAnd: |
| 615 | case BinOpTypeAssignBoolOr: | 615 | case BinOpTypeAssignBoolOr: |
| 616 | case BinOpTypeUnwrapMaybe: | ||
| 616 | zig_unreachable(); | 617 | zig_unreachable(); |
| 617 | } | 618 | } |
| 618 | zig_unreachable(); | 619 | zig_unreachable(); |
| ... | @@ -814,6 +815,70 @@ static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { | ... | @@ -814,6 +815,70 @@ static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { |
| 814 | return gen_assign_raw(g, node, node->data.bin_op_expr.bin_op, target_ref, value, op1_type, op2_type); | 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 | static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { | 882 | static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
| 818 | switch (node->data.bin_op_expr.bin_op) { | 883 | switch (node->data.bin_op_expr.bin_op) { |
| 819 | case BinOpTypeInvalid: | 884 | case BinOpTypeInvalid: |
| ... | @@ -843,6 +908,8 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { | ... | @@ -843,6 +908,8 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
| 843 | case BinOpTypeCmpLessOrEq: | 908 | case BinOpTypeCmpLessOrEq: |
| 844 | case BinOpTypeCmpGreaterOrEq: | 909 | case BinOpTypeCmpGreaterOrEq: |
| 845 | return gen_cmp_expr(g, node); | 910 | return gen_cmp_expr(g, node); |
| 911 | case BinOpTypeUnwrapMaybe: | ||
| 912 | return gen_unwrap_maybe_expr(g, node); | ||
| 846 | case BinOpTypeBinOr: | 913 | case BinOpTypeBinOr: |
| 847 | case BinOpTypeBinXor: | 914 | case BinOpTypeBinXor: |
| 848 | case BinOpTypeBinAnd: | 915 | case BinOpTypeBinAnd: |
| ... | @@ -1124,6 +1191,22 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { | ... | @@ -1124,6 +1191,22 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) { |
| 1124 | return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, ""); | 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 | static LLVMValueRef gen_struct_val_expr(CodeGen *g, AstNode *node) { | 1210 | static LLVMValueRef gen_struct_val_expr(CodeGen *g, AstNode *node) { |
| 1128 | assert(node->type == NodeTypeStructValueExpr); | 1211 | assert(node->type == NodeTypeStructValueExpr); |
| 1129 | 1212 | ||
| ... | @@ -1242,10 +1325,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa | ... | @@ -1242,10 +1325,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa |
| 1242 | LLVMValueRef value; | 1325 | LLVMValueRef value; |
| 1243 | if (unwrap_maybe) { | 1326 | if (unwrap_maybe) { |
| 1244 | assert(var_decl->expr); | 1327 | assert(var_decl->expr); |
| 1245 | add_debug_source_node(g, source_node); | 1328 | value = gen_unwrap_maybe(g, source_node, *init_value); |
| 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, ""); | ||
| 1249 | } else { | 1329 | } else { |
| 1250 | value = *init_value; | 1330 | value = *init_value; |
| 1251 | } | 1331 | } |
| ... | @@ -1375,6 +1455,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { | ... | @@ -1375,6 +1455,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1375 | return LLVMConstAllOnes(LLVMInt1Type()); | 1455 | return LLVMConstAllOnes(LLVMInt1Type()); |
| 1376 | else | 1456 | else |
| 1377 | return LLVMConstNull(LLVMInt1Type()); | 1457 | return LLVMConstNull(LLVMInt1Type()); |
| 1458 | case NodeTypeNullLiteral: | ||
| 1459 | return gen_null_literal(g, node); | ||
| 1378 | case NodeTypeIfBoolExpr: | 1460 | case NodeTypeIfBoolExpr: |
| 1379 | return gen_if_bool_expr(g, node); | 1461 | return gen_if_bool_expr(g, node); |
| 1380 | case NodeTypeIfVarExpr: | 1462 | case NodeTypeIfVarExpr: |
src/parser.cpp+40-3| ... | @@ -48,6 +48,7 @@ static const char *bin_op_str(BinOpType bin_op) { | ... | @@ -48,6 +48,7 @@ static const char *bin_op_str(BinOpType bin_op) { |
| 48 | case BinOpTypeAssignBitOr: return "|="; | 48 | case BinOpTypeAssignBitOr: return "|="; |
| 49 | case BinOpTypeAssignBoolAnd: return "&&="; | 49 | case BinOpTypeAssignBoolAnd: return "&&="; |
| 50 | case BinOpTypeAssignBoolOr: return "||="; | 50 | case BinOpTypeAssignBoolOr: return "||="; |
| 51 | case BinOpTypeUnwrapMaybe: return "??"; | ||
| 51 | } | 52 | } |
| 52 | zig_unreachable(); | 53 | zig_unreachable(); |
| 53 | } | 54 | } |
| ... | @@ -117,6 +118,8 @@ const char *node_type_str(NodeType node_type) { | ... | @@ -117,6 +118,8 @@ const char *node_type_str(NodeType node_type) { |
| 117 | return "Void"; | 118 | return "Void"; |
| 118 | case NodeTypeBoolLiteral: | 119 | case NodeTypeBoolLiteral: |
| 119 | return "BoolLiteral"; | 120 | return "BoolLiteral"; |
| 121 | case NodeTypeNullLiteral: | ||
| 122 | return "NullLiteral"; | ||
| 120 | case NodeTypeIfBoolExpr: | 123 | case NodeTypeIfBoolExpr: |
| 121 | return "IfBoolExpr"; | 124 | return "IfBoolExpr"; |
| 122 | case NodeTypeIfVarExpr: | 125 | case NodeTypeIfVarExpr: |
| ... | @@ -349,6 +352,9 @@ void ast_print(AstNode *node, int indent) { | ... | @@ -349,6 +352,9 @@ void ast_print(AstNode *node, int indent) { |
| 349 | case NodeTypeBoolLiteral: | 352 | case NodeTypeBoolLiteral: |
| 350 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false"); | 353 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false"); |
| 351 | break; | 354 | break; |
| 355 | case NodeTypeNullLiteral: | ||
| 356 | fprintf(stderr, "%s\n", node_type_str(node->type)); | ||
| 357 | break; | ||
| 352 | case NodeTypeIfBoolExpr: | 358 | case NodeTypeIfBoolExpr: |
| 353 | fprintf(stderr, "%s\n", node_type_str(node->type)); | 359 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 354 | if (node->data.if_bool_expr.condition) | 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,6 +1286,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { |
| 1280 | 1286 | ||
| 1281 | /* | 1287 | /* |
| 1282 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | 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 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { | 1291 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1285 | Token *token = &pc->tokens->at(*token_index); | 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,6 +1324,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 1317 | node->data.bool_literal = false; | 1324 | node->data.bool_literal = false; |
| 1318 | *token_index += 1; | 1325 | *token_index += 1; |
| 1319 | return node; | 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 | } else if (token->id == TokenIdSymbol) { | 1331 | } else if (token->id == TokenIdSymbol) { |
| 1321 | Token *next_token = &pc->tokens->at(*token_index + 1); | 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,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 | AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); | 2062 | AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); |
| 2052 | if (!lhs) | 2063 | if (!lhs) |
| 2053 | return nullptr; | 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 | Token *token = &pc->tokens->at(*token_index); | 2092 | Token *token = &pc->tokens->at(*token_index); |
| 2056 | BinOpType ass_op = ast_parse_ass_op(pc, token_index, false); | 2093 | BinOpType ass_op = ast_parse_ass_op(pc, token_index, false); |
| 2057 | if (ass_op == BinOpTypeInvalid) | 2094 | if (ass_op == BinOpTypeInvalid) |
| 2058 | return lhs; | 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 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); | 2099 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); |
| 2063 | node->data.bin_op_expr.op1 = lhs; | 2100 | node->data.bin_op_expr.op1 = lhs; |
src/parser.hpp+2| ... | @@ -45,6 +45,7 @@ enum NodeType { | ... | @@ -45,6 +45,7 @@ enum NodeType { |
| 45 | NodeTypeUse, | 45 | NodeTypeUse, |
| 46 | NodeTypeVoid, | 46 | NodeTypeVoid, |
| 47 | NodeTypeBoolLiteral, | 47 | NodeTypeBoolLiteral, |
| 48 | NodeTypeNullLiteral, | ||
| 48 | NodeTypeIfBoolExpr, | 49 | NodeTypeIfBoolExpr, |
| 49 | NodeTypeIfVarExpr, | 50 | NodeTypeIfVarExpr, |
| 50 | NodeTypeWhileExpr, | 51 | NodeTypeWhileExpr, |
| ... | @@ -161,6 +162,7 @@ enum BinOpType { | ... | @@ -161,6 +162,7 @@ enum BinOpType { |
| 161 | BinOpTypeMult, | 162 | BinOpTypeMult, |
| 162 | BinOpTypeDiv, | 163 | BinOpTypeDiv, |
| 163 | BinOpTypeMod, | 164 | BinOpTypeMod, |
| 165 | BinOpTypeUnwrapMaybe, | ||
| 164 | }; | 166 | }; |
| 165 | 167 | ||
| 166 | struct AstNodeBinOpExpr { | 168 | struct AstNodeBinOpExpr { |
src/tokenizer.cpp+9| ... | @@ -241,6 +241,8 @@ static void end_token(Tokenize *t) { | ... | @@ -241,6 +241,8 @@ static void end_token(Tokenize *t) { |
| 241 | t->cur_tok->id = TokenIdKeywordContinue; | 241 | t->cur_tok->id = TokenIdKeywordContinue; |
| 242 | } else if (mem_eql_str(token_mem, token_len, "break")) { | 242 | } else if (mem_eql_str(token_mem, token_len, "break")) { |
| 243 | t->cur_tok->id = TokenIdKeywordBreak; | 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 | t->cur_tok = nullptr; | 248 | t->cur_tok = nullptr; |
| ... | @@ -418,6 +420,11 @@ void tokenize(Buf *buf, Tokenization *out) { | ... | @@ -418,6 +420,11 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 418 | break; | 420 | break; |
| 419 | case TokenizeStateSawQuestionMark: | 421 | case TokenizeStateSawQuestionMark: |
| 420 | switch (c) { | 422 | switch (c) { |
| 423 | case '?': | ||
| 424 | t.cur_tok->id = TokenIdDoubleQuestion; | ||
| 425 | end_token(&t); | ||
| 426 | t.state = TokenizeStateStart; | ||
| 427 | break; | ||
| 421 | case '=': | 428 | case '=': |
| 422 | t.cur_tok->id = TokenIdMaybeAssign; | 429 | t.cur_tok->id = TokenIdMaybeAssign; |
| 423 | end_token(&t); | 430 | end_token(&t); |
| ... | @@ -1002,6 +1009,7 @@ static const char * token_name(Token *token) { | ... | @@ -1002,6 +1009,7 @@ static const char * token_name(Token *token) { |
| 1002 | case TokenIdKeywordWhile: return "While"; | 1009 | case TokenIdKeywordWhile: return "While"; |
| 1003 | case TokenIdKeywordContinue: return "Continue"; | 1010 | case TokenIdKeywordContinue: return "Continue"; |
| 1004 | case TokenIdKeywordBreak: return "Break"; | 1011 | case TokenIdKeywordBreak: return "Break"; |
| 1012 | case TokenIdKeywordNull: return "Null"; | ||
| 1005 | case TokenIdLParen: return "LParen"; | 1013 | case TokenIdLParen: return "LParen"; |
| 1006 | case TokenIdRParen: return "RParen"; | 1014 | case TokenIdRParen: return "RParen"; |
| 1007 | case TokenIdComma: return "Comma"; | 1015 | case TokenIdComma: return "Comma"; |
| ... | @@ -1052,6 +1060,7 @@ static const char * token_name(Token *token) { | ... | @@ -1052,6 +1060,7 @@ static const char * token_name(Token *token) { |
| 1052 | case TokenIdDot: return "Dot"; | 1060 | case TokenIdDot: return "Dot"; |
| 1053 | case TokenIdEllipsis: return "Ellipsis"; | 1061 | case TokenIdEllipsis: return "Ellipsis"; |
| 1054 | case TokenIdMaybe: return "Maybe"; | 1062 | case TokenIdMaybe: return "Maybe"; |
| 1063 | case TokenIdDoubleQuestion: return "DoubleQuestion"; | ||
| 1055 | case TokenIdMaybeAssign: return "MaybeAssign"; | 1064 | case TokenIdMaybeAssign: return "MaybeAssign"; |
| 1056 | } | 1065 | } |
| 1057 | return "(invalid token)"; | 1066 | return "(invalid token)"; |
src/tokenizer.hpp+2| ... | @@ -35,6 +35,7 @@ enum TokenId { | ... | @@ -35,6 +35,7 @@ enum TokenId { |
| 35 | TokenIdKeywordWhile, | 35 | TokenIdKeywordWhile, |
| 36 | TokenIdKeywordContinue, | 36 | TokenIdKeywordContinue, |
| 37 | TokenIdKeywordBreak, | 37 | TokenIdKeywordBreak, |
| 38 | TokenIdKeywordNull, | ||
| 38 | TokenIdLParen, | 39 | TokenIdLParen, |
| 39 | TokenIdRParen, | 40 | TokenIdRParen, |
| 40 | TokenIdComma, | 41 | TokenIdComma, |
| ... | @@ -85,6 +86,7 @@ enum TokenId { | ... | @@ -85,6 +86,7 @@ enum TokenId { |
| 85 | TokenIdDot, | 86 | TokenIdDot, |
| 86 | TokenIdEllipsis, | 87 | TokenIdEllipsis, |
| 87 | TokenIdMaybe, | 88 | TokenIdMaybe, |
| 89 | TokenIdDoubleQuestion, | ||
| 88 | TokenIdMaybeAssign, | 90 | TokenIdMaybeAssign, |
| 89 | }; | 91 | }; |
| 90 | 92 |
std/std.zig+21| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | use "syscall.zig"; | 1 | use "syscall.zig"; |
| 2 | 2 | ||
| 3 | const stdin_fileno : isize = 0; | ||
| 3 | const stdout_fileno : isize = 1; | 4 | const stdout_fileno : isize = 1; |
| 4 | const stderr_fileno : isize = 2; | 5 | const stderr_fileno : isize = 2; |
| 5 | 6 | ||
| ... | @@ -38,6 +39,26 @@ pub fn print_i64(x: i64) -> isize { | ... | @@ -38,6 +39,26 @@ pub fn print_i64(x: i64) -> isize { |
| 38 | return write(stdout_fileno, buf.ptr, len); | 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 | fn digit_to_char(digit: u64) -> u8 { | 62 | fn digit_to_char(digit: u64) -> u8 { |
| 42 | '0' + (digit as u8) | 63 | '0' + (digit as u8) |
| 43 | } | 64 | } |
std/syscall.zig+5| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | const SYS_read : usize = 0; | ||
| 1 | const SYS_write : usize = 1; | 2 | const SYS_write : usize = 1; |
| 2 | const SYS_exit : usize = 60; | 3 | const SYS_exit : usize = 60; |
| 3 | const SYS_getrandom : usize = 318; | 4 | const SYS_getrandom : usize = 318; |
| ... | @@ -16,6 +17,10 @@ fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize { | ... | @@ -16,6 +17,10 @@ fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize { |
| 16 | : "rcx", "r11") | 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 | pub fn write(fd: isize, buf: &const u8, count: usize) -> isize { | 24 | pub fn write(fd: isize, buf: &const u8, count: usize) -> isize { |
| 20 | syscall3(SYS_write, fd as usize, buf as usize, count) as isize | 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,7 +710,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 710 | 710 | ||
| 711 | add_simple_case("maybe type", R"SOURCE( | 711 | add_simple_case("maybe type", R"SOURCE( |
| 712 | use "std.zig"; | 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 | const x : ?bool = true; | 714 | const x : ?bool = true; |
| 715 | 715 | ||
| 716 | if (const y ?= x) { | 716 | if (const y ?= x) { |
| ... | @@ -722,6 +722,23 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { | ... | @@ -722,6 +722,23 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 722 | } else { | 722 | } else { |
| 723 | print_str("x is none\n"); | 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 | return 0; | 742 | return 0; |
| 726 | } | 743 | } |
| 727 | )SOURCE", "x is true\n"); | 744 | )SOURCE", "x is true\n"); |