authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-07 03:23:38-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-07 03:23:38-07:00
loga3c97081ca37a6f78732c9a5f2244cef5a11cb07
treeaec69a113eacaa084039364b65ef803d695e0dd5
parent9b9fd5ad23e37d1e2b37b05b168abcfd53f4629d

add ?? maybe unwrapping binary operator

add null literal fix number literal / maybe interactions

14 files changed, 287 insertions(+), 23 deletions(-)

CMakeLists.txt+1
......@@ -120,6 +120,7 @@ set(ZIG_STD_SRC
120120 "${CMAKE_SOURCE_DIR}/std/builtin.zig"
121121 "${CMAKE_SOURCE_DIR}/std/std.zig"
122122 "${CMAKE_SOURCE_DIR}/std/syscall.zig"
123 "${CMAKE_SOURCE_DIR}/std/errno.zig"
123124 "${CMAKE_SOURCE_DIR}/std/rand.zig"
124125)
125126
doc/langref.md+9-6
......@@ -98,7 +98,9 @@ AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token
9898
9999AsmClobbers: token(Colon) list(token(String), token(Comma))
100100
101AssignmentExpression : BoolOrExpression AssignmentOperator BoolOrExpression | BoolOrExpression
101UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression
102
103AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression
102104
103105AssignmentOperator : 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)
104106
......@@ -166,7 +168,7 @@ Goto: token(Goto) token(Symbol)
166168
167169GroupedExpression : token(LParen) Expression token(RParen)
168170
169KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False)
171KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null)
170172```
171173
172174## Operator Precedence
......@@ -184,6 +186,7 @@ as
184186== != < > <= >=
185187&&
186188||
189??
187190= *= /= %= += -= <<= >>= &= ^= |= &&= ||=
188191```
189192
......@@ -192,7 +195,7 @@ as
192195### Characters and Strings
193196
194197 | Example | Characters | Escapes | Null Term | Type
195---------------------------------------------------------------------------------
198----------------|----------|-------------|----------------|-----------|----------
196199 Byte | 'H' | All ASCII | Byte | No | u8
197200 UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5; u8]
198201 UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | *const u8
......@@ -200,7 +203,7 @@ as
200203### Byte Escapes
201204
202205 | Name
203-----------------------------------------------
206------|----------------------------------------
204207 \x7F | 8-bit character code (exactly 2 digits)
205208 \n | Newline
206209 \r | Carriage return
......@@ -213,13 +216,13 @@ as
213216### Unicode Escapes
214217
215218 | Name
216----------------------------------------------------------
219----------|-----------------------------------------------
217220 \u{7FFF} | 24-bit Unicode character code (up to 6 digits)
218221
219222### Numbers
220223
221224 Number literals | Example | Exponentiation
222--------------------------------------------------
225--------------------|-------------|---------------
223226 Decimal integer | 98222 | N/A
224227 Hex integer | 0xff | N/A
225228 Octal integer | 0o77 | N/A
doc/vim/syntax/zig.vim+10
......@@ -14,6 +14,7 @@ syn keyword zigStatement goto break return continue asm
1414syn keyword zigConditional if else match
1515syn keyword zigRepeat while for
1616
17syn keyword zigConstant null
1718syn keyword zigKeyword fn unreachable use void
1819syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string
1920
......@@ -28,6 +29,12 @@ syn match zigHexNumber display "\<0x[a-fA-F0-9_]\+\%([iu]\%(size\|8\|16\|32\|64\
2829syn match zigOctNumber display "\<0o[0-7_]\+\%([iu]\%(size\|8\|16\|32\|64\)\)\="
2930syn match zigBinNumber display "\<0b[01_]\+\%([iu]\%(size\|8\|16\|32\|64\)\)\="
3031
32
33syn match zigCharacterInvalid display contained /b\?'\zs[\n\r\t']\ze'/
34syn match zigCharacterInvalidUnicode display contained /b'\zs[^[:cntrl:][:graph:][:alnum:][:space:]]\ze'/
35syn match zigCharacter /b'\([^\\]\|\\\(.\|x\x\{2}\)\)'/ contains=zigEscape,zigEscapeError,zigCharacterInvalid,zigCharacterInvalidUnicode
36syn match zigCharacter /'\([^\\]\|\\\(.\|x\x\{2}\|u\x\{4}\|U\x\{8}\|u{\x\{1,6}}\)\)'/ contains=zigEscape,zigEscapeUnicode,zigEscapeError,zigCharacterInvalid
37
3138syn match zigShebang /\%^#![^[].*/
3239
3340syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
......@@ -64,6 +71,9 @@ hi def link zigCommentBlockDoc zigCommentLineDoc
6471hi def link zigTodo Todo
6572hi def link zigStringContinuation Special
6673hi def link zigString String
74hi def link zigCharacterInvalid Error
75hi def link zigCharacterInvalidUnicode zigCharacterInvalid
76hi def link zigCharacter Character
6777hi def link zigEscape Special
6878hi def link zigEscapeUnicode zigEscape
6979hi 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 {
1414 var err : isize;
1515 if ({err = os_get_random_bytes(&seed as &u8, #sizeof(u32)); err != #sizeof(u32)}) {
1616 // 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");
1818 return 1;
1919 }
2020
......@@ -27,11 +27,14 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
2727 print_u64(answer);
2828 print_str("\n");
2929
30 return 0;
31
32 /*
3330 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 };
3538
3639 if (const guess ?= parse_u64(line)) {
3740 if (guess > answer) {
......@@ -46,5 +49,4 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
4649 print_str("Invalid number format.\n");
4750 }
4851 }
49 */
5052}
example/maybe_type/main.zig+16
......@@ -15,5 +15,21 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
1515 print_str("x is none\n");
1616 }
1717
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
1834 return 0;
1935}
src/analyze.cpp+60-3
......@@ -48,6 +48,7 @@ static AstNode *first_executing_node(AstNode *node) {
4848 case NodeTypeUse:
4949 case NodeTypeVoid:
5050 case NodeTypeBoolLiteral:
51 case NodeTypeNullLiteral:
5152 case NodeTypeIfBoolExpr:
5253 case NodeTypeIfVarExpr:
5354 case NodeTypeLabel:
......@@ -358,6 +359,7 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
358359 case BinOpTypeSub:
359360 case BinOpTypeMult:
360361 case BinOpTypeDiv:
362 case BinOpTypeUnwrapMaybe:
361363 return g->builtin_types.entry_invalid;
362364 case BinOpTypeInvalid:
363365 case BinOpTypeAssign:
......@@ -388,6 +390,8 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
388390 case NodeTypeBoolLiteral:
389391 out_number_literal->data.x_uint = node->data.bool_literal ? 1 : 0;
390392 return node->codegen_node->expr_node.type_entry;
393 case NodeTypeNullLiteral:
394 return node->codegen_node->expr_node.type_entry;
391395 case NodeTypeBinOpExpr:
392396 return eval_const_expr_bin_op(g, context, node, out_number_literal);
393397 case NodeTypeCompilerFnType:
......@@ -877,6 +881,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
877881 case NodeTypeUnreachable:
878882 case NodeTypeVoid:
879883 case NodeTypeBoolLiteral:
884 case NodeTypeNullLiteral:
880885 case NodeTypeSymbol:
881886 case NodeTypeCastExpr:
882887 case NodeTypePrefixOpExpr:
......@@ -951,6 +956,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
951956 case NodeTypeUnreachable:
952957 case NodeTypeVoid:
953958 case NodeTypeBoolLiteral:
959 case NodeTypeNullLiteral:
954960 case NodeTypeSymbol:
955961 case NodeTypeCastExpr:
956962 case NodeTypePrefixOpExpr:
......@@ -1018,7 +1024,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type,
10181024 return false;
10191025 }
10201026 case TypeTableEntryIdMaybe:
1021 return num_lit_fits_in_other_type(g, literal_type, other_type->data.maybe.child_type);
1027 return false;
10221028 }
10231029 zig_unreachable();
10241030}
......@@ -1133,6 +1139,9 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
11331139 if (actual_type->id == TypeTableEntryIdNumberLiteral &&
11341140 num_lit_fits_in_other_type(g, actual_type, expected_type))
11351141 {
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;
11361145 return expected_type;
11371146 }
11381147
......@@ -1419,6 +1428,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
14191428 case BinOpTypeMult:
14201429 case BinOpTypeDiv:
14211430 case BinOpTypeMod:
1431 case BinOpTypeUnwrapMaybe:
14221432 zig_unreachable();
14231433 }
14241434 zig_unreachable();
......@@ -1619,6 +1629,24 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
16191629
16201630 return resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type);
16211631 }
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 }
16221650 case BinOpTypeInvalid:
16231651 zig_unreachable();
16241652 }
......@@ -1695,6 +1723,27 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE
16951723 return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false);
16961724}
16971725
1726static 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
16981747static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import,
16991748 BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node)
17001749{
......@@ -1706,8 +1755,11 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry
17061755 } else if (expected_type) {
17071756 NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
17081757 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;
17111763 } else {
17121764 return num_lit_type;
17131765 }
......@@ -2174,6 +2226,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
21742226 return_type = g->builtin_types.entry_bool;
21752227 break;
21762228
2229 case NodeTypeNullLiteral:
2230 return_type = analyze_null_literal_expr(g, import, context, expected_type, node);
2231 break;
2232
21772233 case NodeTypeSymbol:
21782234 {
21792235 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,
24912547 case NodeTypeUnreachable:
24922548 case NodeTypeVoid:
24932549 case NodeTypeBoolLiteral:
2550 case NodeTypeNullLiteral:
24942551 case NodeTypeSymbol:
24952552 case NodeTypeCastExpr:
24962553 case NodeTypePrefixOpExpr:
src/codegen.cpp+86-4
......@@ -613,6 +613,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
613613 case BinOpTypeAssign:
614614 case BinOpTypeAssignBoolAnd:
615615 case BinOpTypeAssignBoolOr:
616 case BinOpTypeUnwrapMaybe:
616617 zig_unreachable();
617618 }
618619 zig_unreachable();
......@@ -814,6 +815,70 @@ static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {
814815 return gen_assign_raw(g, node, node->data.bin_op_expr.bin_op, target_ref, value, op1_type, op2_type);
815816}
816817
818static 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
825static 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
817882static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
818883 switch (node->data.bin_op_expr.bin_op) {
819884 case BinOpTypeInvalid:
......@@ -843,6 +908,8 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
843908 case BinOpTypeCmpLessOrEq:
844909 case BinOpTypeCmpGreaterOrEq:
845910 return gen_cmp_expr(g, node);
911 case BinOpTypeUnwrapMaybe:
912 return gen_unwrap_maybe_expr(g, node);
846913 case BinOpTypeBinOr:
847914 case BinOpTypeBinXor:
848915 case BinOpTypeBinAnd:
......@@ -1124,6 +1191,22 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) {
11241191 return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
11251192}
11261193
1194static 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
11271210static LLVMValueRef gen_struct_val_expr(CodeGen *g, AstNode *node) {
11281211 assert(node->type == NodeTypeStructValueExpr);
11291212
......@@ -1242,10 +1325,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
12421325 LLVMValueRef value;
12431326 if (unwrap_maybe) {
12441327 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);
12491329 } else {
12501330 value = *init_value;
12511331 }
......@@ -1375,6 +1455,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
13751455 return LLVMConstAllOnes(LLVMInt1Type());
13761456 else
13771457 return LLVMConstNull(LLVMInt1Type());
1458 case NodeTypeNullLiteral:
1459 return gen_null_literal(g, node);
13781460 case NodeTypeIfBoolExpr:
13791461 return gen_if_bool_expr(g, node);
13801462 case NodeTypeIfVarExpr:
src/parser.cpp+40-3
......@@ -48,6 +48,7 @@ static const char *bin_op_str(BinOpType bin_op) {
4848 case BinOpTypeAssignBitOr: return "|=";
4949 case BinOpTypeAssignBoolAnd: return "&&=";
5050 case BinOpTypeAssignBoolOr: return "||=";
51 case BinOpTypeUnwrapMaybe: return "??";
5152 }
5253 zig_unreachable();
5354}
......@@ -117,6 +118,8 @@ const char *node_type_str(NodeType node_type) {
117118 return "Void";
118119 case NodeTypeBoolLiteral:
119120 return "BoolLiteral";
121 case NodeTypeNullLiteral:
122 return "NullLiteral";
120123 case NodeTypeIfBoolExpr:
121124 return "IfBoolExpr";
122125 case NodeTypeIfVarExpr:
......@@ -349,6 +352,9 @@ void ast_print(AstNode *node, int indent) {
349352 case NodeTypeBoolLiteral:
350353 fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false");
351354 break;
355 case NodeTypeNullLiteral:
356 fprintf(stderr, "%s\n", node_type_str(node->type));
357 break;
352358 case NodeTypeIfBoolExpr:
353359 fprintf(stderr, "%s\n", node_type_str(node->type));
354360 if (node->data.if_bool_expr.condition)
......@@ -1280,6 +1286,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) {
12801286
12811287/*
12821288PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType
1289KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null)
12831290*/
12841291static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
12851292 Token *token = &pc->tokens->at(*token_index);
......@@ -1317,6 +1324,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
13171324 node->data.bool_literal = false;
13181325 *token_index += 1;
13191326 return node;
1327 } else if (token->id == TokenIdKeywordNull) {
1328 AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token);
1329 *token_index += 1;
1330 return node;
13201331 } else if (token->id == TokenIdSymbol) {
13211332 Token *next_token = &pc->tokens->at(*token_index + 1);
13221333
......@@ -2045,19 +2056,45 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool manda
20452056}
20462057
20472058/*
2048AssignmentExpression : BoolOrExpression AssignmentOperator BoolOrExpression | BoolOrExpression
2059UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression
20492060*/
2050static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) {
2061static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory) {
20512062 AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory);
20522063 if (!lhs)
20532064 return nullptr;
20542065
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/*
2085AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression
2086*/
2087static 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
20552092 Token *token = &pc->tokens->at(*token_index);
20562093 BinOpType ass_op = ast_parse_ass_op(pc, token_index, false);
20572094 if (ass_op == BinOpTypeInvalid)
20582095 return lhs;
20592096
2060 AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true);
2097 AstNode *rhs = ast_parse_unwrap_maybe_expr(pc, token_index, true);
20612098
20622099 AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
20632100 node->data.bin_op_expr.op1 = lhs;
src/parser.hpp+2
......@@ -45,6 +45,7 @@ enum NodeType {
4545 NodeTypeUse,
4646 NodeTypeVoid,
4747 NodeTypeBoolLiteral,
48 NodeTypeNullLiteral,
4849 NodeTypeIfBoolExpr,
4950 NodeTypeIfVarExpr,
5051 NodeTypeWhileExpr,
......@@ -161,6 +162,7 @@ enum BinOpType {
161162 BinOpTypeMult,
162163 BinOpTypeDiv,
163164 BinOpTypeMod,
165 BinOpTypeUnwrapMaybe,
164166};
165167
166168struct AstNodeBinOpExpr {
src/tokenizer.cpp+9
......@@ -241,6 +241,8 @@ static void end_token(Tokenize *t) {
241241 t->cur_tok->id = TokenIdKeywordContinue;
242242 } else if (mem_eql_str(token_mem, token_len, "break")) {
243243 t->cur_tok->id = TokenIdKeywordBreak;
244 } else if (mem_eql_str(token_mem, token_len, "null")) {
245 t->cur_tok->id = TokenIdKeywordNull;
244246 }
245247
246248 t->cur_tok = nullptr;
......@@ -418,6 +420,11 @@ void tokenize(Buf *buf, Tokenization *out) {
418420 break;
419421 case TokenizeStateSawQuestionMark:
420422 switch (c) {
423 case '?':
424 t.cur_tok->id = TokenIdDoubleQuestion;
425 end_token(&t);
426 t.state = TokenizeStateStart;
427 break;
421428 case '=':
422429 t.cur_tok->id = TokenIdMaybeAssign;
423430 end_token(&t);
......@@ -1002,6 +1009,7 @@ static const char * token_name(Token *token) {
10021009 case TokenIdKeywordWhile: return "While";
10031010 case TokenIdKeywordContinue: return "Continue";
10041011 case TokenIdKeywordBreak: return "Break";
1012 case TokenIdKeywordNull: return "Null";
10051013 case TokenIdLParen: return "LParen";
10061014 case TokenIdRParen: return "RParen";
10071015 case TokenIdComma: return "Comma";
......@@ -1052,6 +1060,7 @@ static const char * token_name(Token *token) {
10521060 case TokenIdDot: return "Dot";
10531061 case TokenIdEllipsis: return "Ellipsis";
10541062 case TokenIdMaybe: return "Maybe";
1063 case TokenIdDoubleQuestion: return "DoubleQuestion";
10551064 case TokenIdMaybeAssign: return "MaybeAssign";
10561065 }
10571066 return "(invalid token)";
src/tokenizer.hpp+2
......@@ -35,6 +35,7 @@ enum TokenId {
3535 TokenIdKeywordWhile,
3636 TokenIdKeywordContinue,
3737 TokenIdKeywordBreak,
38 TokenIdKeywordNull,
3839 TokenIdLParen,
3940 TokenIdRParen,
4041 TokenIdComma,
......@@ -85,6 +86,7 @@ enum TokenId {
8586 TokenIdDot,
8687 TokenIdEllipsis,
8788 TokenIdMaybe,
89 TokenIdDoubleQuestion,
8890 TokenIdMaybeAssign,
8991};
9092
std/std.zig+21
......@@ -1,5 +1,6 @@
11use "syscall.zig";
22
3const stdin_fileno : isize = 0;
34const stdout_fileno : isize = 1;
45const stderr_fileno : isize = 2;
56
......@@ -38,6 +39,26 @@ pub fn print_i64(x: i64) -> isize {
3839 return write(stdout_fileno, buf.ptr, len);
3940}
4041
42/*
43// TODO error handling
44pub 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
4162fn digit_to_char(digit: u64) -> u8 {
4263 '0' + (digit as u8)
4364}
std/syscall.zig+5
......@@ -1,3 +1,4 @@
1const SYS_read : usize = 0;
12const SYS_write : usize = 1;
23const SYS_exit : usize = 60;
34const SYS_getrandom : usize = 318;
......@@ -16,6 +17,10 @@ fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
1617 : "rcx", "r11")
1718}
1819
20pub fn read(fd: isize, buf: &u8, count: usize) -> isize {
21 syscall3(SYS_read, fd as usize, buf as usize, count) as isize
22}
23
1924pub fn write(fd: isize, buf: &const u8, count: usize) -> isize {
2025 syscall3(SYS_write, fd as usize, buf as usize, count) as isize
2126}
test/run_tests.cpp+18-1
......@@ -710,7 +710,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
710710
711711 add_simple_case("maybe type", R"SOURCE(
712712use "std.zig";
713pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
713pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
714714 const x : ?bool = true;
715715
716716 if (const y ?= x) {
......@@ -722,6 +722,23 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
722722 } else {
723723 print_str("x is none\n");
724724 }
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
725742 return 0;
726743}
727744 )SOURCE", "x is true\n");