Skip to content

Commit e4b2a2f

Browse files
authored
Merge pull request #28 from fink-lang/improve-str-escaping
improve str escape char handling and template nesting
2 parents 8afc67d + e1148e8 commit e4b2a2f

File tree

12 files changed

+376
-340
lines changed

12 files changed

+376
-340
lines changed

package-lock.json

Lines changed: 302 additions & 282 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@
3030
"release": "semantic-release"
3131
},
3232
"dependencies": {
33-
"@fink/prattler": "^1.2.0"
33+
"@fink/prattler": "^2.0.0"
3434
},
3535
"devDependencies": {
3636
"@fink/cli": "^2.1.0",
3737
"@fink/jest": "^1.1.0",
38-
"@fink/larix": "^4.5.1",
39-
"@fink/loxia": "^4.5.0",
38+
"@fink/larix": "^4.5.2",
39+
"@fink/loxia": "^4.5.1",
4040
"commitizen": "^4.0.3",
4141
"cz-conventional-changelog": "^3.1.0",
42-
"jest-cli": "^25.1.0",
42+
"jest-cli": "^25.2.3",
4343
"npx-run": "^2.1.2",
4444
"semantic-release": "^17.0.4"
4545
},

src/lang/import/index.test.fnk

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
{parse_expr} = import '../..'
44

5-
# TODO: should not need to escape `
6-
describe:: 'import \`foobar\`', fn:
5+
6+
describe:: 'import `foobar`', fn:
77
it:: 'parses import', fn:
8-
parse_expr('import \`./foobar\`') eq snapshot
8+
parse_expr('import `./foobar`') eq snapshot

src/lang/jsx/index.test.fnk.snap

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,24 @@ jsx-elem (1:0-3:9) Foobar
55
jsx-attr (1:8-1:10) ni
66
null
77
:
8-
jsx-text (1:10-1:11)
8+
jsx-text (1:11-2:2)
99
"\\n "
1010
jsx-elem (2:2-2:10) Spam
11-
jsx-text (2:8-2:10)
11+
jsx-text (2:10-3:0)
1212
"\\n"
1313
`;
1414

1515
exports[`JSX <Foobar>...</Foobar> parses elem with expr in body: <Foobar> {...} </Foobar> 1`] = `
1616
jsx-elem (1:0-3:9) Foobar
1717
:
18-
jsx-text (1:7-1:8)
18+
jsx-text (1:8-2:2)
1919
"\\n "
2020
jsx-expr-container (2:2-2:8)
2121
block (2:2-2:8)
2222
arithm + (2:3-2:8)
2323
number (2:3-2:4) 1
2424
number (2:7-2:8) 2
25-
jsx-text (2:8-2:9)
25+
jsx-text (2:9-3:0)
2626
"\\n"
2727
`;
2828

@@ -45,14 +45,14 @@ jsx-elem (1:0-1:15) Foobar
4545
exports[`JSX <Foobar>...</Foobar> parses self closing elem with str attr: <Foobar spam="ni" /> 1`] = `
4646
jsx-elem (1:0-1:20) Foobar
4747
jsx-attr (1:8-1:17) spam
48-
jsx-string (1:13-1:17)
48+
jsx-string (1:14-1:16)
4949
'ni'
5050
`;
5151

5252
exports[`JSX <Foobar>...</Foobar> parses self closing elem with str attr: <Foobar spam='ni' /> 1`] = `
5353
jsx-elem (1:0-1:20) Foobar
5454
jsx-attr (1:8-1:17) spam
55-
jsx-string (1:13-1:17)
55+
jsx-string (1:14-1:16)
5656
'ni'
5757
`;
5858

src/lang/jsx/jsx.fnk

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jsx_expr_container = fn ctx:
1515

1616

1717
jsx_txt = fn ctx:
18-
[{text, loc}, next_ctx] = collect_text(ctx, '</', '<', '{')
18+
[{value: text, loc}, next_ctx] = collect_text(ctx, rx/'<\/|<|{/)
1919

2020
[{type: 'jsx-text', value: text, loc}, next_ctx]
2121

@@ -48,9 +48,9 @@ jsx_body = fn name, ctx:
4848

4949

5050
jsx_string_val = fn ctx:
51-
[value, next_ctx] = collect_text(ctx, curr_value(ctx))
51+
[{value, loc}, next_ctx] = collect_text(ctx, curr_value(ctx))
5252

53-
[{type: 'jsx-string', value: value.text, loc: value.loc}, next_ctx]
53+
[{type: 'jsx-string', value, loc}, next_ctx]
5454

5555

5656
jsx_attr_value = fn ctx:

src/lang/literals/index.fnk

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ add_object_literal = fn ctx:
2424

2525
add_string_literal = fn ctx:
2626
pipe ctx:
27-
# TODO: bug in lorix prevents the use of '`'
28-
add_operator_like(string(`\``))
27+
add_operator_like(string('`'))
2928
add_operator_like(string(`'`))
3029
add_operator_like(string(`"`))
3130

