Error handling: Make |?| return |null| on unsuccessful match

Before this commit, the |?| operator returned an empty string upon
unsuccessful match. This commit changes the returned value to |null|. It
also updates the PEG.js grammar and the example grammars, which used the
value returned by |?| quite often.

Returning |null| is possible because it no longer indicates a match
failure.

I expect that this change will simplify many real-world grammars, as an
empty string is almost never desirable as a return value (except some
lexer-level rules) and it is often translated into |null| or some other
value in action code.

Implements part of #198.
redux
David Majda 11 years ago
parent 57e806383c
commit 86769a6c5c

@ -291,7 +291,7 @@ expression as many times as possible.
#### *expression* ? #### *expression* ?
Try to match the expression. If the match succeeds, return its match result, Try to match the expression. If the match succeeds, return its match result,
otherwise return an empty string. otherwise return `null`.
#### & *expression* #### & *expression*

@ -33,7 +33,7 @@ stylesheet
return { return {
type: "stylesheet", type: "stylesheet",
charset: charset !== "" ? charset[1] : null, charset: charset !== null ? charset[1] : null,
imports: importsConverted, imports: importsConverted,
rules: rulesConverted rules: rulesConverted
}; };
@ -44,7 +44,7 @@ import
return { return {
type: "import_rule", type: "import_rule",
href: href, href: href,
media: media !== "" ? media : [] media: media !== null ? media : []
}; };
} }
@ -75,16 +75,16 @@ page
declarationsHead:declaration? declarationsHead:declaration?
declarationsTail:(";" S* declaration?)* declarationsTail:(";" S* declaration?)*
"}" S* { "}" S* {
var declarations = declarationsHead !== "" ? [declarationsHead] : []; var declarations = declarationsHead !== null ? [declarationsHead] : [];
for (var i = 0; i < declarationsTail.length; i++) { for (var i = 0; i < declarationsTail.length; i++) {
if (declarationsTail[i][2] !== "") { if (declarationsTail[i][2] !== null) {
declarations.push(declarationsTail[i][2]); declarations.push(declarationsTail[i][2]);
} }
} }
return { return {
type: "page_rule", type: "page_rule",
qualifier: qualifier !== "" ? qualifier : null, qualifier: qualifier,
declarations: declarations declarations: declarations
}; };
} }
@ -119,9 +119,9 @@ ruleset
selectors.push(selectorsTail[i][2]); selectors.push(selectorsTail[i][2]);
} }
var declarations = declarationsHead !== "" ? [declarationsHead] : []; var declarations = declarationsHead !== null ? [declarationsHead] : [];
for (i = 0; i < declarationsTail.length; i++) { for (i = 0; i < declarationsTail.length; i++) {
if (declarationsTail[i][2] !== "") { if (declarationsTail[i][2] !== null) {
declarations.push(declarationsTail[i][2]); declarations.push(declarationsTail[i][2]);
} }
} }
@ -196,8 +196,8 @@ attrib
return { return {
type: "attribute_selector", type: "attribute_selector",
attribute: attribute, attribute: attribute,
operator: operatorAndValue !== "" ? operatorAndValue[0] : null, operator: operatorAndValue !== null ? operatorAndValue[0] : null,
value: operatorAndValue !== "" ? operatorAndValue[2] : null value: operatorAndValue !== null ? operatorAndValue[2] : null
}; };
} }
@ -208,7 +208,7 @@ pseudo
return { return {
type: "function", type: "function",
name: name, name: name,
params: params !== "" ? [params[0]] : [] params: params !== null ? [params[0]] : []
}; };
} }
/ IDENT / IDENT
@ -230,7 +230,7 @@ declaration
type: "declaration", type: "declaration",
property: property, property: property,
expression: expression, expression: expression,
important: important !== "" ? true : false important: important !== null ? true : false
}; };
} }
@ -262,7 +262,12 @@ term
/ FREQ S* / FREQ S*
/ PERCENTAGE S* / PERCENTAGE S*
/ NUMBER S* / NUMBER S*
) { return { type: "value", value: operator + value[0] }; } ) {
return {
type: "value",
value: (operator !== null ? operator : "") + value[0]
};
}
/ value:URI S* { return { type: "uri", value: value }; } / value:URI S* { return { type: "uri", value: value }; }
/ function / function
/ hexcolor / hexcolor
@ -331,7 +336,7 @@ comment
ident ident
= dash:"-"? nmstart:nmstart nmchars:nmchar* { = dash:"-"? nmstart:nmstart nmchars:nmchar* {
return dash + nmstart + nmchars.join(""); return (dash !== null ? dash : "") + nmstart + nmchars.join("");
} }
name name

