diff options
| -rw-r--r-- | doc/langref.md | 15 | ||||
| -rw-r--r-- | example/arrays/arrays.zig | 19 | ||||
| -rw-r--r-- | example/expressions/expressions.zig | 14 | ||||
| -rw-r--r-- | example/hello_world/hello_libc.zig | 2 | ||||
| -rw-r--r-- | example/multiple_files/foo.zig | 2 | ||||
| -rw-r--r-- | example/multiple_files/libc.zig | 2 | ||||
| -rw-r--r-- | example/structs/structs.zig | 2 | ||||
| -rw-r--r-- | src/analyze.cpp | 547 | ||||
| -rw-r--r-- | src/analyze.hpp | 19 | ||||
| -rw-r--r-- | src/codegen.cpp | 95 | ||||
| -rw-r--r-- | src/parseh.cpp | 2 | ||||
| -rw-r--r-- | src/parser.cpp | 317 | ||||
| -rw-r--r-- | src/parser.hpp | 39 | ||||
| -rw-r--r-- | src/util.cpp | 10 | ||||
| -rw-r--r-- | src/util.hpp | 2 | ||||
| -rw-r--r-- | std/std.zig | 7 | ||||
| -rw-r--r-- | test/run_tests.cpp | 33 |
17 files changed, 892 insertions, 235 deletions
diff --git a/doc/langref.md b/doc/langref.md index df1feba78f..7cd66f76e1 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -200,10 +200,11 @@ as ### Numbers - Number literals | Example | Exponentiation -------------------------------------------- - Decimal integer | 98222 | N/A - Hex integer | 0xff | N/A - Octal integer | 0o77 | N/A - Binary integer | 0b11110000 | N/A - Floating-point | 123.0E+77 | Optional + Number literals | Example | Exponentiation +-------------------------------------------------- + Decimal integer | 98222 | N/A + Hex integer | 0xff | N/A + Octal integer | 0o77 | N/A + Binary integer | 0b11110000 | N/A + Floating-point | 123.0E+77 | Optional + Hex floating point | TODO | TODO diff --git a/example/arrays/arrays.zig b/example/arrays/arrays.zig index f5c807a4dc..7d118f5b3f 100644 --- a/example/arrays/arrays.zig +++ b/example/arrays/arrays.zig @@ -1,15 +1,11 @@ export executable "arrays"; -#link("c") -extern { - fn puts(s: *const u8) -> i32; - fn exit(code: i32) -> unreachable; -} +use "std.zig"; -export fn _start() -> unreachable { +export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 { let mut array : [i32; 5]; - let mut i = 0; + let mut i : i32 = 0; loop_start: if i == 5 { goto loop_end; @@ -21,21 +17,22 @@ loop_start: loop_end: i = 0; - let mut accumulator = 0; + let mut accumulator : i32 = 0; loop_2_start: if i == 5 { goto loop_2_end; } - accumulator = accumulator + array[i]; + accumulator += array[i]; i = i + 1; goto loop_2_start; loop_2_end: if accumulator == 15 { - puts("OK"); + print_str("OK" as string); } - exit(0); + + return 0; } diff --git a/example/expressions/expressions.zig b/example/expressions/expressions.zig index 67daf29930..7d0bdc23e0 100644 --- a/example/expressions/expressions.zig +++ b/example/expressions/expressions.zig @@ -14,19 +14,19 @@ fn other_exit() -> unreachable { export fn _start() -> unreachable { let a : i32 = 1; - let b = 2; + let b = 2 as i32; // let c : i32; // not yet support for const variables // let d; // parse error if (a + b == 3) { - let no_conflict = 5; - if (no_conflict == 5) { puts("OK 1"); } + let no_conflict : i32 = 5; + if (no_conflict == 5) { puts(c"OK 1"); } } let c = { - let no_conflict = 10; + let no_conflict : i32 = 10; no_conflict }; - if (c == 10) { puts("OK 2"); } + if (c == 10) { puts(c"OK 2"); } void_fun(1, void, 2); @@ -44,12 +44,12 @@ fn void_fun(a : i32, b : void, c : i32) -> void { } fn test_mutable_vars() { - let mut i = 0; + let mut i : i32 = 0; loop_start: if i == 3 { goto done; } - puts("loop"); + puts(c"loop"); i = i + 1; goto loop_start; done: diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index ea24b4d921..820589ef1d 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -7,6 +7,6 @@ extern { } export fn _start() -> unreachable { - printf("Hello, world!\n"); + printf(c"Hello, world!\n"); exit(0); } diff --git a/example/multiple_files/foo.zig b/example/multiple_files/foo.zig index 021bf71e5b..6240bd3a21 100644 --- a/example/multiple_files/foo.zig +++ b/example/multiple_files/foo.zig @@ -3,7 +3,7 @@ use "libc.zig"; // purposefully conflicting function with main.zig // but it's private so it should be OK fn private_function() { - puts("it works!"); + puts(c"it works!"); } pub fn print_text() { diff --git a/example/multiple_files/libc.zig b/example/multiple_files/libc.zig index 19c1106fd6..e2a048b46c 100644 --- a/example/multiple_files/libc.zig +++ b/example/multiple_files/libc.zig @@ -1,5 +1,5 @@ #link("c") extern { - pub fn puts(s: *mut u8) -> i32; + pub fn puts(s: *const u8) -> i32; pub fn exit(code: i32) -> unreachable; } diff --git a/example/structs/structs.zig b/example/structs/structs.zig index e3b364b219..49ffe73f17 100644 --- a/example/structs/structs.zig +++ b/example/structs/structs.zig @@ -22,6 +22,6 @@ struct Foo { fn test_foo(foo : Foo) { if foo.b { - print_str("OK" as string); + print_str("OK\n" as string); } } diff --git a/src/analyze.cpp b/src/analyze.cpp index 7b7c09280c..7243b8565a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -122,7 +122,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool } } -static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, int array_size) { +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); if (existing_entry) { return existing_entry->value; @@ -130,7 +130,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray); entry->type_ref = LLVMArrayType(child_type->type_ref, array_size); buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "[%s; %d]", buf_ptr(&child_type->name), array_size); + buf_appendf(&entry->name, "[%s; %" PRIu64 "]", buf_ptr(&child_type->name), array_size); entry->size_in_bits = child_type->size_in_bits * array_size; entry->align_in_bits = child_type->align_in_bits; @@ -145,11 +145,6 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in } } -static int parse_int(Buf *number) { - // TODO: think about integer size of array sizes - return atoi(buf_ptr(number)); -} - static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeType); alloc_codegen_node(node); @@ -192,16 +187,15 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { } AstNode *size_node = node->data.type.array_size; - int size; // TODO: think about integer size of array sizes - if (size_node->type != NodeTypeNumberLiteral) { - add_node_error(g, size_node, - buf_create_from_str("array size must be literal number")); - size = -1; + if (size_node->type == NodeTypeNumberLiteral && + is_num_lit_unsigned(size_node->data.number_literal.kind)) + { + type_node->entry = get_array_type(g, child_type, size_node->data.number_literal.data.x_uint); } else { - size = parse_int(&size_node->data.number); + add_node_error(g, size_node, + buf_create_from_str("array size must be literal unsigned integer")); + type_node->entry = g->builtin_types.entry_invalid; } - - type_node->entry = get_array_type(g, child_type, size); return type_node->entry; } } @@ -625,6 +619,44 @@ static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructF *out_i = -1; } +static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) { + NumLit num_lit = literal_type->data.num_lit.kind; + uint64_t lit_size_in_bits = num_lit_bit_count(num_lit); + + switch (other_type->id) { + case TypeTableEntryIdInvalid: + case TypeTableEntryIdNumberLiteral: + zig_unreachable(); + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdPointer: + case TypeTableEntryIdArray: + case TypeTableEntryIdStruct: + return false; + case TypeTableEntryIdInt: + if (is_num_lit_signed(num_lit)) { + if (!other_type->data.integral.is_signed) { + return false; + } + + return lit_size_in_bits <= other_type->size_in_bits; + } else if (is_num_lit_unsigned(num_lit)) { + + return lit_size_in_bits <= other_type->size_in_bits; + } else { + return false; + } + case TypeTableEntryIdFloat: + if (is_num_lit_float(num_lit)) { + return lit_size_in_bits <= other_type->size_in_bits; + } else { + return false; + } + } +} + + static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { @@ -790,6 +822,16 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B cast_node->op = CastOpArrayToString; context->cast_expr_alloca_list.append(node); return wanted_type; + } else if (actual_type->id == TypeTableEntryIdNumberLiteral && + num_lit_fits_in_other_type(g, actual_type, wanted_type)) + { + AstNode *literal_node = node->data.cast_expr.expr; + assert(literal_node->codegen_node); + NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node; + assert(!codegen_num_lit->resolved_type); + codegen_num_lit->resolved_type = wanted_type; + cast_node->op = CastOpNothing; + return wanted_type; } else { add_node_error(g, node, buf_sprintf("invalid cast from type '%s' to '%s'", @@ -799,6 +841,314 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B } } +static TypeTableEntry * resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node, + TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type) +{ + assert(literal_node->codegen_node); + NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node; + + if (num_lit_fits_in_other_type(g, literal_type, non_literal_type)) { + assert(!codegen_num_lit->resolved_type); + codegen_num_lit->resolved_type = non_literal_type; + return non_literal_type; + } else { + return nullptr; + } +} + +static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2) { + TypeTableEntry *type1 = node1->codegen_node->expr_node.type_entry; + TypeTableEntry *type2 = node2->codegen_node->expr_node.type_entry; + + if (type1->id == TypeTableEntryIdNumberLiteral && + type2->id == TypeTableEntryIdNumberLiteral) + { + assert(node1->codegen_node); + assert(node2->codegen_node); + + NumberLiteralNode *codegen_num_lit_1 = &node1->codegen_node->data.num_lit_node; + NumberLiteralNode *codegen_num_lit_2 = &node2->codegen_node->data.num_lit_node; + + assert(!codegen_num_lit_1->resolved_type); + assert(!codegen_num_lit_2->resolved_type); + + if (is_num_lit_float(type1->data.num_lit.kind) && + is_num_lit_float(type2->data.num_lit.kind)) + { + codegen_num_lit_1->resolved_type = g->builtin_types.entry_f64; + codegen_num_lit_2->resolved_type = g->builtin_types.entry_f64; + return g->builtin_types.entry_f64; + } else if (is_num_lit_signed(type1->data.num_lit.kind) && + is_num_lit_signed(type2->data.num_lit.kind)) + { + codegen_num_lit_1->resolved_type = g->builtin_types.entry_i64; + codegen_num_lit_2->resolved_type = g->builtin_types.entry_i64; + return g->builtin_types.entry_i64; + } else if (is_num_lit_unsigned(type1->data.num_lit.kind) && + is_num_lit_unsigned(type2->data.num_lit.kind)) + { + codegen_num_lit_1->resolved_type = g->builtin_types.entry_u64; + codegen_num_lit_2->resolved_type = g->builtin_types.entry_u64; + return g->builtin_types.entry_u64; + } else { + return nullptr; + } + } else if (type1->id == TypeTableEntryIdNumberLiteral) { + return resolve_rhs_number_literal(g, node2, type2, node1, type1); + } else { + assert(type2->id == TypeTableEntryIdNumberLiteral); + return resolve_rhs_number_literal(g, node1, type1, node2, type2); + } +} + +static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + switch (node->data.bin_op_expr.bin_op) { + case BinOpTypeAssign: + case BinOpTypeAssignTimes: + case BinOpTypeAssignDiv: + case BinOpTypeAssignMod: + case BinOpTypeAssignPlus: + case BinOpTypeAssignMinus: + case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftRight: + case BinOpTypeAssignBitAnd: + case BinOpTypeAssignBitXor: + case BinOpTypeAssignBitOr: + case BinOpTypeAssignBoolAnd: + case BinOpTypeAssignBoolOr: + { + AstNode *lhs_node = node->data.bin_op_expr.op1; + TypeTableEntry *expected_rhs_type = nullptr; + if (lhs_node->type == NodeTypeSymbol) { + Buf *name = &lhs_node->data.symbol; + LocalVariableTableEntry *var = find_local_variable(context, name); + if (var) { + if (var->is_const) { + add_node_error(g, lhs_node, + buf_sprintf("cannot assign to constant variable")); + } else { + if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) { + if (var->type->id != TypeTableEntryIdInvalid) { + add_node_error(g, lhs_node, + buf_sprintf("operator not allowed for type '%s'", + buf_ptr(&var->type->name))); + } + } else { + expected_rhs_type = var->type; + } + } + } else { + add_node_error(g, lhs_node, + buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); + } + } else if (lhs_node->type == NodeTypeArrayAccessExpr) { + expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node); + } else if (lhs_node->type == NodeTypeFieldAccessExpr) { + alloc_codegen_node(lhs_node); + expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node); + } else { + add_node_error(g, lhs_node, + buf_sprintf("assignment target must be variable, field, or array element")); + } + analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); + return g->builtin_types.entry_void; + } + case BinOpTypeBoolOr: + case BinOpTypeBoolAnd: + analyze_expression(g, import, context, g->builtin_types.entry_bool, + node->data.bin_op_expr.op1); + analyze_expression(g, import, context, g->builtin_types.entry_bool, + node->data.bin_op_expr.op2); + return g->builtin_types.entry_bool; + case BinOpTypeCmpEq: + case BinOpTypeCmpNotEq: + case BinOpTypeCmpLessThan: + case BinOpTypeCmpGreaterThan: + case BinOpTypeCmpLessOrEq: + case BinOpTypeCmpGreaterOrEq: + { + 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); + TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2); + bool cmp_ok = false; + if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) { + cmp_ok = true; + } else if (lhs_type->id == TypeTableEntryIdNumberLiteral || + rhs_type->id == TypeTableEntryIdNumberLiteral) + { + cmp_ok = resolve_number_literals(g, op1, op2); + } else if (lhs_type->id == TypeTableEntryIdInt) { + if (rhs_type->id == TypeTableEntryIdInt && + lhs_type->data.integral.is_signed == rhs_type->data.integral.is_signed && + lhs_type->size_in_bits == rhs_type->size_in_bits) + { + cmp_ok = true; + } + } else if (lhs_type->id == TypeTableEntryIdFloat) { + if (rhs_type->id == TypeTableEntryIdFloat && + lhs_type->size_in_bits == rhs_type->size_in_bits) + { + cmp_ok = true; + } + } + if (!cmp_ok) { + add_node_error(g, node, buf_sprintf("unable to compare '%s' with '%s'", + buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); + } + return g->builtin_types.entry_bool; + } + case BinOpTypeBinOr: + case BinOpTypeBinXor: + case BinOpTypeBinAnd: + { + // TODO: don't require i32 + analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); + analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); + return g->builtin_types.entry_i32; + } + case BinOpTypeBitShiftLeft: + case BinOpTypeBitShiftRight: + { + // TODO: don't require i32 + analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); + analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); + return g->builtin_types.entry_i32; + } + case BinOpTypeAdd: + case BinOpTypeSub: + { + 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); + TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2); + + TypeTableEntry *return_type = nullptr; + + if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) { + return_type = g->builtin_types.entry_invalid; + } else if (lhs_type->id == TypeTableEntryIdNumberLiteral || + rhs_type->id == TypeTableEntryIdNumberLiteral) + { + return_type = resolve_number_literals(g, op1, op2); + } else if (lhs_type->id == TypeTableEntryIdInt && + lhs_type == rhs_type) + { + return_type = lhs_type; + } else if (lhs_type->id == TypeTableEntryIdFloat && + lhs_type == rhs_type) + { + return_type = lhs_type; + } + if (!return_type) { + if (node->data.bin_op_expr.bin_op == BinOpTypeAdd) { + add_node_error(g, node, buf_sprintf("unable to add '%s' and '%s'", + buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); + } else { + add_node_error(g, node, buf_sprintf("unable to subtract '%s' and '%s'", + buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); + } + return g->builtin_types.entry_invalid; + } + return return_type; + } + case BinOpTypeMult: + case BinOpTypeDiv: + case BinOpTypeMod: + { + // TODO: don't require i32 + analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); + analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); + return g->builtin_types.entry_i32; + } + case BinOpTypeInvalid: + zig_unreachable(); + } + zig_unreachable(); +} + +static TypeTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; + + TypeTableEntry *explicit_type = nullptr; + if (variable_declaration->type != nullptr) { + explicit_type = resolve_type(g, variable_declaration->type); + if (explicit_type->id == TypeTableEntryIdUnreachable) { + add_node_error(g, variable_declaration->type, + buf_sprintf("variable of type 'unreachable' not allowed")); + explicit_type = g->builtin_types.entry_invalid; + } + } + + TypeTableEntry *implicit_type = nullptr; + if (variable_declaration->expr != nullptr) { + implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr); + if (implicit_type->id == TypeTableEntryIdUnreachable) { + add_node_error(g, node, + buf_sprintf("variable initialization is unreachable")); + implicit_type = g->builtin_types.entry_invalid; + } else if (implicit_type->id == TypeTableEntryIdNumberLiteral) { + add_node_error(g, node, + buf_sprintf("unable to infer variable type")); + implicit_type = g->builtin_types.entry_invalid; + } + } + + if (implicit_type == nullptr && variable_declaration->is_const) { + add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'.")); + implicit_type = g->builtin_types.entry_invalid; + } + + TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; + assert(type != nullptr); // should have been caught by the parser + + LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol); + if (existing_variable) { + add_node_error(g, node, + buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol))); + } else { + LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); + buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); + variable_entry->type = type; + variable_entry->is_const = variable_declaration->is_const; + variable_entry->is_ptr = true; + variable_entry->decl_node = node; + context->variable_table.put(&variable_entry->name, variable_entry); + } + return g->builtin_types.entry_void; +} + +static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + TypeTableEntry *num_lit_type = g->num_lit_types[node->data.number_literal.kind]; + if (node->data.number_literal.overflow) { + add_node_error(g, node, + buf_sprintf("number literal too large to be represented in any type")); + return g->builtin_types.entry_invalid; + } else if (expected_type) { + if (expected_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } else if (num_lit_fits_in_other_type(g, num_lit_type, expected_type)) { + NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; + assert(!codegen_num_lit->resolved_type); + codegen_num_lit->resolved_type = expected_type; + + return expected_type; + } else { + add_node_error(g, node, buf_sprintf("expected type '%s', got '%s'", + buf_ptr(&expected_type->name), buf_ptr(&num_lit_type->name))); + return g->builtin_types.entry_invalid; + } + } else { + return num_lit_type; + } +} + static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -854,51 +1204,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, break; } case NodeTypeVariableDeclaration: - { - AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;; - - TypeTableEntry *explicit_type = nullptr; - if (variable_declaration->type != nullptr) { - explicit_type = resolve_type(g, variable_declaration->type); - if (explicit_type->id == TypeTableEntryIdUnreachable) { - add_node_error(g, variable_declaration->type, - buf_sprintf("variable of type 'unreachable' not allowed")); - } - } - - TypeTableEntry *implicit_type = nullptr; - if (variable_declaration->expr != nullptr) { - implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr); - if (implicit_type->id == TypeTableEntryIdUnreachable) { - add_node_error(g, node, - buf_sprintf("variable initialization is unreachable")); - } - } - - if (implicit_type == nullptr && variable_declaration->is_const) { - add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'.")); - } - - TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; - assert(type != nullptr); // should have been caught by the parser - - LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol); - if (existing_variable) { - add_node_error(g, node, - buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol))); - } else { - LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); - buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); - variable_entry->type = type; - variable_entry->is_const = variable_declaration->is_const; - variable_entry->is_ptr = true; - variable_entry->decl_node = node; - context->variable_table.put(&variable_entry->name, variable_entry); - } - return_type = g->builtin_types.entry_void; - break; - } - + return_type = analyze_variable_declaration(g, import, context, expected_type, node); + break; case NodeTypeGoto: { FnTableEntry *fn_table_entry = get_context_fn_entry(context); @@ -928,120 +1235,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, break; } case NodeTypeBinOpExpr: - { - switch (node->data.bin_op_expr.bin_op) { - case BinOpTypeAssign: - case BinOpTypeAssignTimes: - case BinOpTypeAssignDiv: - case BinOpTypeAssignMod: - case BinOpTypeAssignPlus: - case BinOpTypeAssignMinus: - case BinOpTypeAssignBitShiftLeft: - case BinOpTypeAssignBitShiftRight: - case BinOpTypeAssignBitAnd: - case BinOpTypeAssignBitXor: - case BinOpTypeAssignBitOr: - case BinOpTypeAssignBoolAnd: - case BinOpTypeAssignBoolOr: - { - AstNode *lhs_node = node->data.bin_op_expr.op1; - TypeTableEntry *expected_rhs_type = nullptr; - if (lhs_node->type == NodeTypeSymbol) { - Buf *name = &lhs_node->data.symbol; - LocalVariableTableEntry *var = find_local_variable(context, name); - if (var) { - if (var->is_const) { - add_node_error(g, lhs_node, - buf_sprintf("cannot assign to constant variable")); - } else { - if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) { - add_node_error(g, lhs_node, - buf_sprintf("operator not allowed for type '%s'", buf_ptr(&var->type->name))); - } else { - expected_rhs_type = var->type; - } - } - } else { - add_node_error(g, lhs_node, - buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); - } - } else if (lhs_node->type == NodeTypeArrayAccessExpr) { - expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node); - } else if (lhs_node->type == NodeTypeFieldAccessExpr) { - alloc_codegen_node(lhs_node); - expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node); - } else { - add_node_error(g, lhs_node, - buf_sprintf("assignment target must be variable, field, or array element")); - } - analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); - return_type = g->builtin_types.entry_void; - break; - } - case BinOpTypeBoolOr: - case BinOpTypeBoolAnd: - analyze_expression(g, import, context, g->builtin_types.entry_bool, - node->data.bin_op_expr.op1); - analyze_expression(g, import, context, g->builtin_types.entry_bool, - node->data.bin_op_expr.op2); - return_type = g->builtin_types.entry_bool; - break; - case BinOpTypeCmpEq: - case BinOpTypeCmpNotEq: - case BinOpTypeCmpLessThan: - case BinOpTypeCmpGreaterThan: - case BinOpTypeCmpLessOrEq: - case BinOpTypeCmpGreaterOrEq: - // TODO think how should type checking for these work? - analyze_expression(g, import, context, g->builtin_types.entry_i32, - node->data.bin_op_expr.op1); - analyze_expression(g, import, context, g->builtin_types.entry_i32, - node->data.bin_op_expr.op2); - return_type = g->builtin_types.entry_bool; - break; - case BinOpTypeBinOr: - case BinOpTypeBinXor: - case BinOpTypeBinAnd: - { - // TODO: don't require i32 - analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); - analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); - return_type = g->builtin_types.entry_i32; - break; - } - case BinOpTypeBitShiftLeft: - case BinOpTypeBitShiftRight: - { - // TODO: don't require i32 - analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); - analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); - return_type = g->builtin_types.entry_i32; - break; - } - case BinOpTypeAdd: - case BinOpTypeSub: - // TODO think how should type checking for these work? - analyze_expression(g, import, context, g->builtin_types.entry_i32, - node->data.bin_op_expr.op1); - analyze_expression(g, import, context, g->builtin_types.entry_i32, - node->data.bin_op_expr.op2); - return_type = g->builtin_types.entry_i32; - break; - case BinOpTypeMult: - case BinOpTypeDiv: - case BinOpTypeMod: - { - // TODO: don't require i32 - analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); - analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); - return_type = g->builtin_types.entry_i32; - break; - } - case BinOpTypeInvalid: - zig_unreachable(); - } - break; - } + return_type = analyze_bin_op_expr(g, import, context, expected_type, node); + break; case NodeTypeFnCallExpr: { @@ -1116,10 +1311,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, return_type = analyze_field_access_expr(g, import, context, node); break; case NodeTypeNumberLiteral: - // TODO: generic literal int type - return_type = g->builtin_types.entry_i32; + return_type = analyze_number_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; diff --git a/src/analyze.hpp b/src/analyze.hpp index de888cab23..40a6e5150d 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -42,6 +42,10 @@ struct TypeTableEntryStruct { TypeStructField *fields; }; +struct TypeTableEntryNumLit { + NumLit kind; +}; + enum TypeTableEntryId { TypeTableEntryIdInvalid, TypeTableEntryIdVoid, @@ -52,6 +56,7 @@ enum TypeTableEntryId { TypeTableEntryIdPointer, TypeTableEntryIdArray, TypeTableEntryIdStruct, + TypeTableEntryIdNumberLiteral, }; struct TypeTableEntry { @@ -69,12 +74,13 @@ struct TypeTableEntry { TypeTableEntryInt integral; TypeTableEntryArray array; TypeTableEntryStruct structure; + TypeTableEntryNumLit num_lit; } data; // use these fields to make sure we don't duplicate type table entries for the same type TypeTableEntry *pointer_const_parent; TypeTableEntry *pointer_mut_parent; - HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size; + HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size; }; @@ -134,10 +140,13 @@ struct CodeGen { struct { TypeTableEntry *entry_bool; TypeTableEntry *entry_u8; + TypeTableEntry *entry_u64; TypeTableEntry *entry_i32; + TypeTableEntry *entry_i64; TypeTableEntry *entry_isize; TypeTableEntry *entry_usize; TypeTableEntry *entry_f32; + TypeTableEntry *entry_f64; TypeTableEntry *entry_c_string_literal; TypeTableEntry *entry_string; TypeTableEntry *entry_void; @@ -145,6 +154,8 @@ struct CodeGen { TypeTableEntry *entry_invalid; } builtin_types; + TypeTableEntry *num_lit_types[NumLitCount]; + LLVMTargetDataRef target_data_ref; unsigned pointer_size_bytes; bool is_static; @@ -242,6 +253,7 @@ enum CastOp { CastOpPtrToInt, CastOpIntWidenOrShorten, CastOpArrayToString, + CastOpNothing, }; struct CastNode { @@ -251,6 +263,10 @@ struct CastNode { LLVMValueRef ptr; }; +struct NumberLiteralNode { + TypeTableEntry *resolved_type; +}; + struct CodeGenNode { union { TypeNode type_node; // for NodeTypeType @@ -262,6 +278,7 @@ struct CodeGenNode { StructDeclNode struct_decl_node; // for NodeTypeStructDecl FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr CastNode cast_node; // for NodeTypeCastExpr + NumberLiteralNode num_lit_node; // for NodeTypeNumberLiteral } data; ExprNode expr_node; // for all the expression nodes }; diff --git a/src/codegen.cpp b/src/codegen.cpp index fcd0214b07..47f64541a5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -279,6 +279,8 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { CastNode *cast_node = &node->codegen_node->data.cast_node; switch (cast_node->op) { + case CastOpNothing: + return expr_val; case CastOpPtrToInt: return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); case CastOpIntWidenOrShorten: @@ -901,11 +903,29 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { return gen_asm_expr(g, node); case NodeTypeNumberLiteral: { - Buf *number_str = &node->data.number; - LLVMTypeRef number_type = LLVMInt32Type(); - LLVMValueRef number_val = LLVMConstIntOfStringAndSize(number_type, - buf_ptr(number_str), buf_len(number_str), 10); - return number_val; + NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; + assert(codegen_num_lit); + TypeTableEntry *type_entry = codegen_num_lit->resolved_type; + assert(type_entry); + + // TODO this is kinda iffy. make sure josh is on board with this + node->codegen_node->expr_node.type_entry = type_entry; + + if (type_entry->id == TypeTableEntryIdInt) { + // here the union has int64_t and uint64_t and we purposefully read + // the uint64_t value in either case, because we want the twos + // complement representation + + return LLVMConstInt(type_entry->type_ref, + node->data.number_literal.data.x_uint, + type_entry->data.integral.is_signed); + } else if (type_entry->id == TypeTableEntryIdFloat) { + + return LLVMConstReal(type_entry->type_ref, + node->data.number_literal.data.x_float); + } else { + zig_panic("bad number literal type"); + } } case NodeTypeStringLiteral: { @@ -1186,6 +1206,20 @@ static void do_code_gen(CodeGen *g) { #endif } +static const NumLit num_lit_kinds[] = { + NumLitF32, + NumLitF64, + NumLitF128, + NumLitI8, + NumLitU8, + NumLitI16, + NumLitU16, + NumLitI32, + NumLitU32, + NumLitI64, + NumLitU64, +}; + static void define_builtin_types(CodeGen *g) { { // if this type is anywhere in the AST, we should never hit codegen. @@ -1193,6 +1227,19 @@ static void define_builtin_types(CodeGen *g) { buf_init_from_str(&entry->name, "(invalid)"); g->builtin_types.entry_invalid = entry; } + + assert(NumLitCount == array_length(num_lit_kinds)); + for (int i = 0; i < NumLitCount; i += 1) { + NumLit num_lit_kind = num_lit_kinds[i]; + // This type should just create a constant with whatever actual number + // type is expected at the time. + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumberLiteral); + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "(%s literal)", num_lit_str(num_lit_kind)); + entry->data.num_lit.kind = num_lit_kind; + g->num_lit_types[i] = entry; + } + { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool); entry->type_ref = LLVMInt1Type(); @@ -1217,6 +1264,19 @@ static void define_builtin_types(CodeGen *g) { g->type_table.put(&entry->name, entry); g->builtin_types.entry_u8 = entry; } + { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); + entry->type_ref = LLVMInt64Type(); + buf_init_from_str(&entry->name, "u64"); + entry->size_in_bits = 64; + entry->align_in_bits = 64; + entry->data.integral.is_signed = false; + entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + entry->size_in_bits, entry->align_in_bits, + LLVMZigEncoding_DW_ATE_unsigned()); + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_u64 = entry; + } g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true); { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); @@ -1233,6 +1293,19 @@ static void define_builtin_types(CodeGen *g) { } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); + entry->type_ref = LLVMInt64Type(); + buf_init_from_str(&entry->name, "i64"); + entry->size_in_bits = 64; + entry->align_in_bits = 64; + entry->data.integral.is_signed = true; + entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + entry->size_in_bits, entry->align_in_bits, + LLVMZigEncoding_DW_ATE_signed()); + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_i64 = entry; + } + { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); buf_init_from_str(&entry->name, "isize"); entry->size_in_bits = g->pointer_size_bytes * 8; @@ -1270,6 +1343,18 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_f32 = entry; } { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); + entry->type_ref = LLVMFloatType(); + buf_init_from_str(&entry->name, "f64"); + entry->size_in_bits = 64; + entry->align_in_bits = 64; + entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + entry->size_in_bits, entry->align_in_bits, + LLVMZigEncoding_DW_ATE_float()); + g->type_table.put(&entry->name, entry); + g->builtin_types.entry_f64 = entry; + } + { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid); entry->type_ref = LLVMVoidType(); buf_init_from_str(&entry->name, "void"); diff --git a/src/parseh.cpp b/src/parseh.cpp index 7e03a7306e..6dc9972415 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -544,7 +544,7 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS"); if (ZIG_PARSEH_CFLAGS) { - Buf tmp_buf = {0}; + Buf tmp_buf = BUF_INIT; char *start = ZIG_PARSEH_CFLAGS; char *space = strstr(start, " "); while (space) { diff --git a/src/parser.cpp b/src/parser.cpp index 9ccaf6f20b..c94daf5582 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -12,6 +12,7 @@ #include <stdarg.h> #include <stdio.h> + static const char *bin_op_str(BinOpType bin_op) { switch (bin_op) { case BinOpTypeInvalid: return "(invalid)"; @@ -273,9 +274,19 @@ void ast_print(AstNode *node, int indent) { ast_print(node->data.prefix_op_expr.primary_expr, indent + 2); break; case NodeTypeNumberLiteral: - fprintf(stderr, "NumberLiteral %s\n", - buf_ptr(&node->data.number)); - break; + { + NumLit num_lit = node->data.number_literal.kind; + const char *name = node_type_str(node->type); + const char *kind_str = num_lit_str(num_lit); + if (is_num_lit_signed(num_lit)) { + fprintf(stderr, "%s %s %" PRId64 "\n", name, kind_str, node->data.number_literal.data.x_int); + } else if (is_num_lit_unsigned(num_lit)) { + fprintf(stderr, "%s %s %" PRIu64 "\n", name, kind_str, node->data.number_literal.data.x_uint); + } else { + fprintf(stderr, "%s %s %f\n", name, kind_str, node->data.number_literal.data.x_float); + } + break; + } case NodeTypeStringLiteral: { const char *c = node->data.string_literal.c ? "c" : ""; @@ -574,6 +585,186 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool if (offset_map) offset_map->append(pos); } +enum ParseNumLitState { + ParseNumLitStateStart, + ParseNumLitStateBase, + ParseNumLitStateDigits, + ParseNumLitStateExpectFirstDigit, + ParseNumLitStateDecimal, + ParseNumLitStateESign, + ParseNumLitStateEDigit, +}; + +static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLiteral *num_lit) { + ParseNumLitState state = ParseNumLitStateStart; + unsigned long long base = 10; + bool negative = false; + int digits_start; + int digits_end; + int decimal_start = -1; + int decimal_end; + bool e_present = false; + bool e_positive; + int e_digit_start; + int e_digit_end; + + for (int i = token->start_pos; i < token->end_pos; i += 1) { + uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i); + switch (state) { + case ParseNumLitStateStart: + if (c == '-') { + negative = true; + } else if (c == '0') { + state = ParseNumLitStateBase; + } else if (c >= '1' && c <= '9') { + digits_start = i; + state = ParseNumLitStateDigits; + } else { + zig_unreachable(); + } + break; + case ParseNumLitStateBase: + if (c == 'x') { + base = 16; + state = ParseNumLitStateExpectFirstDigit; + } else if (c == 'o') { + base = 8; + state = ParseNumLitStateExpectFirstDigit; + } else if (c == 'b') { + base = 2; + state = ParseNumLitStateExpectFirstDigit; + } else { + zig_unreachable(); + } + break; + + case ParseNumLitStateExpectFirstDigit: + state = ParseNumLitStateDigits; + break; + + case ParseNumLitStateDigits: + if (c == '.') { + assert(base == 10); + digits_end = i; + decimal_start = i + 1; + state = ParseNumLitStateDecimal; + } + break; + case ParseNumLitStateDecimal: + if (c == 'E') { + e_present = false; + decimal_end = i; + state = ParseNumLitStateESign; + } + break; + case ParseNumLitStateESign: + if (c == '+') { + e_positive = true; + e_digit_start = i + 1; + state = ParseNumLitStateEDigit; + } else if (c == '-') { + e_positive = false; + e_digit_start = i + 1; + state = ParseNumLitStateEDigit; + } else { + zig_unreachable(); + } + break; + case ParseNumLitStateEDigit: + assert(c >= '0' && c <= '9'); + break; + } + } + + switch (state) { + case ParseNumLitStateDigits: + digits_end = token->end_pos; + break; + case ParseNumLitStateDecimal: + decimal_end = token->end_pos; + break; + case ParseNumLitStateEDigit: + e_digit_end = token->end_pos; + break; + case ParseNumLitStateBase: + num_lit->kind = NumLitU8; + num_lit->data.x_uint = 0; + return; + case ParseNumLitStateESign: + case ParseNumLitStateExpectFirstDigit: + case ParseNumLitStateStart: + zig_unreachable(); + } + + if (decimal_start >= 0) { + // float + double x; + + (void)x; + zig_panic("TODO parse float"); + } else { + // integer + unsigned long long x = 0; + + unsigned long long mult = 1; + for (int i = digits_end - 1; ; i -= 1) { + uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i); + unsigned long long digit = (c - '0'); + + // digit *= mult + if (__builtin_umulll_overflow(digit, mult, &digit)) { + num_lit->overflow = true; + return; + } + + // x += digit + if (__builtin_uaddll_overflow(x, digit, &x)) { + num_lit->overflow = true; + return; + } + + if (i == digits_start) + break; + + // mult *= base + if (__builtin_umulll_overflow(mult, base, &mult)) { + num_lit->overflow = true; + return; + } + } + + if (negative) { + if (x <= 128ull) { + num_lit->kind = NumLitI8; + } else if (x <= 32768ull) { + num_lit->kind = NumLitI16; + } else if (x <= 2147483648ull) { + num_lit->kind = NumLitI32; + } else if (x <= 9223372036854775808ull) { + num_lit->kind = NumLitI64; + } else { + num_lit->overflow = true; + return; + } + + num_lit->data.x_int = -((int64_t)x); + } else { + num_lit->data.x_uint = x; + + if (x <= UINT8_MAX) { + num_lit->kind = NumLitU8; + } else if (x <= UINT16_MAX) { + num_lit->kind = NumLitU16; + } else if (x <= UINT32_MAX) { + num_lit->kind = NumLitU32; + } else { + num_lit->kind = NumLitU64; + } + } + } +} + + __attribute__ ((noreturn)) static void ast_invalid_token_error(ParseContext *pc, Token *token) { Buf token_value = BUF_INIT; @@ -829,7 +1020,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool if (token->id == TokenIdNumberLiteral) { AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token); - ast_buf_from_token(pc, token, &node->data.number); + parse_number_literal(pc, token, &node->data.number_literal); *token_index += 1; return node; } else if (token->id == TokenIdStringLiteral) { @@ -2152,3 +2343,121 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, Er pc.root = ast_parse_root(&pc, &token_index); return pc.root; } + +const char *num_lit_str(NumLit num_lit) { + switch (num_lit) { + case NumLitF32: + return "f32"; + case NumLitF64: + return "f64"; + case NumLitF128: + return "f128"; + case NumLitI8: + return "i8"; + case NumLitI16: + return "i16"; + case NumLitI32: + return "i32"; + case NumLitI64: + return "i64"; + case NumLitU8: + return "u8"; + case NumLitU16: + return "u16"; + case NumLitU32: + return "u32"; + case NumLitU64: + return "u64"; + case NumLitCount: + zig_unreachable(); + } + zig_unreachable(); +} + +bool is_num_lit_signed(NumLit num_lit) { + switch (num_lit) { + case NumLitI8: + case NumLitI16: + case NumLitI32: + case NumLitI64: + return true; + + case NumLitF32: + case NumLitF64: + case NumLitF128: + case NumLitU8: + case NumLitU16: + case NumLitU32: + case NumLitU64: + return false; + case NumLitCount: + zig_unreachable(); + } + zig_unreachable(); +} + +bool is_num_lit_unsigned(NumLit num_lit) { + switch (num_lit) { + case NumLitF32: + case NumLitF64: + case NumLitF128: + case NumLitI8: + case NumLitI16: + case NumLitI32: + case NumLitI64: + return false; + case NumLitU8: + case NumLitU16: + case NumLitU32: + case NumLitU64: + return true; + case NumLitCount: + zig_unreachable(); + } + zig_unreachable(); +} + +bool is_num_lit_float(NumLit num_lit) { + switch (num_lit) { + case NumLitF32: + case NumLitF64: + case NumLitF128: + return true; + case NumLitI8: + case NumLitI16: + case NumLitI32: + case NumLitI64: + case NumLitU8: + case NumLitU16: + case NumLitU32: + case NumLitU64: + return false; + case NumLitCount: + zig_unreachable(); + } + zig_unreachable(); +} + +uint64_t num_lit_bit_count(NumLit num_lit) { + switch (num_lit) { + case NumLitI8: + case NumLitU8: + return 8; + case NumLitI16: + case NumLitU16: + return 16; + case NumLitI32: + case NumLitU32: + case NumLitF32: + return 32; + case NumLitI64: + case NumLitU64: + case NumLitF64: + return 64; + case NumLitF128: + return 128; + case NumLitCount: + zig_unreachable(); + } + zig_unreachable(); +} diff --git a/src/parser.hpp b/src/parser.hpp index 1468d2a9e1..1ced6bb74f 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -267,6 +267,36 @@ struct AstNodeStringLiteral { bool c; }; +enum NumLit { + NumLitF32, + NumLitF64, + NumLitF128, + NumLitI8, + NumLitU8, + NumLitI16, + NumLitU16, + NumLitI32, + NumLitU32, + NumLitI64, + NumLitU64, + + NumLitCount +}; + +struct AstNodeNumberLiteral { + NumLit kind; + + // overflow is true if when parsing the number, we discovered it would not + // fit without losing data in a uint64_t, int64_t, or double + bool overflow; + + union { + uint64_t x_uint; + int64_t x_int; + double x_float; + } data; +}; + struct AstNode { enum NodeType type; int line; @@ -300,7 +330,7 @@ struct AstNode { AstNodeStructDecl struct_decl; AstNodeStructField struct_field; AstNodeStringLiteral string_literal; - Buf number; + AstNodeNumberLiteral number_literal; Buf symbol; bool bool_literal; } data; @@ -329,4 +359,11 @@ const char *node_type_str(NodeType node_type); void ast_print(AstNode *node, int indent); +const char *num_lit_str(NumLit num_lit); +bool is_num_lit_signed(NumLit num_lit); +bool is_num_lit_unsigned(NumLit num_lit); +bool is_num_lit_float(NumLit num_lit); +uint64_t num_lit_bit_count(NumLit num_lit); + + #endif diff --git a/src/util.cpp b/src/util.cpp index cb87ab6243..ceba4cde85 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -21,8 +21,16 @@ void zig_panic(const char *format, ...) { } uint32_t int_hash(int i) { - return *reinterpret_cast<uint32_t*>(&i); + return (uint32_t)(i % UINT32_MAX); } bool int_eq(int a, int b) { return a == b; } + +uint32_t uint64_hash(uint64_t i) { + return (uint32_t)(i % UINT32_MAX); +} + +bool uint64_eq(uint64_t a, uint64_t b) { + return a == b; +} diff --git a/src/util.hpp b/src/util.hpp index d5729e1ba3..8cf9f7f438 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -81,5 +81,7 @@ static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) uint32_t int_hash(int i); bool int_eq(int a, int b); +uint32_t uint64_hash(uint64_t i); +bool uint64_eq(uint64_t a, uint64_t b); #endif diff --git a/std/std.zig b/std/std.zig index ebb6e9e2b5..08036d73ee 100644 --- a/std/std.zig +++ b/std/std.zig @@ -15,10 +15,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { // TODO error handling // TODO handle buffering and flushing -// TODO non-i32 integer literals so we can remove the casts // TODO constants for SYS_write and stdout_fileno pub fn print_str(str : string) -> isize { - let SYS_write = 1; - let stdout_fileno = 1; - return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); + let SYS_write : isize = 1; + let stdout_fileno : isize = 1; + return syscall3(SYS_write, stdout_fileno, str.ptr as isize, str.len as isize); } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index a90bdd0ea0..dd7c0edf1b 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -266,7 +266,7 @@ extern { export fn _start() -> unreachable { let a : i32 = 1; - let b = 2; + let b = 2 as i32; if (a + b == 3) { puts(c"OK"); } @@ -299,12 +299,12 @@ extern { export fn _start() -> unreachable { if (true) { - let no_conflict = 5; + let no_conflict : i32 = 5; if (no_conflict == 5) { puts(c"OK 1"); } } let c = { - let no_conflict = 10; + let no_conflict = 10 as i32; no_conflict }; if (c == 10) { puts(c"OK 2"); } @@ -343,7 +343,7 @@ export fn _start() -> unreachable { let mut zero : i32; if (zero == 0) { puts(c"zero"); } - let mut i = 0; + let mut i = 0 as i32; loop_start: if i == 3 { goto done; @@ -366,7 +366,7 @@ extern { export fn _start() -> unreachable { let mut array : [i32; 5]; - let mut i = 0; + let mut i : i32 = 0; loop_start: if i == 5 { goto loop_end; @@ -378,7 +378,7 @@ loop_start: loop_end: i = 0; - let mut accumulator = 0; + let mut accumulator = 0 as i32; loop_2_start: if i == 5 { goto loop_2_end; @@ -611,7 +611,7 @@ fn f() -> i32 { fn f() { if (0) {} } - )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got 'i32'"); + )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'"); add_compile_fail_case("assign unreachable", R"SOURCE( fn f() { @@ -685,13 +685,12 @@ fn f(...) {} } -static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) { +static void print_compiler_invocation(TestCase *test_case) { printf("%s", zig_exe); for (int i = 0; i < test_case->compiler_args.length; i += 1) { printf(" %s", test_case->compiler_args.at(i)); } printf("\n"); - printf("%s\n", buf_ptr(zig_stderr)); } static void run_test(TestCase *test_case) { @@ -716,21 +715,24 @@ static void run_test(TestCase *test_case) { printf("========= Expected this compile error: =========\n"); printf("%s\n", err_text); printf("================================================\n"); - print_compiler_invocation(test_case, &zig_stderr); + print_compiler_invocation(test_case); + printf("%s\n", buf_ptr(&zig_stderr)); exit(1); } } return; // success } else { printf("\nCompile failed with return code 0 (Expected failure):\n"); - print_compiler_invocation(test_case, &zig_stderr); + print_compiler_invocation(test_case); + printf("%s\n", buf_ptr(&zig_stderr)); exit(1); } } if (return_code != 0) { printf("\nCompile failed with return code %d:\n", return_code); - print_compiler_invocation(test_case, &zig_stderr); + print_compiler_invocation(test_case); + printf("%s\n", buf_ptr(&zig_stderr)); exit(1); } @@ -740,6 +742,7 @@ static void run_test(TestCase *test_case) { if (return_code != 0) { printf("\nProgram exited with return code %d:\n", return_code); + print_compiler_invocation(test_case); printf("%s", tmp_exe_path); for (int i = 0; i < test_case->program_args.length; i += 1) { printf(" %s", test_case->program_args.at(i)); @@ -751,6 +754,12 @@ static void run_test(TestCase *test_case) { if (!buf_eql_str(&program_stdout, test_case->output)) { printf("\n"); + print_compiler_invocation(test_case); + printf("%s", tmp_exe_path); + for (int i = 0; i < test_case->program_args.length; i += 1) { + printf(" %s", test_case->program_args.at(i)); + } + printf("\n"); printf("==== Test failed. Expected output: ====\n"); printf("%s\n", test_case->output); printf("========= Actual output: ==============\n"); |