src/lang/literals/regex.fnk

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88

99

1010
get_text = fn ctx, op:
11-
[{text, loc: {start, end}}, next_ctx] = collect_text(ctx, op)
11+
[{value: text, loc: {start, end}}, next_ctx] = collect_text(ctx, op)
1212

13-
# TODO: previous larix version has a bug to terminate regex at $
14-
[escaped] = text.match(rx/\\*\\$/) || ['']
13+
[escaped] = text.match(rx/\\*$/)
1514

1615
match true:
1716
escaped.length % 2 == 0:

src/lang/literals/regex.test.fnk

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ describe:: 'regex - parsing failures', fn:
2727
(
2828
fn: parse_expr(`rx/foo.bar`, 'test.fnk')
2929
) to_throw `
30-
test.fnk:1:7
30+
test.fnk:1:10
3131
1| rx/foo.bar
32-
^
32+
^
3333

3434
Unexpected end of code.
3535
`
@@ -39,9 +39,9 @@ describe:: 'regex - parsing failures', fn:
3939
(
4040
fn: parse_expr(`rx/foo.bar/n`, 'test.fnk')
4141
) to_throw `
42-
test.fnk:1:11
42+
test.fnk:1:12
4343
1| rx/foo.bar/n
44-
^
44+
^
4545

4646
Unexpected end of code.
4747
`

src/lang/literals/string.fnk

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,19 @@ get_expr_part = fn ctx:
1717

1818

