Skip to content

Commit

Permalink
syntax: permit \" and \' escapes in both "..." and '...' literals (go…
Browse files Browse the repository at this point in the history
…ogle#279)

Clarify spec on this issue.

Fixes google#278

Also, fix semantic merge conflict in testdata/json.star tests that
relied on "\u" being interpreted as "\\u", causing tests to fail.
  • Loading branch information
adonovan authored Jun 15, 2020
1 parent ac23acb commit 2319aeb
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 10 deletions.
6 changes: 6 additions & 0 deletions doc/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,12 @@ using the other kind, as in these examples:
'Yes, it\'s a classic.'
```

Literal occurrences of the _opposite_ kind of quotation mark, such as
an apostrophe within a double-quoted string literal, may be escaped
by a backslash, but this is not necessary: `"it's"` and `"it\'s"` are
equivalent.


#### String escapes

Within a string literal, the backslash character `\` indicates the
Expand Down
8 changes: 4 additions & 4 deletions starlark/testdata/json.star
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ assert.eq(json.decode('[]'), [])
assert.eq(json.decode('[1]'), [1])
assert.eq(json.decode('[1,2,3]'), [1, 2, 3])
assert.eq(json.decode('{"one": 1, "two": 2}'), dict(one=1, two=2))
assert.eq(json.decode('{"foo\u0000bar": 42}'), {"foo\x00bar": 42})
assert.eq(json.decode('"\ud83d\ude39\ud83d\udc8d"'), "😹💍")
assert.eq(json.decode('"\u0123"'), 'ģ')
assert.eq(json.decode('{"foo\\u0000bar": 42}'), {"foo\x00bar": 42})
assert.eq(json.decode('"\\ud83d\\ude39\\ud83d\\udc8d"'), "😹💍")
assert.eq(json.decode('"\\u0123"'), 'ģ')
assert.eq(json.decode('"\x7f"'), "\x7f")

def decode_error(expr, error):
Expand All @@ -68,7 +68,7 @@ decode_error('truefalse',
"json.decode: at offset 4, unexpected character 'f' after value")

decode_error('"abc', "unclosed string literal")
decode_error('"ab\gc"', "invalid character 'g' in string escape code")
decode_error('"ab\\gc"', "invalid character 'g' in string escape code")
decode_error("'abc'", "unexpected character '\\\\''")

decode_error("1.2.3", "invalid number: 1.2.3")
Expand Down
5 changes: 3 additions & 2 deletions syntax/quote.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,10 @@ func unquote(quoted string) (s string, triple bool, err error) {
// Ignore the escape and the line break.
quoted = quoted[2:]

case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"':
// One-char escape.
// We escape only the kind of quotation mark in use.
// Escapes are allowed for both kinds of quotation
// mark, not just the kind in use.
buf.WriteByte(unesc[quoted[1]])
quoted = quoted[2:]

Expand Down
8 changes: 4 additions & 4 deletions syntax/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,16 +200,16 @@ pass`, "pass newline pass EOF"}, // consecutive newlines are consolidated
{`"\+"`, `foo.star:1:1: invalid escape sequence \+`},
{`"\w"`, `foo.star:1:1: invalid escape sequence \w`},
{`"\""`, `"\"" EOF`},
{`"\'"`, `foo.star:1:1: invalid escape sequence \'`},
{`"\'"`, `"'" EOF`},
{`'\w'`, `foo.star:1:1: invalid escape sequence \w`},
{`'\''`, `"'" EOF`},
{`'\"'`, `foo.star:1:1: invalid escape sequence \"`},
{`'\"'`, `"\"" EOF`},
{`"""\w"""`, `foo.star:1:1: invalid escape sequence \w`},
{`"""\""""`, `"\"" EOF`},
{`"""\'"""`, `foo.star:1:1: invalid escape sequence \'`},
{`"""\'"""`, `"'" EOF`},
{`'''\w'''`, `foo.star:1:1: invalid escape sequence \w`},
{`'''\''''`, `"'" EOF`},
{`'''\"'''`, `foo.star:1:1: invalid escape sequence \"`}, // error
{`'''\"'''`, `"\"" EOF`},
{`r"\w"`, `"\\w" EOF`},
{`r"\""`, `"\\\"" EOF`},
{`r"\'"`, `"\\'" EOF`},
Expand Down

0 comments on commit 2319aeb

Please sign in to comment.