add ?? maybe unwrapping binary operator
add null literal fix number literal / maybe interactionsmaster
parent
9b9fd5ad23
commit
a3c97081ca
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)";
|
||||
|
|
|
@ -35,6 +35,7 @@ enum TokenId {
|
|||
TokenIdKeywordWhile,
|
||||
TokenIdKeywordContinue,
|
||||
TokenIdKeywordBreak,
|
||||
TokenIdKeywordNull,
|
||||
TokenIdLParen,
|
||||
TokenIdRParen,
|
||||
TokenIdComma,
|
||||
|
@ -85,6 +86,7 @@ enum TokenId {
|
|||
TokenIdDot,
|
||||
TokenIdEllipsis,
|
||||
TokenIdMaybe,
|
||||
TokenIdDoubleQuestion,
|
||||
TokenIdMaybeAssign,
|
||||
};
|
||||
|
||||
|
|
21
std/std.zig
21
std/std.zig
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue