diff --git a/doc/langref.md b/doc/langref.md index 98ad619f63..f329d22b27 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -59,9 +59,13 @@ AsmInputItem : "[" "Symbol" "]" "String" "(" Expression ")" AsmClobbers: ":" list("String", ",") -UnwrapMaybeExpression : BoolOrExpression "??" BoolOrExpression | BoolOrExpression +UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression -AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression +UnwrapMaybe : "??" BoolOrExpression + +UnwrapError : "%%" option("|" "Symbol" "|") BoolOrExpression + +AssignmentExpression : UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression AssignmentOperator : "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" @@ -161,7 +165,7 @@ x{} == != < > <= >= && || -?? +?? %% = *= /= %= += -= <<= >>= &= ^= |= &&= ||= ``` diff --git a/example/cat/main.zig b/example/cat/main.zig index 6d82b3b24e..2651f0dc3f 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -4,9 +4,7 @@ import "std.zig"; // Things to do to make this work: // * var args printing -// * update std API // * defer -// * %return // * %% binary operator // * %% prefix operator // * cast err type to string diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 1edbda5c69..10295718df 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -8,7 +8,7 @@ pub fn main(args: [][]u8) %void => { var seed : u32; const seed_bytes = (&u8)(&seed)[0...4]; - os_get_random_bytes(seed_bytes); + os_get_random_bytes(seed_bytes) %% unreachable{}; var rand = rand_new(seed); @@ -18,13 +18,16 @@ pub fn main(args: [][]u8) %void => { stderr.print_str("\nGuess a number between 1 and 100: "); var line_buf : [20]u8; - // TODO print error message instead of returning - const line_len = %return stdin.readline(line_buf); + const line_len = stdin.read(line_buf) %% |err| { + stderr.print_str("Unable to read from stdin.\n"); + return err; + }; - var guess : u64; - if (parse_u64(line_buf[0...line_len - 1], 10, &guess)) { - stderr.print_str("Invalid number format.\n"); - } else if (guess > answer) { + const guess = parse_u64(line_buf[0...line_len - 1], 10) %% { + stderr.print_str("Invalid number.\n"); + continue; + }; + if (guess > answer) { stderr.print_str("Guess lower.\n"); } else if (guess < answer) { stderr.print_str("Guess higher.\n"); diff --git a/src/all_types.hpp b/src/all_types.hpp index 8ab24f33d2..5218b59d0c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -130,6 +130,7 @@ enum NodeType { NodeTypeVariableDeclaration, NodeTypeErrorValueDecl, NodeTypeBinOpExpr, + NodeTypeUnwrapErrorExpr, NodeTypeNumberLiteral, NodeTypeStringLiteral, NodeTypeCharLiteral, @@ -310,6 +311,16 @@ struct AstNodeBinOpExpr { Expr resolved_expr; }; +struct AstNodeUnwrapErrorExpr { + AstNode *op1; + AstNode *symbol; // can be null + AstNode *op2; + + // populated by semantic analyzer: + Expr resolved_expr; + VariableTableEntry *var; +}; + enum CastOp { CastOpNoCast, // signifies the function call expression is not a cast CastOpNoop, // fn call expr is a cast, but does nothing @@ -684,6 +695,7 @@ struct AstNode { AstNodeVariableDeclaration variable_declaration; AstNodeErrorValueDecl error_value_decl; AstNodeBinOpExpr bin_op_expr; + AstNodeUnwrapErrorExpr unwrap_err_expr; AstNodeExternBlock extern_block; AstNodeDirective directive; AstNodePrefixOpExpr prefix_op_expr; diff --git a/src/analyze.cpp b/src/analyze.cpp index 6a81ad8047..29866ffa38 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -28,6 +28,8 @@ static AstNode *first_executing_node(AstNode *node) { return first_executing_node(node->data.fn_call_expr.fn_ref_expr); case NodeTypeBinOpExpr: return first_executing_node(node->data.bin_op_expr.op1); + case NodeTypeUnwrapErrorExpr: + return first_executing_node(node->data.unwrap_err_expr.op1); case NodeTypeArrayAccessExpr: return first_executing_node(node->data.array_access_expr.array_ref_expr); case NodeTypeSliceExpr: @@ -1076,6 +1078,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeRoot: case NodeTypeBlock: case NodeTypeBinOpExpr: + case NodeTypeUnwrapErrorExpr: case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: @@ -2502,6 +2505,38 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Block return variable_entry; } +static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *import, + BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node) +{ + AstNode *op1 = node->data.unwrap_err_expr.op1; + AstNode *op2 = node->data.unwrap_err_expr.op2; + AstNode *var_node = node->data.unwrap_err_expr.symbol; + + TypeTableEntry *lhs_type = analyze_expression(g, import, parent_context, nullptr, op1); + if (lhs_type->id == TypeTableEntryIdInvalid) { + return lhs_type; + } else if (lhs_type->id == TypeTableEntryIdErrorUnion) { + TypeTableEntry *child_type = lhs_type->data.error.child_type; + BlockContext *child_context; + if (var_node) { + child_context = new_block_context(node, parent_context); + Buf *var_name = &var_node->data.symbol_expr.symbol; + node->data.unwrap_err_expr.var = add_local_var(g, var_node, child_context, var_name, + g->builtin_types.entry_pure_error, true); + } else { + child_context = parent_context; + } + + analyze_expression(g, import, child_context, child_type, op2); + return child_type; + } else { + add_node_error(g, op1, + buf_sprintf("expected error type, got '%s'", buf_ptr(&lhs_type->name))); + return g->builtin_types.entry_invalid; + } +} + + static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *source_node, AstNodeVariableDeclaration *variable_declaration, @@ -3845,7 +3880,9 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeBinOpExpr: return_type = analyze_bin_op_expr(g, import, context, expected_type, node); break; - + case NodeTypeUnwrapErrorExpr: + return_type = analyze_unwrap_error_expr(g, import, context, expected_type, node); + break; case NodeTypeFnCallExpr: return_type = analyze_fn_call_expr(g, import, context, expected_type, node); break; @@ -4035,6 +4072,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeRoot: case NodeTypeBlock: case NodeTypeBinOpExpr: + case NodeTypeUnwrapErrorExpr: case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: @@ -4101,6 +4139,10 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode collect_expr_decl_deps(g, import, node->data.bin_op_expr.op1, decl_node); collect_expr_decl_deps(g, import, node->data.bin_op_expr.op2, decl_node); break; + case NodeTypeUnwrapErrorExpr: + collect_expr_decl_deps(g, import, node->data.unwrap_err_expr.op1, decl_node); + collect_expr_decl_deps(g, import, node->data.unwrap_err_expr.op2, decl_node); + break; case NodeTypeReturnExpr: collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node); break; @@ -4388,6 +4430,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeRoot: case NodeTypeBlock: case NodeTypeBinOpExpr: + case NodeTypeUnwrapErrorExpr: case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: @@ -4582,6 +4625,8 @@ Expr *get_resolved_expr(AstNode *node) { return &node->data.return_expr.resolved_expr; case NodeTypeBinOpExpr: return &node->data.bin_op_expr.resolved_expr; + case NodeTypeUnwrapErrorExpr: + return &node->data.unwrap_err_expr.resolved_expr; case NodeTypePrefixOpExpr: return &node->data.prefix_op_expr.resolved_expr; case NodeTypeFnCallExpr: @@ -4669,6 +4714,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeNumberLiteral: case NodeTypeReturnExpr: case NodeTypeBinOpExpr: + case NodeTypeUnwrapErrorExpr: case NodeTypePrefixOpExpr: case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: @@ -4747,10 +4793,30 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) { } bool handle_is_ptr(TypeTableEntry *type_entry) { - return type_entry->id == TypeTableEntryIdStruct || - (type_entry->id == TypeTableEntryIdEnum && type_entry->data.enumeration.gen_field_count != 0) || - type_entry->id == TypeTableEntryIdMaybe || - type_entry->id == TypeTableEntryIdArray || - (type_entry->id == TypeTableEntryIdErrorUnion && type_entry->data.error.child_type->size_in_bits > 0); + switch (type_entry->id) { + case TypeTableEntryIdInvalid: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdUndefLit: + zig_unreachable(); + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdPointer: + case TypeTableEntryIdPureError: + case TypeTableEntryIdFn: + return false; + case TypeTableEntryIdArray: + case TypeTableEntryIdStruct: + case TypeTableEntryIdMaybe: + return true; + case TypeTableEntryIdErrorUnion: + return type_entry->data.error.child_type->size_in_bits > 0; + case TypeTableEntryIdEnum: + return type_entry->data.enumeration.gen_field_count != 0; + } + zig_unreachable(); } - diff --git a/src/codegen.cpp b/src/codegen.cpp index 3497bbdee2..b86f3b3b5a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1255,6 +1255,78 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { zig_unreachable(); } +static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeUnwrapErrorExpr); + + AstNode *op1 = node->data.unwrap_err_expr.op1; + AstNode *op2 = node->data.unwrap_err_expr.op2; + VariableTableEntry *var = node->data.unwrap_err_expr.var; + + LLVMValueRef expr_val = gen_expr(g, op1); + TypeTableEntry *expr_type = get_expr_type(op1); + TypeTableEntry *op2_type = get_expr_type(op2); + assert(expr_type->id == TypeTableEntryIdErrorUnion); + TypeTableEntry *child_type = expr_type->data.error.child_type; + LLVMValueRef err_val; + add_debug_source_node(g, node); + if (handle_is_ptr(expr_type)) { + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, ""); + err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); + } else { + err_val = expr_val; + } + LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref); + LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk"); + LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError"); + LLVMBasicBlockRef end_block; + bool err_reachable = op2_type->id != TypeTableEntryIdUnreachable; + bool have_end_block = err_reachable && (child_type->size_in_bits > 0); + if (have_end_block) { + end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrEnd"); + } + + LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); + + LLVMPositionBuilderAtEnd(g->builder, err_block); + if (var) { + LLVMBuildStore(g->builder, err_val, var->value_ref); + } + LLVMValueRef err_result = gen_expr(g, op2); + add_debug_source_node(g, node); + if (have_end_block) { + LLVMBuildBr(g->builder, end_block); + } else if (err_reachable) { + LLVMBuildBr(g->builder, ok_block); + } + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + if (child_type->size_in_bits == 0) { + return nullptr; + } + LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, ""); + LLVMValueRef child_val; + if (handle_is_ptr(child_type)) { + child_val = child_val_ptr; + } else { + child_val = LLVMBuildLoad(g->builder, child_val_ptr, ""); + } + + if (!have_end_block) { + return child_val; + } + + LLVMBuildBr(g->builder, end_block); + + LLVMPositionBuilderAtEnd(g->builder, end_block); + LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(err_result), ""); + LLVMValueRef incoming_values[2] = {child_val, err_result}; + LLVMBasicBlockRef incoming_blocks[2] = {ok_block, err_block}; + LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); + return phi; +} + static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) { TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.src_return_type; if (handle_is_ptr(return_type)) { @@ -2047,6 +2119,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { switch (node->type) { case NodeTypeBinOpExpr: return gen_bin_op_expr(g, node); + case NodeTypeUnwrapErrorExpr: + return gen_unwrap_err_expr(g, node); case NodeTypeReturnExpr: return gen_return_expr(g, node); case NodeTypeVariableDeclaration: diff --git a/src/parser.cpp b/src/parser.cpp index 523ad1a891..425651a4a2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -95,6 +95,8 @@ const char *node_type_str(NodeType node_type) { return "Block"; case NodeTypeBinOpExpr: return "BinOpExpr"; + case NodeTypeUnwrapErrorExpr: + return "UnwrapErrorExpr"; case NodeTypeFnCallExpr: return "FnCallExpr"; case NodeTypeArrayAccessExpr: @@ -273,6 +275,14 @@ void ast_print(AstNode *node, int indent) { ast_print(node->data.bin_op_expr.op1, indent + 2); ast_print(node->data.bin_op_expr.op2, indent + 2); break; + case NodeTypeUnwrapErrorExpr: + fprintf(stderr, "%s\n", node_type_str(node->type)); + ast_print(node->data.unwrap_err_expr.op1, indent + 2); + if (node->data.unwrap_err_expr.symbol) { + ast_print(node->data.unwrap_err_expr.symbol, indent + 2); + } + ast_print(node->data.unwrap_err_expr.op2, indent + 2); + break; case NodeTypeFnCallExpr: fprintf(stderr, "%s\n", node_type_str(node->type)); ast_print(node->data.fn_call_expr.fn_ref_expr, indent + 2); @@ -964,7 +974,7 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory); static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory); static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory); -static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory); +static AstNode *ast_parse_unwrap_expr(ParseContext *pc, int *token_index, bool mandatory); static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory); static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { @@ -1032,7 +1042,7 @@ static void ast_parse_directives(ParseContext *pc, int *token_index, } /* -ParamDecl : option(token(NoAlias)) token(Symbol) token(Colon) UnwrapMaybeExpression | token(Ellipsis) +ParamDecl : option("noalias") "Symbol" ":" PrefixOpExpression | "..." */ static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { Token *first_token = &pc->tokens->at(*token_index); @@ -1154,7 +1164,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool } /* -ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) UnwrapMaybeExpression +ArrayType : "[" option(Expression) "]" option("const") PrefixOpExpression */ static AstNode *ast_parse_array_type_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *l_bracket = &pc->tokens->at(*token_index); @@ -1207,7 +1217,7 @@ static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode } /* -AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) UnwrapMaybeExpression token(RParen) +AsmOutputItem : "[" "Symbol" "]" "String" "(" ("Symbol" | "->" PrefixOpExpression) ")" */ static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) { ast_eat_token(pc, token_index, TokenIdLBracket); @@ -2132,7 +2142,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m } /* -VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) UnwrapMaybeExpression option(token(Eq) Expression)) +VariableDeclaration : option(FnVisibleMod) ("var" | "const") "Symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) */ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *first_token = &pc->tokens->at(*token_index); @@ -2454,38 +2464,55 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool manda } /* -UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression +UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression +UnwrapMaybe : "??" BoolOrExpression +UnwrapError : "%%" option("|" "Symbol" "|") BoolOrExpression */ -// this is currently the first child expression of assignment -static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory) { +static AstNode *ast_parse_unwrap_expr(ParseContext *pc, int *token_index, bool mandatory) { AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); if (!lhs) return nullptr; Token *token = &pc->tokens->at(*token_index); - if (token->id != TokenIdDoubleQuestion) { + if (token->id == TokenIdDoubleQuestion) { + *token_index += 1; + + AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true); + + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); + node->data.bin_op_expr.op1 = lhs; + node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; + node->data.bin_op_expr.op2 = rhs; + + normalize_parent_ptrs(node); + return node; + } else if (token->id == TokenIdPercentPercent) { + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypeUnwrapErrorExpr, token); + node->data.unwrap_err_expr.op1 = lhs; + + Token *maybe_bar_tok = &pc->tokens->at(*token_index); + if (maybe_bar_tok->id == TokenIdBinOr) { + *token_index += 1; + node->data.unwrap_err_expr.symbol = ast_parse_symbol(pc, token_index); + ast_eat_token(pc, token_index, TokenIdBinOr); + } + node->data.unwrap_err_expr.op2 = ast_parse_expression(pc, token_index, true); + + normalize_parent_ptrs(node); + return node; + } else { return lhs; } - - *token_index += 1; - - AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true); - - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = lhs; - node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; - node->data.bin_op_expr.op2 = rhs; - - normalize_parent_ptrs(node); - return node; } /* -AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression +AssignmentExpression : UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression */ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) { - AstNode *lhs = ast_parse_unwrap_maybe_expr(pc, token_index, mandatory); + AstNode *lhs = ast_parse_unwrap_expr(pc, token_index, mandatory); if (!lhs) return nullptr; @@ -2494,7 +2521,7 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand if (ass_op == BinOpTypeInvalid) return lhs; - AstNode *rhs = ast_parse_unwrap_maybe_expr(pc, token_index, true); + AstNode *rhs = ast_parse_unwrap_expr(pc, token_index, true); AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = lhs; @@ -2646,7 +2673,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato } /* -FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(UnwrapMaybeExpression) +FnProto : many(Directive) option(FnVisibleMod) "fn" "Symbol" ParamDeclList option(PrefixOpExpression) */ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory) { Token *first_token = &pc->tokens->at(*token_index); @@ -3164,6 +3191,11 @@ void normalize_parent_ptrs(AstNode *node) { set_field(&node->data.bin_op_expr.op1); set_field(&node->data.bin_op_expr.op2); break; + case NodeTypeUnwrapErrorExpr: + set_field(&node->data.unwrap_err_expr.op1); + set_field(&node->data.unwrap_err_expr.symbol); + set_field(&node->data.unwrap_err_expr.op2); + break; case NodeTypeNumberLiteral: // none break; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 97186a231c..0f264c5691 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -593,6 +593,11 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; + case '%': + t.cur_tok->id = TokenIdPercentPercent; + end_token(&t); + t.state = TokenizeStateStart; + break; default: t.pos -= 1; end_token(&t); @@ -1097,6 +1102,7 @@ const char * token_name(TokenId id) { case TokenIdBitShiftRight: return ">>"; case TokenIdSlash: return "/"; case TokenIdPercent: return "%"; + case TokenIdPercentPercent: return "%%"; case TokenIdDot: return "."; case TokenIdEllipsis: return "..."; case TokenIdMaybe: return "?"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 0da25cfe0d..da8f3e5aac 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -87,6 +87,7 @@ enum TokenId { TokenIdBitShiftRight, TokenIdSlash, TokenIdPercent, + TokenIdPercentPercent, TokenIdDot, TokenIdEllipsis, TokenIdMaybe, diff --git a/std/std.zig b/std/std.zig index 83ae7d9f57..9a32387b53 100644 --- a/std/std.zig +++ b/std/std.zig @@ -130,7 +130,7 @@ pub struct OutStream { pub struct InStream { fd: isize, - pub fn readline(is: &InStream, buf: []u8) %isize => { + pub fn read(is: &InStream, buf: []u8) %isize => { const amt_read = read(is.fd, buf.ptr, buf.len); if (amt_read < 0) { return switch (-amt_read) { @@ -144,7 +144,6 @@ pub struct InStream { } return amt_read; } - } pub fn os_get_random_bytes(buf: []u8) %void => { @@ -160,30 +159,31 @@ pub fn os_get_random_bytes(buf: []u8) %void => { } -// TODO return %u64 when we support errors -pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => { +pub error InvalidChar; +pub error Overflow; + +pub fn parse_u64(buf: []u8, radix: u8) %u64 => { var x : u64 = 0; for (c, buf) { const digit = char_to_digit(c); if (digit > radix) { - return true; + return error.InvalidChar; } // x *= radix if (@mul_with_overflow(u64, x, radix, &x)) { - return true; + return error.Overflow; } // x += digit if (@add_with_overflow(u64, x, digit, &x)) { - return true; + return error.Overflow; } } - *result = x; - return false; + return x; } fn char_to_digit(c: u8) u8 => {