Skip to content

Commit

Permalink
implement Logical Assignment proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
devsnek committed Mar 12, 2020
1 parent 8c1f6f2 commit a38a19d
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 5 deletions.
4 changes: 4 additions & 0 deletions src/engine.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export const FEATURES = Object.freeze([
name: 'WeakRefs',
url: 'https://github.com/tc39/proposal-weakrefs',
},
{
name: 'LogicalAssignment',
url: 'https://github.com/tc39/proposal-logical-assignment',
},
].map(Object.freeze));

// #sec-agents
Expand Down
33 changes: 29 additions & 4 deletions src/parse.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,25 +76,50 @@ const Parser = acorn.Parser.extend((P) => (class Parse262 extends P {
}

getTokenFromCode(code) {
if (code === 63) {
if (code === 63) { // ?
this.pos += 1;
const next = this.input.charCodeAt(this.pos);
if (next === 46) {
if (next === 46) { // .
const nextNext = this.input.charCodeAt(this.pos + 1);
if (nextNext < 48 || nextNext > 57) {
this.pos += 1;
return this.finishToken(optionalChainToken);
}
}
if (next === 63) {
if (next === 63) { // ??
this.pos += 1;
const nextNext = this.input.charCodeAt(this.pos);
if (nextNext === 61 && surroundingAgent.feature('LogicalAssignment')) { // ??=
this.pos -= 2;
return this.finishOp(acorn.tokTypes.assign, 3);
}
return this.finishToken(nullishCoalescingToken, nullishCoalescingToken.label);
}
return this.finishToken(acorn.tokTypes.question);
}
return super.getTokenFromCode(code);
}

readToken_pipe_amp(code) {
const next = this.input.charCodeAt(this.pos + 1);
if (next === code) { // || or &&
const nextNext = this.input.charCodeAt(this.pos + 2);
// https://tc39.es/proposal-logical-assignment/#sec-assignment-operators
if (nextNext === 61 && surroundingAgent.feature('LogicalAssignment')) { // ||= or &&=
return this.finishOp(acorn.tokTypes.assign, 3);
}
return this.finishOp(code === 124
? acorn.tokTypes.logicalOR
: acorn.tokTypes.logicalAND, 2);
}
if (next === 61) { // |= or &=
return this.finishOp(acorn.tokTypes.assign, 2);
}
return this.finishOp(code === 124
? acorn.tokTypes.bitwiseOR
: acorn.tokTypes.bitwiseAND, 1);
}

parseStatement(context, topLevel, exports) {
if (this.type === acorn.tokTypes._import && surroundingAgent.feature('import.meta')) { // eslint-disable-line no-underscore-dangle
skipWhiteSpace.lastIndex = this.pos;
Expand Down Expand Up @@ -156,7 +181,7 @@ const Parser = acorn.Parser.extend((P) => (class Parse262 extends P {
*
* a.b?.c.d.e
* @=>
* OptionalExpressoin a.b?.c.d.e
* OptionalExpression a.b?.c.d.e
* MemberExpression a.b
* OptionalChain ?.c.d.e
* OptionalChain ?.c.d
Expand Down
63 changes: 62 additions & 1 deletion src/runtime-semantics/AssignmentExpression.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Q, ReturnIfAbrupt } from '../completion.mjs';
import { Value } from '../value.mjs';
import { Q, X, ReturnIfAbrupt } from '../completion.mjs';
import {
GetReferencedName,
GetValue,
PutValue,
ToBoolean,
} from '../abstract-ops/all.mjs';
import {
IsAnonymousFunctionDefinition,
Expand All @@ -19,6 +21,10 @@ import {
// AssignmentExpression :
// LeftHandSideExpression `=` AssignmentExpression
// LeftHandSideExpression AssignmentOperator AssignmentExpression
// https://tc39.es/proposal-logical-assignment/#sec-assignment-operators-runtime-semantics-evaluation
// LeftHandSideExpression `&&=` AssignmentExpression
// LeftHandSideExpression `||=` AssignmentExpression
// LeftHandSideExpression `??=` AssignmentExpression
export function* Evaluate_AssignmentExpression(node) {
const LeftHandSideExpression = node.left;
const AssignmentExpression = node.right;
Expand All @@ -41,6 +47,61 @@ export function* Evaluate_AssignmentExpression(node) {
const rval = Q(GetValue(rref));
Q(yield* DestructuringAssignmentEvaluation_AssignmentPattern(assignmentPattern, rval));
return rval;
} else if (node.operator === '&&=') {
// 1. Let lref be the result of evaluating LeftHandSideExpression.
const lref = yield* Evaluate(LeftHandSideExpression);
// 2. Let lval be ? GetValue(lref).
const lval = Q(GetValue(lval));
// 3. Let lbool be ! ToBoolean(lval).
const lbool = X(ToBoolean(lval));
// 4. If lbool is false, return lval.
if (lbool === Value.false) {
return lval;
}
// 5. Let rref be the result of evaluating AssignmentExpression.
const rref = yield* Evaluate(AssignmentExpression);
// 6. Let rval be ? GetValue(rref).
const rval = Q(GetValue(rref));
// 7. Perform ? PutValue(lref, rval).
Q(PutValue(lref, rval));
// 8. Return rval.
return rval;
} else if (node.operator === '||=') {
// 1. Let lref be the result of evaluating LeftHandSideExpression.
const lref = yield* Evaluate(LeftHandSideExpression);
// 2. Let lval be ? GetValue(lref).
const lval = Q(GetValue(lref));
// 3. Let lbool be ! ToBoolean(lval).
const lbool = X(ToBoolean(lval));
// 4. If lbool is true, return lval.
if (lbool === Value.true) {
return lval;
}
// 5. Let rref be the result of evaluating AssignmentExpression.
const rref = yield* Evaluate(AssignmentExpression);
// 6. Let rval be ? GetValue(rref).
const rval = Q(GetValue(rref));
// 7. Perform ? PutValue(lref, rval).
Q(PutValue(lref, rval));
// 8. Return rval.
return rval;
} else if (node.operator === '??=') {
// 1.Let lref be the result of evaluating LeftHandSideExpression.
const lref = yield* Evaluate(LeftHandSideExpression);
// 2. Let lval be ? GetValue(lref).
const lval = Q(GetValue(lref));
// 3. If lval is not undefined nor null, return lval.
if (lval !== Value.undefined && lval !== Value.null) {
return lval;
}
// 4. Let rref be the result of evaluating AssignmentExpression.
const rref = yield* Evaluate(AssignmentExpression);
// 5. Let rval be ? GetValue(rref).
const rval = Q(GetValue(rref));
// 6. Perform ? PutValue(lref, rval).
Q(PutValue(lref, rval));
// 7. Return rval.
return rval;
} else {
const AssignmentOperator = node.operator;

Expand Down

0 comments on commit a38a19d

Please sign in to comment.