1919
get_str_part = fn ctx, op:
20-
# TODO: should not need to escape `$` unless it is `${`
21-
[{text, loc: {start, end}}, next_ctx] = collect_text(ctx, op, '\$')
20+
[{value: text, loc: {start, end}}, next_ctx] = match op:
21+
'"': collect_text(ctx, rx/"|\${/)
22+
'`': collect_text(ctx, rx/`|\${/)
23+
else: collect_text(ctx, rx/'|\${/)
2224

23-
# TODO: previous larix version has a bug to terminate regex at $
24-
[escaped] = text.match(rx/\\*\\$/) || ['']
25+
26+
# TODO: can we do the escapeing check in the regex above?
27+
[escaped] = text.match(rx/\\*$/)
2528

2629
match true:
2730
escaped.length % 2 == 0:
2831
[{type: 'string:text', value: text, loc: {start, end}}, next_ctx]
2932

30-
# TODO: should check for ${ and continue too
3133
else:
3234
[str_part, final_ctx] = get_str_part(next_ctx, op)
3335

@@ -63,18 +65,19 @@ get_unindented_text = fn ctx, op:
6365
else:
6466
first
6567

66-
[...parts] = pipe rest:
68+
[...rest_parts] = pipe rest:
6769
map part:
68-
match true:
69-
(part.type == 'string:text'):
70+
match part:
71+
{type: 'string:text'}:
7072
{...part, value: unindent_text(part.value, ind)}
7173

7274
else:
7375
part
7476

77+
parts = [first_part, ...rest_parts]
7578
{end} = curr_loc(next_ctx)
7679

77-
[{type: 'string', op, parts: [first_part, ...parts], loc: {start, end}}, next_ctx]
80+
[{type: 'string', op, parts, loc: {start, end}}, next_ctx]
7881

7982

8083
string = fn op:

src/lang/literals/string.test.fnk

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1-
{describe, it, eq, to_throw, snapshot} = import '../../testing/jest'
1+
{describe, it, it_only, eq, to_throw, snapshot} = import '../../testing/jest'
22

33
{parse_expr, parse} = import '../../'
44

55

66
describe:: 'strings', fn:
77

88
it:: 'parses empty strings', fn:
9-
# TODO: should not need to escape `
10-
parse_expr('\`\`') eq snapshot
9+
parse_expr('``') eq snapshot
1110
parse_expr('""') eq snapshot
1211
parse_expr(`''`) eq snapshot
1312

1413

1514
it:: 'parses single line strings', fn:
16-
# TODO: should not need to escape `
17-
parse_expr('\`foo bar\`') eq snapshot
15+
parse_expr('`foo bar`') eq snapshot
1816
parse_expr('"foo bar"') eq snapshot
1917
parse_expr(`'foo bar'`) eq snapshot
2018

@@ -28,22 +26,21 @@ describe:: 'strings', fn:
2826

2927

3028
it:: 'parses escape characters', fn:
31-
# TODO: should not need to escape `
32-
parse_expr('\` \\n \\t \\\\ \\\` \`') eq snapshot
33-
parse_expr(`' \\n \\t \\\\ \\\` '`) eq snapshot
34-
parse_expr(`" \\n \\t \\\\ \\\` \\\\"`) eq snapshot
29+
# TODO: should not need to escape \$
30+
# TODO: add test for \${foo}
31+
parse_expr('` \$ \\n \\t \\` \\\\`') eq snapshot
32+
parse_expr(`' \$ \\n \\t \\' \\\\'`) eq snapshot
33+
parse_expr(`" \$ \\n \\t \\" \\\\"`) eq snapshot
3534

3635

3736
it:: 'parses tagged strings', fn:
38-
# TODO: should not need to escape `
39-
parse_expr('foo\`bar spam\`') eq snapshot
37+
parse_expr('foo`bar spam`') eq snapshot
4038
parse_expr(`foo'bar spam'`) eq snapshot
4139
parse_expr(`foo"bar spam"`) eq snapshot
4240

4341

4442
it:: 'parses template strings with expressions', fn:
45-
# TODO: should not need to escape `
46-
parse_expr('\`foo \${bar + spam} spam\`') eq snapshot
43+
parse_expr('`foo \${bar + spam} spam`') eq snapshot
4744
parse_expr(`'foo \${bar + spam} spam'`) eq snapshot
4845
parse_expr(`"foo \${bar + spam} spam"`) eq snapshot
4946

@@ -58,6 +55,12 @@ describe:: 'strings', fn:
5855
`) eq snapshot
5956

6057

58+
it:: 'parses nested template strings', fn:
59+
parse_expr('
60+
`\\n\${foo(bar, ` \${spam}`)}\\n`
61+
') eq snapshot
62+
63+
6164
it:: `handles default indentation for lbp`, fn:
6265
parse(`
6366
'foobar'
@@ -71,9 +74,9 @@ describe:: 'string - parsing failures', fn:
7174
(
7275
fn: parse_expr('"foo bar,', 'test.fnk')
7376
) to_throw `
74-
test.fnk:1:8
77+
test.fnk:1:9
7578
1| "foo bar,
76-
^
79+
^
7780

7881
Unexpected end of code.
7982
`
@@ -89,5 +92,3 @@ describe:: 'string - parsing failures', fn:
8992

9093
Expected identifier before tagged string.
9194
`
92-
93-

src/lang/literals/string.test.fnk.snap

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@ string ' (1:0-1:2)
2424
`;
2525

2626
exports[`strings parses escape characters 1`] = `
27-
string \` (1:0-1:15)
28-
\` \\n \\t \\\\ \\\` \`
27+
string \` (1:0-1:16)
28+
\` $ \\n \\t \\\` \\\\\`
2929
`;
3030

3131
exports[`strings parses escape characters 2`] = `
32-
string ' (1:0-1:15)
33-
\` \\n \\t \\\\ \\\` \`
32+
string ' (1:0-1:16)
33+
\` $ \\n \\t \\' \\\\\`
3434
`;
3535

3636
exports[`strings parses escape characters 3`] = `
37-
string " (1:0-1:17)
38-
\` \\n \\t \\\\ \\\` \\\\\`
37+
string " (1:0-1:16)
38+
\` $ \\n \\t \\" \\\\\`
3939
`;
4040

4141
exports[`strings parses multi line strings 1`] = `
@@ -61,6 +61,20 @@ string ' (1:0-5:1)
6161
\`
6262
`;
6363

64+
exports[`strings parses nested template strings 1`] = `
65+
string \` (1:0-1:30)
66+
\`\\n\`
67+
call (1:5-1:26)
68+
ident (1:5-1:8) foo
69+
ident (1:9-1:12) bar
70+
string \` (1:14-1:25)
71+
\` \`
72+
ident (1:19-1:23) spam
73+
\`\`
74+
75+
\`\\n\`
76+
`;
77+
6478
exports[`strings parses single line strings 1`] = `
6579
string \` (1:0-1:9)
6680
\`foo bar\`

src/testing/serialize.fnk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ serialize_str = fn node, serialize, indent:
7070
[lne, ...lns] = node.parts
7171
.map(fn part:
7272
match part.type:
73-
'string:text': `\`${part.value}\``
73+
'string:text': '`${part.value}`'
7474
else:
7575
value = serialize(part, ' ${indent}')
7676
`\n${value}\n`

0 commit comments

Comments
 (0)