authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-25 21:56:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-25 21:56:29-07:00
loga3e288ab5be1008dafa86b47c18ebf480cc9d6a7
treeadd4bab3f4caf9cf5e6d8c01ccc12f2283c5bdcd
parent1d68150242c9304ac7f35a3d827c3c6e5aeea521

implement compile time string concatenation

See #76

9 files changed, 98 insertions(+), 3 deletions(-)

README.md+5
...@@ -61,6 +61,11 @@ compromises backward compatibility....@@ -61,6 +61,11 @@ compromises backward compatibility.
6161
62## Building62## Building
6363
64### Dependencies
65
66 * LLVM 3.7
67 * libclang 3.7
68
64### Debug / Development Build69### Debug / Development Build
6570
66If you have gcc or clang installed, you can find out what `ZIG_LIBC_DIR` should71If 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,7 +111,7 @@ BitShiftOperator : "<<" | ">>"
111111
112AdditionExpression : MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression112AdditionExpression : MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression
113113
114AdditionOperator : "+" | "-"114AdditionOperator : "+" | "-" | "++"
115115
116MultiplyExpression : CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression116MultiplyExpression : CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression
117117
...@@ -157,7 +157,7 @@ x() x[] x.y...@@ -157,7 +157,7 @@ x() x[] x.y
157!x -x ~x *x &x ?x %x %%x157!x -x ~x *x &x ?x %x %%x
158x{}158x{}
159* / %159* / %
160+ -160+ - ++
161<< >>161<< >>
162&162&
163^163^
src/all_types.hpp+1
...@@ -296,6 +296,7 @@ enum BinOpType {...@@ -296,6 +296,7 @@ enum BinOpType {
296 BinOpTypeDiv,296 BinOpTypeDiv,
297 BinOpTypeMod,297 BinOpTypeMod,
298 BinOpTypeUnwrapMaybe,298 BinOpTypeUnwrapMaybe,
299 BinOpTypeStrCat,
299};300};
300301
301struct AstNodeBinOpExpr {302struct AstNodeBinOpExpr {
src/analyze.cpp+65
...@@ -1437,6 +1437,8 @@ static TypeTableEntry *create_and_analyze_cast_node(CodeGen *g, ImportTableEntry...@@ -1437,6 +1437,8 @@ static TypeTableEntry *create_and_analyze_cast_node(CodeGen *g, ImportTableEntry
1437 BlockContext *context, TypeTableEntry *cast_to_type, AstNode *node)1437 BlockContext *context, TypeTableEntry *cast_to_type, AstNode *node)
1438{1438{
1439 AstNode *new_parent_node = create_ast_node(g, import, NodeTypeFnCallExpr);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 *node->parent_field = new_parent_node;1442 *node->parent_field = new_parent_node;
1441 new_parent_node->parent_field = node->parent_field;1443 new_parent_node->parent_field = node->parent_field;
14421444
...@@ -2146,6 +2148,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {...@@ -2146,6 +2148,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
2146 case BinOpTypeDiv:2148 case BinOpTypeDiv:
2147 case BinOpTypeMod:2149 case BinOpTypeMod:
2148 case BinOpTypeUnwrapMaybe:2150 case BinOpTypeUnwrapMaybe:
2151 case BinOpTypeStrCat:
2149 zig_unreachable();2152 zig_unreachable();
2150 }2153 }
2151 zig_unreachable();2154 zig_unreachable();
...@@ -2454,6 +2457,68 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,...@@ -2454,6 +2457,68 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
2454 return g->builtin_types.entry_invalid;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 case BinOpTypeInvalid:2522 case BinOpTypeInvalid:
2458 zig_unreachable();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,6 +951,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
951 case BinOpTypeAssignBoolAnd:951 case BinOpTypeAssignBoolAnd:
952 case BinOpTypeAssignBoolOr:952 case BinOpTypeAssignBoolOr:
953 case BinOpTypeUnwrapMaybe:953 case BinOpTypeUnwrapMaybe:
954 case BinOpTypeStrCat:
954 zig_unreachable();955 zig_unreachable();
955 }956 }
956 zig_unreachable();957 zig_unreachable();
...@@ -1228,6 +1229,7 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {...@@ -1228,6 +1229,7 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
1228static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {1229static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
1229 switch (node->data.bin_op_expr.bin_op) {1230 switch (node->data.bin_op_expr.bin_op) {
1230 case BinOpTypeInvalid:1231 case BinOpTypeInvalid:
1232 case BinOpTypeStrCat:
1231 zig_unreachable();1233 zig_unreachable();
1232 case BinOpTypeAssign:1234 case BinOpTypeAssign:
1233 case BinOpTypeAssignTimes:1235 case BinOpTypeAssignTimes:
src/parser.cpp+3-1
...@@ -49,6 +49,7 @@ static const char *bin_op_str(BinOpType bin_op) {...@@ -49,6 +49,7 @@ static const char *bin_op_str(BinOpType bin_op) {
49 case BinOpTypeAssignBoolAnd: return "&&=";49 case BinOpTypeAssignBoolAnd: return "&&=";
50 case BinOpTypeAssignBoolOr: return "||=";50 case BinOpTypeAssignBoolOr: return "||=";
51 case BinOpTypeUnwrapMaybe: return "??";51 case BinOpTypeUnwrapMaybe: return "??";
52 case BinOpTypeStrCat: return "++";
52 }53 }
53 zig_unreachable();54 zig_unreachable();
54}55}
...@@ -1769,12 +1770,13 @@ static BinOpType tok_to_add_op(Token *token) {...@@ -1769,12 +1770,13 @@ static BinOpType tok_to_add_op(Token *token) {
1769 switch (token->id) {1770 switch (token->id) {
1770 case TokenIdPlus: return BinOpTypeAdd;1771 case TokenIdPlus: return BinOpTypeAdd;
1771 case TokenIdDash: return BinOpTypeSub;1772 case TokenIdDash: return BinOpTypeSub;
1773 case TokenIdPlusPlus: return BinOpTypeStrCat;
1772 default: return BinOpTypeInvalid;1774 default: return BinOpTypeInvalid;
1773 }1775 }
1774}1776}
17751777
1776/*1778/*
1777AdditionOperator : token(Plus) | token(Minus)1779AdditionOperator : "+" | "-" | "++"
1778*/1780*/
1779static BinOpType ast_parse_add_op(ParseContext *pc, int *token_index, bool mandatory) {1781static BinOpType ast_parse_add_op(ParseContext *pc, int *token_index, bool mandatory) {
1780 Token *token = &pc->tokens->at(*token_index);1782 Token *token = &pc->tokens->at(*token_index);
src/tokenizer.cpp+6
...@@ -612,6 +612,11 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -612,6 +612,11 @@ void tokenize(Buf *buf, Tokenization *out) {
612 end_token(&t);612 end_token(&t);
613 t.state = TokenizeStateStart;613 t.state = TokenizeStateStart;
614 break;614 break;
615 case '+':
616 t.cur_tok->id = TokenIdPlusPlus;
617 end_token(&t);
618 t.state = TokenizeStateStart;
619 break;
615 default:620 default:
616 t.pos -= 1;621 t.pos -= 1;
617 end_token(&t);622 end_token(&t);
...@@ -1067,6 +1072,7 @@ const char * token_name(TokenId id) {...@@ -1067,6 +1072,7 @@ const char * token_name(TokenId id) {
1067 case TokenIdSemicolon: return ";";1072 case TokenIdSemicolon: return ";";
1068 case TokenIdNumberLiteral: return "NumberLiteral";1073 case TokenIdNumberLiteral: return "NumberLiteral";
1069 case TokenIdPlus: return "+";1074 case TokenIdPlus: return "+";
1075 case TokenIdPlusPlus: return "++";
1070 case TokenIdColon: return ":";1076 case TokenIdColon: return ":";
1071 case TokenIdArrow: return "->";1077 case TokenIdArrow: return "->";
1072 case TokenIdFatArrow: return "=>";1078 case TokenIdFatArrow: return "=>";
src/tokenizer.hpp+1
...@@ -52,6 +52,7 @@ enum TokenId {...@@ -52,6 +52,7 @@ enum TokenId {
52 TokenIdSemicolon,52 TokenIdSemicolon,
53 TokenIdNumberLiteral,53 TokenIdNumberLiteral,
54 TokenIdPlus,54 TokenIdPlus,
55 TokenIdPlusPlus,
55 TokenIdColon,56 TokenIdColon,
56 TokenIdArrow,57 TokenIdArrow,
57 TokenIdFatArrow,58 TokenIdFatArrow,
test/run_tests.cpp+13
...@@ -1279,6 +1279,13 @@ pub fn main(args: [][]u8) -> %void {...@@ -1279,6 +1279,13 @@ pub fn main(args: [][]u8) -> %void {
1279 %%stdout.printf("OK\n");1279 %%stdout.printf("OK\n");
1280}1280}
1281 )SOURCE", "OK\n");1281 )SOURCE", "OK\n");
1282
1283 add_simple_case("string concatenation", R"SOURCE(
1284import "std.zig";
1285pub fn main(args: [][]u8) -> %void {
1286 %%stdout.printf("OK" ++ " IT " ++ "WORKED\n");
1287}
1288 )SOURCE", "OK IT WORKED\n");
1282}1289}
12831290
12841291
...@@ -1645,6 +1652,12 @@ extern {...@@ -1645,6 +1652,12 @@ extern {
1645const x = foo();1652const x = foo();
1646 )SOURCE", 1, ".tmp_source.zig:5:11: error: global variable initializer requires constant expression");1653 )SOURCE", 1, ".tmp_source.zig:5:11: error: global variable initializer requires constant expression");
16471654
1655 add_compile_fail_case("non compile time string concatenation", R"SOURCE(
1656fn 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}
16491662
1650static void print_compiler_invocation(TestCase *test_case) {1663static void print_compiler_invocation(TestCase *test_case) {