add ?? maybe unwrapping binary operator

add null literal
fix number literal / maybe interactions
master
Andrew Kelley 2016-01-07 03:23:38 -07:00
parent 9b9fd5ad23
commit a3c97081ca
14 changed files with 287 additions and 23 deletions

View File

@ -120,6 +120,7 @@ set(ZIG_STD_SRC
"${CMAKE_SOURCE_DIR}/std/builtin.zig"
"${CMAKE_SOURCE_DIR}/std/std.zig"
"${CMAKE_SOURCE_DIR}/std/syscall.zig"
"${CMAKE_SOURCE_DIR}/std/errno.zig"
"${CMAKE_SOURCE_DIR}/std/rand.zig"
)

View File

@ -98,7 +98,9 @@ AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token
AsmClobbers: token(Colon) list(token(String), token(Comma))
AssignmentExpression : BoolOrExpression AssignmentOperator BoolOrExpression | BoolOrExpression
UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression
AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression
AssignmentOperator : token(Eq) | token(TimesEq) | token(DivEq) | token(ModEq) | token(PlusEq) | token(MinusEq) | token(BitShiftLeftEq) | token(BitShiftRightEq) | token(BitAndEq) | token(BitXorEq) | token(BitOrEq) | token(BoolAndEq) | token(BoolOrEq)
@ -166,7 +168,7 @@ Goto: token(Goto) token(Symbol)
GroupedExpression : token(LParen) Expression token(RParen)
KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False)
KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null)
```
## Operator Precedence
@ -184,6 +186,7 @@ as
== != < > <= >=
&&
||
??
= *= /= %= += -= <<= >>= &= ^= |= &&= ||=
```
@ -192,7 +195,7 @@ as
### Characters and Strings
| Example | Characters | Escapes | Null Term | Type
---------------------------------------------------------------------------------
----------------|----------|-------------|----------------|-----------|----------
Byte | 'H' | All ASCII | Byte | No | u8
UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5; u8]
UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | *const u8
@ -200,7 +203,7 @@ as
### Byte Escapes
| Name
-----------------------------------------------
------|----------------------------------------
\x7F | 8-bit character code (exactly 2 digits)
\n | Newline
\r | Carriage return
@ -213,13 +216,13 @@ as
### Unicode Escapes
| Name
----------------------------------------------------------
----------|-----------------------------------------------
\u{7FFF} | 24-bit Unicode character code (up to 6 digits)
### Numbers
Number literals | Example | Exponentiation
--------------------------------------------------
--------------------|-------------|---------------
Decimal integer | 98222 | N/A
Hex integer | 0xff | N/A
Octal integer | 0o77 | N/A

View File

@ -14,6 +14,7 @@ syn keyword zigStatement goto break return continue asm
syn keyword zigConditional if else match
syn keyword zigRepeat while for
syn keyword zigConstant null
syn keyword zigKeyword fn unreachable use void
syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string
@ -28,6 +29,12 @@ syn match zigHexNumber display "\<0x[a-fA-F0-9_]\+\%([iu]\%(size\|8\|16\|32\|64\
syn match zigOctNumber display "\<0o[0-7_]\+\%([iu]\%(size\|8\|16\|32\|64\)\)\="
syn match zigBinNumber display "\<0b[01_]\+\%([iu]\%(size\|8\|16\|32\|64\)\)\="
syn match zigCharacterInvalid display contained /b\?'\zs[\n\r\t']\ze'/
syn match zigCharacterInvalidUnicode display contained /b'\zs[^[:cntrl:][:graph:][:alnum:][:space:]]\ze'/
syn match zigCharacter /b'\([^\\]\|\\\(.\|x\x\{2}\)\)'/ contains=zigEscape,zigEscapeError,zigCharacterInvalid,zigCharacterInvalidUnicode
syn match zigCharacter /'\([^\\]\|\\\(.\|x\x\{2}\|u\x\{4}\|U\x\{8}\|u{\x\{1,6}}\)\)'/ contains=zigEscape,zigEscapeUnicode,zigEscapeError,zigCharacterInvalid
syn match zigShebang /\%^#![^[].*/
syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
@ -64,6 +71,9 @@ hi def link zigCommentBlockDoc zigCommentLineDoc
hi def link zigTodo Todo
hi def link zigStringContinuation Special
hi def link zigString String
hi def link zigCharacterInvalid Error
hi def link zigCharacterInvalidUnicode zigCharacterInvalid
hi def link zigCharacter Character
hi def link zigEscape Special
hi def link zigEscapeUnicode zigEscape
hi def link zigEscapeError Error