@ -478,25 +478,25 @@ ArrayLiteral
= "[" __ elision:(Elision __)? "]" { = "[" __ elision:(Elision __)? "]" {
return { return {
type: "ArrayLiteral", type: "ArrayLiteral",
elements: elision !== "" ? elision[0] : [] elements: elision !== null ? elision[0] : []
}; };
} }
/ "[" __ elements:ElementList __ elision:("," __ (Elision __)?)? "]" { / "[" __ elements:ElementList __ elision:("," __ (Elision __)?)? "]" {
return { return {
type: "ArrayLiteral", type: "ArrayLiteral",
elements: elements.concat(elision !== "" && elision[2] !== "" ? elision[2][0] : []) elements: elements.concat(elision !== null && elision[2] !== null ? elision[2][0] : [])
}; };
} }
ElementList ElementList
= head:( = head:(
elision:(Elision __)? element:AssignmentExpression { elision:(Elision __)? element:AssignmentExpression {
return (elision !== "" ? elision[0] : []).concat(element); return (elision !== null ? elision[0] : []).concat(element);
} }
) )
tail:( tail:(
__ "," __ elision:(Elision __)? element:AssignmentExpression { __ "," __ elision:(Elision __)? element:AssignmentExpression {
return (elision !== "" ? elision[0] : []).concat(element); return (elision !== null ? elision[0] : []).concat(element);
} }
)* { )* {
var result = head; var result = head;
@ -519,7 +519,7 @@ ObjectLiteral
= "{" __ properties:(PropertyNameAndValueList __ ("," __)?)? "}" { = "{" __ properties:(PropertyNameAndValueList __ ("," __)?)? "}" {
return { return {
type: "ObjectLiteral", type: "ObjectLiteral",
properties: properties !== "" ? properties[0] : [] properties: properties !== null ? properties[0] : []
}; };
} }
@ -663,7 +663,7 @@ CallExpression
Arguments Arguments
= "(" __ args:ArgumentList? __ ")" { = "(" __ args:ArgumentList? __ ")" {
return args !== "" ? args : []; return args !== null ? args : [];
} }
ArgumentList ArgumentList
@ -1143,7 +1143,7 @@ Block
= "{" __ statements:(StatementList __)? "}" { = "{" __ statements:(StatementList __)? "}" {
return { return {
type: "Block", type: "Block",
statements: statements !== "" ? statements[0] : [] statements: statements !== null ? statements[0] : []
}; };
} }
@ -1187,7 +1187,7 @@ VariableDeclaration
return { return {
type: "VariableDeclaration", type: "VariableDeclaration",
name: name, name: name,
value: value !== "" ? value[1] : null value: value !== null ? value[1] : null
}; };
} }
@ -1196,7 +1196,7 @@ VariableDeclarationNoIn
return { return {
type: "VariableDeclaration", type: "VariableDeclaration",
name: name, name: name,
value: value !== "" ? value[1] : null value: value !== null ? value[1] : null
}; };
} }
@ -1221,7 +1221,7 @@ IfStatement
type: "IfStatement", type: "IfStatement",
condition: condition, condition: condition,
ifStatement: ifStatement, ifStatement: ifStatement,
elseStatement: elseStatement !== "" ? elseStatement[3] : null elseStatement: elseStatement !== null ? elseStatement[3] : null
}; };
} }
@ -1272,9 +1272,9 @@ ForStatement
{ {
return { return {
type: "ForStatement", type: "ForStatement",
initializer: initializer !== "" ? initializer : null, initializer: initializer,
test: test !== "" ? test : null, test: test,
counter: counter !== "" ? counter : null, counter: counter,
statement: statement statement: statement
}; };
} }
@ -1358,10 +1358,10 @@ CaseBlock
before:CaseClauses? before:CaseClauses?
defaultAndAfter:(__ DefaultClause __ CaseClauses?)? __ defaultAndAfter:(__ DefaultClause __ CaseClauses?)? __
"}" { "}" {
var before = before !== "" ? before : []; var before = before !== null ? before : [];
if (defaultAndAfter !== "") { if (defaultAndAfter !== null) {
var defaultClause = defaultAndAfter[1]; var defaultClause = defaultAndAfter[1];
var clausesAfter = defaultAndAfter[3] !== "" var clausesAfter = defaultAndAfter[3] !== null
? defaultAndAfter[3] ? defaultAndAfter[3]
: []; : [];
} else { } else {
@ -1386,7 +1386,7 @@ CaseClause
return { return {
type: "CaseClause", type: "CaseClause",
selector: selector, selector: selector,
statements: statements !== "" ? statements[1] : [] statements: statements !== null ? statements[1] : []
}; };
} }
@ -1394,7 +1394,7 @@ DefaultClause
= DefaultToken __ ":" statements:(__ StatementList)? { = DefaultToken __ ":" statements:(__ StatementList)? {
return { return {
type: "DefaultClause", type: "DefaultClause",
statements: statements !== "" ? statements[1] : [] statements: statements !== null ? statements[1] : []
}; };
} }
@ -1470,7 +1470,7 @@ FunctionDeclaration
return { return {
type: "Function", type: "Function",
name: name, name: name,
params: params !== "" ? params : [], params: params !== null ? params : [],
elements: elements elements: elements
}; };
} }
@ -1481,8 +1481,8 @@ FunctionExpression
"{" __ elements:FunctionBody __ "}" { "{" __ elements:FunctionBody __ "}" {
return { return {
type: "Function", type: "Function",
name: name !== "" ? name : null, name: name,
params: params !== "" ? params : [], params: params !== null ? params : [],
elements: elements elements: elements
}; };
} }
@ -1497,13 +1497,13 @@ FormalParameterList
} }
FunctionBody FunctionBody
= elements:SourceElements? { return elements !== "" ? elements : []; } = elements:SourceElements? { return elements !== null ? elements : []; }
Program Program
= elements:SourceElements? { = elements:SourceElements? {
return { return {
type: "Program", type: "Program",
elements: elements !== "" ? elements : [] elements: elements !== null ? elements : []
}; };
} }

