Skip to content

Commit

Permalink
Merge pull request facebook#7 from syranide/xjsns
Browse files Browse the repository at this point in the history
Add support for XML/XJS identifier namespaces
  • Loading branch information
Jeff Morrison authored and Jeff Morrison committed May 10, 2014
2 parents f7d3248 + 5a16fa7 commit 992d3e6
Show file tree
Hide file tree
Showing 3 changed files with 314 additions and 4 deletions.
58 changes: 54 additions & 4 deletions esprima.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ parseYieldExpression: true
WhileStatement: 'WhileStatement',
WithStatement: 'WithStatement',
XJSIdentifier: 'XJSIdentifier',
XJSMemberExpression: 'XJSMemberExpression',
XJSEmptyExpression: 'XJSEmptyExpression',
XJSExpressionContainer: 'XJSExpressionContainer',
XJSElement: 'XJSElement',
Expand Down Expand Up @@ -266,6 +267,7 @@ parseYieldExpression: true
EachNotAllowed: 'Each is not supported',
InvalidXJSTagName: 'XJS tag name can not be empty',
InvalidXJSAttributeValue: 'XJS value should be either an expression or a quoted XJS text',
XJSMemberExpressionNamespaceNotAllowed: 'XJS member expressions does not support namespaces',
ExpectedXJSClosingTag: 'Expected corresponding XJS closing tag for %0'
};

Expand Down Expand Up @@ -1801,6 +1803,18 @@ parseYieldExpression: true
};
},

createXJSMemberExpression: function (object, property) {
if (property.namespace) {
throwError({}, Messages.XJSMemberExpressionNamespaceNotAllowed);
}

return {
type: Syntax.XJSMemberExpression,
object: object,
property: property
};
},

createXJSElement: function (openingElement, closingElement, children) {
return {
type: Syntax.XJSElement,
Expand Down Expand Up @@ -5292,6 +5306,18 @@ parseYieldExpression: true
diams: '\u2666'
};

function getQualifiedXJSName(object) {
if (object.type === Syntax.XJSIdentifier) {
return (object.namespace ? object.namespace + ':' : '') + object.name;
}
if (object.type === Syntax.XJSMemberExpression) {
return (
getQualifiedXJSName(object.object) + '.' +
getQualifiedXJSName(object.property)
);
}
}

function isXJSIdentifierStart(ch) {
// exclude backslash (\)
return (ch !== 92) && isIdentifierStart(ch);
Expand Down Expand Up @@ -5442,6 +5468,30 @@ parseYieldExpression: true
return markerApply(marker, delegate.createXJSIdentifier(token.value, token.namespace));
}

function parseXJSMemberExpression() {
var marker = markerCreate(),
expr = parseXJSIdentifier();

if (expr.namespace) {
throwError({}, Messages.XJSMemberExpressionNamespaceNotAllowed);
}

while (match('.')) {
lex();
expr = markerApply(marker, delegate.createXJSMemberExpression(expr, parseXJSIdentifier()));
}

return expr;
}

function parseXJSElementName() {
if (lookahead2().value === '.') {
return parseXJSMemberExpression();
}

return parseXJSIdentifier();
}

function parseXJSAttributeValue() {
var value, marker;
if (match('{')) {
Expand Down Expand Up @@ -5531,7 +5581,7 @@ parseYieldExpression: true
state.inXJSTag = true;
expect('<');
expect('/');
name = parseXJSIdentifier();
name = parseXJSElementName();
// Because advance() (called by lex() called by expect()) expects there
// to be a valid token after >, it needs to know whether to look for a
// standard JS token or an XJS text node
Expand All @@ -5551,7 +5601,7 @@ parseYieldExpression: true

expect('<');

name = parseXJSIdentifier();
name = parseXJSElementName();

while (index < length &&
lookahead.value !== '/' &&
Expand Down Expand Up @@ -5596,8 +5646,8 @@ parseYieldExpression: true
state.inXJSChild = origInXJSChild;
state.inXJSTag = origInXJSTag;
closingElement = parseXJSClosingElement();
if (closingElement.name.namespace !== openingElement.name.namespace || closingElement.name.name !== openingElement.name.name) {
throwError({}, Messages.ExpectedXJSClosingTag, openingElement.name.namespace ? openingElement.name.namespace + ':' + openingElement.name.name : openingElement.name.name);
if (getQualifiedXJSName(closingElement.name) !== getQualifiedXJSName(openingElement.name)) {
throwError({}, Messages.ExpectedXJSClosingTag, getQualifiedXJSName(openingElement.name));
}
}

Expand Down
259 changes: 259 additions & 0 deletions test/fbtest.js
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,216 @@ var fbTestFixture = {
"column": 57
}
}
},

'<a.b></a.b>': {
type: 'ExpressionStatement',
expression: {
type: 'XJSElement',
openingElement: {
type: 'XJSOpeningElement',
name: {
type: 'XJSMemberExpression',
object: {
type: 'XJSIdentifier',
name: 'a',
range: [1, 2],
loc: {
start: { line: 1, column: 1 },
end: { line: 1, column: 2 }
}
},
property: {
type: 'XJSIdentifier',
name: 'b',
range: [3, 4],
loc: {
start: { line: 1, column: 3 },
end: { line: 1, column: 4 }
}
},
range: [1, 4],
loc: {
start: { line: 1, column: 1 },
end: { line: 1, column: 4 }
}
},
selfClosing: false,
attributes: [],
range: [0, 5],
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 5 }
}
},
closingElement: {
type: 'XJSClosingElement',
name: {
type: 'XJSMemberExpression',
object: {
type: 'XJSIdentifier',
name: 'a',
range: [7, 8],
loc: {
start: { line: 1, column: 7 },
end: { line: 1, column: 8 }
}
},
property: {
type: 'XJSIdentifier',
name: 'b',
range: [9, 10],
loc: {
start: { line: 1, column: 9 },
end: { line: 1, column: 10 }
}
},
range: [7, 10],
loc: {
start: { line: 1, column: 7 },
end: { line: 1, column: 10 }
}
},
range: [5, 11],
loc: {
start: { line: 1, column: 5 },
end: { line: 1, column: 11 }
}
},
children: [],
range: [0, 11],
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 11 }
}
},
range: [0, 11],
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 11 }
}
},

