Skip to content

Commit e1bc5aa

Browse files
committed
parser: return correct errors for unterminated triple quoted strings
1 parent d9667bc commit e1bc5aa

File tree

4 files changed

+31
-12
lines changed

4 files changed

+31
-12
lines changed

parser/grammar_data_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ var grammarTestData = []struct {
2929
{"b'abc' b'''123'''", "eval", "Expression(body=Bytes(s=b'abc123'))", nil, ""},
3030
{"1234", "eval", "Expression(body=Num(n=1234))", nil, ""},
3131
{"01234", "eval", "", py.SyntaxError, "illegal decimal with leading zero"},
32-
{"1234d", "eval", "", py.SyntaxError, "unexpected EOF while parsing"},
32+
{"1234d", "eval", "", py.SyntaxError, "invalid syntax"},
3333
{"1234d", "exec", "", py.SyntaxError, "invalid syntax"},
3434
{"1234d", "single", "", py.SyntaxError, "unexpected EOF while parsing"},
3535
{"0x1234", "eval", "Expression(body=Num(n=4660))", nil, ""},
@@ -317,4 +317,10 @@ var grammarTestData = []struct {
317317
{"pass\n", "single", "Interactive(body=[Pass()])", nil, ""},
318318
{"if True:\n pass\n\n", "single", "Interactive(body=[If(test=NameConstant(value=True), body=[Pass()], orelse=[])])", nil, ""},
319319
{"while True:\n pass\nelse:\n return\n", "single", "Interactive(body=[While(test=NameConstant(value=True), body=[Pass()], orelse=[Return(value=None)])])", nil, ""},
320+
{"a='potato", "eval", "", py.SyntaxError, "invalid syntax"},
321+
{"a='potato", "exec", "", py.SyntaxError, "EOL while scanning string literal"},
322+
{"a='potato", "single", "", py.SyntaxError, "EOL while scanning string literal"},
323+
{"a='''potato", "eval", "", py.SyntaxError, "invalid syntax"},
324+
{"a='''potato", "exec", "", py.SyntaxError, "EOF while scanning triple-quoted string literal"},
325+
{"a='''potato", "single", "", py.SyntaxError, "EOF while scanning triple-quoted string literal"},
320326
}

parser/lexer.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -639,12 +639,14 @@ func (x *yyLex) readOperator() int {
639639

640640
const pointFloat = `([0-9]*\.[0-9]+|[0-9]+\.)`
641641

642-
var decimalInteger = regexp.MustCompile(`^[0-9]+[jJ]?`)
643-
var illegalDecimalInteger = regexp.MustCompile(`^0[0-9]*[1-9][0-9]*$`)
644-
var octalInteger = regexp.MustCompile(`^0[oO][0-7]+`)
645-
var hexInteger = regexp.MustCompile(`^0[xX][0-9a-fA-F]+`)
646-
var binaryInteger = regexp.MustCompile(`^0[bB][01]+`)
647-
var floatNumber = regexp.MustCompile(`^(([0-9]+|` + pointFloat + `)[eE][+-]?[0-9]+|` + pointFloat + `)[jJ]?`)
642+
var (
643+
decimalInteger = regexp.MustCompile(`^[0-9]+[jJ]?`)
644+
illegalDecimalInteger = regexp.MustCompile(`^0[0-9]*[1-9][0-9]*$`)
645+
octalInteger = regexp.MustCompile(`^0[oO][0-7]+`)
646+
hexInteger = regexp.MustCompile(`^0[xX][0-9a-fA-F]+`)
647+
binaryInteger = regexp.MustCompile(`^0[bB][01]+`)
648+
floatNumber = regexp.MustCompile(`^(([0-9]+|` + pointFloat + `)[eE][+-]?[0-9]+|` + pointFloat + `)[jJ]?`)
649+
)
648650

649651
// Read one of the many types of python number
650652
//
@@ -837,12 +839,16 @@ found:
837839
}
838840
}
839841
if !multiLineString {
840-
x.SyntaxErrorf("Unterminated %sx%s string", stringEnd, stringEnd)
842+
x.SyntaxErrorf("EOL while scanning string literal")
841843
return eofError, nil
842844
}
843845
readMore:
844846
if x.eof {
845-
x.SyntaxErrorf("Unterminated %sx%s string", stringEnd, stringEnd)
847+
if multiLineString {
848+
x.SyntaxErrorf("EOF while scanning triple-quoted string literal")
849+
} else {
850+
x.SyntaxErrorf("EOL while scanning string literal")
851+
}
846852
return eofError, nil
847853
}
848854
x.refill()
@@ -887,7 +893,7 @@ func (x *yyLex) SyntaxErrorf(format string, a ...interface{}) {
887893
func (x *yyLex) ErrorReturn() error {
888894
if x.error {
889895
if x.errorString == "" {
890-
if x.eof && !x.exec {
896+
if x.eof && x.interactive {
891897
x.errorString = "unexpected EOF while parsing"
892898
} else {
893899
x.errorString = "invalid syntax"

parser/lexer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ func TestLex(t *testing.T) {
342342
{STRING, py.String("1\n2\n")},
343343
{ENDMARKER, nil},
344344
}},
345-
{"\"hello\n", "Unterminated \"x\" string", "eval", LexTokens{
345+
{"\"hello\n", "EOL while scanning string literal", "eval", LexTokens{
346346
{EVAL_INPUT, nil},
347347
}},
348348
{"1 >>-3\na <<=+12", "", "eval", LexTokens{

parser/make_grammar_test.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
("b'abc' b'''123'''", "eval"),
2626
("1234", "eval"),
2727
("01234", "eval", SyntaxError, "illegal decimal with leading zero"),
28-
("1234d", "eval", SyntaxError),
28+
("1234d", "eval", SyntaxError, "invalid syntax"),
2929
("1234d", "exec", SyntaxError),
3030
("1234d", "single", SyntaxError),
3131
("0x1234", "eval"),
@@ -467,6 +467,13 @@ class A(B):
467467
("pass\n", "single"),
468468
("if True:\n pass\n\n", "single"),
469469
("while True:\n pass\nelse:\n return\n", "single"),
470+
# unfinished strings
471+
("a='potato", "eval", SyntaxError),
472+
("a='potato", "exec", SyntaxError),
473+
("a='potato", "single", SyntaxError),
474+
("a='''potato", "eval", SyntaxError),
475+
("a='''potato", "exec", SyntaxError),
476+
("a='''potato", "single", SyntaxError),
470477
]
471478

472479
def dump(source, mode):

0 commit comments

Comments
 (0)