diff options
| -rw-r--r-- | doc/langref.md | 33 | ||||
| -rw-r--r-- | example/cat/main.zig | 34 | ||||
| -rw-r--r-- | example/hello_world/hello.zig | 1 | ||||
| -rw-r--r-- | src/all_types.hpp | 35 | ||||
| -rw-r--r-- | src/analyze.cpp | 172 | ||||
| -rw-r--r-- | src/codegen.cpp | 13 | ||||
| -rw-r--r-- | src/parser.cpp | 175 | ||||
| -rw-r--r-- | src/tokenizer.cpp | 6 | ||||
| -rw-r--r-- | src/tokenizer.hpp | 1 | ||||
| -rw-r--r-- | std/std.zig | 166 |
10 files changed, 548 insertions, 88 deletions
diff --git a/doc/langref.md b/doc/langref.md index ca69f8c516..f1092631ee 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -5,9 +5,11 @@ ``` Root : many(TopLevelDecl) "EOF" -TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration +TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl -VariableDeclaration : option(FnVisibleMod) ("var" | "const") "symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) +ErrorValueDecl : option(FnVisibleMod) "%." "Symbol" + +VariableDeclaration : option(FnVisibleMod) ("var" | "const") "Symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) ContainerDecl : many(Directive) option(FnVisibleMod) ("struct" | "enum") "Symbol" "{" many(StructMember) "}" @@ -77,7 +79,7 @@ ForExpression : "for" "(" "Symbol" "," Expression option("," "Symbol") ")" Expre BoolOrExpression : BoolAndExpression "||" BoolOrExpression | BoolAndExpression -ReturnExpression : "return" option(Expression) +ReturnExpression : option("%" | "?") "return" option(Expression) IfExpression : IfVarExpression | IfBoolExpression @@ -133,7 +135,7 @@ StructLiteralField : "." "Symbol" "=" Expression PrefixOp : "!" | "-" | "~" | "*" | ("&" option("const")) | "?" -PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression +PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("%." "Symbol") ArrayType : "[" option(Expression) "]" option("const") PrefixOpExpression @@ -148,7 +150,7 @@ KeywordLiteral : "true" | "false" | "null" | "break" | "continue" ``` x() x[] x.y -!x -x ~x *x &x ?x +!x -x ~x *x &x ?x %x x{} * / % + - @@ -199,12 +201,20 @@ c_ulonglong unsigned long long for ABI compatibility with C ### Boolean Type The boolean type has the name `bool` and represents either true or false. -### Function Types +### Function Type TODO -### Array Types -TODO -Also, are there slices? +### Fixed-Size Array Type + +Example: The string `"aoeu"` has type `[4]u8`. + +The size is known at compile time and is part of the type. + +### Slice Type + +A slice can be obtained with the slicing syntax: `array[start...end]` + +Example: `"aoeu"[0...2]` has type `[]u8`. ### Struct Types TODO @@ -213,10 +223,13 @@ TODO TODO ### Unreachable Type + The unreachable type has the name `unreachable`. TODO explanation ### Void Type -The void type has the name `void`. TODO explanation + +The void type has the name `void`. void types are zero bits and are omitted +from codegen. ## Expressions diff --git a/example/cat/main.zig b/example/cat/main.zig index b1dd3b4e0c..2016e8a9c9 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -3,50 +3,50 @@ export executable "cat"; import "std.zig"; // Things to do to make this work: -// * isize instead of usize for things // * var args printing -// * update std API -// * !void type +// * %void type // * defer -// * !return -// * !! operator -// * make main return !void -// * how to reference error values (!void).Invalid ? !Invalid ? -// * ~ is bool not, not ! +// * %return +// * %% operator +// * make main return %void +// * how to reference error values %.Invalid // * cast err type to string +// * update std API + +pub %.Invalid; -pub fn main(args: [][]u8) !void => { +pub fn main(args: [][]u8) %void => { const exe = args[0]; var catted_anything = false; for (arg, args[1...]) { if (arg == "-") { catted_anything = true; - !return cat_stream(stdin); + %return cat_stream(stdin); } else if (arg[0] == '-') { return usage(exe); } else { var is: InputStream; - is.open(arg, OpenReadOnly) !! (err) => { + is.open(arg, OpenReadOnly) %% (err) => { stderr.print("Unable to open file: {}", ([]u8])(err)); return err; } defer is.close(); catted_anything = true; - !return cat_stream(is); + %return cat_stream(is); } } - if (~catted_anything) { - !return cat_stream(stdin) + if (!catted_anything) { + %return cat_stream(stdin) } } -fn usage(exe: []u8) !void => { +fn usage(exe: []u8) %void => { stderr.print("Usage: {} [FILE]...\n", exe); - return !Invalid; + return %.Invalid; } -fn cat_stream(is: InputStream) !void => { +fn cat_stream(is: InputStream) %void => { var buf: [1024 * 4]u8; while (true) { diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index 6a0fc6283b..63b7a44d1b 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -3,6 +3,7 @@ export executable "hello"; import "std.zig"; pub fn main(args: [][]u8) i32 => { + //stderr.print_str("Hello, world!\n"); print_str("Hello, world!\n"); return 0; } diff --git a/src/all_types.hpp b/src/all_types.hpp index eb926dbf50..89619cd112 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -129,10 +129,12 @@ enum NodeType { NodeTypeDirective, NodeTypeReturnExpr, NodeTypeVariableDeclaration, + NodeTypeErrorValueDecl, NodeTypeBinOpExpr, NodeTypeNumberLiteral, NodeTypeStringLiteral, NodeTypeCharLiteral, + NodeTypeErrorLiteral, NodeTypeSymbol, NodeTypePrefixOpExpr, NodeTypeFnCallExpr, @@ -222,7 +224,14 @@ struct AstNodeBlock { Expr resolved_expr; }; +enum ReturnKind { + ReturnKindUnconditional, + ReturnKindMaybe, + ReturnKindError, +}; + struct AstNodeReturnExpr { + ReturnKind kind; // might be null in case of return void; AstNode *expr; @@ -243,6 +252,14 @@ struct AstNodeVariableDeclaration { Expr resolved_expr; }; +struct AstNodeErrorValueDecl { + VisibMod visib_mod; + Buf name; + + // populated by semantic analyzer + TopLevelDecl top_level_decl; +}; + enum BinOpType { BinOpTypeInvalid, BinOpTypeAssign, @@ -358,6 +375,7 @@ enum PrefixOp { PrefixOpConstAddressOf, PrefixOpDereference, PrefixOpMaybe, + PrefixOpError, }; struct AstNodePrefixOpExpr { @@ -564,6 +582,14 @@ struct AstNodeNumberLiteral { Expr resolved_expr; }; +struct AstNodeErrorLiteral { + Buf symbol; + + // populated by semantic analyzer + NumLitCodeGen codegen; + Expr resolved_expr; +}; + struct AstNodeStructValueField { Buf name; AstNode *expr; @@ -644,6 +670,7 @@ struct AstNode { AstNodeBlock block; AstNodeReturnExpr return_expr; AstNodeVariableDeclaration variable_declaration; + AstNodeErrorValueDecl error_value_decl; AstNodeBinOpExpr bin_op_expr; AstNodeExternBlock extern_block; AstNodeDirective directive; @@ -668,6 +695,7 @@ struct AstNode { AstNodeStringLiteral string_literal; AstNodeCharLiteral char_literal; AstNodeNumberLiteral number_literal; + AstNodeErrorLiteral error_literal; AstNodeContainerInitExpr container_init_expr; AstNodeStructValueField struct_val_field; AstNodeNullLiteral null_literal; @@ -738,6 +766,10 @@ struct TypeTableEntryMaybe { TypeTableEntry *child_type; }; +struct TypeTableEntryError { + TypeTableEntry *child_type; +}; + struct TypeTableEntryEnum { AstNode *decl_node; uint32_t field_count; @@ -778,6 +810,7 @@ enum TypeTableEntryId { TypeTableEntryIdStruct, TypeTableEntryIdNumberLiteral, TypeTableEntryIdMaybe, + TypeTableEntryIdError, TypeTableEntryIdEnum, TypeTableEntryIdFn, }; @@ -799,6 +832,7 @@ struct TypeTableEntry { TypeTableEntryStruct structure; TypeTableEntryNumLit num_lit; TypeTableEntryMaybe maybe; + TypeTableEntryError error; TypeTableEntryEnum enumeration; TypeTableEntryFn fn; } data; @@ -808,6 +842,7 @@ struct TypeTableEntry { TypeTableEntry *unknown_size_array_parent[2]; HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size; TypeTableEntry *maybe_parent; + TypeTableEntry *error_parent; }; struct ImporterInfo { diff --git a/src/analyze.cpp b/src/analyze.cpp index faac0aa699..810d6006fb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -43,7 +43,9 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeDirective: case NodeTypeReturnExpr: case NodeTypeVariableDeclaration: + case NodeTypeErrorValueDecl: case NodeTypeNumberLiteral: + case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeSymbol: @@ -104,6 +106,7 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { case TypeTableEntryIdNumberLiteral: case TypeTableEntryIdMaybe: case TypeTableEntryIdFn: + case TypeTableEntryIdError: // nothing to init break; case TypeTableEntryIdStruct: @@ -215,6 +218,57 @@ static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { } } +static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { + if (child_type->error_parent) { + return child_type->error_parent; + } else { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdError); + zig_panic("TODO get_error_type"); + // create a struct with a boolean whether this is the null value + assert(child_type->type_ref); + LLVMTypeRef elem_types[] = { + child_type->type_ref, + LLVMInt1Type(), + }; + entry->type_ref = LLVMStructType(elem_types, 2, false); + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); + entry->size_in_bits = child_type->size_in_bits + 8; + entry->align_in_bits = child_type->align_in_bits; + assert(child_type->di_type); + + + LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); + LLVMZigDIFile *di_file = nullptr; + unsigned line = 0; + entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, + LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name), + compile_unit_scope, di_file, line); + + LLVMZigDIType *di_element_types[] = { + LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), + "val", di_file, line, child_type->size_in_bits, child_type->align_in_bits, 0, 0, + child_type->di_type), + LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), + "maybe", di_file, line, 8, 8, 8, 0, + child_type->di_type), + }; + LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, + compile_unit_scope, + buf_ptr(&entry->name), + di_file, line, entry->size_in_bits, entry->align_in_bits, 0, + nullptr, di_element_types, 2, 0, nullptr, ""); + + LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); + entry->di_type = replacement_di_type; + + entry->data.maybe.child_type = child_type; + + child_type->maybe_parent = entry; + return entry; + } +} + static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) { auto existing_entry = child_type->arrays_by_size.maybe_get(array_size); @@ -922,6 +976,11 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode g->global_vars.append(var); break; } + case NodeTypeErrorValueDecl: + { + zig_panic("TODO resolve_top_level_decl NodeTypeErrorValueDecl"); + break; + } case NodeTypeUse: // nothing to do here break; @@ -937,6 +996,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeNumberLiteral: + case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: @@ -1005,6 +1065,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, case TypeTableEntryIdEnum: case TypeTableEntryIdMetaType: case TypeTableEntryIdFn: + case TypeTableEntryIdError: return false; case TypeTableEntryIdInt: if (is_num_lit_unsigned(num_lit)) { @@ -2263,6 +2324,12 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry } } +static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, + BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) +{ + zig_panic("TODO analyze_error_literal_expr"); +} + static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -3021,7 +3088,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo if (meta_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (meta_type->id == TypeTableEntryIdUnreachable) { - add_node_error(g, node, buf_create_from_str("maybe unreachable type not allowed")); + add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in maybe type")); return g->builtin_types.entry_invalid; } else { 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 return get_maybe_type(g, type_entry); } } + case PrefixOpError: + { + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); + + if (type_entry->id == TypeTableEntryIdInvalid) { + return type_entry; + } else if (type_entry->id == TypeTableEntryIdMetaType) { + TypeTableEntry *meta_type = resolve_type(g, expr_node); + if (meta_type->id == TypeTableEntryIdInvalid) { + return meta_type; + } else if (meta_type->id == TypeTableEntryIdUnreachable) { + add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in error type")); + return g->builtin_types.entry_invalid; + } else { + return resolve_expr_const_val_as_type(g, node, get_error_type(g, meta_type)); + } + } else if (type_entry->id == TypeTableEntryIdUnreachable) { + add_node_error(g, expr_node, buf_sprintf("unable to wrap unreachable in error type")); + return g->builtin_types.entry_invalid; + } else { + // TODO eval const expr + return get_error_type(g, type_entry); + } + + } } zig_unreachable(); } @@ -3099,6 +3191,37 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, return expected_type; } +static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + if (!context->fn_entry) { + add_node_error(g, node, buf_sprintf("return expression outside function definition")); + return g->builtin_types.entry_invalid; + } + + if (node->data.return_expr.kind != ReturnKindUnconditional) { + zig_panic("TODO analyze_return_expr conditional"); + } + + TypeTableEntry *expected_return_type = get_return_type(context); + TypeTableEntry *actual_return_type; + if (node->data.return_expr.expr) { + actual_return_type = analyze_expression(g, import, context, expected_return_type, node->data.return_expr.expr); + } else { + actual_return_type = g->builtin_types.entry_void; + } + + if (actual_return_type->id == TypeTableEntryIdUnreachable) { + // "return exit(0)" should just be "exit(0)". + add_node_error(g, node, buf_sprintf("returning is unreachable")); + actual_return_type = g->builtin_types.entry_invalid; + } + + resolve_type_compatibility(g, context, node, expected_return_type, actual_return_type); + + return g->builtin_types.entry_unreachable; +} + static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -3140,29 +3263,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, } case NodeTypeReturnExpr: - { - if (context->fn_entry) { - TypeTableEntry *expected_return_type = get_return_type(context); - TypeTableEntry *actual_return_type; - if (node->data.return_expr.expr) { - actual_return_type = analyze_expression(g, import, context, expected_return_type, node->data.return_expr.expr); - } else { - actual_return_type = g->builtin_types.entry_void; - } - - if (actual_return_type->id == TypeTableEntryIdUnreachable) { - // "return exit(0)" should just be "exit(0)". - add_node_error(g, node, buf_sprintf("returning is unreachable")); - actual_return_type = g->builtin_types.entry_invalid; - } - - resolve_type_compatibility(g, context, node, expected_return_type, actual_return_type); - } else { - add_node_error(g, node, buf_sprintf("return expression outside function definition")); - } - return_type = g->builtin_types.entry_unreachable; - break; - } + return_type = analyze_return_expr(g, import, context, expected_type, node); + break; case NodeTypeVariableDeclaration: analyze_variable_declaration(g, import, context, expected_type, node); return_type = g->builtin_types.entry_void; @@ -3236,6 +3338,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeNumberLiteral: return_type = analyze_number_literal_expr(g, import, context, expected_type, node); break; + case NodeTypeErrorLiteral: + return_type = analyze_error_literal_expr(g, import, context, expected_type, node); + break; case NodeTypeStringLiteral: if (node->data.string_literal.c) { return_type = g->builtin_types.entry_c_string_literal; @@ -3294,6 +3399,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeStructDecl: case NodeTypeStructField: case NodeTypeStructValueField: + case NodeTypeErrorValueDecl: zig_unreachable(); } assert(return_type); @@ -3411,6 +3517,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeExternBlock: case NodeTypeUse: case NodeTypeVariableDeclaration: + case NodeTypeErrorValueDecl: // already took care of these break; case NodeTypeDirective: @@ -3425,6 +3532,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeNumberLiteral: + case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: @@ -3457,6 +3565,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode { switch (node->type) { case NodeTypeNumberLiteral: + case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: @@ -3464,6 +3573,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: + case NodeTypeErrorValueDecl: // no dependencies on other top level declarations break; case NodeTypeSymbol: @@ -3758,6 +3868,10 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeUse: // already taken care of break; + case NodeTypeErrorValueDecl: + // error value declarations do not depend on other top level decls + resolve_top_level_decl(g, import, node); + break; case NodeTypeDirective: case NodeTypeParamDecl: case NodeTypeFnDecl: @@ -3769,6 +3883,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeNumberLiteral: + case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: @@ -3966,6 +4081,8 @@ Expr *get_resolved_expr(AstNode *node) { return &node->data.container_init_expr.resolved_expr; case NodeTypeNumberLiteral: return &node->data.number_literal.resolved_expr; + case NodeTypeErrorLiteral: + return &node->data.error_literal.resolved_expr; case NodeTypeStringLiteral: return &node->data.string_literal.resolved_expr; case NodeTypeBlock: @@ -4006,6 +4123,7 @@ Expr *get_resolved_expr(AstNode *node) { case NodeTypeStructDecl: case NodeTypeStructField: case NodeTypeStructValueField: + case NodeTypeErrorValueDecl: zig_unreachable(); } zig_unreachable(); @@ -4015,6 +4133,8 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) { switch (node->type) { case NodeTypeNumberLiteral: return &node->data.number_literal.codegen; + case NodeTypeErrorLiteral: + return &node->data.error_literal.codegen; case NodeTypeFnCallExpr: return &node->data.fn_call_expr.resolved_num_lit; case NodeTypeReturnExpr: @@ -4056,6 +4176,7 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) { case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeArrayType: + case NodeTypeErrorValueDecl: zig_unreachable(); } zig_unreachable(); @@ -4069,7 +4190,10 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { return &node->data.fn_proto.top_level_decl; case NodeTypeStructDecl: return &node->data.struct_decl.top_level_decl; + case NodeTypeErrorValueDecl: + return &node->data.error_value_decl.top_level_decl; case NodeTypeNumberLiteral: + case NodeTypeErrorLiteral: case NodeTypeReturnExpr: case NodeTypeBinOpExpr: case NodeTypePrefixOpExpr: diff --git a/src/codegen.cpp b/src/codegen.cpp index 64f7f24d68..25ca9bf54a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -829,6 +829,10 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { { zig_panic("TODO codegen PrefixOpMaybe"); } + case PrefixOpError: + { + zig_panic("TODO codegen PrefixOpError"); + } } zig_unreachable(); } @@ -1937,6 +1941,12 @@ static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *node) { return gen_number_literal_raw(g, node, codegen_num_lit, &node->data.number_literal); } +static LLVMValueRef gen_error_literal(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeErrorLiteral); + + zig_panic("TODO gen_error_literal"); +} + static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeSymbol); VariableTableEntry *variable = node->data.symbol_expr.variable; @@ -2070,6 +2080,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { return gen_asm_expr(g, node); case NodeTypeNumberLiteral: return gen_number_literal(g, node); + case NodeTypeErrorLiteral: + return gen_error_literal(g, node); case NodeTypeStringLiteral: { Buf *str = &node->data.string_literal.buf; @@ -2125,6 +2137,7 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { case NodeTypeArrayType: case NodeTypeSwitchProng: case NodeTypeSwitchRange: + case NodeTypeErrorValueDecl: zig_unreachable(); } zig_unreachable(); diff --git a/src/parser.cpp b/src/parser.cpp index 0dfb60a47a..aa60665389 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -63,6 +63,16 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpConstAddressOf: return "&const"; case PrefixOpDereference: return "*"; case PrefixOpMaybe: return "?"; + case PrefixOpError: return "%"; + } + zig_unreachable(); +} + +static const char *return_prefix_str(ReturnKind kind) { + switch (kind) { + case ReturnKindError: return "%"; + case ReturnKindMaybe: return "?"; + case ReturnKindUnconditional: return ""; } zig_unreachable(); } @@ -99,8 +109,12 @@ const char *node_type_str(NodeType node_type) { return "ReturnExpr"; case NodeTypeVariableDeclaration: return "VariableDeclaration"; + case NodeTypeErrorValueDecl: + return "ErrorValueDecl"; case NodeTypeNumberLiteral: return "NumberLiteral"; + case NodeTypeErrorLiteral: + return "ErrorLiteral"; case NodeTypeStringLiteral: return "StringLiteral"; case NodeTypeCharLiteral: @@ -214,10 +228,13 @@ void ast_print(AstNode *node, int indent) { break; } case NodeTypeReturnExpr: - fprintf(stderr, "%s\n", node_type_str(node->type)); - if (node->data.return_expr.expr) - ast_print(node->data.return_expr.expr, indent + 2); - break; + { + const char *prefix_str = return_prefix_str(node->data.return_expr.kind); + fprintf(stderr, "%s%s\n", prefix_str, node_type_str(node->type)); + if (node->data.return_expr.expr) + ast_print(node->data.return_expr.expr, indent + 2); + break; + } case NodeTypeVariableDeclaration: { Buf *name_buf = &node->data.variable_declaration.symbol; @@ -228,6 +245,12 @@ void ast_print(AstNode *node, int indent) { ast_print(node->data.variable_declaration.expr, indent + 2); break; } + case NodeTypeErrorValueDecl: + { + Buf *name_buf = &node->data.error_value_decl.name; + fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf)); + break; + } case NodeTypeExternBlock: { fprintf(stderr, "%s\n", node_type_str(node->type)); @@ -288,6 +311,11 @@ void ast_print(AstNode *node, int indent) { } break; } + case NodeTypeErrorLiteral: + { + fprintf(stderr, "%s '%s'", node_type_str(node->type), buf_ptr(&node->data.error_literal.symbol)); + break; + } case NodeTypeStringLiteral: { 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 } /* -PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | token(Symbol) | (token(AtSign) token(Symbol) FnCallExpression) | ArrayType | AsmExpression +PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("%." "Symbol") KeywordLiteral : token(True) | token(False) | token(Null) | token(Break) | token(Continue) */ 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 ast_buf_from_token(pc, dest_symbol, &node->data.goto_expr.name); return node; + } else if (token->id == TokenIdPercentDot) { + *token_index += 1; + Token *symbol_tok = ast_eat_token(pc, token_index, TokenIdSymbol); + AstNode *node = ast_create_node(pc, NodeTypeErrorLiteral, token); + ast_buf_from_token(pc, symbol_tok, &node->data.error_literal.symbol); + return node; } AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); @@ -1612,6 +1646,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdAmpersand: return PrefixOpAddressOf; case TokenIdStar: return PrefixOpDereference; case TokenIdMaybe: return PrefixOpMaybe; + case TokenIdPercent: return PrefixOpError; case TokenIdBoolAnd: return PrefixOpAddressOf; default: return PrefixOpInvalid; } @@ -2031,20 +2066,46 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda } /* -ReturnExpression : token(Return) option(Expression) +ReturnExpression : option("%" | "?") "return" option(Expression) */ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool mandatory) { - Token *return_tok = &pc->tokens->at(*token_index); - if (return_tok->id == TokenIdKeywordReturn) { + Token *token = &pc->tokens->at(*token_index); + + ReturnKind kind; + + if (token->id == TokenIdPercent) { + Token *next_token = &pc->tokens->at(*token_index + 1); + if (next_token->id == TokenIdKeywordReturn) { + kind = ReturnKindError; + *token_index += 2; + } else if (mandatory) { + ast_invalid_token_error(pc, token); + } else { + return nullptr; + } + } else if (token->id == TokenIdMaybe) { + Token *next_token = &pc->tokens->at(*token_index + 1); + if (next_token->id == TokenIdKeywordReturn) { + kind = ReturnKindMaybe; + *token_index += 2; + } else if (mandatory) { + ast_invalid_token_error(pc, token); + } else { + return nullptr; + } + } else if (token->id == TokenIdKeywordReturn) { + kind = ReturnKindUnconditional; *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, return_tok); - node->data.return_expr.expr = ast_parse_expression(pc, token_index, false); - return node; } else if (mandatory) { - ast_invalid_token_error(pc, return_tok); + ast_invalid_token_error(pc, token); } else { return nullptr; } + + AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token); + node->data.return_expr.kind = kind; + node->data.return_expr.expr = ast_parse_expression(pc, token_index, false); + return node; } /* @@ -2054,27 +2115,46 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token Token *first_token = &pc->tokens->at(*token_index); VisibMod visib_mod; + bool is_const; if (first_token->id == TokenIdKeywordPub) { - *token_index += 1; - visib_mod = VisibModPub; + Token *next_token = &pc->tokens->at(*token_index + 1); + if (next_token->id == TokenIdKeywordVar || + next_token->id == TokenIdKeywordConst) + { + visib_mod = VisibModPub; + is_const = (next_token->id == TokenIdKeywordConst); + *token_index += 2; + } else if (mandatory) { + ast_invalid_token_error(pc, next_token); + } else { + return nullptr; + } } else if (first_token->id == TokenIdKeywordExport) { - *token_index += 1; - visib_mod = VisibModExport; + Token *next_token = &pc->tokens->at(*token_index + 1); + if (next_token->id == TokenIdKeywordVar || + next_token->id == TokenIdKeywordConst) + { + visib_mod = VisibModExport; + is_const = (next_token->id == TokenIdKeywordConst); + *token_index += 2; + } else if (mandatory) { + ast_invalid_token_error(pc, next_token); + } else { + return nullptr; + } } else if (first_token->id == TokenIdKeywordVar || first_token->id == TokenIdKeywordConst) { visib_mod = VisibModPrivate; + is_const = (first_token->id == TokenIdKeywordConst); + *token_index += 1; } else if (mandatory) { ast_invalid_token_error(pc, first_token); } else { return nullptr; } - Token *var_or_const_tok = &pc->tokens->at(*token_index); - bool is_const = (var_or_const_tok->id == TokenIdKeywordConst); - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, first_token); node->data.variable_declaration.is_const = is_const; @@ -2836,7 +2916,54 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { } /* -TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | StructDecl | VariableDeclaration | EnumDecl +ErrorValueDecl : option(FnVisibleMod) "%." "Symbol" +*/ +static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, bool mandatory) { + Token *first_token = &pc->tokens->at(*token_index); + + VisibMod visib_mod; + + if (first_token->id == TokenIdKeywordPub) { + Token *next_token = &pc->tokens->at(*token_index + 1); + if (next_token->id == TokenIdPercentDot) { + visib_mod = VisibModPub; + *token_index += 2; + } else if (mandatory) { + ast_invalid_token_error(pc, next_token); + } else { + return nullptr; + } + } else if (first_token->id == TokenIdKeywordExport) { + Token *next_token = &pc->tokens->at(*token_index + 1); + if (next_token->id == TokenIdPercentDot) { + visib_mod = VisibModExport; + *token_index += 2; + } else if (mandatory) { + ast_invalid_token_error(pc, next_token); + } else { + return nullptr; + } + } else if (first_token->id == TokenIdPercentDot) { + visib_mod = VisibModPrivate; + *token_index += 1; + } else if (mandatory) { + ast_invalid_token_error(pc, first_token); + } else { + return nullptr; + } + + Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); + ast_eat_token(pc, token_index, TokenIdSemicolon); + + AstNode *node = ast_create_node(pc, NodeTypeErrorValueDecl, first_token); + node->data.error_value_decl.visib_mod = visib_mod; + ast_buf_from_token(pc, name_tok, &node->data.error_value_decl.name); + + return node; +} + +/* +TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl */ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) { for (;;) { @@ -2887,6 +3014,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis continue; } + AstNode *error_value_node = ast_parse_error_value_decl(pc, token_index, false); + if (error_value_node) { + top_level_decls->append(error_value_node); + continue; + } + return; } zig_unreachable(); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index f989cf39d7..432b079503 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -584,6 +584,11 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; + case '.': + t.cur_tok->id = TokenIdPercentDot; + end_token(&t); + t.state = TokenizeStateStart; + break; default: t.pos -= 1; end_token(&t); @@ -1092,6 +1097,7 @@ const char * token_name(TokenId id) { case TokenIdDoubleQuestion: return "??"; case TokenIdMaybeAssign: return "?="; case TokenIdAtSign: return "@"; + case TokenIdPercentDot: return "%."; } return "(invalid token)"; } diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index b25417b80f..ac71f25a19 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -91,6 +91,7 @@ enum TokenId { TokenIdDoubleQuestion, TokenIdMaybeAssign, TokenIdAtSign, + TokenIdPercentDot, }; struct Token { diff --git a/std/std.zig b/std/std.zig index 341b6fbcd8..2d8962ec3c 100644 --- a/std/std.zig +++ b/std/std.zig @@ -1,43 +1,177 @@ import "syscall.zig"; +//import "errno.zig"; pub const stdin_fileno : isize = 0; pub const stdout_fileno : isize = 1; pub const stderr_fileno : isize = 2; -// TODO error handling -pub fn os_get_random_bytes(buf: []u8) isize => { - getrandom(buf.ptr, buf.len, 0) +/* +pub var stdin = InStream { + .fd = stdin_fileno, +}; + +pub var stdout = OutStream { + .fd = stdout_fileno, + .buffer = uninitialized, + .index = 0, + .buffered = true, +}; + +pub var stderr = OutStream { + .fd = stderr_fileno, + .buffer = uninitialized, + .index = 0, + .buffered = false, +}; + +pub %.Unexpected; +pub %.DiskQuota; +pub %.FileTooBig; +pub %.SigInterrupt; +pub %.Io; +pub %.NoSpaceLeft; +pub %.BadPerm; +pub %.PipeFail; +*/ + +const buffer_size: u16 = 4 * 1024; +const max_u64_base10_digits: isize = 20; + +/* +pub struct OutStream { + fd: isize, + buffer: [buffer_size]u8, + index: @typeof(buffer_size), + buffered: bool, + + pub fn print_str(os: &OutStream, str: []const u8) %isize => { + var src_bytes_left = str.len; + var src_index: @typeof(str.len) = 0; + const dest_space_left = os.buffer.len - index; + + while (src_bytes_left > 0) { + const copy_amt = min_isize(dest_space_left, src_bytes_left); + @memcpy(&buffer[os.index], &str[src_index], copy_amt); + os.index += copy_amt; + if (os.index == os.buffer.len) { + %return os.flush(); + } + src_bytes_left -= copy_amt; + } + if (!os.buffered) { + %return os.flush(); + } + return str.len; + } + + pub fn print_u64(os: &OutStream, x: u64) %isize => { + if (os.index + max_u64_base10_digits >= os.buffer.len) { + %return os.flush(); + } + const amt_printed = buf_print_u64(buf[os.index...], x); + os.index += amt_printed; + + if (!os.buffered) { + %return os.flush(); + } + + return amt_printed; + } + + + pub fn print_i64(os: &OutStream, x: i64) %isize => { + if (os.index + max_u64_base10_digits >= os.buffer.len) { + %return os.flush(); + } + const amt_printed = buf_print_i64(buf[os.index...], x); + os.index += amt_printed; + + if (!os.buffered) { + %return os.flush(); + } + + return amt_printed; + } + + + pub fn flush(os: &OutStream) %void => { + const amt_to_write = os.index; + os.index = 0; + switch (write(fd, os.buffer.ptr, amt_to_write)) { + EINVAL => unreachable{}, + EDQUOT => %.DiskQuota, + EFBIG => %.FileTooBig, + EINTR => %.SigInterrupt, + EIO => %.Io, + ENOSPC => %.NoSpaceLeft, + EPERM => %.BadPerm, + EPIPE => %.PipeFail, + else => %.Unexpected, + } + } +} + +pub struct InStream { + fd: isize, + + pub fn readline(buf: []u8) %isize => { + const amt_read = read(stdin_fileno, buf.ptr, buf.len); + if (amt_read < 0) { + switch (-amt_read) { + EINVAL => unreachable{}, + EFAULT => unreachable{}, + EBADF => %.BadFd, + EINTR => %.SigInterrupt, + EIO => %.Io, + else => %.Unexpected, + } + } + return amt_read; + } + +} + +pub fn os_get_random_bytes(buf: []u8) %void => { + switch (getrandom(buf.ptr, buf.len, 0)) { + EINVAL => unreachable{}, + EFAULT => unreachable{}, + EINTR => %.SigInterrupt, + else => %.Unexpected, + } } +*/ + -// TODO error handling -// TODO handle buffering and flushing (mutex protected) +// TODO remove this pub fn print_str(str: []const u8) isize => { fprint_str(stdout_fileno, str) } -// TODO error handling -// TODO handle buffering and flushing (mutex protected) +// TODO remove this pub fn fprint_str(fd: isize, str: []const u8) isize => { write(fd, str.ptr, str.len) } -// TODO handle buffering and flushing (mutex protected) -// TODO error handling +// TODO remove this +pub fn os_get_random_bytes(buf: []u8) isize => { + getrandom(buf.ptr, buf.len, 0) +} + +// TODO remove this pub fn print_u64(x: u64) isize => { var buf: [max_u64_base10_digits]u8; const len = buf_print_u64(buf, x); return write(stdout_fileno, buf.ptr, len); } -// TODO handle buffering and flushing (mutex protected) -// TODO error handling +// TODO remove this pub fn print_i64(x: i64) isize => { var buf: [max_u64_base10_digits]u8; const len = buf_print_i64(buf, x); return write(stdout_fileno, buf.ptr, len); } -// TODO error handling +// TODO remove this pub fn readline(buf: []u8, out_len: &isize) bool => { const amt_read = read(stdin_fileno, buf.ptr, buf.len); if (amt_read < 0) { @@ -47,7 +181,8 @@ pub fn readline(buf: []u8, out_len: &isize) bool => { return false; } -// TODO return ?u64 when we support returning struct byval + +// TODO return %u64 when we support errors pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => { var x : u64 = 0; @@ -74,6 +209,7 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => { } fn char_to_digit(c: u8) u8 => { + // TODO use switch with range if ('0' <= c && c <= '9') { c - '0' } else if ('A' <= c && c <= 'Z') { @@ -85,8 +221,6 @@ fn char_to_digit(c: u8) u8 => { } } -const max_u64_base10_digits: isize = 20; - fn buf_print_i64(out_buf: []u8, x: i64) isize => { if (x < 0) { out_buf[0] = '-'; @@ -112,7 +246,7 @@ fn buf_print_u64(out_buf: []u8, x: u64) isize => { const len = buf.len - index; - @memcpy(out_buf.ptr, &buf[index], len); + @memcpy(&out_buf[0], &buf[index], len); return len; } |