View File

@ -14,7 +14,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
var err : isize;
if ({err = os_get_random_bytes(&seed as &u8, #sizeof(u32)); err != #sizeof(u32)}) {
// TODO full error message
fprint_str(stderr_fileno, "unable to get random bytes");
fprint_str(stderr_fileno, "unable to get random bytes\n");
return 1;
}
@ -27,11 +27,14 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
print_u64(answer);
print_str("\n");
return 0;
/*
while (true) {
const line = readline("\nGuess a number between 1 and 100: ");
print_str("\nGuess a number between 1 and 100: ");
var line_buf : [20]u8;
const line = readline(line_buf) ?? {
// TODO full error message
fprint_str(stderr_fileno, "unable to read input\n");
return 1;
};
if (const guess ?= parse_u64(line)) {
if (guess > answer) {
@ -46,5 +49,4 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
print_str("Invalid number format.\n");
}
}
*/
}

View File

@ -15,5 +15,21 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
print_str("x is none\n");
}
const next_x : ?i32 = null;
const z = next_x ?? 1234;
if (z != 1234) {
print_str("BAD\n");
}
const final_x : ?i32 = 13;
const num = final_x ?? unreachable;
if (num != 13) {
print_str("BAD\n");
}
return 0;
}

View File

@ -48,6 +48,7 @@ static AstNode *first_executing_node(AstNode *node) {
case NodeTypeUse:
case NodeTypeVoid:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeLabel:
@ -358,6 +359,7 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
case BinOpTypeSub:
case BinOpTypeMult:
case BinOpTypeDiv:
case BinOpTypeUnwrapMaybe:
return g->builtin_types.entry_invalid;
case BinOpTypeInvalid:
case BinOpTypeAssign:
@ -388,6 +390,8 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
case NodeTypeBoolLiteral:
out_number_literal->data.x_uint = node->data.bool_literal ? 1 : 0;
return node->codegen_node->expr_node.type_entry;
case NodeTypeNullLiteral:
return node->codegen_node->expr_node.type_entry;
case NodeTypeBinOpExpr:
return eval_const_expr_bin_op(g, context, node, out_number_literal);
case NodeTypeCompilerFnType:
@ -877,6 +881,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
case NodeTypeUnreachable:
case NodeTypeVoid:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:
@ -951,6 +956,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
case NodeTypeUnreachable:
case NodeTypeVoid:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:
@ -1018,7 +1024,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type,
return false;
}
case TypeTableEntryIdMaybe:
return num_lit_fits_in_other_type(g, literal_type, other_type->data.maybe.child_type);
return false;
}
zig_unreachable();
}
@ -1133,6 +1139,9 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
if (actual_type->id == TypeTableEntryIdNumberLiteral &&
num_lit_fits_in_other_type(g, actual_type, expected_type))
{
assert(!node->codegen_node->data.num_lit_node.resolved_type ||
node->codegen_node->data.num_lit_node.resolved_type == expected_type);
node->codegen_node->data.num_lit_node.resolved_type = expected_type;
return expected_type;
}
@ -1419,6 +1428,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
case BinOpTypeMult:
case BinOpTypeDiv:
case BinOpTypeMod:
case BinOpTypeUnwrapMaybe:
zig_unreachable();
}
zig_unreachable();
@ -1619,6 +1629,24 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
return resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type);
}
case BinOpTypeUnwrapMaybe:
{
AstNode *op1 = node->data.bin_op_expr.op1;
AstNode *op2 = node->data.bin_op_expr.op2;
TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
if (lhs_type->id == TypeTableEntryIdInvalid) {
return lhs_type;
} else if (lhs_type->id == TypeTableEntryIdMaybe) {
TypeTableEntry *child_type = lhs_type->data.maybe.child_type;
analyze_expression(g, import, context, child_type, op2);
return child_type;
} else {
add_node_error(g, op1,
buf_sprintf("expected maybe type, got '%s'",
buf_ptr(&lhs_type->name)));
}
}
case BinOpTypeInvalid:
zig_unreachable();
}
@ -1695,6 +1723,27 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE
return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false);
}
static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *import,
BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node)
{
assert(node->type == NodeTypeNullLiteral);
if (expected_type) {
assert(expected_type->id == TypeTableEntryIdMaybe);
assert(node->codegen_node);
node->codegen_node->data.struct_val_expr_node.type_entry = expected_type;
node->codegen_node->data.struct_val_expr_node.source_node = node;
block_context->struct_val_expr_alloca_list.append(&node->codegen_node->data.struct_val_expr_node);
return expected_type;
} else {
add_node_error(g, node,
buf_sprintf("unable to determine null type"));
return g->builtin_types.entry_invalid;
}
}
static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import,
BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node)
{
@ -1706,8 +1755,11 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry
} else if (expected_type) {
NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
assert(!codegen_num_lit->resolved_type);
codegen_num_lit->resolved_type = resolve_type_compatibility(g, block_context, node, expected_type, num_lit_type);
return codegen_num_lit->resolved_type;
TypeTableEntry *after_implicit_cast_resolved_type =
resolve_type_compatibility(g, block_context, node, expected_type, num_lit_type);
assert(codegen_num_lit->resolved_type ||
after_implicit_cast_resolved_type->id == TypeTableEntryIdInvalid);
return after_implicit_cast_resolved_type;
} else {
return num_lit_type;
}
@ -2174,6 +2226,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
return_type = g->builtin_types.entry_bool;
break;
case NodeTypeNullLiteral:
return_type = analyze_null_literal_expr(g, import, context, expected_type, node);
break;
case NodeTypeSymbol:
{
return_type = analyze_variable_name(g, import, context, node, &node->data.symbol);
@ -2491,6 +2547,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
case NodeTypeUnreachable:
case NodeTypeVoid:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:

View File

@ -613,6 +613,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
case BinOpTypeAssign:
case BinOpTypeAssignBoolAnd:
case BinOpTypeAssignBoolOr:
case BinOpTypeUnwrapMaybe:
zig_unreachable();
}
zig_unreachable();
@ -814,6 +815,70 @@ static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {
return gen_assign_raw(g, node, node->data.bin_op_expr.bin_op, target_ref, value, op1_type, op2_type);
}
static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref) {
add_debug_source_node(g, node);
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");
// TODO if it's a struct we might not want to load the pointer
return LLVMBuildLoad(g->builder, maybe_field_ptr, "");
}
static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
assert(node->data.bin_op_expr.bin_op == BinOpTypeUnwrapMaybe);
AstNode *op1_node = node->data.bin_op_expr.op1;
AstNode *op2_node = node->data.bin_op_expr.op2;
LLVMValueRef maybe_struct_ref = gen_expr(g, op1_node);
add_debug_source_node(g, node);
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, "");
LLVMValueRef cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNonNull");
LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNull");
LLVMBasicBlockRef end_block;
bool non_null_reachable = get_expr_type(op1_node)->id != TypeTableEntryIdUnreachable;
bool null_reachable = get_expr_type(op2_node)->id != TypeTableEntryIdUnreachable;
bool end_reachable = non_null_reachable || null_reachable;
if (end_reachable) {
end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd");
}
LLVMBuildCondBr(g->builder, cond_value, non_null_block, null_block);
LLVMPositionBuilderAtEnd(g->builder, non_null_block);
LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref);
if (non_null_reachable) {
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, end_block);
}
LLVMPositionBuilderAtEnd(g->builder, null_block);
LLVMValueRef null_result = gen_expr(g, op2_node);
if (null_reachable) {
add_debug_source_node(g, node);
LLVMBuildBr(g->builder, end_block);
}
if (end_reachable) {
LLVMPositionBuilderAtEnd(g->builder, end_block);
if (null_reachable) {
add_debug_source_node(g, node);
LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), "");
LLVMValueRef incoming_values[2] = {non_null_result, null_result};
LLVMBasicBlockRef incoming_blocks[2] = {non_null_block, null_block};
LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
return phi;
} else {
return non_null_result;
}
}
return nullptr;
}
static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
switch (node->data.bin_op_expr.bin_op) {
case BinOpTypeInvalid:
@ -843,6 +908,8 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
case BinOpTypeCmpLessOrEq:
case BinOpTypeCmpGreaterOrEq:
return gen_cmp_expr(g, node);
case BinOpTypeUnwrapMaybe:
return gen_unwrap_maybe_expr(g, node);
case BinOpTypeBinOr:
case BinOpTypeBinXor:
case BinOpTypeBinAnd:
@ -1124,6 +1191,22 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) {
return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
}
static LLVMValueRef gen_null_literal(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeNullLiteral);
TypeTableEntry *type_entry = get_expr_type(node);
assert(type_entry->id == TypeTableEntryIdMaybe);
LLVMValueRef tmp_struct_ptr = node->codegen_node->data.struct_val_expr_node.ptr;
add_debug_source_node(g, node);
LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
LLVMValueRef null_value = LLVMConstNull(LLVMInt1Type());
LLVMBuildStore(g->builder, null_value, field_ptr);
return tmp_struct_ptr;
}
static LLVMValueRef gen_struct_val_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeStructValueExpr);
@ -1242,10 +1325,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
LLVMValueRef value;
if (unwrap_maybe) {
assert(var_decl->expr);
add_debug_source_node(g, source_node);
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, *init_value, 0, "");
// TODO if it's a struct we might not want to load the pointer
value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
value = gen_unwrap_maybe(g, source_node, *init_value);
} else {
value = *init_value;
}
@ -1375,6 +1455,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
return LLVMConstAllOnes(LLVMInt1Type());
else
return LLVMConstNull(LLVMInt1Type());
case NodeTypeNullLiteral:
return gen_null_literal(g, node);
case NodeTypeIfBoolExpr:
return gen_if_bool_expr(g, node);
case NodeTypeIfVarExpr:

