Improve error when reserved word used as label (#552)

Before this commit error looks like (for input `start = break:'a'`)

> Expected "!", "$", "&", "(", "*", "+", ".", "/", "/*", "//", ";", "?", character class, code block, comment, end of line, identifier, literal, or whitespace but ":" found.

After this error looks like

> Label can't be a reserved word "break".
master
Mingun 6 years ago committed by Futago-za Ryuu
parent 93f9068c99
commit 4cc9185a78

File diff suppressed because one or more lines are too long

@ -39,6 +39,45 @@
"!": "semantic_not"
};
const RESERVED_WORDS = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"enum",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"instanceof",
"in",
"new",
"null",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with"
];
function extractOptional(optional, index) {
return optional ? optional[index] : null;
}
@ -129,9 +168,13 @@ SequenceExpression
LabeledExpression
= label:Identifier __ ":" __ expression:PrefixedExpression {
if (RESERVED_WORDS.indexOf(label[0]) >= 0) {
error(`Label can't be a reserved word "${label[0]}".`, label[1]);
}
return {
type: "labeled",
label: label,
label: label[0],
expression: expression,
location: location()
};
@ -240,7 +283,7 @@ SingleLineComment
= "//" (!LineTerminator SourceCharacter)*
Identifier
= !ReservedWord name:IdentifierName { return name; }
= name:IdentifierName { return [name, location()]; }
IdentifierName "identifier"
= head:IdentifierStart tail:IdentifierPart* { return head + tail.join(""); }
@ -277,56 +320,6 @@ UnicodeDigit
UnicodeConnectorPunctuation
= Pc
ReservedWord
= Keyword
/ FutureReservedWord
/ NullLiteral
/ BooleanLiteral
Keyword
= BreakToken
/ CaseToken
/ CatchToken
/ ContinueToken
/ DebuggerToken
/ DefaultToken
/ DeleteToken
/ DoToken
/ ElseToken
/ FinallyToken
/ ForToken
/ FunctionToken
/ IfToken
/ InstanceofToken
/ InToken
/ NewToken
/ ReturnToken
/ SwitchToken
/ ThisToken
/ ThrowToken
/ TryToken
/ TypeofToken
/ VarToken
/ VoidToken
/ WhileToken
/ WithToken
FutureReservedWord
= ClassToken
/ ConstToken
/ EnumToken
/ ExportToken
/ ExtendsToken
/ ImportToken
/ SuperToken
NullLiteral
= NullToken
BooleanLiteral
= TrueToken
/ FalseToken
LiteralMatcher "literal"
= value:StringLiteral ignoreCase:"i"? {
return {
@ -498,45 +491,6 @@ Pc = [\u005F\u203F-\u2040\u2054\uFE33-\uFE34\uFE4D-\uFE4F\uFF3F]
// Separator, Space
Zs = [\u0020\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]
// Tokens
BreakToken = "break" !IdentifierPart
CaseToken = "case" !IdentifierPart
CatchToken = "catch" !IdentifierPart
ClassToken = "class" !IdentifierPart
ConstToken = "const" !IdentifierPart
ContinueToken = "continue" !IdentifierPart
DebuggerToken = "debugger" !IdentifierPart
DefaultToken = "default" !IdentifierPart
DeleteToken = "delete" !IdentifierPart
DoToken = "do" !IdentifierPart
ElseToken = "else" !IdentifierPart
EnumToken = "enum" !IdentifierPart
ExportToken = "export" !IdentifierPart
ExtendsToken = "extends" !IdentifierPart
FalseToken = "false" !IdentifierPart
FinallyToken = "finally" !IdentifierPart
ForToken = "for" !IdentifierPart
FunctionToken = "function" !IdentifierPart
IfToken = "if" !IdentifierPart
ImportToken = "import" !IdentifierPart
InstanceofToken = "instanceof" !IdentifierPart
InToken = "in" !IdentifierPart
NewToken = "new" !IdentifierPart
NullToken = "null" !IdentifierPart
ReturnToken = "return" !IdentifierPart
SuperToken = "super" !IdentifierPart
SwitchToken = "switch" !IdentifierPart
ThisToken = "this" !IdentifierPart
ThrowToken = "throw" !IdentifierPart
TrueToken = "true" !IdentifierPart
TryToken = "try" !IdentifierPart
TypeofToken = "typeof" !IdentifierPart
VarToken = "var" !IdentifierPart
VoidToken = "void" !IdentifierPart
WhileToken = "while" !IdentifierPart
WithToken = "with" !IdentifierPart
// Skipped
__

@ -308,6 +308,91 @@ describe( "PEG.js API", function () {
// The |plugins| option is tested in plugin API tests.
describe( "reserved words", function () {
const RESERVED_WORDS = [
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"enum",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"instanceof",
"in",
"new",
"null",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with"
];
describe( "throws an exception on reserved JS words used as labels", function () {
for ( const label of RESERVED_WORDS ) {
it( label, function () {
expect( () => {
peg.generate( [
"start = " + label + ":end",
"end = 'a'"
].join( "\n" ), { output: "source" } );
} ).to.throw( peg.parser.SyntaxError );
} );
}
} );
describe( "not throws an exception on reserved JS words used as rule name", function () {
for ( const rule of RESERVED_WORDS ) {
it( rule, function () {
expect( () => {
peg.generate( [
"start = " + rule,
rule + " = 'a'"
].join( "\n" ), { output: "source" } );
} ).to.not.throw( peg.parser.SyntaxError );
} );
}
} );
} );
it( "accepts custom options", function () {
peg.generate( "start = 'a'", { foo: 42 } );

Loading…
Cancel
Save