| author | |
| committer | |
| log | e411467e1dd4557bb11698f2a5d979dfcbaea444 |
| tree | 8b9bf14a1dee5310a8e18099fc354e1b9b1d2152 |
| parent | 3d8eb10897a86b2616c6a1aa843b7ebe4134ac51 |
it gets implicitly casted to whatever is needed.
closes #2417 files changed, 892 insertions(+), 235 deletions(-)
doc/langref.md+8-7| ... | ... | @@ -200,10 +200,11 @@ as |
| 200 | 200 | |
| 201 | 201 | ### Numbers |
| 202 | 202 | |
| 203 | Number literals | Example | Exponentiation | |
| 204 | ------------------------------------------- | |
| 205 | Decimal integer | 98222 | N/A | |
| 206 | Hex integer | 0xff | N/A | |
| 207 | Octal integer | 0o77 | N/A | |
| 208 | Binary integer | 0b11110000 | N/A | |
| 209 | Floating-point | 123.0E+77 | Optional | |
| 203 | Number literals | Example | Exponentiation | |
| 204 | -------------------------------------------------- | |
| 205 | Decimal integer | 98222 | N/A | |
| 206 | Hex integer | 0xff | N/A | |
| 207 | Octal integer | 0o77 | N/A | |
| 208 | Binary integer | 0b11110000 | N/A | |
| 209 | Floating-point | 123.0E+77 | Optional | |
| 210 | Hex floating point | TODO | TODO |
example/arrays/arrays.zig+8-11| ... | ... | @@ -1,15 +1,11 @@ |
| 1 | 1 | export executable "arrays"; |
| 2 | 2 | |
| 3 | #link("c") | |
| 4 | extern { | |
| 5 | fn puts(s: *const u8) -> i32; | |
| 6 | fn exit(code: i32) -> unreachable; | |
| 7 | } | |
| 3 | use "std.zig"; | |
| 8 | 4 | |
| 9 | export fn _start() -> unreachable { | |
| 5 | export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 { | |
| 10 | 6 | let mut array : [i32; 5]; |
| 11 | 7 | |
| 12 | let mut i = 0; | |
| 8 | let mut i : i32 = 0; | |
| 13 | 9 | loop_start: |
| 14 | 10 | if i == 5 { |
| 15 | 11 | goto loop_end; |
| ... | ... | @@ -21,21 +17,22 @@ loop_start: |
| 21 | 17 | loop_end: |
| 22 | 18 | |
| 23 | 19 | i = 0; |
| 24 | let mut accumulator = 0; | |
| 20 | let mut accumulator : i32 = 0; | |
| 25 | 21 | loop_2_start: |
| 26 | 22 | if i == 5 { |
| 27 | 23 | goto loop_2_end; |
| 28 | 24 | } |
| 29 | 25 | |
| 30 | accumulator = accumulator + array[i]; | |
| 26 | accumulator += array[i]; | |
| 31 | 27 | |
| 32 | 28 | i = i + 1; |
| 33 | 29 | goto loop_2_start; |
| 34 | 30 | loop_2_end: |
| 35 | 31 | |
| 36 | 32 | if accumulator == 15 { |
| 37 | puts("OK"); | |
| 33 | print_str("OK" as string); | |
| 38 | 34 | } |
| 39 | 35 | |
| 40 | exit(0); | |
| 36 | ||
| 37 | return 0; | |
| 41 | 38 | } |
example/expressions/expressions.zig+7-7| ... | ... | @@ -14,19 +14,19 @@ fn other_exit() -> unreachable { |
| 14 | 14 | |
| 15 | 15 | export fn _start() -> unreachable { |
| 16 | 16 | let a : i32 = 1; |
| 17 | let b = 2; | |
| 17 | let b = 2 as i32; | |
| 18 | 18 | // let c : i32; // not yet support for const variables |
| 19 | 19 | // let d; // parse error |
| 20 | 20 | if (a + b == 3) { |
| 21 | let no_conflict = 5; | |
| 22 | if (no_conflict == 5) { puts("OK 1"); } | |
| 21 | let no_conflict : i32 = 5; | |
| 22 | if (no_conflict == 5) { puts(c"OK 1"); } | |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | 25 | let c = { |
| 26 | let no_conflict = 10; | |
| 26 | let no_conflict : i32 = 10; | |
| 27 | 27 | no_conflict |
| 28 | 28 | }; |
| 29 | if (c == 10) { puts("OK 2"); } | |
| 29 | if (c == 10) { puts(c"OK 2"); } | |
| 30 | 30 | |
| 31 | 31 | void_fun(1, void, 2); |
| 32 | 32 | |
| ... | ... | @@ -44,12 +44,12 @@ fn void_fun(a : i32, b : void, c : i32) -> void { |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | 46 | fn test_mutable_vars() { |
| 47 | let mut i = 0; | |
| 47 | let mut i : i32 = 0; | |
| 48 | 48 | loop_start: |
| 49 | 49 | if i == 3 { |
| 50 | 50 | goto done; |
| 51 | 51 | } |
| 52 | puts("loop"); | |
| 52 | puts(c"loop"); | |
| 53 | 53 | i = i + 1; |
| 54 | 54 | goto loop_start; |
| 55 | 55 | done: |
example/hello_world/hello_libc.zig+1-1| ... | ... | @@ -7,6 +7,6 @@ extern { |
| 7 | 7 | } |
| 8 | 8 | |
| 9 | 9 | export fn _start() -> unreachable { |
| 10 | printf("Hello, world!\n"); | |
| 10 | printf(c"Hello, world!\n"); | |
| 11 | 11 | exit(0); |
| 12 | 12 | } |
example/multiple_files/foo.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ use "libc.zig"; |
| 3 | 3 | // purposefully conflicting function with main.zig |
| 4 | 4 | // but it's private so it should be OK |
| 5 | 5 | fn private_function() { |
| 6 | puts("it works!"); | |
| 6 | puts(c"it works!"); | |
| 7 | 7 | } |
| 8 | 8 | |
| 9 | 9 | pub fn print_text() { |
example/multiple_files/libc.zig+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | #link("c") |
| 2 | 2 | extern { |
| 3 | pub fn puts(s: *mut u8) -> i32; | |
| 3 | pub fn puts(s: *const u8) -> i32; | |
| 4 | 4 | pub fn exit(code: i32) -> unreachable; |
| 5 | 5 | } |
example/structs/structs.zig+1-1| ... | ... | @@ -22,6 +22,6 @@ struct Foo { |
| 22 | 22 | |
| 23 | 23 | fn test_foo(foo : Foo) { |
| 24 | 24 | if foo.b { |
| 25 | print_str("OK" as string); | |
| 25 | print_str("OK\n" as string); | |
| 26 | 26 | } |
| 27 | 27 | } |
src/analyze.cpp+370-177| ... | ... | @@ -122,7 +122,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool |
| 122 | 122 | } |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, int array_size) { | |
| 125 | static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) { | |
| 126 | 126 | auto existing_entry = child_type->arrays_by_size.maybe_get(array_size); |
| 127 | 127 | if (existing_entry) { |
| 128 | 128 | return existing_entry->value; |
| ... | ... | @@ -130,7 +130,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in |
| 130 | 130 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray); |
| 131 | 131 | entry->type_ref = LLVMArrayType(child_type->type_ref, array_size); |
| 132 | 132 | buf_resize(&entry->name, 0); |
| 133 | buf_appendf(&entry->name, "[%s; %d]", buf_ptr(&child_type->name), array_size); | |
| 133 | buf_appendf(&entry->name, "[%s; %" PRIu64 "]", buf_ptr(&child_type->name), array_size); | |
| 134 | 134 | |
| 135 | 135 | entry->size_in_bits = child_type->size_in_bits * array_size; |
| 136 | 136 | entry->align_in_bits = child_type->align_in_bits; |
| ... | ... | @@ -145,11 +145,6 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in |
| 145 | 145 | } |
| 146 | 146 | } |
| 147 | 147 | |
| 148 | static int parse_int(Buf *number) { | |
| 149 | // TODO: think about integer size of array sizes | |
| 150 | return atoi(buf_ptr(number)); | |
| 151 | } | |
| 152 | ||
| 153 | 148 | static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { |
| 154 | 149 | assert(node->type == NodeTypeType); |
| 155 | 150 | alloc_codegen_node(node); |
| ... | ... | @@ -192,16 +187,15 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { |
| 192 | 187 | } |
| 193 | 188 | |
| 194 | 189 | AstNode *size_node = node->data.type.array_size; |
| 195 | int size; // TODO: think about integer size of array sizes | |
| 196 | if (size_node->type != NodeTypeNumberLiteral) { | |
| 197 | add_node_error(g, size_node, | |
| 198 | buf_create_from_str("array size must be literal number")); | |
| 199 | size = -1; | |
| 190 | if (size_node->type == NodeTypeNumberLiteral && | |
| 191 | is_num_lit_unsigned(size_node->data.number_literal.kind)) | |
| 192 | { | |
| 193 | type_node->entry = get_array_type(g, child_type, size_node->data.number_literal.data.x_uint); | |
| 200 | 194 | } else { |
| 201 | size = parse_int(&size_node->data.number); | |
| 195 | add_node_error(g, size_node, | |
| 196 | buf_create_from_str("array size must be literal unsigned integer")); | |
| 197 | type_node->entry = g->builtin_types.entry_invalid; | |
| 202 | 198 | } |
| 203 | ||
| 204 | type_node->entry = get_array_type(g, child_type, size); | |
| 205 | 199 | return type_node->entry; |
| 206 | 200 | } |
| 207 | 201 | } |
| ... | ... | @@ -625,6 +619,44 @@ static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructF |
| 625 | 619 | *out_i = -1; |
| 626 | 620 | } |
| 627 | 621 | |
| 622 | static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) { | |
| 623 | NumLit num_lit = literal_type->data.num_lit.kind; | |
| 624 | uint64_t lit_size_in_bits = num_lit_bit_count(num_lit); | |
| 625 | ||
| 626 | switch (other_type->id) { | |
| 627 | case TypeTableEntryIdInvalid: | |
| 628 | case TypeTableEntryIdNumberLiteral: | |
| 629 | zig_unreachable(); | |
| 630 | case TypeTableEntryIdVoid: | |
| 631 | case TypeTableEntryIdBool: | |
| 632 | case TypeTableEntryIdUnreachable: | |
| 633 | case TypeTableEntryIdPointer: | |
| 634 | case TypeTableEntryIdArray: | |
| 635 | case TypeTableEntryIdStruct: | |
| 636 | return false; | |
| 637 | case TypeTableEntryIdInt: | |
| 638 | if (is_num_lit_signed(num_lit)) { | |
| 639 | if (!other_type->data.integral.is_signed) { | |
| 640 | return false; | |
| 641 | } | |
| 642 | ||
| 643 | return lit_size_in_bits <= other_type->size_in_bits; | |
| 644 | } else if (is_num_lit_unsigned(num_lit)) { | |
| 645 | ||
| 646 | return lit_size_in_bits <= other_type->size_in_bits; | |
| 647 | } else { | |
| 648 | return false; | |
| 649 | } | |
| 650 | case TypeTableEntryIdFloat: | |
| 651 | if (is_num_lit_float(num_lit)) { | |
| 652 | return lit_size_in_bits <= other_type->size_in_bits; | |
| 653 | } else { | |
| 654 | return false; | |
| 655 | } | |
| 656 | } | |
| 657 | } | |
| 658 | ||
| 659 | ||
| 628 | 660 | static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 629 | 661 | AstNode *node) |
| 630 | 662 | { |
| ... | ... | @@ -790,6 +822,16 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B |
| 790 | 822 | cast_node->op = CastOpArrayToString; |
| 791 | 823 | context->cast_expr_alloca_list.append(node); |
| 792 | 824 | return wanted_type; |
| 825 | } else if (actual_type->id == TypeTableEntryIdNumberLiteral && | |
| 826 | num_lit_fits_in_other_type(g, actual_type, wanted_type)) | |
| 827 | { | |
| 828 | AstNode *literal_node = node->data.cast_expr.expr; | |
| 829 | assert(literal_node->codegen_node); | |
| 830 | NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node; | |
| 831 | assert(!codegen_num_lit->resolved_type); | |
| 832 | codegen_num_lit->resolved_type = wanted_type; | |
| 833 | cast_node->op = CastOpNothing; | |
| 834 | return wanted_type; | |
| 793 | 835 | } else { |
| 794 | 836 | add_node_error(g, node, |
| 795 | 837 | buf_sprintf("invalid cast from type '%s' to '%s'", |
| ... | ... | @@ -799,6 +841,314 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B |
| 799 | 841 | } |
| 800 | 842 | } |
| 801 | 843 | |
| 844 | static TypeTableEntry * resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node, | |
| 845 | TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type) | |
| 846 | { | |
| 847 | assert(literal_node->codegen_node); | |
| 848 | NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node; | |
| 849 | ||
| 850 | if (num_lit_fits_in_other_type(g, literal_type, non_literal_type)) { | |
| 851 | assert(!codegen_num_lit->resolved_type); | |
| 852 | codegen_num_lit->resolved_type = non_literal_type; | |
| 853 | return non_literal_type; | |
| 854 | } else { | |
| 855 | return nullptr; | |
| 856 | } | |
| 857 | } | |
| 858 | ||
| 859 | static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2) { | |
| 860 | TypeTableEntry *type1 = node1->codegen_node->expr_node.type_entry; | |
| 861 | TypeTableEntry *type2 = node2->codegen_node->expr_node.type_entry; | |
| 862 | ||
| 863 | if (type1->id == TypeTableEntryIdNumberLiteral && | |
| 864 | type2->id == TypeTableEntryIdNumberLiteral) | |
| 865 | { | |
| 866 | assert(node1->codegen_node); | |
| 867 | assert(node2->codegen_node); | |
| 868 | ||
| 869 | NumberLiteralNode *codegen_num_lit_1 = &node1->codegen_node->data.num_lit_node; | |
| 870 | NumberLiteralNode *codegen_num_lit_2 = &node2->codegen_node->data.num_lit_node; | |
| 871 | ||
| 872 | assert(!codegen_num_lit_1->resolved_type); | |
| 873 | assert(!codegen_num_lit_2->resolved_type); | |
| 874 | ||
| 875 | if (is_num_lit_float(type1->data.num_lit.kind) && | |
| 876 | is_num_lit_float(type2->data.num_lit.kind)) | |
| 877 | { | |
| 878 | codegen_num_lit_1->resolved_type = g->builtin_types.entry_f64; | |
| 879 | codegen_num_lit_2->resolved_type = g->builtin_types.entry_f64; | |
| 880 | return g->builtin_types.entry_f64; | |
| 881 | } else if (is_num_lit_signed(type1->data.num_lit.kind) && | |
| 882 | is_num_lit_signed(type2->data.num_lit.kind)) | |
| 883 | { | |
| 884 | codegen_num_lit_1->resolved_type = g->builtin_types.entry_i64; | |
| 885 | codegen_num_lit_2->resolved_type = g->builtin_types.entry_i64; | |
| 886 | return g->builtin_types.entry_i64; | |
| 887 | } else if (is_num_lit_unsigned(type1->data.num_lit.kind) && | |
| 888 | is_num_lit_unsigned(type2->data.num_lit.kind)) | |
| 889 | { | |
| 890 | codegen_num_lit_1->resolved_type = g->builtin_types.entry_u64; | |
| 891 | codegen_num_lit_2->resolved_type = g->builtin_types.entry_u64; | |
| 892 | return g->builtin_types.entry_u64; | |
| 893 | } else { | |
| 894 | return nullptr; | |
| 895 | } | |
| 896 | } else if (type1->id == TypeTableEntryIdNumberLiteral) { | |
| 897 | return resolve_rhs_number_literal(g, node2, type2, node1, type1); | |
| 898 | } else { | |
| 899 | assert(type2->id == TypeTableEntryIdNumberLiteral); | |
| 900 | return resolve_rhs_number_literal(g, node1, type1, node2, type2); | |
| 901 | } | |
| 902 | } | |
| 903 | ||
| 904 | static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 905 | TypeTableEntry *expected_type, AstNode *node) | |
| 906 | { | |
| 907 | switch (node->data.bin_op_expr.bin_op) { | |
| 908 | case BinOpTypeAssign: | |
| 909 | case BinOpTypeAssignTimes: | |
| 910 | case BinOpTypeAssignDiv: | |
| 911 | case BinOpTypeAssignMod: | |
| 912 | case BinOpTypeAssignPlus: | |
| 913 | case BinOpTypeAssignMinus: | |
| 914 | case BinOpTypeAssignBitShiftLeft: | |
| 915 | case BinOpTypeAssignBitShiftRight: | |
| 916 | case BinOpTypeAssignBitAnd: | |
| 917 | case BinOpTypeAssignBitXor: | |
| 918 | case BinOpTypeAssignBitOr: | |
| 919 | case BinOpTypeAssignBoolAnd: | |
| 920 | case BinOpTypeAssignBoolOr: | |
| 921 | { | |
| 922 | AstNode *lhs_node = node->data.bin_op_expr.op1; | |
| 923 | TypeTableEntry *expected_rhs_type = nullptr; | |
| 924 | if (lhs_node->type == NodeTypeSymbol) { | |
| 925 | Buf *name = &lhs_node->data.symbol; | |
| 926 | LocalVariableTableEntry *var = find_local_variable(context, name); | |
| 927 | if (var) { | |
| 928 | if (var->is_const) { | |
| 929 | add_node_error(g, lhs_node, | |
| 930 | buf_sprintf("cannot assign to constant variable")); | |
| 931 | } else { | |
| 932 | if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) { | |
| 933 | if (var->type->id != TypeTableEntryIdInvalid) { | |
| 934 | add_node_error(g, lhs_node, | |
| 935 | buf_sprintf("operator not allowed for type '%s'", | |
| 936 | buf_ptr(&var->type->name))); | |
| 937 | } | |
| 938 | } else { | |
| 939 | expected_rhs_type = var->type; | |
| 940 | } | |
| 941 | } | |
| 942 | } else { | |
| 943 | add_node_error(g, lhs_node, | |
| 944 | buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); | |
| 945 | } | |
| 946 | } else if (lhs_node->type == NodeTypeArrayAccessExpr) { | |
| 947 | expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node); | |
| 948 | } else if (lhs_node->type == NodeTypeFieldAccessExpr) { | |
| 949 | alloc_codegen_node(lhs_node); | |
| 950 | expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node); | |
| 951 | } else { | |
| 952 | add_node_error(g, lhs_node, | |
| 953 | buf_sprintf("assignment target must be variable, field, or array element")); | |
| 954 | } | |
| 955 | analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); | |
| 956 | return g->builtin_types.entry_void; | |
| 957 | } | |
| 958 | case BinOpTypeBoolOr: | |
| 959 | case BinOpTypeBoolAnd: | |
| 960 | analyze_expression(g, import, context, g->builtin_types.entry_bool, | |
| 961 | node->data.bin_op_expr.op1); | |
| 962 | analyze_expression(g, import, context, g->builtin_types.entry_bool, | |
| 963 | node->data.bin_op_expr.op2); | |
| 964 | return g->builtin_types.entry_bool; | |
| 965 | case BinOpTypeCmpEq: | |
| 966 | case BinOpTypeCmpNotEq: | |
| 967 | case BinOpTypeCmpLessThan: | |
| 968 | case BinOpTypeCmpGreaterThan: | |
| 969 | case BinOpTypeCmpLessOrEq: | |
| 970 | case BinOpTypeCmpGreaterOrEq: | |
| 971 | { | |
| 972 | AstNode *op1 = node->data.bin_op_expr.op1; | |
| 973 | AstNode *op2 = node->data.bin_op_expr.op2; | |
| 974 | TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1); | |
| 975 | TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2); | |
| 976 | bool cmp_ok = false; | |
| 977 | if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) { | |
| 978 | cmp_ok = true; | |
| 979 | } else if (lhs_type->id == TypeTableEntryIdNumberLiteral || | |
| 980 | rhs_type->id == TypeTableEntryIdNumberLiteral) | |
| 981 | { | |
| 982 | cmp_ok = resolve_number_literals(g, op1, op2); | |
| 983 | } else if (lhs_type->id == TypeTableEntryIdInt) { | |
| 984 | if (rhs_type->id == TypeTableEntryIdInt && | |
| 985 | lhs_type->data.integral.is_signed == rhs_type->data.integral.is_signed && | |
| 986 | lhs_type->size_in_bits == rhs_type->size_in_bits) | |
| 987 | { | |
| 988 | cmp_ok = true; | |
| 989 | } | |
| 990 | } else if (lhs_type->id == TypeTableEntryIdFloat) { | |
| 991 | if (rhs_type->id == TypeTableEntryIdFloat && | |
| 992 | lhs_type->size_in_bits == rhs_type->size_in_bits) | |
| 993 | { | |
| 994 | cmp_ok = true; | |
| 995 | } | |
| 996 | } | |
| 997 | if (!cmp_ok) { | |
| 998 | add_node_error(g, node, buf_sprintf("unable to compare '%s' with '%s'", | |
| 999 | buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); | |
| 1000 | } | |
| 1001 | return g->builtin_types.entry_bool; | |
| 1002 | } | |
| 1003 | case BinOpTypeBinOr: | |
| 1004 | case BinOpTypeBinXor: | |
| 1005 | case BinOpTypeBinAnd: | |
| 1006 | { | |
| 1007 | // TODO: don't require i32 | |
| 1008 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | |
| 1009 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | |
| 1010 | return g->builtin_types.entry_i32; | |
| 1011 | } | |
| 1012 | case BinOpTypeBitShiftLeft: | |
| 1013 | case BinOpTypeBitShiftRight: | |
| 1014 | { | |
| 1015 | // TODO: don't require i32 | |
| 1016 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | |
| 1017 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | |
| 1018 | return g->builtin_types.entry_i32; | |
| 1019 | } | |
| 1020 | case BinOpTypeAdd: | |
| 1021 | case BinOpTypeSub: | |
| 1022 | { | |
| 1023 | AstNode *op1 = node->data.bin_op_expr.op1; | |
| 1024 | AstNode *op2 = node->data.bin_op_expr.op2; | |
| 1025 | TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1); | |
| 1026 | TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2); | |
| 1027 | ||
| 1028 | TypeTableEntry *return_type = nullptr; | |
| 1029 | ||
| 1030 | if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) { | |
| 1031 | return_type = g->builtin_types.entry_invalid; | |
| 1032 | } else if (lhs_type->id == TypeTableEntryIdNumberLiteral || | |
| 1033 | rhs_type->id == TypeTableEntryIdNumberLiteral) | |
| 1034 | { | |
| 1035 | return_type = resolve_number_literals(g, op1, op2); | |
| 1036 | } else if (lhs_type->id == TypeTableEntryIdInt && | |
| 1037 | lhs_type == rhs_type) | |
| 1038 | { | |
| 1039 | return_type = lhs_type; | |
| 1040 | } else if (lhs_type->id == TypeTableEntryIdFloat && | |
| 1041 | lhs_type == rhs_type) | |
| 1042 | { | |
| 1043 | return_type = lhs_type; | |
| 1044 | } | |
| 1045 | if (!return_type) { | |
| 1046 | if (node->data.bin_op_expr.bin_op == BinOpTypeAdd) { | |
| 1047 | add_node_error(g, node, buf_sprintf("unable to add '%s' and '%s'", | |
| 1048 | buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); | |
| 1049 | } else { | |
| 1050 | add_node_error(g, node, buf_sprintf("unable to subtract '%s' and '%s'", | |
| 1051 | buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); | |
| 1052 | } | |
| 1053 | return g->builtin_types.entry_invalid; | |
| 1054 | } | |
| 1055 | return return_type; | |
| 1056 | } | |
| 1057 | case BinOpTypeMult: | |
| 1058 | case BinOpTypeDiv: | |
| 1059 | case BinOpTypeMod: | |
| 1060 | { | |
| 1061 | // TODO: don't require i32 | |
| 1062 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | |
| 1063 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | |
| 1064 | return g->builtin_types.entry_i32; | |
| 1065 | } | |
| 1066 | case BinOpTypeInvalid: | |
| 1067 | zig_unreachable(); | |
| 1068 | } | |
| 1069 | zig_unreachable(); | |
| 1070 | } | |
| 1071 | ||
| 1072 | static TypeTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 1073 | TypeTableEntry *expected_type, AstNode *node) | |
| 1074 | { | |
| 1075 | AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; | |
| 1076 | ||
| 1077 | TypeTableEntry *explicit_type = nullptr; | |
| 1078 | if (variable_declaration->type != nullptr) { | |
| 1079 | explicit_type = resolve_type(g, variable_declaration->type); | |
| 1080 | if (explicit_type->id == TypeTableEntryIdUnreachable) { | |
| 1081 | add_node_error(g, variable_declaration->type, | |
| 1082 | buf_sprintf("variable of type 'unreachable' not allowed")); | |
| 1083 | explicit_type = g->builtin_types.entry_invalid; | |
| 1084 | } | |
| 1085 | } | |
| 1086 | ||
| 1087 | TypeTableEntry *implicit_type = nullptr; | |
| 1088 | if (variable_declaration->expr != nullptr) { | |
| 1089 | implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr); | |
| 1090 | if (implicit_type->id == TypeTableEntryIdUnreachable) { | |
| 1091 | add_node_error(g, node, | |
| 1092 | buf_sprintf("variable initialization is unreachable")); | |
| 1093 | implicit_type = g->builtin_types.entry_invalid; | |
| 1094 | } else if (implicit_type->id == TypeTableEntryIdNumberLiteral) { | |
| 1095 | add_node_error(g, node, | |
| 1096 | buf_sprintf("unable to infer variable type")); | |
| 1097 | implicit_type = g->builtin_types.entry_invalid; | |
| 1098 | } | |
| 1099 | } | |
| 1100 | ||
| 1101 | if (implicit_type == nullptr && variable_declaration->is_const) { | |
| 1102 | add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'.")); | |
| 1103 | implicit_type = g->builtin_types.entry_invalid; | |
| 1104 | } | |
| 1105 | ||
| 1106 | TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; | |
| 1107 | assert(type != nullptr); // should have been caught by the parser | |
| 1108 | ||
| 1109 | LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol); | |
| 1110 | if (existing_variable) { | |
| 1111 | add_node_error(g, node, | |
| 1112 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol))); | |
| 1113 | } else { | |
| 1114 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); | |
| 1115 | buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); | |
| 1116 | variable_entry->type = type; | |
| 1117 | variable_entry->is_const = variable_declaration->is_const; | |
| 1118 | variable_entry->is_ptr = true; | |
| 1119 | variable_entry->decl_node = node; | |
| 1120 | context->variable_table.put(&variable_entry->name, variable_entry); | |
| 1121 | } | |
| 1122 | return g->builtin_types.entry_void; | |
| 1123 | } | |
| 1124 | ||
| 1125 | static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 1126 | TypeTableEntry *expected_type, AstNode *node) | |
| 1127 | { | |
| 1128 | TypeTableEntry *num_lit_type = g->num_lit_types[node->data.number_literal.kind]; | |
| 1129 | if (node->data.number_literal.overflow) { | |
| 1130 | add_node_error(g, node, | |
| 1131 | buf_sprintf("number literal too large to be represented in any type")); | |
| 1132 | return g->builtin_types.entry_invalid; | |
| 1133 | } else if (expected_type) { | |
| 1134 | if (expected_type->id == TypeTableEntryIdInvalid) { | |
| 1135 | return g->builtin_types.entry_invalid; | |
| 1136 | } else if (num_lit_fits_in_other_type(g, num_lit_type, expected_type)) { | |
| 1137 | NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; | |
| 1138 | assert(!codegen_num_lit->resolved_type); | |
| 1139 | codegen_num_lit->resolved_type = expected_type; | |
| 1140 | ||
| 1141 | return expected_type; | |
| 1142 | } else { | |
| 1143 | add_node_error(g, node, buf_sprintf("expected type '%s', got '%s'", | |
| 1144 | buf_ptr(&expected_type->name), buf_ptr(&num_lit_type->name))); | |
| 1145 | return g->builtin_types.entry_invalid; | |
| 1146 | } | |
| 1147 | } else { | |
| 1148 | return num_lit_type; | |
| 1149 | } | |
| 1150 | } | |
| 1151 | ||
| 802 | 1152 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 803 | 1153 | TypeTableEntry *expected_type, AstNode *node) |
| 804 | 1154 | { |
| ... | ... | @@ -854,51 +1204,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 854 | 1204 | break; |
| 855 | 1205 | } |
| 856 | 1206 | case NodeTypeVariableDeclaration: |
| 857 | { | |
| 858 | AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;; | |
| 859 | ||
| 860 | TypeTableEntry *explicit_type = nullptr; | |
| 861 | if (variable_declaration->type != nullptr) { | |
| 862 | explicit_type = resolve_type(g, variable_declaration->type); | |
| 863 | if (explicit_type->id == TypeTableEntryIdUnreachable) { | |
| 864 | add_node_error(g, variable_declaration->type, | |
| 865 | buf_sprintf("variable of type 'unreachable' not allowed")); | |
| 866 | } | |
| 867 | } | |
| 868 | ||
| 869 | TypeTableEntry *implicit_type = nullptr; | |
| 870 | if (variable_declaration->expr != nullptr) { | |
| 871 | implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr); | |
| 872 | if (implicit_type->id == TypeTableEntryIdUnreachable) { | |
| 873 | add_node_error(g, node, | |
| 874 | buf_sprintf("variable initialization is unreachable")); | |
| 875 | } | |
| 876 | } | |
| 877 | ||
| 878 | if (implicit_type == nullptr && variable_declaration->is_const) { | |
| 879 | add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'.")); | |
| 880 | } | |
| 881 | ||
| 882 | TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; | |
| 883 | assert(type != nullptr); // should have been caught by the parser | |
| 884 | ||
| 885 | LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol); | |
| 886 | if (existing_variable) { | |
| 887 | add_node_error(g, node, | |
| 888 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol))); | |
| 889 | } else { | |
| 890 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); | |
| 891 | buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); | |
| 892 | variable_entry->type = type; | |
| 893 | variable_entry->is_const = variable_declaration->is_const; | |
| 894 | variable_entry->is_ptr = true; | |
| 895 | variable_entry->decl_node = node; | |
| 896 | context->variable_table.put(&variable_entry->name, variable_entry); | |
| 897 | } | |
| 898 | return_type = g->builtin_types.entry_void; | |
| 899 | break; | |
| 900 | } | |
| 901 | ||
| 1207 | return_type = analyze_variable_declaration(g, import, context, expected_type, node); | |
| 1208 | break; | |
| 902 | 1209 | case NodeTypeGoto: |
| 903 | 1210 | { |
| 904 | 1211 | FnTableEntry *fn_table_entry = get_context_fn_entry(context); |
| ... | ... | @@ -928,120 +1235,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 928 | 1235 | break; |
| 929 | 1236 | } |
| 930 | 1237 | case NodeTypeBinOpExpr: |
| 931 | { | |
| 932 | switch (node->data.bin_op_expr.bin_op) { | |
| 933 | case BinOpTypeAssign: | |
| 934 | case BinOpTypeAssignTimes: | |
| 935 | case BinOpTypeAssignDiv: | |
| 936 | case BinOpTypeAssignMod: | |
| 937 | case BinOpTypeAssignPlus: | |
| 938 | case BinOpTypeAssignMinus: | |
| 939 | case BinOpTypeAssignBitShiftLeft: | |
| 940 | case BinOpTypeAssignBitShiftRight: | |
| 941 | case BinOpTypeAssignBitAnd: | |
| 942 | case BinOpTypeAssignBitXor: | |
| 943 | case BinOpTypeAssignBitOr: | |
| 944 | case BinOpTypeAssignBoolAnd: | |
| 945 | case BinOpTypeAssignBoolOr: | |
| 946 | { | |
| 947 | AstNode *lhs_node = node->data.bin_op_expr.op1; | |
| 948 | TypeTableEntry *expected_rhs_type = nullptr; | |
| 949 | if (lhs_node->type == NodeTypeSymbol) { | |
| 950 | Buf *name = &lhs_node->data.symbol; | |
| 951 | LocalVariableTableEntry *var = find_local_variable(context, name); | |
| 952 | if (var) { | |
| 953 | if (var->is_const) { | |
| 954 | add_node_error(g, lhs_node, | |
| 955 | buf_sprintf("cannot assign to constant variable")); | |
| 956 | } else { | |
| 957 | if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) { | |
| 958 | add_node_error(g, lhs_node, | |
| 959 | buf_sprintf("operator not allowed for type '%s'", buf_ptr(&var->type->name))); | |
| 960 | } else { | |
| 961 | expected_rhs_type = var->type; | |
| 962 | } | |
| 963 | } | |
| 964 | } else { | |
| 965 | add_node_error(g, lhs_node, | |
| 966 | buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); | |
| 967 | } | |
| 968 | } else if (lhs_node->type == NodeTypeArrayAccessExpr) { | |
| 969 | expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node); | |
| 970 | } else if (lhs_node->type == NodeTypeFieldAccessExpr) { | |
| 971 | alloc_codegen_node(lhs_node); | |
| 972 | expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node); | |
| 973 | } else { | |
| 974 | add_node_error(g, lhs_node, | |
| 975 | buf_sprintf("assignment target must be variable, field, or array element")); | |
| 976 | } | |
| 977 | analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); | |
| 978 | return_type = g->builtin_types.entry_void; | |
| 979 | break; | |
| 980 | } | |
| 981 | case BinOpTypeBoolOr: | |
| 982 | case BinOpTypeBoolAnd: | |
| 983 | analyze_expression(g, import, context, g->builtin_types.entry_bool, | |
| 984 | node->data.bin_op_expr.op1); | |
| 985 | analyze_expression(g, import, context, g->builtin_types.entry_bool, | |
| 986 | node->data.bin_op_expr.op2); | |
| 987 | return_type = g->builtin_types.entry_bool; | |
| 988 | break; | |
| 989 | case BinOpTypeCmpEq: | |
| 990 | case BinOpTypeCmpNotEq: | |
| 991 | case BinOpTypeCmpLessThan: | |
| 992 | case BinOpTypeCmpGreaterThan: | |
| 993 | case BinOpTypeCmpLessOrEq: | |
| 994 | case BinOpTypeCmpGreaterOrEq: | |
| 995 | // TODO think how should type checking for these work? | |
| 996 | analyze_expression(g, import, context, g->builtin_types.entry_i32, | |
| 997 | node->data.bin_op_expr.op1); | |
| 998 | analyze_expression(g, import, context, g->builtin_types.entry_i32, | |
| 999 | node->data.bin_op_expr.op2); | |
| 1000 | return_type = g->builtin_types.entry_bool; | |
| 1001 | break; | |
| 1002 | case BinOpTypeBinOr: | |
| 1003 | case BinOpTypeBinXor: | |
| 1004 | case BinOpTypeBinAnd: | |
| 1005 | { | |
| 1006 | // TODO: don't require i32 | |
| 1007 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | |
| 1008 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | |
| 1009 | return_type = g->builtin_types.entry_i32; | |
| 1010 | break; | |
| 1011 | } | |
| 1012 | case BinOpTypeBitShiftLeft: | |
| 1013 | case BinOpTypeBitShiftRight: | |
| 1014 | { | |
| 1015 | // TODO: don't require i32 | |
| 1016 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | |
| 1017 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | |
| 1018 | return_type = g->builtin_types.entry_i32; | |
| 1019 | break; | |
| 1020 | } | |
| 1021 | case BinOpTypeAdd: | |
| 1022 | case BinOpTypeSub: | |
| 1023 | // TODO think how should type checking for these work? | |
| 1024 | analyze_expression(g, import, context, g->builtin_types.entry_i32, | |
| 1025 | node->data.bin_op_expr.op1); | |
| 1026 | analyze_expression(g, import, context, g->builtin_types.entry_i32, | |
| 1027 | node->data.bin_op_expr.op2); | |
| 1028 | return_type = g->builtin_types.entry_i32; | |
| 1029 | break; | |
| 1030 | case BinOpTypeMult: | |
| 1031 | case BinOpTypeDiv: | |
| 1032 | case BinOpTypeMod: | |
| 1033 | { | |
| 1034 | // TODO: don't require i32 | |
| 1035 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | |
| 1036 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | |
| 1037 | return_type = g->builtin_types.entry_i32; | |
| 1038 | break; | |
| 1039 | } | |
| 1040 | case BinOpTypeInvalid: | |
| 1041 | zig_unreachable(); | |
| 1042 | } | |
| 1043 | break; | |
| 1044 | } | |
| 1238 | return_type = analyze_bin_op_expr(g, import, context, expected_type, node); | |
| 1239 | break; | |
| 1045 | 1240 | |
| 1046 | 1241 | case NodeTypeFnCallExpr: |
| 1047 | 1242 | { |
| ... | ... | @@ -1116,10 +1311,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1116 | 1311 | return_type = analyze_field_access_expr(g, import, context, node); |
| 1117 | 1312 | break; |
| 1118 | 1313 | case NodeTypeNumberLiteral: |
| 1119 | // TODO: generic literal int type | |
| 1120 | return_type = g->builtin_types.entry_i32; | |
| 1314 | return_type = analyze_number_literal_expr(g, import, context, expected_type, node); | |
| 1121 | 1315 | break; |
| 1122 | ||
| 1123 | 1316 | case NodeTypeStringLiteral: |
| 1124 | 1317 | if (node->data.string_literal.c) { |
| 1125 | 1318 | return_type = g->builtin_types.entry_c_string_literal; |
src/analyze.hpp+18-1| ... | ... | @@ -42,6 +42,10 @@ struct TypeTableEntryStruct { |
| 42 | 42 | TypeStructField *fields; |
| 43 | 43 | }; |
| 44 | 44 | |
| 45 | struct TypeTableEntryNumLit { | |
| 46 | NumLit kind; | |
| 47 | }; | |
| 48 | ||
| 45 | 49 | enum TypeTableEntryId { |
| 46 | 50 | TypeTableEntryIdInvalid, |
| 47 | 51 | TypeTableEntryIdVoid, |
| ... | ... | @@ -52,6 +56,7 @@ enum TypeTableEntryId { |
| 52 | 56 | TypeTableEntryIdPointer, |
| 53 | 57 | TypeTableEntryIdArray, |
| 54 | 58 | TypeTableEntryIdStruct, |
| 59 | TypeTableEntryIdNumberLiteral, | |
| 55 | 60 | }; |
| 56 | 61 | |
| 57 | 62 | struct TypeTableEntry { |
| ... | ... | @@ -69,12 +74,13 @@ struct TypeTableEntry { |
| 69 | 74 | TypeTableEntryInt integral; |
| 70 | 75 | TypeTableEntryArray array; |
| 71 | 76 | TypeTableEntryStruct structure; |
| 77 | TypeTableEntryNumLit num_lit; | |
| 72 | 78 | } data; |
| 73 | 79 | |
| 74 | 80 | // use these fields to make sure we don't duplicate type table entries for the same type |
| 75 | 81 | TypeTableEntry *pointer_const_parent; |
| 76 | 82 | TypeTableEntry *pointer_mut_parent; |
| 77 | HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size; | |
| 83 | HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size; | |
| 78 | 84 | |
| 79 | 85 | }; |
| 80 | 86 | |
| ... | ... | @@ -134,10 +140,13 @@ struct CodeGen { |
| 134 | 140 | struct { |
| 135 | 141 | TypeTableEntry *entry_bool; |
| 136 | 142 | TypeTableEntry *entry_u8; |
| 143 | TypeTableEntry *entry_u64; | |
| 137 | 144 | TypeTableEntry *entry_i32; |
| 145 | TypeTableEntry *entry_i64; | |
| 138 | 146 | TypeTableEntry *entry_isize; |
| 139 | 147 | TypeTableEntry *entry_usize; |
| 140 | 148 | TypeTableEntry *entry_f32; |
| 149 | TypeTableEntry *entry_f64; | |
| 141 | 150 | TypeTableEntry *entry_c_string_literal; |
| 142 | 151 | TypeTableEntry *entry_string; |
| 143 | 152 | TypeTableEntry *entry_void; |
| ... | ... | @@ -145,6 +154,8 @@ struct CodeGen { |
| 145 | 154 | TypeTableEntry *entry_invalid; |
| 146 | 155 | } builtin_types; |
| 147 | 156 | |
| 157 | TypeTableEntry *num_lit_types[NumLitCount]; | |
| 158 | ||
| 148 | 159 | LLVMTargetDataRef target_data_ref; |
| 149 | 160 | unsigned pointer_size_bytes; |
| 150 | 161 | bool is_static; |
| ... | ... | @@ -242,6 +253,7 @@ enum CastOp { |
| 242 | 253 | CastOpPtrToInt, |
| 243 | 254 | CastOpIntWidenOrShorten, |
| 244 | 255 | CastOpArrayToString, |
| 256 | CastOpNothing, | |
| 245 | 257 | }; |
| 246 | 258 | |
| 247 | 259 | struct CastNode { |
| ... | ... | @@ -251,6 +263,10 @@ struct CastNode { |
| 251 | 263 | LLVMValueRef ptr; |
| 252 | 264 | }; |
| 253 | 265 | |
| 266 | struct NumberLiteralNode { | |
| 267 | TypeTableEntry *resolved_type; | |
| 268 | }; | |
| 269 | ||
| 254 | 270 | struct CodeGenNode { |
| 255 | 271 | union { |
| 256 | 272 | TypeNode type_node; // for NodeTypeType |
| ... | ... | @@ -262,6 +278,7 @@ struct CodeGenNode { |
| 262 | 278 | StructDeclNode struct_decl_node; // for NodeTypeStructDecl |
| 263 | 279 | FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr |
| 264 | 280 | CastNode cast_node; // for NodeTypeCastExpr |
| 281 | NumberLiteralNode num_lit_node; // for NodeTypeNumberLiteral | |
| 265 | 282 | } data; |
| 266 | 283 | ExprNode expr_node; // for all the expression nodes |
| 267 | 284 | }; |
src/codegen.cpp+90-5| ... | ... | @@ -279,6 +279,8 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { |
| 279 | 279 | CastNode *cast_node = &node->codegen_node->data.cast_node; |
| 280 | 280 | |
| 281 | 281 | switch (cast_node->op) { |
| 282 | case CastOpNothing: | |
| 283 | return expr_val; | |
| 282 | 284 | case CastOpPtrToInt: |
| 283 | 285 | return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); |
| 284 | 286 | case CastOpIntWidenOrShorten: |
| ... | ... | @@ -901,11 +903,29 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 901 | 903 | return gen_asm_expr(g, node); |
| 902 | 904 | case NodeTypeNumberLiteral: |
| 903 | 905 | { |
| 904 | Buf *number_str = &node->data.number; | |
| 905 | LLVMTypeRef number_type = LLVMInt32Type(); | |
| 906 | LLVMValueRef number_val = LLVMConstIntOfStringAndSize(number_type, | |
| 907 | buf_ptr(number_str), buf_len(number_str), 10); | |
| 908 | return number_val; | |
| 906 | NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; | |
| 907 | assert(codegen_num_lit); | |
| 908 | TypeTableEntry *type_entry = codegen_num_lit->resolved_type; | |
| 909 | assert(type_entry); | |
| 910 | ||
| 911 | // TODO this is kinda iffy. make sure josh is on board with this | |
| 912 | node->codegen_node->expr_node.type_entry = type_entry; | |
| 913 | ||
| 914 | if (type_entry->id == TypeTableEntryIdInt) { | |
| 915 | // here the union has int64_t and uint64_t and we purposefully read | |
| 916 | // the uint64_t value in either case, because we want the twos | |
| 917 | // complement representation | |
| 918 | ||
| 919 | return LLVMConstInt(type_entry->type_ref, | |
| 920 | node->data.number_literal.data.x_uint, | |
| 921 | type_entry->data.integral.is_signed); | |
| 922 | } else if (type_entry->id == TypeTableEntryIdFloat) { | |
| 923 | ||
| 924 | return LLVMConstReal(type_entry->type_ref, | |
| 925 | node->data.number_literal.data.x_float); | |
| 926 | } else { | |
| 927 | zig_panic("bad number literal type"); | |
| 928 | } | |
| 909 | 929 | } |
| 910 | 930 | case NodeTypeStringLiteral: |
| 911 | 931 | { |
| ... | ... | @@ -1186,6 +1206,20 @@ static void do_code_gen(CodeGen *g) { |
| 1186 | 1206 | #endif |
| 1187 | 1207 | } |
| 1188 | 1208 | |
| 1209 | static const NumLit num_lit_kinds[] = { | |
| 1210 | NumLitF32, | |
| 1211 | NumLitF64, | |
| 1212 | NumLitF128, | |
| 1213 | NumLitI8, | |
| 1214 | NumLitU8, | |
| 1215 | NumLitI16, | |
| 1216 | NumLitU16, | |
| 1217 | NumLitI32, | |
| 1218 | NumLitU32, | |
| 1219 | NumLitI64, | |
| 1220 | NumLitU64, | |
| 1221 | }; | |
| 1222 | ||
| 1189 | 1223 | static void define_builtin_types(CodeGen *g) { |
| 1190 | 1224 | { |
| 1191 | 1225 | // if this type is anywhere in the AST, we should never hit codegen. |
| ... | ... | @@ -1193,6 +1227,19 @@ static void define_builtin_types(CodeGen *g) { |
| 1193 | 1227 | buf_init_from_str(&entry->name, "(invalid)"); |
| 1194 | 1228 | g->builtin_types.entry_invalid = entry; |
| 1195 | 1229 | } |
| 1230 | ||
| 1231 | assert(NumLitCount == array_length(num_lit_kinds)); | |
| 1232 | for (int i = 0; i < NumLitCount; i += 1) { | |
| 1233 | NumLit num_lit_kind = num_lit_kinds[i]; | |
| 1234 | // This type should just create a constant with whatever actual number | |
| 1235 | // type is expected at the time. | |
| 1236 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumberLiteral); | |
| 1237 | buf_resize(&entry->name, 0); | |
| 1238 | buf_appendf(&entry->name, "(%s literal)", num_lit_str(num_lit_kind)); | |
| 1239 | entry->data.num_lit.kind = num_lit_kind; | |
| 1240 | g->num_lit_types[i] = entry; | |
| 1241 | } | |
| 1242 | ||
| 1196 | 1243 | { |
| 1197 | 1244 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool); |
| 1198 | 1245 | entry->type_ref = LLVMInt1Type(); |
| ... | ... | @@ -1217,6 +1264,19 @@ static void define_builtin_types(CodeGen *g) { |
| 1217 | 1264 | g->type_table.put(&entry->name, entry); |
| 1218 | 1265 | g->builtin_types.entry_u8 = entry; |
| 1219 | 1266 | } |
| 1267 | { | |
| 1268 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); | |
| 1269 | entry->type_ref = LLVMInt64Type(); | |
| 1270 | buf_init_from_str(&entry->name, "u64"); | |
| 1271 | entry->size_in_bits = 64; | |
| 1272 | entry->align_in_bits = 64; | |
| 1273 | entry->data.integral.is_signed = false; | |
| 1274 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), | |
| 1275 | entry->size_in_bits, entry->align_in_bits, | |
| 1276 | LLVMZigEncoding_DW_ATE_unsigned()); | |
| 1277 | g->type_table.put(&entry->name, entry); | |
| 1278 | g->builtin_types.entry_u64 = entry; | |
| 1279 | } | |
| 1220 | 1280 | g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true); |
| 1221 | 1281 | { |
| 1222 | 1282 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); |
| ... | ... | @@ -1231,6 +1291,19 @@ static void define_builtin_types(CodeGen *g) { |
| 1231 | 1291 | g->type_table.put(&entry->name, entry); |
| 1232 | 1292 | g->builtin_types.entry_i32 = entry; |
| 1233 | 1293 | } |
| 1294 | { | |
| 1295 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); | |
| 1296 | entry->type_ref = LLVMInt64Type(); | |
| 1297 | buf_init_from_str(&entry->name, "i64"); | |
| 1298 | entry->size_in_bits = 64; | |
| 1299 | entry->align_in_bits = 64; | |
| 1300 | entry->data.integral.is_signed = true; | |
| 1301 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), | |
| 1302 | entry->size_in_bits, entry->align_in_bits, | |
| 1303 | LLVMZigEncoding_DW_ATE_signed()); | |
| 1304 | g->type_table.put(&entry->name, entry); | |
| 1305 | g->builtin_types.entry_i64 = entry; | |
| 1306 | } | |
| 1234 | 1307 | { |
| 1235 | 1308 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); |
| 1236 | 1309 | entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); |
| ... | ... | @@ -1269,6 +1342,18 @@ static void define_builtin_types(CodeGen *g) { |
| 1269 | 1342 | g->type_table.put(&entry->name, entry); |
| 1270 | 1343 | g->builtin_types.entry_f32 = entry; |
| 1271 | 1344 | } |
| 1345 | { | |
| 1346 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); | |
| 1347 | entry->type_ref = LLVMFloatType(); | |
| 1348 | buf_init_from_str(&entry->name, "f64"); | |
| 1349 | entry->size_in_bits = 64; | |
| 1350 | entry->align_in_bits = 64; | |
| 1351 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), | |
| 1352 | entry->size_in_bits, entry->align_in_bits, | |
| 1353 | LLVMZigEncoding_DW_ATE_float()); | |
| 1354 | g->type_table.put(&entry->name, entry); | |
| 1355 | g->builtin_types.entry_f64 = entry; | |
| 1356 | } | |
| 1272 | 1357 | { |
| 1273 | 1358 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid); |
| 1274 | 1359 | entry->type_ref = LLVMVoidType(); |
src/parseh.cpp+1-1| ... | ... | @@ -544,7 +544,7 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI |
| 544 | 544 | |
| 545 | 545 | char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS"); |
| 546 | 546 | if (ZIG_PARSEH_CFLAGS) { |
| 547 | Buf tmp_buf = {0}; | |
| 547 | Buf tmp_buf = BUF_INIT; | |
| 548 | 548 | char *start = ZIG_PARSEH_CFLAGS; |
| 549 | 549 | char *space = strstr(start, " "); |
| 550 | 550 | while (space) { |
src/parser.cpp+313-4| ... | ... | @@ -12,6 +12,7 @@ |
| 12 | 12 | #include <stdarg.h> |
| 13 | 13 | #include <stdio.h> |
| 14 | 14 | |
| 15 | ||
| 15 | 16 | static const char *bin_op_str(BinOpType bin_op) { |
| 16 | 17 | switch (bin_op) { |
| 17 | 18 | case BinOpTypeInvalid: return "(invalid)"; |
| ... | ... | @@ -273,9 +274,19 @@ void ast_print(AstNode *node, int indent) { |
| 273 | 274 | ast_print(node->data.prefix_op_expr.primary_expr, indent + 2); |
| 274 | 275 | break; |
| 275 | 276 | case NodeTypeNumberLiteral: |
| 276 | fprintf(stderr, "NumberLiteral %s\n", | |
| 277 | buf_ptr(&node->data.number)); | |
| 278 | break; | |
| 277 | { | |
| 278 | NumLit num_lit = node->data.number_literal.kind; | |
| 279 | const char *name = node_type_str(node->type); | |
| 280 | const char *kind_str = num_lit_str(num_lit); | |
| 281 | if (is_num_lit_signed(num_lit)) { | |
| 282 | fprintf(stderr, "%s %s %" PRId64 "\n", name, kind_str, node->data.number_literal.data.x_int); | |
| 283 | } else if (is_num_lit_unsigned(num_lit)) { | |
| 284 | fprintf(stderr, "%s %s %" PRIu64 "\n", name, kind_str, node->data.number_literal.data.x_uint); | |
| 285 | } else { | |
| 286 | fprintf(stderr, "%s %s %f\n", name, kind_str, node->data.number_literal.data.x_float); | |
| 287 | } | |
| 288 | break; | |
| 289 | } | |
| 279 | 290 | case NodeTypeStringLiteral: |
| 280 | 291 | { |
| 281 | 292 | 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 |
| 574 | 585 | if (offset_map) offset_map->append(pos); |
| 575 | 586 | } |
| 576 | 587 | |
| 588 | enum ParseNumLitState { | |
| 589 | ParseNumLitStateStart, | |
| 590 | ParseNumLitStateBase, | |
| 591 | ParseNumLitStateDigits, | |
| 592 | ParseNumLitStateExpectFirstDigit, | |
| 593 | ParseNumLitStateDecimal, | |
| 594 | ParseNumLitStateESign, | |
| 595 | ParseNumLitStateEDigit, | |
| 596 | }; | |
| 597 | ||
| 598 | static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLiteral *num_lit) { | |
| 599 | ParseNumLitState state = ParseNumLitStateStart; | |
| 600 | unsigned long long base = 10; | |
| 601 | bool negative = false; | |
| 602 | int digits_start; | |
| 603 | int digits_end; | |
| 604 | int decimal_start = -1; | |
| 605 | int decimal_end; | |
| 606 | bool e_present = false; | |
| 607 | bool e_positive; | |
| 608 | int e_digit_start; | |
| 609 | int e_digit_end; | |
| 610 | ||
| 611 | for (int i = token->start_pos; i < token->end_pos; i += 1) { | |
| 612 | uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i); | |
| 613 | switch (state) { | |
| 614 | case ParseNumLitStateStart: | |
| 615 | if (c == '-') { | |
| 616 | negative = true; | |
| 617 | } else if (c == '0') { | |
| 618 | state = ParseNumLitStateBase; | |
| 619 | } else if (c >= '1' && c <= '9') { | |
| 620 | digits_start = i; | |
| 621 | state = ParseNumLitStateDigits; | |
| 622 | } else { | |
| 623 | zig_unreachable(); | |
| 624 | } | |
| 625 | break; | |
| 626 | case ParseNumLitStateBase: | |
| 627 | if (c == 'x') { | |
| 628 | base = 16; | |
| 629 | state = ParseNumLitStateExpectFirstDigit; | |
| 630 | } else if (c == 'o') { | |
| 631 | base = 8; | |
| 632 | state = ParseNumLitStateExpectFirstDigit; | |
| 633 | } else if (c == 'b') { | |
| 634 | base = 2; | |
| 635 | state = ParseNumLitStateExpectFirstDigit; | |
| 636 | } else { | |
| 637 | zig_unreachable(); | |
| 638 | } | |
| 639 | break; | |
| 640 | ||
| 641 | case ParseNumLitStateExpectFirstDigit: | |
| 642 | state = ParseNumLitStateDigits; | |
| 643 | break; | |
| 644 | ||
| 645 | case ParseNumLitStateDigits: | |
| 646 | if (c == '.') { | |
| 647 | assert(base == 10); | |
| 648 | digits_end = i; | |
| 649 | decimal_start = i + 1; | |
| 650 | state = ParseNumLitStateDecimal; | |
| 651 | } | |
| 652 | break; | |
| 653 | case ParseNumLitStateDecimal: | |
| 654 | if (c == 'E') { | |
| 655 | e_present = false; | |
| 656 | decimal_end = i; | |
| 657 | state = ParseNumLitStateESign; | |
| 658 | } | |
| 659 | break; | |
| 660 | case ParseNumLitStateESign: | |
| 661 | if (c == '+') { | |
| 662 | e_positive = true; | |
| 663 | e_digit_start = i + 1; | |
| 664 | state = ParseNumLitStateEDigit; | |
| 665 | } else if (c == '-') { | |
| 666 | e_positive = false; | |
| 667 | e_digit_start = i + 1; | |
| 668 | state = ParseNumLitStateEDigit; | |
| 669 | } else { | |
| 670 | zig_unreachable(); | |
| 671 | } | |
| 672 | break; | |
| 673 | case ParseNumLitStateEDigit: | |
| 674 | assert(c >= '0' && c <= '9'); | |
| 675 | break; | |
| 676 | } | |
| 677 | } | |
| 678 | ||
| 679 | switch (state) { | |
| 680 | case ParseNumLitStateDigits: | |
| 681 | digits_end = token->end_pos; | |
| 682 | break; | |
| 683 | case ParseNumLitStateDecimal: | |
| 684 | decimal_end = token->end_pos; | |
| 685 | break; | |
| 686 | case ParseNumLitStateEDigit: | |
| 687 | e_digit_end = token->end_pos; | |
| 688 | break; | |
| 689 | case ParseNumLitStateBase: | |
| 690 | num_lit->kind = NumLitU8; | |
| 691 | num_lit->data.x_uint = 0; | |
| 692 | return; | |
| 693 | case ParseNumLitStateESign: | |
| 694 | case ParseNumLitStateExpectFirstDigit: | |
| 695 | case ParseNumLitStateStart: | |
| 696 | zig_unreachable(); | |
| 697 | } | |
| 698 | ||
| 699 | if (decimal_start >= 0) { | |
| 700 | // float | |
| 701 | double x; | |
| 702 | ||
| 703 | (void)x; | |
| 704 | zig_panic("TODO parse float"); | |
| 705 | } else { | |
| 706 | // integer | |
| 707 | unsigned long long x = 0; | |
| 708 | ||
| 709 | unsigned long long mult = 1; | |
| 710 | for (int i = digits_end - 1; ; i -= 1) { | |
| 711 | uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i); | |
| 712 | unsigned long long digit = (c - '0'); | |
| 713 | ||
| 714 | // digit *= mult | |
| 715 | if (__builtin_umulll_overflow(digit, mult, &digit)) { | |
| 716 | num_lit->overflow = true; | |
| 717 | return; | |
| 718 | } | |
| 719 | ||
| 720 | // x += digit | |
| 721 | if (__builtin_uaddll_overflow(x, digit, &x)) { | |
| 722 | num_lit->overflow = true; | |
| 723 | return; | |
| 724 | } | |
| 725 | ||
| 726 | if (i == digits_start) | |
| 727 | break; | |
| 728 | ||
| 729 | // mult *= base | |
| 730 | if (__builtin_umulll_overflow(mult, base, &mult)) { | |
| 731 | num_lit->overflow = true; | |
| 732 | return; | |
| 733 | } | |
| 734 | } | |
| 735 | ||
| 736 | if (negative) { | |
| 737 | if (x <= 128ull) { | |
| 738 | num_lit->kind = NumLitI8; | |
| 739 | } else if (x <= 32768ull) { | |
| 740 | num_lit->kind = NumLitI16; | |
| 741 | } else if (x <= 2147483648ull) { | |
| 742 | num_lit->kind = NumLitI32; | |
| 743 | } else if (x <= 9223372036854775808ull) { | |
| 744 | num_lit->kind = NumLitI64; | |
| 745 | } else { | |
| 746 | num_lit->overflow = true; | |
| 747 | return; | |
| 748 | } | |
| 749 | ||
| 750 | num_lit->data.x_int = -((int64_t)x); | |
| 751 | } else { | |
| 752 | num_lit->data.x_uint = x; | |
| 753 | ||
| 754 | if (x <= UINT8_MAX) { | |
| 755 | num_lit->kind = NumLitU8; | |
| 756 | } else if (x <= UINT16_MAX) { | |
| 757 | num_lit->kind = NumLitU16; | |
| 758 | } else if (x <= UINT32_MAX) { | |
| 759 | num_lit->kind = NumLitU32; | |
| 760 | } else { | |
| 761 | num_lit->kind = NumLitU64; | |
| 762 | } | |
| 763 | } | |
| 764 | } | |
| 765 | } | |
| 766 | ||
| 767 | ||
| 577 | 768 | __attribute__ ((noreturn)) |
| 578 | 769 | static void ast_invalid_token_error(ParseContext *pc, Token *token) { |
| 579 | 770 | Buf token_value = BUF_INIT; |
| ... | ... | @@ -829,7 +1020,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 829 | 1020 | |
| 830 | 1021 | if (token->id == TokenIdNumberLiteral) { |
| 831 | 1022 | AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token); |
| 832 | ast_buf_from_token(pc, token, &node->data.number); | |
| 1023 | parse_number_literal(pc, token, &node->data.number_literal); | |
| 833 | 1024 | *token_index += 1; |
| 834 | 1025 | return node; |
| 835 | 1026 | } else if (token->id == TokenIdStringLiteral) { |
| ... | ... | @@ -2152,3 +2343,121 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, Er |
| 2152 | 2343 | pc.root = ast_parse_root(&pc, &token_index); |
| 2153 | 2344 | return pc.root; |
| 2154 | 2345 | } |
| 2346 | ||
| 2347 | const char *num_lit_str(NumLit num_lit) { | |
| 2348 | switch (num_lit) { | |
| 2349 | case NumLitF32: | |
| 2350 | return "f32"; | |
| 2351 | case NumLitF64: | |
| 2352 | return "f64"; | |
| 2353 | case NumLitF128: | |
| 2354 | return "f128"; | |
| 2355 | case NumLitI8: | |
| 2356 | return "i8"; | |
| 2357 | case NumLitI16: | |
| 2358 | return "i16"; | |
| 2359 | case NumLitI32: | |
| 2360 | return "i32"; | |
| 2361 | case NumLitI64: | |
| 2362 | return "i64"; | |
| 2363 | case NumLitU8: | |
| 2364 | return "u8"; | |
| 2365 | case NumLitU16: | |
| 2366 | return "u16"; | |
| 2367 | case NumLitU32: | |
| 2368 | return "u32"; | |
| 2369 | case NumLitU64: | |
| 2370 | return "u64"; | |
| 2371 | case NumLitCount: | |
| 2372 | zig_unreachable(); | |
| 2373 | } | |
| 2374 | zig_unreachable(); | |
| 2375 | } | |
| 2376 | ||
| 2377 | bool is_num_lit_signed(NumLit num_lit) { | |
| 2378 | switch (num_lit) { | |
| 2379 | case NumLitI8: | |
| 2380 | case NumLitI16: | |
| 2381 | case NumLitI32: | |
| 2382 | case NumLitI64: | |
| 2383 | return true; | |
| 2384 | ||
| 2385 | case NumLitF32: | |
| 2386 | case NumLitF64: | |
| 2387 | case NumLitF128: | |
| 2388 | case NumLitU8: | |
| 2389 | case NumLitU16: | |
| 2390 | case NumLitU32: | |
| 2391 | case NumLitU64: | |
| 2392 | return false; | |
| 2393 | case NumLitCount: | |
| 2394 | zig_unreachable(); | |
| 2395 | } | |
| 2396 | zig_unreachable(); | |
| 2397 | } | |
| 2398 | ||
| 2399 | bool is_num_lit_unsigned(NumLit num_lit) { | |
| 2400 | switch (num_lit) { | |
| 2401 | case NumLitF32: | |
| 2402 | case NumLitF64: | |
| 2403 | case NumLitF128: | |
| 2404 | case NumLitI8: | |
| 2405 | case NumLitI16: | |
| 2406 | case NumLitI32: | |
| 2407 | case NumLitI64: | |
| 2408 | return false; | |
| 2409 | case NumLitU8: | |
| 2410 | case NumLitU16: | |
| 2411 | case NumLitU32: | |
| 2412 | case NumLitU64: | |
| 2413 | return true; | |
| 2414 | case NumLitCount: | |
| 2415 | zig_unreachable(); | |
| 2416 | } | |
| 2417 | zig_unreachable(); | |
| 2418 | } | |
| 2419 | ||
| 2420 | bool is_num_lit_float(NumLit num_lit) { | |
| 2421 | switch (num_lit) { | |
| 2422 | case NumLitF32: | |
| 2423 | case NumLitF64: | |
| 2424 | case NumLitF128: | |
| 2425 | return true; | |
| 2426 | case NumLitI8: | |
| 2427 | case NumLitI16: | |
| 2428 | case NumLitI32: | |
| 2429 | case NumLitI64: | |
| 2430 | case NumLitU8: | |
| 2431 | case NumLitU16: | |
| 2432 | case NumLitU32: | |
| 2433 | case NumLitU64: | |
| 2434 | return false; | |
| 2435 | case NumLitCount: | |
| 2436 | zig_unreachable(); | |
| 2437 | } | |
| 2438 | zig_unreachable(); | |
| 2439 | } | |
| 2440 | ||
| 2441 | uint64_t num_lit_bit_count(NumLit num_lit) { | |
| 2442 | switch (num_lit) { | |
| 2443 | case NumLitI8: | |
| 2444 | case NumLitU8: | |
| 2445 | return 8; | |
| 2446 | case NumLitI16: | |
| 2447 | case NumLitU16: | |
| 2448 | return 16; | |
| 2449 | case NumLitI32: | |
| 2450 | case NumLitU32: | |
| 2451 | case NumLitF32: | |
| 2452 | return 32; | |
| 2453 | case NumLitI64: | |
| 2454 | case NumLitU64: | |
| 2455 | case NumLitF64: | |
| 2456 | return 64; | |
| 2457 | case NumLitF128: | |
| 2458 | return 128; | |
| 2459 | case NumLitCount: | |
| 2460 | zig_unreachable(); | |
| 2461 | } | |
| 2462 | zig_unreachable(); | |
| 2463 | } |
src/parser.hpp+38-1| ... | ... | @@ -267,6 +267,36 @@ struct AstNodeStringLiteral { |
| 267 | 267 | bool c; |
| 268 | 268 | }; |
| 269 | 269 | |
| 270 | enum NumLit { | |
| 271 | NumLitF32, | |
| 272 | NumLitF64, | |
| 273 | NumLitF128, | |
| 274 | NumLitI8, | |
| 275 | NumLitU8, | |
| 276 | NumLitI16, | |
| 277 | NumLitU16, | |
| 278 | NumLitI32, | |
| 279 | NumLitU32, | |
| 280 | NumLitI64, | |
| 281 | NumLitU64, | |
| 282 | ||
| 283 | NumLitCount | |
| 284 | }; | |
| 285 | ||
| 286 | struct AstNodeNumberLiteral { | |
| 287 | NumLit kind; | |
| 288 | ||
| 289 | // overflow is true if when parsing the number, we discovered it would not | |
| 290 | // fit without losing data in a uint64_t, int64_t, or double | |
| 291 | bool overflow; | |
| 292 | ||
| 293 | union { | |
| 294 | uint64_t x_uint; | |
| 295 | int64_t x_int; | |
| 296 | double x_float; | |
| 297 | } data; | |
| 298 | }; | |
| 299 | ||
| 270 | 300 | struct AstNode { |
| 271 | 301 | enum NodeType type; |
| 272 | 302 | int line; |
| ... | ... | @@ -300,7 +330,7 @@ struct AstNode { |
| 300 | 330 | AstNodeStructDecl struct_decl; |
| 301 | 331 | AstNodeStructField struct_field; |
| 302 | 332 | AstNodeStringLiteral string_literal; |
| 303 | Buf number; | |
| 333 | AstNodeNumberLiteral number_literal; | |
| 304 | 334 | Buf symbol; |
| 305 | 335 | bool bool_literal; |
| 306 | 336 | } data; |
| ... | ... | @@ -329,4 +359,11 @@ const char *node_type_str(NodeType node_type); |
| 329 | 359 | |
| 330 | 360 | void ast_print(AstNode *node, int indent); |
| 331 | 361 | |
| 362 | const char *num_lit_str(NumLit num_lit); | |
| 363 | bool is_num_lit_signed(NumLit num_lit); | |
| 364 | bool is_num_lit_unsigned(NumLit num_lit); | |
| 365 | bool is_num_lit_float(NumLit num_lit); | |
| 366 | uint64_t num_lit_bit_count(NumLit num_lit); | |
| 367 | ||
| 368 | ||
| 332 | 369 | #endif |
src/util.cpp+9-1| ... | ... | @@ -21,8 +21,16 @@ void zig_panic(const char *format, ...) { |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | 23 | uint32_t int_hash(int i) { |
| 24 | return *reinterpret_cast<uint32_t*>(&i); | |
| 24 | return (uint32_t)(i % UINT32_MAX); | |
| 25 | 25 | } |
| 26 | 26 | bool int_eq(int a, int b) { |
| 27 | 27 | return a == b; |
| 28 | 28 | } |
| 29 | ||
| 30 | uint32_t uint64_hash(uint64_t i) { | |
| 31 | return (uint32_t)(i % UINT32_MAX); | |
| 32 | } | |
| 33 | ||
| 34 | bool uint64_eq(uint64_t a, uint64_t b) { | |
| 35 | return a == b; | |
| 36 | } |
src/util.hpp+2| ... | ... | @@ -81,5 +81,7 @@ static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) |
| 81 | 81 | |
| 82 | 82 | uint32_t int_hash(int i); |
| 83 | 83 | bool int_eq(int a, int b); |
| 84 | uint32_t uint64_hash(uint64_t i); | |
| 85 | bool uint64_eq(uint64_t a, uint64_t b); | |
| 84 | 86 | |
| 85 | 87 | #endif |
std/std.zig+3-4| ... | ... | @@ -15,10 +15,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { |
| 15 | 15 | |
| 16 | 16 | // TODO error handling |
| 17 | 17 | // TODO handle buffering and flushing |
| 18 | // TODO non-i32 integer literals so we can remove the casts | |
| 19 | 18 | // TODO constants for SYS_write and stdout_fileno |
| 20 | 19 | pub fn print_str(str : string) -> isize { |
| 21 | let SYS_write = 1; | |
| 22 | let stdout_fileno = 1; | |
| 23 | return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); | |
| 20 | let SYS_write : isize = 1; | |
| 21 | let stdout_fileno : isize = 1; | |
| 22 | return syscall3(SYS_write, stdout_fileno, str.ptr as isize, str.len as isize); | |
| 24 | 23 | } |
test/run_tests.cpp+21-12| ... | ... | @@ -266,7 +266,7 @@ extern { |
| 266 | 266 | |
| 267 | 267 | export fn _start() -> unreachable { |
| 268 | 268 | let a : i32 = 1; |
| 269 | let b = 2; | |
| 269 | let b = 2 as i32; | |
| 270 | 270 | if (a + b == 3) { |
| 271 | 271 | puts(c"OK"); |
| 272 | 272 | } |
| ... | ... | @@ -299,12 +299,12 @@ extern { |
| 299 | 299 | |
| 300 | 300 | export fn _start() -> unreachable { |
| 301 | 301 | if (true) { |
| 302 | let no_conflict = 5; | |
| 302 | let no_conflict : i32 = 5; | |
| 303 | 303 | if (no_conflict == 5) { puts(c"OK 1"); } |
| 304 | 304 | } |
| 305 | 305 | |
| 306 | 306 | let c = { |
| 307 | let no_conflict = 10; | |
| 307 | let no_conflict = 10 as i32; | |
| 308 | 308 | no_conflict |
| 309 | 309 | }; |
| 310 | 310 | if (c == 10) { puts(c"OK 2"); } |
| ... | ... | @@ -343,7 +343,7 @@ export fn _start() -> unreachable { |
| 343 | 343 | let mut zero : i32; |
| 344 | 344 | if (zero == 0) { puts(c"zero"); } |
| 345 | 345 | |
| 346 | let mut i = 0; | |
| 346 | let mut i = 0 as i32; | |
| 347 | 347 | loop_start: |
| 348 | 348 | if i == 3 { |
| 349 | 349 | goto done; |
| ... | ... | @@ -366,7 +366,7 @@ extern { |
| 366 | 366 | export fn _start() -> unreachable { |
| 367 | 367 | let mut array : [i32; 5]; |
| 368 | 368 | |
| 369 | let mut i = 0; | |
| 369 | let mut i : i32 = 0; | |
| 370 | 370 | loop_start: |
| 371 | 371 | if i == 5 { |
| 372 | 372 | goto loop_end; |
| ... | ... | @@ -378,7 +378,7 @@ loop_start: |
| 378 | 378 | loop_end: |
| 379 | 379 | |
| 380 | 380 | i = 0; |
| 381 | let mut accumulator = 0; | |
| 381 | let mut accumulator = 0 as i32; | |
| 382 | 382 | loop_2_start: |
| 383 | 383 | if i == 5 { |
| 384 | 384 | goto loop_2_end; |
| ... | ... | @@ -611,7 +611,7 @@ fn f() -> i32 { |
| 611 | 611 | fn f() { |
| 612 | 612 | if (0) {} |
| 613 | 613 | } |
| 614 | )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got 'i32'"); | |
| 614 | )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'"); | |
| 615 | 615 | |
| 616 | 616 | add_compile_fail_case("assign unreachable", R"SOURCE( |
| 617 | 617 | fn f() { |
| ... | ... | @@ -685,13 +685,12 @@ fn f(...) {} |
| 685 | 685 | |
| 686 | 686 | } |
| 687 | 687 | |
| 688 | static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) { | |
| 688 | static void print_compiler_invocation(TestCase *test_case) { | |
| 689 | 689 | printf("%s", zig_exe); |
| 690 | 690 | for (int i = 0; i < test_case->compiler_args.length; i += 1) { |
| 691 | 691 | printf(" %s", test_case->compiler_args.at(i)); |
| 692 | 692 | } |
| 693 | 693 | printf("\n"); |
| 694 | printf("%s\n", buf_ptr(zig_stderr)); | |
| 695 | 694 | } |
| 696 | 695 | |
| 697 | 696 | static void run_test(TestCase *test_case) { |
| ... | ... | @@ -716,21 +715,24 @@ static void run_test(TestCase *test_case) { |
| 716 | 715 | printf("========= Expected this compile error: =========\n"); |
| 717 | 716 | printf("%s\n", err_text); |
| 718 | 717 | printf("================================================\n"); |
| 719 | print_compiler_invocation(test_case, &zig_stderr); | |
| 718 | print_compiler_invocation(test_case); | |
| 719 | printf("%s\n", buf_ptr(&zig_stderr)); | |
| 720 | 720 | exit(1); |
| 721 | 721 | } |
| 722 | 722 | } |
| 723 | 723 | return; // success |
| 724 | 724 | } else { |
| 725 | 725 | printf("\nCompile failed with return code 0 (Expected failure):\n"); |
| 726 | print_compiler_invocation(test_case, &zig_stderr); | |
| 726 | print_compiler_invocation(test_case); | |
| 727 | printf("%s\n", buf_ptr(&zig_stderr)); | |
| 727 | 728 | exit(1); |
| 728 | 729 | } |
| 729 | 730 | } |
| 730 | 731 | |
| 731 | 732 | if (return_code != 0) { |
| 732 | 733 | printf("\nCompile failed with return code %d:\n", return_code); |
| 733 | print_compiler_invocation(test_case, &zig_stderr); | |
| 734 | print_compiler_invocation(test_case); | |
| 735 | printf("%s\n", buf_ptr(&zig_stderr)); | |
| 734 | 736 | exit(1); |
| 735 | 737 | } |
| 736 | 738 | |
| ... | ... | @@ -740,6 +742,7 @@ static void run_test(TestCase *test_case) { |
| 740 | 742 | |
| 741 | 743 | if (return_code != 0) { |
| 742 | 744 | printf("\nProgram exited with return code %d:\n", return_code); |
| 745 | print_compiler_invocation(test_case); | |
| 743 | 746 | printf("%s", tmp_exe_path); |
| 744 | 747 | for (int i = 0; i < test_case->program_args.length; i += 1) { |
| 745 | 748 | printf(" %s", test_case->program_args.at(i)); |
| ... | ... | @@ -750,6 +753,12 @@ static void run_test(TestCase *test_case) { |
| 750 | 753 | } |
| 751 | 754 | |
| 752 | 755 | if (!buf_eql_str(&program_stdout, test_case->output)) { |
| 756 | printf("\n"); | |
| 757 | print_compiler_invocation(test_case); | |
| 758 | printf("%s", tmp_exe_path); | |
| 759 | for (int i = 0; i < test_case->program_args.length; i += 1) { | |
| 760 | printf(" %s", test_case->program_args.at(i)); | |
| 761 | } | |
| 753 | 762 | printf("\n"); |
| 754 | 763 | printf("==== Test failed. Expected output: ====\n"); |
| 755 | 764 | printf("%s\n", test_case->output); |