View File

@ -48,6 +48,7 @@ static const char *bin_op_str(BinOpType bin_op) {
case BinOpTypeAssignBitOr: return "|=";
case BinOpTypeAssignBoolAnd: return "&&=";
case BinOpTypeAssignBoolOr: return "||=";
case BinOpTypeUnwrapMaybe: return "??";
}
zig_unreachable();
}
@ -117,6 +118,8 @@ const char *node_type_str(NodeType node_type) {
return "Void";
case NodeTypeBoolLiteral:
return "BoolLiteral";
case NodeTypeNullLiteral:
return "NullLiteral";
case NodeTypeIfBoolExpr:
return "IfBoolExpr";
case NodeTypeIfVarExpr:
@ -349,6 +352,9 @@ void ast_print(AstNode *node, int indent) {
case NodeTypeBoolLiteral:
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false");
break;
case NodeTypeNullLiteral:
fprintf(stderr, "%s\n", node_type_str(node->type));
break;
case NodeTypeIfBoolExpr:
fprintf(stderr, "%s\n", node_type_str(node->type));
if (node->data.if_bool_expr.condition)
@ -1280,6 +1286,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) {
/*
PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType
KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null)
*/
static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
@ -1317,6 +1324,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
node->data.bool_literal = false;
*token_index += 1;
return node;
} else if (token->id == TokenIdKeywordNull) {
AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token);
*token_index += 1;
return node;
} else if (token->id == TokenIdSymbol) {
Token *next_token = &pc->tokens->at(*token_index + 1);
@ -2045,10 +2056,36 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool manda
}
/*
AssignmentExpression : BoolOrExpression AssignmentOperator BoolOrExpression | BoolOrExpression
UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression
*/
static AstNode *ast_parse_unwrap_maybe_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) {
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;
return node;
}
/*
AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression
*/
static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) {
AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory);
AstNode *lhs = ast_parse_unwrap_maybe_expr(pc, token_index, mandatory);
if (!lhs)
return nullptr;
@ -2057,7 +2094,7 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand
if (ass_op == BinOpTypeInvalid)
return lhs;
AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true);
AstNode *rhs = ast_parse_unwrap_maybe_expr(pc, token_index, true);
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
node->data.bin_op_expr.op1 = lhs;

