?. && ?[ as member access with nullPropagation. If the object is undefined this will return undefined. If the object on which you want to access the property is defined, the value of the propery will be given back.

This commit is contained in:
Kristof Degrave 2017-02-11 16:44:28 +01:00
parent c79dd953ea
commit b902fe6c7b
6 changed files with 612 additions and 2 deletions

View File

@ -857,10 +857,11 @@ interface MemberExpression <: Expression, Pattern {
object: Expression | Super;
property: Expression;
computed: boolean;
nullPropagation: boolean;
}
```
A member expression. If `computed` is `true`, the node corresponds to a computed (`a[b]`) member expression and `property` is an `Expression`. If `computed` is `false`, the node corresponds to a static (`a.b`) member expression and `property` is an `Identifier`.
A member expression. If `computed` is `true`, the node corresponds to a computed (`a[b]`) member expression and `property` is an `Expression`. If `computed` is `false`, the node corresponds to a static (`a.b`) member expression and `property` is an `Identifier`. The `nullPropagation` flags indecates that the member expression can be called even if the object is null or undefined. If this is the object value (null/undefined) should be returned.
### BindExpression

View File

@ -284,17 +284,33 @@ pp.parseSubscripts = function (base, startPos, startLoc, noCalls) {
node.object = base;
node.callee = this.parseNoCallExpr();
return this.parseSubscripts(this.finishNode(node, "BindExpression"), startPos, startLoc, noCalls);
} else if (this.eat(tt.questionDot)) {
let node = this.startNodeAt(startPos, startLoc);
node.object = base;
node.property = this.parseIdentifier(true);
node.computed = false;
node.nullPropagation = true;
base = this.finishNode(node, "MemberExpression");
} else if (this.eat(tt.dot)) {
const node = this.startNodeAt(startPos, startLoc);
node.object = base;
node.property = this.parseIdentifier(true);
node.computed = false;
node.nullPropagation = false;
base = this.finishNode(node, "MemberExpression");
} else if (this.eat(tt.questionBracketL)) {
let node = this.startNodeAt(startPos, startLoc);
node.object = base;
node.property = this.parseExpression();
node.computed = true;
node.nullPropagation = true;
this.expect(tt.bracketR);
} else if (this.eat(tt.bracketL)) {
const node = this.startNodeAt(startPos, startLoc);
node.object = base;
node.property = this.parseExpression();
node.computed = true;
node.nullPropagation = false;
this.expect(tt.bracketR);
base = this.finishNode(node, "MemberExpression");
} else if (!noCalls && this.match(tt.parenL)) {

View File

@ -391,6 +391,22 @@ export default class Tokenizer {
return this.finishOp(code === 61 ? tt.eq : tt.prefix, 1);
}
readToken_question() { // '?'
let next = this.input.charCodeAt(this.state.pos + 1);
if(next === 46){ // 46 = question '.'
this.state.pos += 2;
return this.finishToken(tt.questionDot);
}
else if(next === 91){ // 91 = question '['
this.state.pos += 2;
return this.finishToken(tt.questionBracketL);
}
else {
++this.state.pos;
return this.finishToken(tt.question);
}
}
getTokenFromCode(code) {
switch (code) {
// The interpretation of a dot depends on whether it is followed
@ -425,7 +441,7 @@ export default class Tokenizer {
return this.finishToken(tt.colon);
}
case 63: ++this.state.pos; return this.finishToken(tt.question);
case 63: return this.readToken_question();
case 64: ++this.state.pos; return this.finishToken(tt.at);
case 96: // '`'

View File

@ -75,6 +75,8 @@ export const types = {
doubleColon: new TokenType("::", { beforeExpr }),
dot: new TokenType("."),
question: new TokenType("?", { beforeExpr }),
questionBracketL: new TokenType("?[", { beforeExpr, startsExpr }),
questionBracketL: new TokenType("?."),
arrow: new TokenType("=>", { beforeExpr }),
template: new TokenType("template"),
ellipsis: new TokenType("...", { beforeExpr }),

View File

@ -0,0 +1,5 @@
o?.x?.y
o?.x ? o.x.z?.w : o.y?.z?.w
o?[0]?[1]?.x

View File

@ -0,0 +1,570 @@
{
"type": "File",
"start": 0,
"end": 50,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 5,
"column": 12
}
},
"program": {
"type": "Program",
"start": 0,
"end": 50,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 5,
"column": 12
}
},
"sourceType": "script",
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"expression": {
"type": "MemberExpression",
"start": 0,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
},
"object": {
"type": "MemberExpression",
"start": 0,
"end": 4,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 4
}
},
"object": {
"type": "Identifier",
"start": 0,
"end": 1,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 1
}
},
"name": "o",
"existentialOperator": true
},
"property": {
"type": "Identifier",
"start": 3,
"end": 4,
"loc": {
"start": {
"line": 1,
"column": 3
},
"end": {
"line": 1,
"column": 4
}
},
"name": "x"
},
"computed": false,
"existentialOperator": true
},
"property": {
"type": "Identifier",
"start": 6,
"end": 7,
"loc": {
"start": {
"line": 1,
"column": 6
},
"end": {
"line": 1,
"column": 7
}
},
"name": "y"
},
"computed": false
}
},
{
"type": "ExpressionStatement",
"start": 9,
"end": 36,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 27
}
},
"expression": {
"type": "ConditionalExpression",
"start": 9,
"end": 36,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 27
}
},
"test": {
"type": "MemberExpression",
"start": 9,
"end": 13,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 4
}
},
"object": {
"type": "Identifier",
"start": 9,
"end": 10,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 1
}
},
"name": "o",
"existentialOperator": true
},
"property": {
"type": "Identifier",
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 3,
"column": 3
},
"end": {
"line": 3,
"column": 4
}
},
"name": "x"
},
"computed": false
},
"consequent": {
"type": "MemberExpression",
"start": 16,
"end": 24,
"loc": {
"start": {
"line": 3,
"column": 7
},
"end": {
"line": 3,
"column": 15
}
},
"object": {
"type": "MemberExpression",
"start": 16,
"end": 21,
"loc": {
"start": {
"line": 3,
"column": 7
},
"end": {
"line": 3,
"column": 12
}
},
"object": {
"type": "MemberExpression",
"start": 16,
"end": 19,
"loc": {
"start": {
"line": 3,
"column": 7
},
"end": {
"line": 3,
"column": 10
}
},
"object": {
"type": "Identifier",
"start": 16,
"end": 17,
"loc": {
"start": {
"line": 3,
"column": 7
},
"end": {
"line": 3,
"column": 8
}
},
"name": "o"
},
"property": {
"type": "Identifier",
"start": 18,
"end": 19,
"loc": {
"start": {
"line": 3,
"column": 9
},
"end": {
"line": 3,
"column": 10
}
},
"name": "x"
},
"computed": false
},
"property": {
"type": "Identifier",
"start": 20,
"end": 21,
"loc": {
"start": {
"line": 3,
"column": 11
},
"end": {
"line": 3,
"column": 12
}
},
"name": "z"
},
"computed": false,
"existentialOperator": true
},
"property": {
"type": "Identifier",
"start": 23,
"end": 24,
"loc": {
"start": {
"line": 3,
"column": 14
},
"end": {
"line": 3,
"column": 15
}
},
"name": "w"
},
"computed": false
},
"alternate": {
"type": "MemberExpression",
"start": 27,
"end": 36,
"loc": {
"start": {
"line": 3,
"column": 18
},
"end": {
"line": 3,
"column": 27
}
},
"object": {
"type": "MemberExpression",
"start": 27,
"end": 33,
"loc": {
"start": {
"line": 3,
"column": 18
},
"end": {
"line": 3,
"column": 24
}
},
"object": {
"type": "MemberExpression",
"start": 27,
"end": 30,
"loc": {
"start": {
"line": 3,
"column": 18
},
"end": {
"line": 3,
"column": 21
}
},
"object": {
"type": "Identifier",
"start": 27,
"end": 28,
"loc": {
"start": {
"line": 3,
"column": 18
},
"end": {
"line": 3,
"column": 19
}
},
"name": "o"
},
"property": {
"type": "Identifier",
"start": 29,
"end": 30,
"loc": {
"start": {
"line": 3,
"column": 20
},
"end": {
"line": 3,
"column": 21
}
},
"name": "y"
},
"computed": false,
"existentialOperator": true
},
"property": {
"type": "Identifier",
"start": 32,
"end": 33,
"loc": {
"start": {
"line": 3,
"column": 23
},
"end": {
"line": 3,
"column": 24
}
},
"name": "z"
},
"computed": false,
"existentialOperator": true
},
"property": {
"type": "Identifier",
"start": 35,
"end": 36,
"loc": {
"start": {
"line": 3,
"column": 26
},
"end": {
"line": 3,
"column": 27
}
},
"name": "w"
},
"computed": false
}
}
},
{
"type": "ExpressionStatement",
"start": 38,
"end": 50,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 12
}
},
"expression": {
"type": "MemberExpression",
"start": 38,
"end": 50,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 12
}
},
"object": {
"type": "MemberExpression",
"start": 38,
"end": 47,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 9
}
},
"object": {
"type": "MemberExpression",
"start": 38,
"end": 43,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 5
}
},
"object": {
"type": "Identifier",
"start": 38,
"end": 39,
"loc": {
"start": {
"line": 5,
"column": 0
},
"end": {
"line": 5,
"column": 1
}
},
"name": "o",
"existentialOperator": true
},
"property": {
"type": "Literal",
"start": 41,
"end": 42,
"loc": {
"start": {
"line": 5,
"column": 3
},
"end": {
"line": 5,
"column": 4
}
},
"value": 0,
"rawValue": 0,
"raw": "0"
},
"computed": true,
"existentialOperator": true
},
"property": {
"type": "Literal",
"start": 45,
"end": 46,
"loc": {
"start": {
"line": 5,
"column": 7
},
"end": {
"line": 5,
"column": 8
}
},
"value": 1,
"rawValue": 1,
"raw": "1"
},
"computed": true,
"existentialOperator": true
},
"property": {
"type": "Identifier",
"start": 49,
"end": 50,
"loc": {
"start": {
"line": 5,
"column": 11
},
"end": {
"line": 5,
"column": 12
}
},
"name": "x"
},
"computed": false
}
}
]
}
}