@ -466,7 +466,7 @@ module.exports = function(ast, options) {
}, },
optional: function(node, context) { optional: function(node, context) {
var emptyStringIndex = addConst('""'); var nullIndex = addConst('null');
return buildSequence( return buildSequence(
generate(node.expression, { generate(node.expression, {
@ -476,7 +476,7 @@ module.exports = function(ast, options) {
}), }),
buildCondition( buildCondition(
[op.IF_ERROR], [op.IF_ERROR],
buildSequence([op.POP], [op.PUSH, emptyStringIndex]), buildSequence([op.POP], [op.PUSH, nullIndex]),
[] []
) )
); );

File diff suppressed because it is too large Load Diff

@ -477,7 +477,7 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'""', 'null',
'"a"', '"a"',
'{ type: "literal", value: "a", description: "\\"a\\"" }' '{ type: "literal", value: "a", description: "\\"a\\"" }'
])); ]));

@ -621,7 +621,7 @@ describe("generated parser", function() {
it("matches correctly", function() { it("matches correctly", function() {
var parser = PEG.buildParser('start = "a"?', options); var parser = PEG.buildParser('start = "a"?', options);
expect(parser).toParse("", ""); expect(parser).toParse("", null);
expect(parser).toParse("a", "a"); expect(parser).toParse("a", "a");
}); });
}); });
@ -1112,8 +1112,8 @@ describe("generated parser", function() {
*/ */
var parser = PEG.buildParser([ var parser = PEG.buildParser([
'S = &(A "c") a:"a"+ B:B !("a" / "b" / "c") { return a.join("") + B; }', 'S = &(A "c") a:"a"+ B:B !("a" / "b" / "c") { return a.join("") + B; }',
'A = a:"a" A:A? b:"b" { return a + A + b; }', 'A = a:"a" A:A? b:"b" { return [a, A, b].join(""); }',
'B = b:"b" B:B? c:"c" { return b + B + c; }' 'B = b:"b" B:B? c:"c" { return [b, B, c].join(""); }'
].join("\n"), options); ].join("\n"), options);
expect(parser).toParse("abc", "abc"); expect(parser).toParse("abc", "abc");

@ -6,7 +6,7 @@ grammar
= __ initializer:initializer? rules:rule+ { = __ initializer:initializer? rules:rule+ {
return { return {
type: "grammar", type: "grammar",
initializer: initializer !== "" ? initializer : null, initializer: initializer,
rules: rules rules: rules
}; };
} }
@ -24,7 +24,7 @@ rule
return { return {
type: "rule", type: "rule",
name: name, name: name,
expression: displayName !== "" expression: displayName !== null
? { ? {
type: "named", type: "named",
name: displayName, name: displayName,
@ -249,10 +249,10 @@ class "character class"
= "[" inverted:"^"? parts:(classCharacterRange / classCharacter)* "]" flags:"i"? __ { = "[" inverted:"^"? parts:(classCharacterRange / classCharacter)* "]" flags:"i"? __ {
var partsConverted = utils.map(parts, function(part) { return part.data; }); var partsConverted = utils.map(parts, function(part) { return part.data; });
var rawText = "[" var rawText = "["
+ inverted + (inverted !== null ? inverted : "")
+ utils.map(parts, function(part) { return part.rawText; }).join("") + utils.map(parts, function(part) { return part.rawText; }).join("")
+ "]" + "]"
+ flags; + (flags !== null ? flags : "");
return { return {
type: "class", type: "class",

Loading…
Cancel
Save