| author | |
| committer | |
| log | 5e212db29cf9e2c06aba363736ffb965e631aa2d |
| tree | 2ec22f86549bca4cceb423bb3b66aa796754951f |
| parent | 82d1b51b1d34a0c1b21ec2aaae70051379b37f43 |
and return with '?' or '%' prefix10 files changed, 548 insertions(+), 88 deletions(-)
doc/langref.md+23-10| ... | ... | @@ -5,9 +5,11 @@ |
| 5 | 5 | ``` |
| 6 | 6 | Root : many(TopLevelDecl) "EOF" |
| 7 | 7 | |
| 8 | TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | |
| 8 | TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl | |
| 9 | 9 | |
| 10 | VariableDeclaration : option(FnVisibleMod) ("var" | "const") "symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) | |
| 10 | ErrorValueDecl : option(FnVisibleMod) "%." "Symbol" | |
| 11 | ||
| 12 | VariableDeclaration : option(FnVisibleMod) ("var" | "const") "Symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) | |
| 11 | 13 | |
| 12 | 14 | ContainerDecl : many(Directive) option(FnVisibleMod) ("struct" | "enum") "Symbol" "{" many(StructMember) "}" |
| 13 | 15 | |
| ... | ... | @@ -77,7 +79,7 @@ ForExpression : "for" "(" "Symbol" "," Expression option("," "Symbol") ")" Expre |
| 77 | 79 | |
| 78 | 80 | BoolOrExpression : BoolAndExpression "||" BoolOrExpression | BoolAndExpression |
| 79 | 81 | |
| 80 | ReturnExpression : "return" option(Expression) | |
| 82 | ReturnExpression : option("%" | "?") "return" option(Expression) | |
| 81 | 83 | |
| 82 | 84 | IfExpression : IfVarExpression | IfBoolExpression |
| 83 | 85 | |
| ... | ... | @@ -133,7 +135,7 @@ StructLiteralField : "." "Symbol" "=" Expression |
| 133 | 135 | |
| 134 | 136 | PrefixOp : "!" | "-" | "~" | "*" | ("&" option("const")) | "?" |
| 135 | 137 | |
| 136 | PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | |
| 138 | PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("%." "Symbol") | |
| 137 | 139 | |
| 138 | 140 | ArrayType : "[" option(Expression) "]" option("const") PrefixOpExpression |
| 139 | 141 | |
| ... | ... | @@ -148,7 +150,7 @@ KeywordLiteral : "true" | "false" | "null" | "break" | "continue" |
| 148 | 150 | |
| 149 | 151 | ``` |
| 150 | 152 | x() x[] x.y |
| 151 | !x -x ~x *x &x ?x | |
| 153 | !x -x ~x *x &x ?x %x | |
| 152 | 154 | x{} |
| 153 | 155 | * / % |
| 154 | 156 | + - |
| ... | ... | @@ -199,12 +201,20 @@ c_ulonglong unsigned long long for ABI compatibility with C |
| 199 | 201 | ### Boolean Type |
| 200 | 202 | The boolean type has the name `bool` and represents either true or false. |
| 201 | 203 | |
| 202 | ### Function Types | |
| 204 | ### Function Type | |
| 203 | 205 | TODO |
| 204 | 206 | |
| 205 | ### Array Types | |
| 206 | TODO | |
| 207 | Also, are there slices? | |
| 207 | ### Fixed-Size Array Type | |
| 208 | ||
| 209 | Example: The string `"aoeu"` has type `[4]u8`. | |
| 210 | ||
| 211 | The size is known at compile time and is part of the type. | |
| 212 | ||
| 213 | ### Slice Type | |
| 214 | ||
| 215 | A slice can be obtained with the slicing syntax: `array[start...end]` | |
| 216 | ||
| 217 | Example: `"aoeu"[0...2]` has type `[]u8`. | |
| 208 | 218 | |
| 209 | 219 | ### Struct Types |
| 210 | 220 | TODO |
| ... | ... | @@ -213,10 +223,13 @@ TODO |
| 213 | 223 | TODO |
| 214 | 224 | |
| 215 | 225 | ### Unreachable Type |
| 226 | ||
| 216 | 227 | The unreachable type has the name `unreachable`. TODO explanation |
| 217 | 228 | |
| 218 | 229 | ### Void Type |
| 219 | The void type has the name `void`. TODO explanation | |
| 230 | ||
| 231 | The void type has the name `void`. void types are zero bits and are omitted | |
| 232 | from codegen. | |
| 220 | 233 | |
| 221 | 234 | |
| 222 | 235 | ## Expressions |
example/cat/main.zig+17-17| ... | ... | @@ -3,50 +3,50 @@ export executable "cat"; |
| 3 | 3 | import "std.zig"; |
| 4 | 4 | |
| 5 | 5 | // Things to do to make this work: |
| 6 | // * isize instead of usize for things | |
| 7 | 6 | // * var args printing |
| 8 | // * update std API | |
| 9 | // * !void type | |
| 7 | // * %void type | |
| 10 | 8 | // * defer |
| 11 | // * !return | |
| 12 | // * !! operator | |
| 13 | // * make main return !void | |
| 14 | // * how to reference error values (!void).Invalid ? !Invalid ? | |
| 15 | // * ~ is bool not, not ! | |
| 9 | // * %return | |
| 10 | // * %% operator | |
| 11 | // * make main return %void | |
| 12 | // * how to reference error values %.Invalid | |
| 16 | 13 | // * cast err type to string |
| 14 | // * update std API | |
| 15 | ||
| 16 | pub %.Invalid; | |
| 17 | 17 | |
| 18 | pub fn main(args: [][]u8) !void => { | |
| 18 | pub fn main(args: [][]u8) %void => { | |
| 19 | 19 | const exe = args[0]; |
| 20 | 20 | var catted_anything = false; |
| 21 | 21 | for (arg, args[1...]) { |
| 22 | 22 | if (arg == "-") { |
| 23 | 23 | catted_anything = true; |
| 24 | !return cat_stream(stdin); | |
| 24 | %return cat_stream(stdin); | |
| 25 | 25 | } else if (arg[0] == '-') { |
| 26 | 26 | return usage(exe); |
| 27 | 27 | } else { |
| 28 | 28 | var is: InputStream; |
| 29 | is.open(arg, OpenReadOnly) !! (err) => { | |
| 29 | is.open(arg, OpenReadOnly) %% (err) => { | |
| 30 | 30 | stderr.print("Unable to open file: {}", ([]u8])(err)); |
| 31 | 31 | return err; |
| 32 | 32 | } |
| 33 | 33 | defer is.close(); |
| 34 | 34 | |
| 35 | 35 | catted_anything = true; |
| 36 | !return cat_stream(is); | |
| 36 | %return cat_stream(is); | |
| 37 | 37 | } |
| 38 | 38 | } |
| 39 | if (~catted_anything) { | |
| 40 | !return cat_stream(stdin) | |
| 39 | if (!catted_anything) { | |
| 40 | %return cat_stream(stdin) | |
| 41 | 41 | } |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | fn usage(exe: []u8) !void => { | |
| 44 | fn usage(exe: []u8) %void => { | |
| 45 | 45 | stderr.print("Usage: {} [FILE]...\n", exe); |
| 46 | return !Invalid; | |
| 46 | return %.Invalid; | |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | fn cat_stream(is: InputStream) !void => { | |
| 49 | fn cat_stream(is: InputStream) %void => { | |
| 50 | 50 | var buf: [1024 * 4]u8; |
| 51 | 51 | |
| 52 | 52 | while (true) { |
example/hello_world/hello.zig+1| ... | ... | @@ -3,6 +3,7 @@ export executable "hello"; |
| 3 | 3 | import "std.zig"; |
| 4 | 4 | |
| 5 | 5 | pub fn main(args: [][]u8) i32 => { |
| 6 | //stderr.print_str("Hello, world!\n"); | |
| 6 | 7 | print_str("Hello, world!\n"); |
| 7 | 8 | return 0; |
| 8 | 9 | } |
src/all_types.hpp+35| ... | ... | @@ -129,10 +129,12 @@ enum NodeType { |
| 129 | 129 | NodeTypeDirective, |
| 130 | 130 | NodeTypeReturnExpr, |
| 131 | 131 | NodeTypeVariableDeclaration, |
| 132 | NodeTypeErrorValueDecl, | |
| 132 | 133 | NodeTypeBinOpExpr, |
| 133 | 134 | NodeTypeNumberLiteral, |
| 134 | 135 | NodeTypeStringLiteral, |
| 135 | 136 | NodeTypeCharLiteral, |
| 137 | NodeTypeErrorLiteral, | |
| 136 | 138 | NodeTypeSymbol, |
| 137 | 139 | NodeTypePrefixOpExpr, |
| 138 | 140 | NodeTypeFnCallExpr, |
| ... | ... | @@ -222,7 +224,14 @@ struct AstNodeBlock { |
| 222 | 224 | Expr resolved_expr; |
| 223 | 225 | }; |
| 224 | 226 | |
| 227 | enum ReturnKind { | |
| 228 | ReturnKindUnconditional, | |
| 229 | ReturnKindMaybe, | |
| 230 | ReturnKindError, | |
| 231 | }; | |
| 232 | ||
| 225 | 233 | struct AstNodeReturnExpr { |
| 234 | ReturnKind kind; | |
| 226 | 235 | // might be null in case of return void; |
| 227 | 236 | AstNode *expr; |
| 228 | 237 | |
| ... | ... | @@ -243,6 +252,14 @@ struct AstNodeVariableDeclaration { |
| 243 | 252 | Expr resolved_expr; |
| 244 | 253 | }; |
| 245 | 254 | |
| 255 | struct AstNodeErrorValueDecl { | |
| 256 | VisibMod visib_mod; | |
| 257 | Buf name; | |
| 258 | ||
| 259 | // populated by semantic analyzer | |
| 260 | TopLevelDecl top_level_decl; | |
| 261 | }; | |
| 262 | ||
| 246 | 263 | enum BinOpType { |
| 247 | 264 | BinOpTypeInvalid, |
| 248 | 265 | BinOpTypeAssign, |
| ... | ... | @@ -358,6 +375,7 @@ enum PrefixOp { |
| 358 | 375 | PrefixOpConstAddressOf, |
| 359 | 376 | PrefixOpDereference, |
| 360 | 377 | PrefixOpMaybe, |
| 378 | PrefixOpError, | |
| 361 | 379 | }; |
| 362 | 380 | |
| 363 | 381 | struct AstNodePrefixOpExpr { |
| ... | ... | @@ -564,6 +582,14 @@ struct AstNodeNumberLiteral { |
| 564 | 582 | Expr resolved_expr; |
| 565 | 583 | }; |
| 566 | 584 | |
| 585 | struct AstNodeErrorLiteral { | |
| 586 | Buf symbol; | |
| 587 | ||
| 588 | // populated by semantic analyzer | |
| 589 | NumLitCodeGen codegen; | |
| 590 | Expr resolved_expr; | |
| 591 | }; | |
| 592 | ||
| 567 | 593 | struct AstNodeStructValueField { |
| 568 | 594 | Buf name; |
| 569 | 595 | AstNode *expr; |
| ... | ... | @@ -644,6 +670,7 @@ struct AstNode { |
| 644 | 670 | AstNodeBlock block; |
| 645 | 671 | AstNodeReturnExpr return_expr; |
| 646 | 672 | AstNodeVariableDeclaration variable_declaration; |
| 673 | AstNodeErrorValueDecl error_value_decl; | |
| 647 | 674 | AstNodeBinOpExpr bin_op_expr; |
| 648 | 675 | AstNodeExternBlock extern_block; |
| 649 | 676 | AstNodeDirective directive; |
| ... | ... | @@ -668,6 +695,7 @@ struct AstNode { |
| 668 | 695 | AstNodeStringLiteral string_literal; |
| 669 | 696 | AstNodeCharLiteral char_literal; |
| 670 | 697 | AstNodeNumberLiteral number_literal; |
| 698 | AstNodeErrorLiteral error_literal; | |
| 671 | 699 | AstNodeContainerInitExpr container_init_expr; |
| 672 | 700 | AstNodeStructValueField struct_val_field; |
| 673 | 701 | AstNodeNullLiteral null_literal; |
| ... | ... | @@ -738,6 +766,10 @@ struct TypeTableEntryMaybe { |
| 738 | 766 | TypeTableEntry *child_type; |
| 739 | 767 | }; |
| 740 | 768 | |
| 769 | struct TypeTableEntryError { | |
| 770 | TypeTableEntry *child_type; | |
| 771 | }; | |
| 772 | ||
| 741 | 773 | struct TypeTableEntryEnum { |
| 742 | 774 | AstNode *decl_node; |
| 743 | 775 | uint32_t field_count; |
| ... | ... | @@ -778,6 +810,7 @@ enum TypeTableEntryId { |
| 778 | 810 | TypeTableEntryIdStruct, |
| 779 | 811 | TypeTableEntryIdNumberLiteral, |
| 780 | 812 | TypeTableEntryIdMaybe, |
| 813 | TypeTableEntryIdError, | |
| 781 | 814 | TypeTableEntryIdEnum, |
| 782 | 815 | TypeTableEntryIdFn, |
| 783 | 816 | }; |
| ... | ... | @@ -799,6 +832,7 @@ struct TypeTableEntry { |
| 799 | 832 | TypeTableEntryStruct structure; |
| 800 | 833 | TypeTableEntryNumLit num_lit; |
| 801 | 834 | TypeTableEntryMaybe maybe; |
| 835 | TypeTableEntryError error; | |
| 802 | 836 | TypeTableEntryEnum enumeration; |
| 803 | 837 | TypeTableEntryFn fn; |
| 804 | 838 | } data; |
| ... | ... | @@ -808,6 +842,7 @@ struct TypeTableEntry { |
| 808 | 842 | TypeTableEntry *unknown_size_array_parent[2]; |
| 809 | 843 | HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size; |
| 810 | 844 | TypeTableEntry *maybe_parent; |
| 845 | TypeTableEntry *error_parent; | |
| 811 | 846 | }; |
| 812 | 847 | |
| 813 | 848 | struct ImporterInfo { |
src/analyze.cpp+148-24| ... | ... | @@ -43,7 +43,9 @@ static AstNode *first_executing_node(AstNode *node) { |
| 43 | 43 | case NodeTypeDirective: |
| 44 | 44 | case NodeTypeReturnExpr: |
| 45 | 45 | case NodeTypeVariableDeclaration: |
| 46 | case NodeTypeErrorValueDecl: | |
| 46 | 47 | case NodeTypeNumberLiteral: |
| 48 | case NodeTypeErrorLiteral: | |
| 47 | 49 | case NodeTypeStringLiteral: |
| 48 | 50 | case NodeTypeCharLiteral: |
| 49 | 51 | case NodeTypeSymbol: |
| ... | ... | @@ -104,6 +106,7 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { |
| 104 | 106 | case TypeTableEntryIdNumberLiteral: |
| 105 | 107 | case TypeTableEntryIdMaybe: |
| 106 | 108 | case TypeTableEntryIdFn: |
| 109 | case TypeTableEntryIdError: | |
| 107 | 110 | // nothing to init |
| 108 | 111 | break; |
| 109 | 112 | case TypeTableEntryIdStruct: |
| ... | ... | @@ -215,6 +218,57 @@ static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { |
| 215 | 218 | } |
| 216 | 219 | } |
| 217 | 220 | |
| 221 | static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { | |
| 222 | if (child_type->error_parent) { | |
| 223 | return child_type->error_parent; | |
| 224 | } else { | |
| 225 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdError); | |
| 226 | zig_panic("TODO get_error_type"); | |
| 227 | // create a struct with a boolean whether this is the null value | |
| 228 | assert(child_type->type_ref); | |
| 229 | LLVMTypeRef elem_types[] = { | |
| 230 | child_type->type_ref, | |
| 231 | LLVMInt1Type(), | |
| 232 | }; | |
| 233 | entry->type_ref = LLVMStructType(elem_types, 2, false); | |
| 234 | buf_resize(&entry->name, 0); | |
| 235 | buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); | |
| 236 | entry->size_in_bits = child_type->size_in_bits + 8; | |
| 237 | entry->align_in_bits = child_type->align_in_bits; | |
| 238 | assert(child_type->di_type); | |
| 239 | ||
| 240 | ||
| 241 | LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); | |
| 242 | LLVMZigDIFile *di_file = nullptr; | |
| 243 | unsigned line = 0; | |
| 244 | entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, | |
| 245 | LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name), | |
| 246 | compile_unit_scope, di_file, line); | |
| 247 | ||
| 248 | LLVMZigDIType *di_element_types[] = { | |
| 249 | LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), | |
| 250 | "val", di_file, line, child_type->size_in_bits, child_type->align_in_bits, 0, 0, | |
| 251 | child_type->di_type), | |
| 252 | LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), | |
| 253 | "maybe", di_file, line, 8, 8, 8, 0, | |
| 254 | child_type->di_type), | |
| 255 | }; | |
| 256 | LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, | |
| 257 | compile_unit_scope, | |
| 258 | buf_ptr(&entry->name), | |
| 259 | di_file, line, entry->size_in_bits, entry->align_in_bits, 0, | |
| 260 | nullptr, di_element_types, 2, 0, nullptr, ""); | |
| 261 | ||
| 262 | LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); | |
| 263 | entry->di_type = replacement_di_type; | |
| 264 | ||
| 265 | entry->data.maybe.child_type = child_type; | |
| 266 | ||
| 267 | child_type->maybe_parent = entry; | |
| 268 | return entry; | |
| 269 | } | |
| 270 | } | |
| 271 | ||
| 218 | 272 | static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) |
| 219 | 273 | { |
| 220 | 274 | auto existing_entry = child_type->arrays_by_size.maybe_get(array_size); |
| ... | ... | @@ -922,6 +976,11 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 922 | 976 | g->global_vars.append(var); |
| 923 | 977 | break; |
| 924 | 978 | } |
| 979 | case NodeTypeErrorValueDecl: | |
| 980 | { | |
| 981 | zig_panic("TODO resolve_top_level_decl NodeTypeErrorValueDecl"); | |
| 982 | break; | |
| 983 | } | |
| 925 | 984 | case NodeTypeUse: |
| 926 | 985 | // nothing to do here |
| 927 | 986 | break; |
| ... | ... | @@ -937,6 +996,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 937 | 996 | case NodeTypeArrayAccessExpr: |
| 938 | 997 | case NodeTypeSliceExpr: |
| 939 | 998 | case NodeTypeNumberLiteral: |
| 999 | case NodeTypeErrorLiteral: | |
| 940 | 1000 | case NodeTypeStringLiteral: |
| 941 | 1001 | case NodeTypeCharLiteral: |
| 942 | 1002 | case NodeTypeBoolLiteral: |
| ... | ... | @@ -1005,6 +1065,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, |
| 1005 | 1065 | case TypeTableEntryIdEnum: |
| 1006 | 1066 | case TypeTableEntryIdMetaType: |
| 1007 | 1067 | case TypeTableEntryIdFn: |
| 1068 | case TypeTableEntryIdError: | |
| 1008 | 1069 | return false; |
| 1009 | 1070 | case TypeTableEntryIdInt: |
| 1010 | 1071 | if (is_num_lit_unsigned(num_lit)) { |
| ... | ... | @@ -2263,6 +2324,12 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry |
| 2263 | 2324 | } |
| 2264 | 2325 | } |
| 2265 | 2326 | |
| 2327 | static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, | |
| 2328 | BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) | |
| 2329 | { | |
| 2330 | zig_panic("TODO analyze_error_literal_expr"); | |
| 2331 | } | |
| 2332 | ||
| 2266 | 2333 | static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 2267 | 2334 | TypeTableEntry *expected_type, AstNode *node) |
| 2268 | 2335 | { |
| ... | ... | @@ -3021,7 +3088,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo |
| 3021 | 3088 | if (meta_type->id == TypeTableEntryIdInvalid) { |
| 3022 | 3089 | return g->builtin_types.entry_invalid; |
| 3023 | 3090 | } else if (meta_type->id == TypeTableEntryIdUnreachable) { |
| 3024 | add_node_error(g, node, buf_create_from_str("maybe unreachable type not allowed")); | |
| 3091 | add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in maybe type")); | |
| 3025 | 3092 | return g->builtin_types.entry_invalid; |
| 3026 | 3093 | } else { |
| 3027 | 3094 | return resolve_expr_const_val_as_type(g, node, get_maybe_type(g, meta_type)); |
| ... | ... | @@ -3034,6 +3101,31 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo |
| 3034 | 3101 | return get_maybe_type(g, type_entry); |
| 3035 | 3102 | } |
| 3036 | 3103 | } |
| 3104 | case PrefixOpError: | |
| 3105 | { | |
| 3106 | TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); | |
| 3107 | ||
| 3108 | if (type_entry->id == TypeTableEntryIdInvalid) { | |
| 3109 | return type_entry; | |
| 3110 | } else if (type_entry->id == TypeTableEntryIdMetaType) { | |
| 3111 | TypeTableEntry *meta_type = resolve_type(g, expr_node); | |
| 3112 | if (meta_type->id == TypeTableEntryIdInvalid) { | |
| 3113 | return meta_type; | |
| 3114 | } else if (meta_type->id == TypeTableEntryIdUnreachable) { | |
| 3115 | add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in error type")); | |
| 3116 | return g->builtin_types.entry_invalid; | |
| 3117 | } else { | |
| 3118 | return resolve_expr_const_val_as_type(g, node, get_error_type(g, meta_type)); | |
| 3119 | } | |
| 3120 | } else if (type_entry->id == TypeTableEntryIdUnreachable) { | |
| 3121 | add_node_error(g, expr_node, buf_sprintf("unable to wrap unreachable in error type")); | |
| 3122 | return g->builtin_types.entry_invalid; | |
| 3123 | } else { | |
| 3124 | // TODO eval const expr | |
| 3125 | return get_error_type(g, type_entry); | |
| 3126 | } | |
| 3127 | ||
| 3128 | } | |
| 3037 | 3129 | } |
| 3038 | 3130 | zig_unreachable(); |
| 3039 | 3131 | } |
| ... | ... | @@ -3099,6 +3191,37 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 3099 | 3191 | return expected_type; |
| 3100 | 3192 | } |
| 3101 | 3193 | |
| 3194 | static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 3195 | TypeTableEntry *expected_type, AstNode *node) | |
| 3196 | { | |
| 3197 | if (!context->fn_entry) { | |
| 3198 | add_node_error(g, node, buf_sprintf("return expression outside function definition")); | |
| 3199 | return g->builtin_types.entry_invalid; | |
| 3200 | } | |
| 3201 | ||
| 3202 | if (node->data.return_expr.kind != ReturnKindUnconditional) { | |
| 3203 | zig_panic("TODO analyze_return_expr conditional"); | |
| 3204 | } | |
| 3205 | ||
| 3206 | TypeTableEntry *expected_return_type = get_return_type(context); | |
| 3207 | TypeTableEntry *actual_return_type; | |
| 3208 | if (node->data.return_expr.expr) { | |
| 3209 | actual_return_type = analyze_expression(g, import, context, expected_return_type, node->data.return_expr.expr); | |
| 3210 | } else { | |
| 3211 | actual_return_type = g->builtin_types.entry_void; | |
| 3212 | } | |
| 3213 | ||
| 3214 | if (actual_return_type->id == TypeTableEntryIdUnreachable) { | |
| 3215 | // "return exit(0)" should just be "exit(0)". | |
| 3216 | add_node_error(g, node, buf_sprintf("returning is unreachable")); | |
| 3217 | actual_return_type = g->builtin_types.entry_invalid; | |
| 3218 | } | |
| 3219 | ||
| 3220 | resolve_type_compatibility(g, context, node, expected_return_type, actual_return_type); | |
| 3221 | ||
| 3222 | return g->builtin_types.entry_unreachable; | |
| 3223 | } | |
| 3224 | ||
| 3102 | 3225 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 3103 | 3226 | TypeTableEntry *expected_type, AstNode *node) |
| 3104 | 3227 | { |
| ... | ... | @@ -3140,29 +3263,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 3140 | 3263 | } |
| 3141 | 3264 | |
| 3142 | 3265 | case NodeTypeReturnExpr: |
| 3143 | { | |
| 3144 | if (context->fn_entry) { | |
| 3145 | TypeTableEntry *expected_return_type = get_return_type(context); | |
| 3146 | TypeTableEntry *actual_return_type; | |
| 3147 | if (node->data.return_expr.expr) { | |
| 3148 | actual_return_type = analyze_expression(g, import, context, expected_return_type, node->data.return_expr.expr); | |
| 3149 | } else { | |
| 3150 | actual_return_type = g->builtin_types.entry_void; | |
| 3151 | } | |
| 3152 | ||
| 3153 | if (actual_return_type->id == TypeTableEntryIdUnreachable) { | |
| 3154 | // "return exit(0)" should just be "exit(0)". | |
| 3155 | add_node_error(g, node, buf_sprintf("returning is unreachable")); | |
| 3156 | actual_return_type = g->builtin_types.entry_invalid; | |
| 3157 | } | |
| 3158 | ||
| 3159 | resolve_type_compatibility(g, context, node, expected_return_type, actual_return_type); | |
| 3160 | } else { | |
| 3161 | add_node_error(g, node, buf_sprintf("return expression outside function definition")); | |
| 3162 | } | |
| 3163 | return_type = g->builtin_types.entry_unreachable; | |
| 3164 | break; | |
| 3165 | } | |
| 3266 | return_type = analyze_return_expr(g, import, context, expected_type, node); | |
| 3267 | break; | |
| 3166 | 3268 | case NodeTypeVariableDeclaration: |
| 3167 | 3269 | analyze_variable_declaration(g, import, context, expected_type, node); |
| 3168 | 3270 | return_type = g->builtin_types.entry_void; |
| ... | ... | @@ -3236,6 +3338,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 3236 | 3338 | case NodeTypeNumberLiteral: |
| 3237 | 3339 | return_type = analyze_number_literal_expr(g, import, context, expected_type, node); |
| 3238 | 3340 | break; |
| 3341 | case NodeTypeErrorLiteral: | |
| 3342 | return_type = analyze_error_literal_expr(g, import, context, expected_type, node); | |
| 3343 | break; | |
| 3239 | 3344 | case NodeTypeStringLiteral: |
| 3240 | 3345 | if (node->data.string_literal.c) { |
| 3241 | 3346 | return_type = g->builtin_types.entry_c_string_literal; |
| ... | ... | @@ -3294,6 +3399,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 3294 | 3399 | case NodeTypeStructDecl: |
| 3295 | 3400 | case NodeTypeStructField: |
| 3296 | 3401 | case NodeTypeStructValueField: |
| 3402 | case NodeTypeErrorValueDecl: | |
| 3297 | 3403 | zig_unreachable(); |
| 3298 | 3404 | } |
| 3299 | 3405 | assert(return_type); |
| ... | ... | @@ -3411,6 +3517,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 3411 | 3517 | case NodeTypeExternBlock: |
| 3412 | 3518 | case NodeTypeUse: |
| 3413 | 3519 | case NodeTypeVariableDeclaration: |
| 3520 | case NodeTypeErrorValueDecl: | |
| 3414 | 3521 | // already took care of these |
| 3415 | 3522 | break; |
| 3416 | 3523 | case NodeTypeDirective: |
| ... | ... | @@ -3425,6 +3532,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 3425 | 3532 | case NodeTypeArrayAccessExpr: |
| 3426 | 3533 | case NodeTypeSliceExpr: |
| 3427 | 3534 | case NodeTypeNumberLiteral: |
| 3535 | case NodeTypeErrorLiteral: | |
| 3428 | 3536 | case NodeTypeStringLiteral: |
| 3429 | 3537 | case NodeTypeCharLiteral: |
| 3430 | 3538 | case NodeTypeBoolLiteral: |
| ... | ... | @@ -3457,6 +3565,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 3457 | 3565 | { |
| 3458 | 3566 | switch (node->type) { |
| 3459 | 3567 | case NodeTypeNumberLiteral: |
| 3568 | case NodeTypeErrorLiteral: | |
| 3460 | 3569 | case NodeTypeStringLiteral: |
| 3461 | 3570 | case NodeTypeCharLiteral: |
| 3462 | 3571 | case NodeTypeBoolLiteral: |
| ... | ... | @@ -3464,6 +3573,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 3464 | 3573 | case NodeTypeGoto: |
| 3465 | 3574 | case NodeTypeBreak: |
| 3466 | 3575 | case NodeTypeContinue: |
| 3576 | case NodeTypeErrorValueDecl: | |
| 3467 | 3577 | // no dependencies on other top level declarations |
| 3468 | 3578 | break; |
| 3469 | 3579 | case NodeTypeSymbol: |
| ... | ... | @@ -3758,6 +3868,10 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast |
| 3758 | 3868 | case NodeTypeUse: |
| 3759 | 3869 | // already taken care of |
| 3760 | 3870 | break; |
| 3871 | case NodeTypeErrorValueDecl: | |
| 3872 | // error value declarations do not depend on other top level decls | |
| 3873 | resolve_top_level_decl(g, import, node); | |
| 3874 | break; | |
| 3761 | 3875 | case NodeTypeDirective: |
| 3762 | 3876 | case NodeTypeParamDecl: |
| 3763 | 3877 | case NodeTypeFnDecl: |
| ... | ... | @@ -3769,6 +3883,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast |
| 3769 | 3883 | case NodeTypeArrayAccessExpr: |
| 3770 | 3884 | case NodeTypeSliceExpr: |
| 3771 | 3885 | case NodeTypeNumberLiteral: |
| 3886 | case NodeTypeErrorLiteral: | |
| 3772 | 3887 | case NodeTypeStringLiteral: |
| 3773 | 3888 | case NodeTypeCharLiteral: |
| 3774 | 3889 | case NodeTypeBoolLiteral: |
| ... | ... | @@ -3966,6 +4081,8 @@ Expr *get_resolved_expr(AstNode *node) { |
| 3966 | 4081 | return &node->data.container_init_expr.resolved_expr; |
| 3967 | 4082 | case NodeTypeNumberLiteral: |
| 3968 | 4083 | return &node->data.number_literal.resolved_expr; |
| 4084 | case NodeTypeErrorLiteral: | |
| 4085 | return &node->data.error_literal.resolved_expr; | |
| 3969 | 4086 | case NodeTypeStringLiteral: |
| 3970 | 4087 | return &node->data.string_literal.resolved_expr; |
| 3971 | 4088 | case NodeTypeBlock: |
| ... | ... | @@ -4006,6 +4123,7 @@ Expr *get_resolved_expr(AstNode *node) { |
| 4006 | 4123 | case NodeTypeStructDecl: |
| 4007 | 4124 | case NodeTypeStructField: |
| 4008 | 4125 | case NodeTypeStructValueField: |
| 4126 | case NodeTypeErrorValueDecl: | |
| 4009 | 4127 | zig_unreachable(); |
| 4010 | 4128 | } |
| 4011 | 4129 | zig_unreachable(); |
| ... | ... | @@ -4015,6 +4133,8 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) { |
| 4015 | 4133 | switch (node->type) { |
| 4016 | 4134 | case NodeTypeNumberLiteral: |
| 4017 | 4135 | return &node->data.number_literal.codegen; |
| 4136 | case NodeTypeErrorLiteral: | |
| 4137 | return &node->data.error_literal.codegen; | |
| 4018 | 4138 | case NodeTypeFnCallExpr: |
| 4019 | 4139 | return &node->data.fn_call_expr.resolved_num_lit; |
| 4020 | 4140 | case NodeTypeReturnExpr: |
| ... | ... | @@ -4056,6 +4176,7 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) { |
| 4056 | 4176 | case NodeTypeStructField: |
| 4057 | 4177 | case NodeTypeStructValueField: |
| 4058 | 4178 | case NodeTypeArrayType: |
| 4179 | case NodeTypeErrorValueDecl: | |
| 4059 | 4180 | zig_unreachable(); |
| 4060 | 4181 | } |
| 4061 | 4182 | zig_unreachable(); |
| ... | ... | @@ -4069,7 +4190,10 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { |
| 4069 | 4190 | return &node->data.fn_proto.top_level_decl; |
| 4070 | 4191 | case NodeTypeStructDecl: |
| 4071 | 4192 | return &node->data.struct_decl.top_level_decl; |
| 4193 | case NodeTypeErrorValueDecl: | |
| 4194 | return &node->data.error_value_decl.top_level_decl; | |
| 4072 | 4195 | case NodeTypeNumberLiteral: |
| 4196 | case NodeTypeErrorLiteral: | |
| 4073 | 4197 | case NodeTypeReturnExpr: |
| 4074 | 4198 | case NodeTypeBinOpExpr: |
| 4075 | 4199 | case NodeTypePrefixOpExpr: |
src/codegen.cpp+13| ... | ... | @@ -829,6 +829,10 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 829 | 829 | { |
| 830 | 830 | zig_panic("TODO codegen PrefixOpMaybe"); |
| 831 | 831 | } |
| 832 | case PrefixOpError: | |
| 833 | { | |
| 834 | zig_panic("TODO codegen PrefixOpError"); | |
| 835 | } | |
| 832 | 836 | } |
| 833 | 837 | zig_unreachable(); |
| 834 | 838 | } |
| ... | ... | @@ -1937,6 +1941,12 @@ static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *node) { |
| 1937 | 1941 | return gen_number_literal_raw(g, node, codegen_num_lit, &node->data.number_literal); |
| 1938 | 1942 | } |
| 1939 | 1943 | |
| 1944 | static LLVMValueRef gen_error_literal(CodeGen *g, AstNode *node) { | |
| 1945 | assert(node->type == NodeTypeErrorLiteral); | |
| 1946 | ||
| 1947 | zig_panic("TODO gen_error_literal"); | |
| 1948 | } | |
| 1949 | ||
| 1940 | 1950 | static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { |
| 1941 | 1951 | assert(node->type == NodeTypeSymbol); |
| 1942 | 1952 | VariableTableEntry *variable = node->data.symbol_expr.variable; |
| ... | ... | @@ -2070,6 +2080,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 2070 | 2080 | return gen_asm_expr(g, node); |
| 2071 | 2081 | case NodeTypeNumberLiteral: |
| 2072 | 2082 | return gen_number_literal(g, node); |
| 2083 | case NodeTypeErrorLiteral: | |
| 2084 | return gen_error_literal(g, node); | |
| 2073 | 2085 | case NodeTypeStringLiteral: |
| 2074 | 2086 | { |
| 2075 | 2087 | Buf *str = &node->data.string_literal.buf; |
| ... | ... | @@ -2125,6 +2137,7 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 2125 | 2137 | case NodeTypeArrayType: |
| 2126 | 2138 | case NodeTypeSwitchProng: |
| 2127 | 2139 | case NodeTypeSwitchRange: |
| 2140 | case NodeTypeErrorValueDecl: | |
| 2128 | 2141 | zig_unreachable(); |
| 2129 | 2142 | } |
| 2130 | 2143 | zig_unreachable(); |
src/parser.cpp+154-21| ... | ... | @@ -63,6 +63,16 @@ static const char *prefix_op_str(PrefixOp prefix_op) { |
| 63 | 63 | case PrefixOpConstAddressOf: return "&const"; |
| 64 | 64 | case PrefixOpDereference: return "*"; |
| 65 | 65 | case PrefixOpMaybe: return "?"; |
| 66 | case PrefixOpError: return "%"; | |
| 67 | } | |
| 68 | zig_unreachable(); | |
| 69 | } | |
| 70 | ||
| 71 | static const char *return_prefix_str(ReturnKind kind) { | |
| 72 | switch (kind) { | |
| 73 | case ReturnKindError: return "%"; | |
| 74 | case ReturnKindMaybe: return "?"; | |
| 75 | case ReturnKindUnconditional: return ""; | |
| 66 | 76 | } |
| 67 | 77 | zig_unreachable(); |
| 68 | 78 | } |
| ... | ... | @@ -99,8 +109,12 @@ const char *node_type_str(NodeType node_type) { |
| 99 | 109 | return "ReturnExpr"; |
| 100 | 110 | case NodeTypeVariableDeclaration: |
| 101 | 111 | return "VariableDeclaration"; |
| 112 | case NodeTypeErrorValueDecl: | |
| 113 | return "ErrorValueDecl"; | |
| 102 | 114 | case NodeTypeNumberLiteral: |
| 103 | 115 | return "NumberLiteral"; |
| 116 | case NodeTypeErrorLiteral: | |
| 117 | return "ErrorLiteral"; | |
| 104 | 118 | case NodeTypeStringLiteral: |
| 105 | 119 | return "StringLiteral"; |
| 106 | 120 | case NodeTypeCharLiteral: |
| ... | ... | @@ -214,10 +228,13 @@ void ast_print(AstNode *node, int indent) { |
| 214 | 228 | break; |
| 215 | 229 | } |
| 216 | 230 | case NodeTypeReturnExpr: |
| 217 | fprintf(stderr, "%s\n", node_type_str(node->type)); | |
| 218 | if (node->data.return_expr.expr) | |
| 219 | ast_print(node->data.return_expr.expr, indent + 2); | |
| 220 | break; | |
| 231 | { | |
| 232 | const char *prefix_str = return_prefix_str(node->data.return_expr.kind); | |
| 233 | fprintf(stderr, "%s%s\n", prefix_str, node_type_str(node->type)); | |
| 234 | if (node->data.return_expr.expr) | |
| 235 | ast_print(node->data.return_expr.expr, indent + 2); | |
| 236 | break; | |
| 237 | } | |
| 221 | 238 | case NodeTypeVariableDeclaration: |
| 222 | 239 | { |
| 223 | 240 | Buf *name_buf = &node->data.variable_declaration.symbol; |
| ... | ... | @@ -228,6 +245,12 @@ void ast_print(AstNode *node, int indent) { |
| 228 | 245 | ast_print(node->data.variable_declaration.expr, indent + 2); |
| 229 | 246 | break; |
| 230 | 247 | } |
| 248 | case NodeTypeErrorValueDecl: | |
| 249 | { | |
| 250 | Buf *name_buf = &node->data.error_value_decl.name; | |
| 251 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf)); | |
| 252 | break; | |
| 253 | } | |
| 231 | 254 | case NodeTypeExternBlock: |
| 232 | 255 | { |
| 233 | 256 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| ... | ... | @@ -288,6 +311,11 @@ void ast_print(AstNode *node, int indent) { |
| 288 | 311 | } |
| 289 | 312 | break; |
| 290 | 313 | } |
| 314 | case NodeTypeErrorLiteral: | |
| 315 | { | |
| 316 | fprintf(stderr, "%s '%s'", node_type_str(node->type), buf_ptr(&node->data.error_literal.symbol)); | |
| 317 | break; | |
| 318 | } | |
| 291 | 319 | case NodeTypeStringLiteral: |
| 292 | 320 | { |
| 293 | 321 | const char *c = node->data.string_literal.c ? "c" : ""; |
| ... | ... | @@ -1345,7 +1373,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand |
| 1345 | 1373 | } |
| 1346 | 1374 | |
| 1347 | 1375 | /* |
| 1348 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | token(Symbol) | (token(AtSign) token(Symbol) FnCallExpression) | ArrayType | AsmExpression | |
| 1376 | PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("%." "Symbol") | |
| 1349 | 1377 | KeywordLiteral : token(True) | token(False) | token(Null) | token(Break) | token(Continue) |
| 1350 | 1378 | */ |
| 1351 | 1379 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| ... | ... | @@ -1415,6 +1443,12 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 1415 | 1443 | |
| 1416 | 1444 | ast_buf_from_token(pc, dest_symbol, &node->data.goto_expr.name); |
| 1417 | 1445 | return node; |
| 1446 | } else if (token->id == TokenIdPercentDot) { | |
| 1447 | *token_index += 1; | |
| 1448 | Token *symbol_tok = ast_eat_token(pc, token_index, TokenIdSymbol); | |
| 1449 | AstNode *node = ast_create_node(pc, NodeTypeErrorLiteral, token); | |
| 1450 | ast_buf_from_token(pc, symbol_tok, &node->data.error_literal.symbol); | |
| 1451 | return node; | |
| 1418 | 1452 | } |
| 1419 | 1453 | |
| 1420 | 1454 | AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); |
| ... | ... | @@ -1612,6 +1646,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { |
| 1612 | 1646 | case TokenIdAmpersand: return PrefixOpAddressOf; |
| 1613 | 1647 | case TokenIdStar: return PrefixOpDereference; |
| 1614 | 1648 | case TokenIdMaybe: return PrefixOpMaybe; |
| 1649 | case TokenIdPercent: return PrefixOpError; | |
| 1615 | 1650 | case TokenIdBoolAnd: return PrefixOpAddressOf; |
| 1616 | 1651 | default: return PrefixOpInvalid; |
| 1617 | 1652 | } |
| ... | ... | @@ -2031,20 +2066,46 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda |
| 2031 | 2066 | } |
| 2032 | 2067 | |
| 2033 | 2068 | /* |
| 2034 | ReturnExpression : token(Return) option(Expression) | |
| 2069 | ReturnExpression : option("%" | "?") "return" option(Expression) | |
| 2035 | 2070 | */ |
| 2036 | 2071 | static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 2037 | Token *return_tok = &pc->tokens->at(*token_index); | |
| 2038 | if (return_tok->id == TokenIdKeywordReturn) { | |
| 2072 | Token *token = &pc->tokens->at(*token_index); | |
| 2073 | ||
| 2074 | ReturnKind kind; | |
| 2075 | ||
| 2076 | if (token->id == TokenIdPercent) { | |
| 2077 | Token *next_token = &pc->tokens->at(*token_index + 1); | |
| 2078 | if (next_token->id == TokenIdKeywordReturn) { | |
| 2079 | kind = ReturnKindError; | |
| 2080 | *token_index += 2; | |
| 2081 | } else if (mandatory) { | |
| 2082 | ast_invalid_token_error(pc, token); | |
| 2083 | } else { | |
| 2084 | return nullptr; | |
| 2085 | } | |
| 2086 | } else if (token->id == TokenIdMaybe) { | |
| 2087 | Token *next_token = &pc->tokens->at(*token_index + 1); | |
| 2088 | if (next_token->id == TokenIdKeywordReturn) { | |
| 2089 | kind = ReturnKindMaybe; | |
| 2090 | *token_index += 2; | |
| 2091 | } else if (mandatory) { | |
| 2092 | ast_invalid_token_error(pc, token); | |
| 2093 | } else { | |
| 2094 | return nullptr; | |
| 2095 | } | |
| 2096 | } else if (token->id == TokenIdKeywordReturn) { | |
| 2097 | kind = ReturnKindUnconditional; | |
| 2039 | 2098 | *token_index += 1; |
| 2040 | AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, return_tok); | |
| 2041 | node->data.return_expr.expr = ast_parse_expression(pc, token_index, false); | |
| 2042 | return node; | |
| 2043 | 2099 | } else if (mandatory) { |
| 2044 | ast_invalid_token_error(pc, return_tok); | |
| 2100 | ast_invalid_token_error(pc, token); | |
| 2045 | 2101 | } else { |
| 2046 | 2102 | return nullptr; |
| 2047 | 2103 | } |
| 2104 | ||
| 2105 | AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token); | |
| 2106 | node->data.return_expr.kind = kind; | |
| 2107 | node->data.return_expr.expr = ast_parse_expression(pc, token_index, false); | |
| 2108 | return node; | |
| 2048 | 2109 | } |
| 2049 | 2110 | |
| 2050 | 2111 | /* |
| ... | ... | @@ -2054,27 +2115,46 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token |
| 2054 | 2115 | Token *first_token = &pc->tokens->at(*token_index); |
| 2055 | 2116 | |
| 2056 | 2117 | VisibMod visib_mod; |
| 2118 | bool is_const; | |
| 2057 | 2119 | |
| 2058 | 2120 | if (first_token->id == TokenIdKeywordPub) { |
| 2059 | *token_index += 1; | |
| 2060 | visib_mod = VisibModPub; | |
| 2121 | Token *next_token = &pc->tokens->at(*token_index + 1); | |
| 2122 | if (next_token->id == TokenIdKeywordVar || | |
| 2123 | next_token->id == TokenIdKeywordConst) | |
| 2124 | { | |
| 2125 | visib_mod = VisibModPub; | |
| 2126 | is_const = (next_token->id == TokenIdKeywordConst); | |
| 2127 | *token_index += 2; | |
| 2128 | } else if (mandatory) { | |
| 2129 | ast_invalid_token_error(pc, next_token); | |
| 2130 | } else { | |
| 2131 | return nullptr; | |
| 2132 | } | |
| 2061 | 2133 | } else if (first_token->id == TokenIdKeywordExport) { |
| 2062 | *token_index += 1; | |
| 2063 | visib_mod = VisibModExport; | |
| 2134 | Token *next_token = &pc->tokens->at(*token_index + 1); | |
| 2135 | if (next_token->id == TokenIdKeywordVar || | |
| 2136 | next_token->id == TokenIdKeywordConst) | |
| 2137 | { | |
| 2138 | visib_mod = VisibModExport; | |
| 2139 | is_const = (next_token->id == TokenIdKeywordConst); | |
| 2140 | *token_index += 2; | |
| 2141 | } else if (mandatory) { | |
| 2142 | ast_invalid_token_error(pc, next_token); | |
| 2143 | } else { | |
| 2144 | return nullptr; | |
| 2145 | } | |
| 2064 | 2146 | } else if (first_token->id == TokenIdKeywordVar || |
| 2065 | 2147 | first_token->id == TokenIdKeywordConst) |
| 2066 | 2148 | { |
| 2067 | 2149 | visib_mod = VisibModPrivate; |
| 2150 | is_const = (first_token->id == TokenIdKeywordConst); | |
| 2151 | *token_index += 1; | |
| 2068 | 2152 | } else if (mandatory) { |
| 2069 | 2153 | ast_invalid_token_error(pc, first_token); |
| 2070 | 2154 | } else { |
| 2071 | 2155 | return nullptr; |
| 2072 | 2156 | } |
| 2073 | 2157 | |
| 2074 | Token *var_or_const_tok = &pc->tokens->at(*token_index); | |
| 2075 | bool is_const = (var_or_const_tok->id == TokenIdKeywordConst); | |
| 2076 | *token_index += 1; | |
| 2077 | ||
| 2078 | 2158 | AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, first_token); |
| 2079 | 2159 | |
| 2080 | 2160 | node->data.variable_declaration.is_const = is_const; |
| ... | ... | @@ -2836,7 +2916,54 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { |
| 2836 | 2916 | } |
| 2837 | 2917 | |
| 2838 | 2918 | /* |
| 2839 | TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | StructDecl | VariableDeclaration | EnumDecl | |
| 2919 | ErrorValueDecl : option(FnVisibleMod) "%." "Symbol" | |
| 2920 | */ | |
| 2921 | static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, bool mandatory) { | |
| 2922 | Token *first_token = &pc->tokens->at(*token_index); | |
| 2923 | ||
| 2924 | VisibMod visib_mod; | |
| 2925 | ||
| 2926 | if (first_token->id == TokenIdKeywordPub) { | |
| 2927 | Token *next_token = &pc->tokens->at(*token_index + 1); | |
| 2928 | if (next_token->id == TokenIdPercentDot) { | |
| 2929 | visib_mod = VisibModPub; | |
| 2930 | *token_index += 2; | |
| 2931 | } else if (mandatory) { | |
| 2932 | ast_invalid_token_error(pc, next_token); | |
| 2933 | } else { | |
| 2934 | return nullptr; | |
| 2935 | } | |
| 2936 | } else if (first_token->id == TokenIdKeywordExport) { | |
| 2937 | Token *next_token = &pc->tokens->at(*token_index + 1); | |
| 2938 | if (next_token->id == TokenIdPercentDot) { | |
| 2939 | visib_mod = VisibModExport; | |
| 2940 | *token_index += 2; | |
| 2941 | } else if (mandatory) { | |
| 2942 | ast_invalid_token_error(pc, next_token); | |
| 2943 | } else { | |
| 2944 | return nullptr; | |
| 2945 | } | |
| 2946 | } else if (first_token->id == TokenIdPercentDot) { | |
| 2947 | visib_mod = VisibModPrivate; | |
| 2948 | *token_index += 1; | |
| 2949 | } else if (mandatory) { | |
| 2950 | ast_invalid_token_error(pc, first_token); | |
| 2951 | } else { | |
| 2952 | return nullptr; | |
| 2953 | } | |
| 2954 | ||
| 2955 | Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); | |
| 2956 | ast_eat_token(pc, token_index, TokenIdSemicolon); | |
| 2957 | ||
| 2958 | AstNode *node = ast_create_node(pc, NodeTypeErrorValueDecl, first_token); | |
| 2959 | node->data.error_value_decl.visib_mod = visib_mod; | |
| 2960 | ast_buf_from_token(pc, name_tok, &node->data.error_value_decl.name); | |
| 2961 | ||
| 2962 | return node; | |
| 2963 | } | |
| 2964 | ||
| 2965 | /* | |
| 2966 | TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl | |
| 2840 | 2967 | */ |
| 2841 | 2968 | static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) { |
| 2842 | 2969 | for (;;) { |
| ... | ... | @@ -2887,6 +3014,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis |
| 2887 | 3014 | continue; |
| 2888 | 3015 | } |
| 2889 | 3016 | |
| 3017 | AstNode *error_value_node = ast_parse_error_value_decl(pc, token_index, false); | |
| 3018 | if (error_value_node) { | |
| 3019 | top_level_decls->append(error_value_node); | |
| 3020 | continue; | |
| 3021 | } | |
| 3022 | ||
| 2890 | 3023 | return; |
| 2891 | 3024 | } |
| 2892 | 3025 | zig_unreachable(); |
src/tokenizer.cpp+6| ... | ... | @@ -584,6 +584,11 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 584 | 584 | end_token(&t); |
| 585 | 585 | t.state = TokenizeStateStart; |
| 586 | 586 | break; |
| 587 | case '.': | |
| 588 | t.cur_tok->id = TokenIdPercentDot; | |
| 589 | end_token(&t); | |
| 590 | t.state = TokenizeStateStart; | |
| 591 | break; | |
| 587 | 592 | default: |
| 588 | 593 | t.pos -= 1; |
| 589 | 594 | end_token(&t); |
| ... | ... | @@ -1092,6 +1097,7 @@ const char * token_name(TokenId id) { |
| 1092 | 1097 | case TokenIdDoubleQuestion: return "??"; |
| 1093 | 1098 | case TokenIdMaybeAssign: return "?="; |
| 1094 | 1099 | case TokenIdAtSign: return "@"; |
| 1100 | case TokenIdPercentDot: return "%."; | |
| 1095 | 1101 | } |
| 1096 | 1102 | return "(invalid token)"; |
| 1097 | 1103 | } |
src/tokenizer.hpp+1| ... | ... | @@ -91,6 +91,7 @@ enum TokenId { |
| 91 | 91 | TokenIdDoubleQuestion, |
| 92 | 92 | TokenIdMaybeAssign, |
| 93 | 93 | TokenIdAtSign, |
| 94 | TokenIdPercentDot, | |
| 94 | 95 | }; |
| 95 | 96 | |
| 96 | 97 | struct Token { |
std/std.zig+150-16| ... | ... | @@ -1,43 +1,177 @@ |
| 1 | 1 | import "syscall.zig"; |
| 2 | //import "errno.zig"; | |
| 2 | 3 | |
| 3 | 4 | pub const stdin_fileno : isize = 0; |
| 4 | 5 | pub const stdout_fileno : isize = 1; |
| 5 | 6 | pub const stderr_fileno : isize = 2; |
| 6 | 7 | |
| 7 | // TODO error handling | |
| 8 | pub fn os_get_random_bytes(buf: []u8) isize => { | |
| 9 | getrandom(buf.ptr, buf.len, 0) | |
| 8 | /* | |
| 9 | pub var stdin = InStream { | |
| 10 | .fd = stdin_fileno, | |
| 11 | }; | |
| 12 | ||
| 13 | pub var stdout = OutStream { | |
| 14 | .fd = stdout_fileno, | |
| 15 | .buffer = uninitialized, | |
| 16 | .index = 0, | |
| 17 | .buffered = true, | |
| 18 | }; | |
| 19 | ||
| 20 | pub var stderr = OutStream { | |
| 21 | .fd = stderr_fileno, | |
| 22 | .buffer = uninitialized, | |
| 23 | .index = 0, | |
| 24 | .buffered = false, | |
| 25 | }; | |
| 26 | ||
| 27 | pub %.Unexpected; | |
| 28 | pub %.DiskQuota; | |
| 29 | pub %.FileTooBig; | |
| 30 | pub %.SigInterrupt; | |
| 31 | pub %.Io; | |
| 32 | pub %.NoSpaceLeft; | |
| 33 | pub %.BadPerm; | |
| 34 | pub %.PipeFail; | |
| 35 | */ | |
| 36 | ||
| 37 | const buffer_size: u16 = 4 * 1024; | |
| 38 | const max_u64_base10_digits: isize = 20; | |
| 39 | ||
| 40 | /* | |
| 41 | pub struct OutStream { | |
| 42 | fd: isize, | |
| 43 | buffer: [buffer_size]u8, | |
| 44 | index: @typeof(buffer_size), | |
| 45 | buffered: bool, | |
| 46 | ||
| 47 | pub fn print_str(os: &OutStream, str: []const u8) %isize => { | |
| 48 | var src_bytes_left = str.len; | |
| 49 | var src_index: @typeof(str.len) = 0; | |
| 50 | const dest_space_left = os.buffer.len - index; | |
| 51 | ||
| 52 | while (src_bytes_left > 0) { | |
| 53 | const copy_amt = min_isize(dest_space_left, src_bytes_left); | |
| 54 | @memcpy(&buffer[os.index], &str[src_index], copy_amt); | |
| 55 | os.index += copy_amt; | |
| 56 | if (os.index == os.buffer.len) { | |
| 57 | %return os.flush(); | |
| 58 | } | |
| 59 | src_bytes_left -= copy_amt; | |
| 60 | } | |
| 61 | if (!os.buffered) { | |
| 62 | %return os.flush(); | |
| 63 | } | |
| 64 | return str.len; | |
| 65 | } | |
| 66 | ||
| 67 | pub fn print_u64(os: &OutStream, x: u64) %isize => { | |
| 68 | if (os.index + max_u64_base10_digits >= os.buffer.len) { | |
| 69 | %return os.flush(); | |
| 70 | } | |
| 71 | const amt_printed = buf_print_u64(buf[os.index...], x); | |
| 72 | os.index += amt_printed; | |
| 73 | ||
| 74 | if (!os.buffered) { | |
| 75 | %return os.flush(); | |
| 76 | } | |
| 77 | ||
| 78 | return amt_printed; | |
| 79 | } | |
| 80 | ||
| 81 | ||
| 82 | pub fn print_i64(os: &OutStream, x: i64) %isize => { | |
| 83 | if (os.index + max_u64_base10_digits >= os.buffer.len) { | |
| 84 | %return os.flush(); | |
| 85 | } | |
| 86 | const amt_printed = buf_print_i64(buf[os.index...], x); | |
| 87 | os.index += amt_printed; | |
| 88 | ||
| 89 | if (!os.buffered) { | |
| 90 | %return os.flush(); | |
| 91 | } | |
| 92 | ||
| 93 | return amt_printed; | |
| 94 | } | |
| 95 | ||
| 96 | ||
| 97 | pub fn flush(os: &OutStream) %void => { | |
| 98 | const amt_to_write = os.index; | |
| 99 | os.index = 0; | |
| 100 | switch (write(fd, os.buffer.ptr, amt_to_write)) { | |
| 101 | EINVAL => unreachable{}, | |
| 102 | EDQUOT => %.DiskQuota, | |
| 103 | EFBIG => %.FileTooBig, | |
| 104 | EINTR => %.SigInterrupt, | |
| 105 | EIO => %.Io, | |
| 106 | ENOSPC => %.NoSpaceLeft, | |
| 107 | EPERM => %.BadPerm, | |
| 108 | EPIPE => %.PipeFail, | |
| 109 | else => %.Unexpected, | |
| 110 | } | |
| 111 | } | |
| 112 | } | |
| 113 | ||
| 114 | pub struct InStream { | |
| 115 | fd: isize, | |
| 116 | ||
| 117 | pub fn readline(buf: []u8) %isize => { | |
| 118 | const amt_read = read(stdin_fileno, buf.ptr, buf.len); | |
| 119 | if (amt_read < 0) { | |
| 120 | switch (-amt_read) { | |
| 121 | EINVAL => unreachable{}, | |
| 122 | EFAULT => unreachable{}, | |
| 123 | EBADF => %.BadFd, | |
| 124 | EINTR => %.SigInterrupt, | |
| 125 | EIO => %.Io, | |
| 126 | else => %.Unexpected, | |
| 127 | } | |
| 128 | } | |
| 129 | return amt_read; | |
| 130 | } | |
| 131 | ||
| 132 | } | |
| 133 | ||
| 134 | pub fn os_get_random_bytes(buf: []u8) %void => { | |
| 135 | switch (getrandom(buf.ptr, buf.len, 0)) { | |
| 136 | EINVAL => unreachable{}, | |
| 137 | EFAULT => unreachable{}, | |
| 138 | EINTR => %.SigInterrupt, | |
| 139 | else => %.Unexpected, | |
| 140 | } | |
| 10 | 141 | } |
| 142 | */ | |
| 143 | ||
| 11 | 144 | |
| 12 | // TODO error handling | |
| 13 | // TODO handle buffering and flushing (mutex protected) | |
| 145 | // TODO remove this | |
| 14 | 146 | pub fn print_str(str: []const u8) isize => { |
| 15 | 147 | fprint_str(stdout_fileno, str) |
| 16 | 148 | } |
| 17 | 149 | |
| 18 | // TODO error handling | |
| 19 | // TODO handle buffering and flushing (mutex protected) | |
| 150 | // TODO remove this | |
| 20 | 151 | pub fn fprint_str(fd: isize, str: []const u8) isize => { |
| 21 | 152 | write(fd, str.ptr, str.len) |
| 22 | 153 | } |
| 23 | 154 | |
| 24 | // TODO handle buffering and flushing (mutex protected) | |
| 25 | // TODO error handling | |
| 155 | // TODO remove this | |
| 156 | pub fn os_get_random_bytes(buf: []u8) isize => { | |
| 157 | getrandom(buf.ptr, buf.len, 0) | |
| 158 | } | |
| 159 | ||
| 160 | // TODO remove this | |
| 26 | 161 | pub fn print_u64(x: u64) isize => { |
| 27 | 162 | var buf: [max_u64_base10_digits]u8; |
| 28 | 163 | const len = buf_print_u64(buf, x); |
| 29 | 164 | return write(stdout_fileno, buf.ptr, len); |
| 30 | 165 | } |
| 31 | 166 | |
| 32 | // TODO handle buffering and flushing (mutex protected) | |
| 33 | // TODO error handling | |
| 167 | // TODO remove this | |
| 34 | 168 | pub fn print_i64(x: i64) isize => { |
| 35 | 169 | var buf: [max_u64_base10_digits]u8; |
| 36 | 170 | const len = buf_print_i64(buf, x); |
| 37 | 171 | return write(stdout_fileno, buf.ptr, len); |
| 38 | 172 | } |
| 39 | 173 | |
| 40 | // TODO error handling | |
| 174 | // TODO remove this | |
| 41 | 175 | pub fn readline(buf: []u8, out_len: &isize) bool => { |
| 42 | 176 | const amt_read = read(stdin_fileno, buf.ptr, buf.len); |
| 43 | 177 | if (amt_read < 0) { |
| ... | ... | @@ -47,7 +181,8 @@ pub fn readline(buf: []u8, out_len: &isize) bool => { |
| 47 | 181 | return false; |
| 48 | 182 | } |
| 49 | 183 | |
| 50 | // TODO return ?u64 when we support returning struct byval | |
| 184 | ||
| 185 | // TODO return %u64 when we support errors | |
| 51 | 186 | pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => { |
| 52 | 187 | var x : u64 = 0; |
| 53 | 188 | |
| ... | ... | @@ -74,6 +209,7 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => { |
| 74 | 209 | } |
| 75 | 210 | |
| 76 | 211 | fn char_to_digit(c: u8) u8 => { |
| 212 | // TODO use switch with range | |
| 77 | 213 | if ('0' <= c && c <= '9') { |
| 78 | 214 | c - '0' |
| 79 | 215 | } else if ('A' <= c && c <= 'Z') { |
| ... | ... | @@ -85,8 +221,6 @@ fn char_to_digit(c: u8) u8 => { |
| 85 | 221 | } |
| 86 | 222 | } |
| 87 | 223 | |
| 88 | const max_u64_base10_digits: isize = 20; | |
| 89 | ||
| 90 | 224 | fn buf_print_i64(out_buf: []u8, x: i64) isize => { |
| 91 | 225 | if (x < 0) { |
| 92 | 226 | out_buf[0] = '-'; |
| ... | ... | @@ -112,7 +246,7 @@ fn buf_print_u64(out_buf: []u8, x: u64) isize => { |
| 112 | 246 | |
| 113 | 247 | const len = buf.len - index; |
| 114 | 248 | |
| 115 | @memcpy(out_buf.ptr, &buf[index], len); | |
| 249 | @memcpy(&out_buf[0], &buf[index], len); | |
| 116 | 250 | |
| 117 | 251 | return len; |
| 118 | 252 | } |