View File

@ -45,6 +45,7 @@ enum NodeType {
NodeTypeUse,
NodeTypeVoid,
NodeTypeBoolLiteral,
NodeTypeNullLiteral,
NodeTypeIfBoolExpr,
NodeTypeIfVarExpr,
NodeTypeWhileExpr,
@ -161,6 +162,7 @@ enum BinOpType {
BinOpTypeMult,
BinOpTypeDiv,
BinOpTypeMod,
BinOpTypeUnwrapMaybe,
};
struct AstNodeBinOpExpr {

View File

@ -241,6 +241,8 @@ static void end_token(Tokenize *t) {
t->cur_tok->id = TokenIdKeywordContinue;
} else if (mem_eql_str(token_mem, token_len, "break")) {
t->cur_tok->id = TokenIdKeywordBreak;
} else if (mem_eql_str(token_mem, token_len, "null")) {
t->cur_tok->id = TokenIdKeywordNull;
}
t->cur_tok = nullptr;
@ -418,6 +420,11 @@ void tokenize(Buf *buf, Tokenization *out) {
break;
case TokenizeStateSawQuestionMark:
switch (c) {
case '?':
t.cur_tok->id = TokenIdDoubleQuestion;
end_token(&t);
t.state = TokenizeStateStart;
break;
case '=':
t.cur_tok->id = TokenIdMaybeAssign;
end_token(&t);
@ -1002,6 +1009,7 @@ static const char * token_name(Token *token) {
case TokenIdKeywordWhile: return "While";
case TokenIdKeywordContinue: return "Continue";
case TokenIdKeywordBreak: return "Break";
case TokenIdKeywordNull: return "Null";
case TokenIdLParen: return "LParen";
case TokenIdRParen: return "RParen";
case TokenIdComma: return "Comma";
@ -1052,6 +1060,7 @@ static const char * token_name(Token *token) {
case TokenIdDot: return "Dot";
case TokenIdEllipsis: return "Ellipsis";
case TokenIdMaybe: return "Maybe";
case TokenIdDoubleQuestion: return "DoubleQuestion";
case TokenIdMaybeAssign: return "MaybeAssign";
}
return "(invalid token)";

View File

@ -35,6 +35,7 @@ enum TokenId {
TokenIdKeywordWhile,
TokenIdKeywordContinue,
TokenIdKeywordBreak,
TokenIdKeywordNull,
TokenIdLParen,
TokenIdRParen,
TokenIdComma,
@ -85,6 +86,7 @@ enum TokenId {
TokenIdDot,
TokenIdEllipsis,
TokenIdMaybe,
TokenIdDoubleQuestion,
TokenIdMaybeAssign,
};

View File

@ -1,5 +1,6 @@
use "syscall.zig";
const stdin_fileno : isize = 0;
const stdout_fileno : isize = 1;
const stderr_fileno : isize = 2;
@ -38,6 +39,26 @@ pub fn print_i64(x: i64) -> isize {
return write(stdout_fileno, buf.ptr, len);
}
/*
// TODO error handling
pub fn readline(buf: []u8) -> ?[]u8 {
var index = 0;
while (index < buf.len) {
// TODO unknown size array indexing operator
const err = read(stdin_fileno, &buf.ptr[index], 1);
if (err != 0) {
return null;
}
// TODO unknown size array indexing operator
if (buf.ptr[index] == '\n') {
return buf[0...index + 1];
}
index += 1;
}
return null;
}
*/
fn digit_to_char(digit: u64) -> u8 {
'0' + (digit as u8)
}

View File

@ -1,3 +1,4 @@
const SYS_read : usize = 0;
const SYS_write : usize = 1;
const SYS_exit : usize = 60;
const SYS_getrandom : usize = 318;
@ -16,6 +17,10 @@ fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
: "rcx", "r11")
}
pub fn read(fd: isize, buf: &u8, count: usize) -> isize {
syscall3(SYS_read, fd as usize, buf as usize, count) as isize
}
pub fn write(fd: isize, buf: &const u8, count: usize) -> isize {
syscall3(SYS_write, fd as usize, buf as usize, count) as isize
}

View File

@ -710,7 +710,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
add_simple_case("maybe type", R"SOURCE(
use "std.zig";
pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
const x : ?bool = true;
if (const y ?= x) {
@ -722,6 +722,23 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
} else {
print_str("x is none\n");
}
const next_x : ?i32 = null;
const z = next_x ?? 1234;
if (z != 1234) {
print_str("BAD\n");
}
const final_x : ?i32 = 13;
const num = final_x ?? unreachable;
if (num != 13) {
print_str("BAD\n");
}
return 0;
}
)SOURCE", "x is true\n");