| author | |
| committer | |
| log | a3e288ab5be1008dafa86b47c18ebf480cc9d6a7 |
| tree | add4bab3f4caf9cf5e6d8c01ccc12f2283c5bdcd |
| parent | 1d68150242c9304ac7f35a3d827c3c6e5aeea521 |
See #769 files changed, 98 insertions(+), 3 deletions(-)
README.md+5| ... | ... | @@ -61,6 +61,11 @@ compromises backward compatibility. |
| 61 | 61 | |
| 62 | 62 | ## Building |
| 63 | 63 | |
| 64 | ### Dependencies | |
| 65 | ||
| 66 | * LLVM 3.7 | |
| 67 | * libclang 3.7 | |
| 68 | ||
| 64 | 69 | ### Debug / Development Build |
| 65 | 70 | |
| 66 | 71 | If you have gcc or clang installed, you can find out what `ZIG_LIBC_DIR` should |
doc/langref.md+2-2| ... | ... | @@ -111,7 +111,7 @@ BitShiftOperator : "<<" | ">>" |
| 111 | 111 | |
| 112 | 112 | AdditionExpression : MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression |
| 113 | 113 | |
| 114 | AdditionOperator : "+" | "-" | |
| 114 | AdditionOperator : "+" | "-" | "++" | |
| 115 | 115 | |
| 116 | 116 | MultiplyExpression : CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression |
| 117 | 117 | |
| ... | ... | @@ -157,7 +157,7 @@ x() x[] x.y |
| 157 | 157 | !x -x ~x *x &x ?x %x %%x |
| 158 | 158 | x{} |
| 159 | 159 | * / % |
| 160 | + - | |
| 160 | + - ++ | |
| 161 | 161 | << >> |
| 162 | 162 | & |
| 163 | 163 | ^ |
src/all_types.hpp+1| ... | ... | @@ -296,6 +296,7 @@ enum BinOpType { |
| 296 | 296 | BinOpTypeDiv, |
| 297 | 297 | BinOpTypeMod, |
| 298 | 298 | BinOpTypeUnwrapMaybe, |
| 299 | BinOpTypeStrCat, | |
| 299 | 300 | }; |
| 300 | 301 | |
| 301 | 302 | struct AstNodeBinOpExpr { |
src/analyze.cpp+65| ... | ... | @@ -1437,6 +1437,8 @@ static TypeTableEntry *create_and_analyze_cast_node(CodeGen *g, ImportTableEntry |
| 1437 | 1437 | BlockContext *context, TypeTableEntry *cast_to_type, AstNode *node) |
| 1438 | 1438 | { |
| 1439 | 1439 | AstNode *new_parent_node = create_ast_node(g, import, NodeTypeFnCallExpr); |
| 1440 | new_parent_node->line = node->line; | |
| 1441 | new_parent_node->column = node->column; | |
| 1440 | 1442 | *node->parent_field = new_parent_node; |
| 1441 | 1443 | new_parent_node->parent_field = node->parent_field; |
| 1442 | 1444 | |
| ... | ... | @@ -2146,6 +2148,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { |
| 2146 | 2148 | case BinOpTypeDiv: |
| 2147 | 2149 | case BinOpTypeMod: |
| 2148 | 2150 | case BinOpTypeUnwrapMaybe: |
| 2151 | case BinOpTypeStrCat: | |
| 2149 | 2152 | zig_unreachable(); |
| 2150 | 2153 | } |
| 2151 | 2154 | zig_unreachable(); |
| ... | ... | @@ -2454,6 +2457,68 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 2454 | 2457 | return g->builtin_types.entry_invalid; |
| 2455 | 2458 | } |
| 2456 | 2459 | } |
| 2460 | case BinOpTypeStrCat: | |
| 2461 | { | |
| 2462 | AstNode **op1 = node->data.bin_op_expr.op1->parent_field; | |
| 2463 | AstNode **op2 = node->data.bin_op_expr.op2->parent_field; | |
| 2464 | ||
| 2465 | TypeTableEntry *str_type = get_unknown_size_array_type(g, g->builtin_types.entry_u8, true); | |
| 2466 | ||
| 2467 | TypeTableEntry *op1_type = analyze_expression(g, import, context, str_type, *op1); | |
| 2468 | TypeTableEntry *op2_type = analyze_expression(g, import, context, str_type, *op2); | |
| 2469 | ||
| 2470 | if (op1_type->id == TypeTableEntryIdInvalid || | |
| 2471 | op2_type->id == TypeTableEntryIdInvalid) | |
| 2472 | { | |
| 2473 | return g->builtin_types.entry_invalid; | |
| 2474 | } | |
| 2475 | ||
| 2476 | ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val; | |
| 2477 | ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val; | |
| 2478 | ||
| 2479 | AstNode *bad_node; | |
| 2480 | if (!op1_val->ok) { | |
| 2481 | bad_node = *op1; | |
| 2482 | } else if (!op2_val->ok) { | |
| 2483 | bad_node = *op2; | |
| 2484 | } else { | |
| 2485 | bad_node = nullptr; | |
| 2486 | } | |
| 2487 | if (bad_node) { | |
| 2488 | add_node_error(g, bad_node, buf_sprintf("string concatenation requires constant expression")); | |
| 2489 | return g->builtin_types.entry_invalid; | |
| 2490 | } | |
| 2491 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | |
| 2492 | const_val->ok = true; | |
| 2493 | ||
| 2494 | ConstExprValue *all_fields = allocate<ConstExprValue>(2); | |
| 2495 | ConstExprValue *ptr_field = &all_fields[0]; | |
| 2496 | ConstExprValue *len_field = &all_fields[1]; | |
| 2497 | ||
| 2498 | const_val->data.x_struct.fields = allocate<ConstExprValue*>(2); | |
| 2499 | const_val->data.x_struct.fields[0] = ptr_field; | |
| 2500 | const_val->data.x_struct.fields[1] = len_field; | |
| 2501 | ||
| 2502 | len_field->ok = true; | |
| 2503 | uint64_t op1_len = op1_val->data.x_struct.fields[1]->data.x_bignum.data.x_uint; | |
| 2504 | uint64_t op2_len = op2_val->data.x_struct.fields[1]->data.x_bignum.data.x_uint; | |
| 2505 | uint64_t len = op1_len + op2_len; | |
| 2506 | bignum_init_unsigned(&len_field->data.x_bignum, len); | |
| 2507 | ||
| 2508 | ptr_field->ok = true; | |
| 2509 | ptr_field->data.x_ptr.ptr = allocate<ConstExprValue*>(len); | |
| 2510 | ptr_field->data.x_ptr.len = len; | |
| 2511 | ||
| 2512 | uint64_t i = 0; | |
| 2513 | for (uint64_t op1_i = 0; op1_i < op1_len; op1_i += 1, i += 1) { | |
| 2514 | ptr_field->data.x_ptr.ptr[i] = op1_val->data.x_struct.fields[0]->data.x_ptr.ptr[op1_i]; | |
| 2515 | } | |
| 2516 | for (uint64_t op2_i = 0; op2_i < op2_len; op2_i += 1, i += 1) { | |
| 2517 | ptr_field->data.x_ptr.ptr[i] = op2_val->data.x_struct.fields[0]->data.x_ptr.ptr[op2_i]; | |
| 2518 | } | |
| 2519 | ||
| 2520 | return str_type; | |
| 2521 | } | |
| 2457 | 2522 | case BinOpTypeInvalid: |
| 2458 | 2523 | zig_unreachable(); |
| 2459 | 2524 | } |
src/codegen.cpp+2| ... | ... | @@ -951,6 +951,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, |
| 951 | 951 | case BinOpTypeAssignBoolAnd: |
| 952 | 952 | case BinOpTypeAssignBoolOr: |
| 953 | 953 | case BinOpTypeUnwrapMaybe: |
| 954 | case BinOpTypeStrCat: | |
| 954 | 955 | zig_unreachable(); |
| 955 | 956 | } |
| 956 | 957 | zig_unreachable(); |
| ... | ... | @@ -1228,6 +1229,7 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) { |
| 1228 | 1229 | static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
| 1229 | 1230 | switch (node->data.bin_op_expr.bin_op) { |
| 1230 | 1231 | case BinOpTypeInvalid: |
| 1232 | case BinOpTypeStrCat: | |
| 1231 | 1233 | zig_unreachable(); |
| 1232 | 1234 | case BinOpTypeAssign: |
| 1233 | 1235 | case BinOpTypeAssignTimes: |
src/parser.cpp+3-1| ... | ... | @@ -49,6 +49,7 @@ static const char *bin_op_str(BinOpType bin_op) { |
| 49 | 49 | case BinOpTypeAssignBoolAnd: return "&&="; |
| 50 | 50 | case BinOpTypeAssignBoolOr: return "||="; |
| 51 | 51 | case BinOpTypeUnwrapMaybe: return "??"; |
| 52 | case BinOpTypeStrCat: return "++"; | |
| 52 | 53 | } |
| 53 | 54 | zig_unreachable(); |
| 54 | 55 | } |
| ... | ... | @@ -1769,12 +1770,13 @@ static BinOpType tok_to_add_op(Token *token) { |
| 1769 | 1770 | switch (token->id) { |
| 1770 | 1771 | case TokenIdPlus: return BinOpTypeAdd; |
| 1771 | 1772 | case TokenIdDash: return BinOpTypeSub; |
| 1773 | case TokenIdPlusPlus: return BinOpTypeStrCat; | |
| 1772 | 1774 | default: return BinOpTypeInvalid; |
| 1773 | 1775 | } |
| 1774 | 1776 | } |
| 1775 | 1777 | |
| 1776 | 1778 | /* |
| 1777 | AdditionOperator : token(Plus) | token(Minus) | |
| 1779 | AdditionOperator : "+" | "-" | "++" | |
| 1778 | 1780 | */ |
| 1779 | 1781 | static BinOpType ast_parse_add_op(ParseContext *pc, int *token_index, bool mandatory) { |
| 1780 | 1782 | Token *token = &pc->tokens->at(*token_index); |
src/tokenizer.cpp+6| ... | ... | @@ -612,6 +612,11 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 612 | 612 | end_token(&t); |
| 613 | 613 | t.state = TokenizeStateStart; |
| 614 | 614 | break; |
| 615 | case '+': | |
| 616 | t.cur_tok->id = TokenIdPlusPlus; | |
| 617 | end_token(&t); | |
| 618 | t.state = TokenizeStateStart; | |
| 619 | break; | |
| 615 | 620 | default: |
| 616 | 621 | t.pos -= 1; |
| 617 | 622 | end_token(&t); |
| ... | ... | @@ -1067,6 +1072,7 @@ const char * token_name(TokenId id) { |
| 1067 | 1072 | case TokenIdSemicolon: return ";"; |
| 1068 | 1073 | case TokenIdNumberLiteral: return "NumberLiteral"; |
| 1069 | 1074 | case TokenIdPlus: return "+"; |
| 1075 | case TokenIdPlusPlus: return "++"; | |
| 1070 | 1076 | case TokenIdColon: return ":"; |
| 1071 | 1077 | case TokenIdArrow: return "->"; |
| 1072 | 1078 | case TokenIdFatArrow: return "=>"; |
src/tokenizer.hpp+1| ... | ... | @@ -52,6 +52,7 @@ enum TokenId { |
| 52 | 52 | TokenIdSemicolon, |
| 53 | 53 | TokenIdNumberLiteral, |
| 54 | 54 | TokenIdPlus, |
| 55 | TokenIdPlusPlus, | |
| 55 | 56 | TokenIdColon, |
| 56 | 57 | TokenIdArrow, |
| 57 | 58 | TokenIdFatArrow, |
test/run_tests.cpp+13| ... | ... | @@ -1279,6 +1279,13 @@ pub fn main(args: [][]u8) -> %void { |
| 1279 | 1279 | %%stdout.printf("OK\n"); |
| 1280 | 1280 | } |
| 1281 | 1281 | )SOURCE", "OK\n"); |
| 1282 | ||
| 1283 | add_simple_case("string concatenation", R"SOURCE( | |
| 1284 | import "std.zig"; | |
| 1285 | pub fn main(args: [][]u8) -> %void { | |
| 1286 | %%stdout.printf("OK" ++ " IT " ++ "WORKED\n"); | |
| 1287 | } | |
| 1288 | )SOURCE", "OK IT WORKED\n"); | |
| 1282 | 1289 | } |
| 1283 | 1290 | |
| 1284 | 1291 | |
| ... | ... | @@ -1645,6 +1652,12 @@ extern { |
| 1645 | 1652 | const x = foo(); |
| 1646 | 1653 | )SOURCE", 1, ".tmp_source.zig:5:11: error: global variable initializer requires constant expression"); |
| 1647 | 1654 | |
| 1655 | add_compile_fail_case("non compile time string concatenation", R"SOURCE( | |
| 1656 | fn f(s: []u8) -> []u8 { | |
| 1657 | s ++ "foo" | |
| 1658 | } | |
| 1659 | )SOURCE", 1, ".tmp_source.zig:3:5: error: string concatenation requires constant expression"); | |
| 1660 | ||
| 1648 | 1661 | } |
| 1649 | 1662 | |
| 1650 | 1663 | static void print_compiler_invocation(TestCase *test_case) { |