4 files changed, 55 insertions(+), 0 deletions(-)
CHANGELOG.md+1| ... | ... | @@ -12,6 +12,7 @@ |
| 12 | 12 | - feat: rename `noreturn` to `never` |
| 13 | 13 | - feat: allow instantiating `@extern` at compile time |
| 14 | 14 | - feat: implement tuples |
| 15 | - feat: implement multiline string literals | |
| 15 | 16 | - fix: crash when printing unused struct declaration error |
| 16 | 17 | - fix: provide array result type to sema |
| 17 | 18 | - fix: crash when analyzing an empty array without a result type |
src/core/Parse.zig+18| ... | ... | @@ -819,6 +819,7 @@ fn isExpressionStart(p: *Parse) bool { |
| 819 | 819 | .kw_fn, |
| 820 | 820 | .kw_struct, |
| 821 | 821 | .kw_tuple, |
| 822 | .multiline_string, | |
| 822 | 823 | => true, |
| 823 | 824 | else => false, |
| 824 | 825 | }; |
| ... | ... | @@ -877,6 +878,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 877 | 878 | .number_int => try p.blankNode(.number_int), |
| 878 | 879 | .number_float => try p.blankNode(.number_float), |
| 879 | 880 | .string => try p.blankNode(.string), |
| 881 | .multiline_string => try p.parseMultilineString(), | |
| 880 | 882 | |
| 881 | 883 | .builtin_id => try p.parseBuiltinCall(), |
| 882 | 884 | .dot => try p.parseAnonStructInit(), |
| ... | ... | @@ -1320,6 +1322,22 @@ fn parseTupleType(p: *Parse) Error!NodeIndex { |
| 1320 | 1322 | }); |
| 1321 | 1323 | } |
| 1322 | 1324 | |
| 1325 | fn parseMultilineString(p: *Parse) !NodeIndex { | |
| 1326 | const start = p.assertToken(.multiline_string); | |
| 1327 | ||
| 1328 | while (p.eatToken(.multiline_string) != null) {} | |
| 1329 | ||
| 1330 | const len = p.pos - start; | |
| 1331 | return p.addNode(.{ | |
| 1332 | .tag = .multiline_string, | |
| 1333 | .main_token = start, | |
| 1334 | .data = .{ .list = .{ | |
| 1335 | .start = start, | |
| 1336 | .len = len, | |
| 1337 | } }, | |
| 1338 | }); | |
| 1339 | } | |
| 1340 | ||
| 1323 | 1341 | // helpers |
| 1324 | 1342 | |
| 1325 | 1343 | /// Append a node and return its index. |
src/core/Transform.zig+22| ... | ... | @@ -1620,6 +1620,28 @@ fn transformExpression( |
| 1620 | 1620 | }); |
| 1621 | 1621 | }, |
| 1622 | 1622 | |
| 1623 | .multiline_string => { | |
| 1624 | const token_list = t.ast.nodeData(node).list; | |
| 1625 | ||
| 1626 | assert(t.id_decode_buf.items.len == 0); | |
| 1627 | defer t.id_decode_buf.clearRetainingCapacity(); | |
| 1628 | ||
| 1629 | try t.id_decode_buf.appendSlice(t.ast.tokenSlice(token_list.start)[2..]); | |
| 1630 | for (token_list.start + 1..token_list.start + token_list.len) |i| { | |
| 1631 | try t.id_decode_buf.append('\n'); | |
| 1632 | try t.id_decode_buf.appendSlice(t.ast.tokenSlice(@intCast(i))[2..]); | |
| 1633 | } | |
| 1634 | ||
| 1635 | return try t.addRef(.string, .{ | |
| 1636 | .src_node = ctx.nodeOffset(node), | |
| 1637 | .extra_index = try t.extra.append( | |
| 1638 | t.gpa, | |
| 1639 | SlicedString, | |
| 1640 | try t.addSlicedString(t.id_decode_buf.items), | |
| 1641 | ), | |
| 1642 | }); | |
| 1643 | }, | |
| 1644 | ||
| 1623 | 1645 | else => |tag| std.debug.panic("TODO: expr {s}", .{@tagName(tag)}), |
| 1624 | 1646 | } |
| 1625 | 1647 | } |
test/behavior/string created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | let assertEqual = @extern("assertEqual"); | |
| 2 | ||
| 3 | fn main { | |
| 4 | assertEqual( | |
| 5 | \\hello world | |
| 6 | \\etc | |
| 7 | \\ | |
| 8 | , "hello world\netc\n" | |
| 9 | ); | |
| 10 | assertEqual( | |
| 11 | \\language compiler | |
| 12 | , "language compiler" | |
| 13 | ); | |
| 14 | } |