'<a.b.c></a.b.c>': {
type: 'ExpressionStatement',
expression: {
type: 'XJSElement',
openingElement: {
type: 'XJSOpeningElement',
name: {
type: 'XJSMemberExpression',
object: {
type: 'XJSMemberExpression',
object: {
type: 'XJSIdentifier',
name: 'a',
range: [1, 2],
loc: {
start: { line: 1, column: 1 },
end: { line: 1, column: 2 }
}
},
property: {
type: 'XJSIdentifier',
name: 'b',
range: [3, 4],
loc: {
start: { line: 1, column: 3 },
end: { line: 1, column: 4 }
}
},
range: [1, 4],
loc: {
start: { line: 1, column: 1 },
end: { line: 1, column: 4 }
}
},
property: {
type: 'XJSIdentifier',
name: 'c',
range: [5, 6],
loc: {
start: { line: 1, column: 5 },
end: { line: 1, column: 6 }
}
},
range: [1, 6],
loc: {
start: { line: 1, column: 1 },
end: { line: 1, column: 6 }
}
},
selfClosing: false,
attributes: [],
range: [0, 7],
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 7 }
}
},
closingElement: {
type: 'XJSClosingElement',
name: {
type: 'XJSMemberExpression',
object: {
type: 'XJSMemberExpression',
object: {
type: 'XJSIdentifier',
name: 'a',
range: [9, 10],
loc: {
start: { line: 1, column: 9 },
end: { line: 1, column: 10 }
}
},
property: {
type: 'XJSIdentifier',
name: 'b',
range: [11, 12],
loc: {
start: { line: 1, column: 11 },
end: { line: 1, column: 12 }
}
},
range: [9, 12],
loc: {
start: { line: 1, column: 9 },
end: { line: 1, column: 12 }
}
},
property: {
type: 'XJSIdentifier',
name: 'c',
range: [13, 14],
loc: {
start: { line: 1, column: 13 },
end: { line: 1, column: 14 }
}
},
range: [9, 14],
loc: {
start: { line: 1, column: 9 },
end: { line: 1, column: 14 }
}
},
range: [7, 15],
loc: {
start: { line: 1, column: 7 },
end: { line: 1, column: 15 }
}
},
children: [],
range: [0, 15],
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 15 }
}
},
range: [0, 15],
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 15 }
}
}
},

Expand Down Expand Up @@ -1798,6 +2008,55 @@ var fbTestFixture = {
message: "Error: Line 1: Expected corresponding XJS closing tag for a:b",
},

'<a:b.c></a:b.c>': {
index: 4,
lineNumber: 1,
column: 5,
message: 'Error: Line 1: XJS member expressions does not support namespaces'
},

'<a.b:c></a.b:c>': {
index: 6,
lineNumber: 1,
column: 7,
message: 'Error: Line 1: XJS member expressions does not support namespaces'
},

'<a.b.c></a>': {
index: 11,
lineNumber: 1,
column: 12,
message: "Error: Line 1: Expected corresponding XJS closing tag for a.b.c"
},

'<.a></.a>': {
index: 1,
lineNumber: 1,
column: 2,
message: "Error: Line 1: Unexpected token ."
},

'<a.></a.>': {
index: 3,
lineNumber: 1,
column: 4,
message: "Error: Line 1: Unexpected token >"
},

'<a[foo]></a[foo]>': {
index: 2,
lineNumber: 1,
column: 3,
message: "Error: Line 1: Unexpected token ["
},

'<a[\'foo\']></a[\'foo\']>': {
index: 2,
lineNumber: 1,
column: 3,
message: "Error: Line 1: Unexpected token ["
},

'<a><a />': {
index: 8,
lineNumber: 1,
Expand Down
1 change: 1 addition & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19511,6 +19511,7 @@ var testFixture = {
WhileStatement: 'WhileStatement',
WithStatement: 'WithStatement',
XJSIdentifier: 'XJSIdentifier',
XJSMemberExpression: "XJSMemberExpression",
XJSEmptyExpression: "XJSEmptyExpression",
XJSExpressionContainer: "XJSExpressionContainer",
XJSElement: 'XJSElement',
Expand Down

0 comments on commit 992d3e6

Please sign in to comment.