Skip to content

Commit

Permalink
fix: add SoakedProtoMemberAccessOp to both code paths (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
eventualbuddha authored Jan 11, 2017
1 parent 1956adc commit e3c5667
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 16 deletions.
36 changes: 22 additions & 14 deletions src/mappers/mapValue.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SourceType } from 'coffee-lex';
import SourceToken from 'coffee-lex/dist/SourceToken';
import SourceTokenListIndex from 'coffee-lex/dist/SourceTokenListIndex';
import { Access, Literal, LocationData, Value } from 'decaffeinate-coffeescript/lib/coffee-script/nodes';
import { inspect } from 'util';
import { Identifier, MemberAccessOp, Node, ProtoMemberAccessOp } from '../nodes';
import { Identifier, MemberAccessOp, Node, ProtoMemberAccessOp, SoakedMemberAccessOp, SoakedProtoMemberAccessOp } from '../nodes';
import ParseContext from '../util/ParseContext';
import mapAny from './mapAny';
import { UnsupportedNodeError } from './mapAnyWithFallback';
Expand All @@ -11,14 +11,24 @@ export default function mapValue(context: ParseContext, node: Value): Node {
let result = mapAny(context, node.base);

for (let property of node.properties) {
if (property instanceof Access && !property.soak) {
if (property instanceof Access) {
let name = property.name;

if (!(name instanceof Literal)) {
throw new Error(`unexpected non-Literal property access name: ${inspect(name)}`);
}

let startToken = tokenAtLocation(context, property.locationData);
let startTokenIndex = tokenIndexAtLocation(context, property.locationData);
let startToken = startTokenIndex && context.sourceTokens.tokenAtIndex(startTokenIndex);

if (startToken && property.soak) {
if (startToken.type !== SourceType.EXISTENCE) {
throw new Error(`expected EXISTENCE token ('?') but got ${SourceType[startToken.type]}: ${inspect(startToken)}`);
}

startTokenIndex = startTokenIndex && startTokenIndex.next();
startToken = startTokenIndex && context.sourceTokens.tokenAtIndex(startTokenIndex);
}

if (!startToken) {
throw new Error(`cannot find token at start of property: ${inspect(property)}`);
Expand All @@ -32,7 +42,9 @@ export default function mapValue(context: ParseContext, node: Value): Node {
let isPrototypeAccess = startToken.type === SourceType.PROTO;

if (isPrototypeAccess) {
result = new ProtoMemberAccessOp(
let AccessOp = property.soak ? SoakedProtoMemberAccessOp : ProtoMemberAccessOp;

result = new AccessOp(
result.line,
result.column,
result.start,
Expand All @@ -48,7 +60,9 @@ export default function mapValue(context: ParseContext, node: Value): Node {
throw new Error(`unexpected non-Identifier access member: ${inspect(member)}`);
}

result = new MemberAccessOp(
let AccessOp = property.soak ? SoakedMemberAccessOp : MemberAccessOp;

result = new AccessOp(
result.line,
result.column,
result.start,
Expand All @@ -67,7 +81,7 @@ export default function mapValue(context: ParseContext, node: Value): Node {
return result;
}

function tokenAtLocation(context: ParseContext, location: LocationData): SourceToken | null {
function tokenIndexAtLocation(context: ParseContext, location: LocationData): SourceTokenListIndex | null {
let start = context.linesAndColumns.indexForLocation({
line: location.first_line,
column: location.first_column
Expand All @@ -77,11 +91,5 @@ function tokenAtLocation(context: ParseContext, location: LocationData): SourceT
return null;
}

let startTokenIndex = context.sourceTokens.indexOfTokenContainingSourceIndex(start);

if (startTokenIndex === null) {
return null;
}

return context.sourceTokens.tokenAtIndex(startTokenIndex);
return context.sourceTokens.indexOfTokenContainingSourceIndex(start);
}
32 changes: 32 additions & 0 deletions src/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,38 @@ export class ProtoMemberAccessOp extends AccessOp {
}
}

export class SoakedMemberAccessOp extends AccessOp {
readonly member: Identifier;

constructor(
line: number,
column: number,
start: number,
end: number,
raw: string,
virtual: boolean,
expression: Node,
member: Identifier
) {
super('SoakedMemberAccessOp', line, column, start, end, raw, virtual, expression);
this.member = member;
}
}

export class SoakedProtoMemberAccessOp extends AccessOp {
constructor(
line: number,
column: number,
start: number,
end: number,
raw: string,
virtual: boolean,
expression: Node
) {
super('SoakedProtoMemberAccessOp', line, column, start, end, raw, virtual, expression);
}
}

export class Quasi extends Node {
readonly data: string;

Expand Down
20 changes: 18 additions & 2 deletions src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -976,11 +976,27 @@ function convert(context) {
*/
function accessOpForProperty(expression, prop, loc) {
switch (type(prop)) {
case 'Access':
case 'Access': {
let member = convertChild(prop.name);
let accessTokenIndex = context.sourceTokens.indexOfTokenStartingAtSourceIndex(member.range[0]);
let accessToken = accessTokenIndex && context.sourceTokens.tokenAtIndex(accessTokenIndex);

if (prop.soak && accessToken && accessToken.type === SourceType.EXISTENCE) {
accessTokenIndex = accessTokenIndex.next();
accessToken = accessTokenIndex && context.sourceTokens.tokenAtIndex(accessTokenIndex);
}

if (accessToken && accessToken.type === SourceType.PROTO) {
return makeNode(context, prop.soak ? 'SoakedProtoMemberAccessOp' : 'ProtoMemberAccessOp', mergeLocations(loc, prop.locationData), {
expression
});
}

return makeNode(context, prop.soak ? 'SoakedMemberAccessOp' : 'MemberAccessOp', mergeLocations(loc, prop.locationData), {
expression,
member: convertChild(prop.name)
member
});
}

case 'Index':
return makeNode(context, prop.soak ? 'SoakedDynamicMemberAccessOp' : 'DynamicMemberAccessOp', mergeLocations(loc, prop.locationData), {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a()::
54 changes: 54 additions & 0 deletions test/examples/dangling-prototype-access-of-call/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"type": "Program",
"line": 1,
"column": 1,
"range": [
0,
6
],
"raw": "a()::\n",
"body": {
"type": "Block",
"line": 1,
"column": 1,
"range": [
0,
5
],
"statements": [
{
"type": "ProtoMemberAccessOp",
"line": 1,
"column": 1,
"range": [
0,
5
],
"expression": {
"type": "FunctionApplication",
"line": 1,
"column": 1,
"range": [
0,
3
],
"function": {
"type": "Identifier",
"line": 1,
"column": 1,
"range": [
0,
1
],
"raw": "a",
"data": "a"
},
"arguments": [],
"raw": "a()"
},
"raw": "a()::"
}
],
"raw": "a()::"
}
}
1 change: 1 addition & 0 deletions test/examples/soaked-prototype-access/input.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a?::b
64 changes: 64 additions & 0 deletions test/examples/soaked-prototype-access/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"type": "Program",
"line": 1,
"column": 1,
"range": [
0,
6
],
"raw": "a?::b\n",
"body": {
"type": "Block",
"line": 1,
"column": 1,
"range": [
0,
5
],
"statements": [
{
"type": "MemberAccessOp",
"line": 1,
"column": 1,
"range": [
0,
5
],
"expression": {
"type": "SoakedProtoMemberAccessOp",
"line": 1,
"column": 1,
"range": [
0,
4
],
"expression": {
"type": "Identifier",
"line": 1,
"column": 1,
"range": [
0,
1
],
"raw": "a",
"data": "a"
},
"raw": "a?::"
},
"member": {
"type": "Identifier",
"line": 1,
"column": 5,
"range": [
4,
5
],
"raw": "b",
"data": "b"
},
"raw": "a?::b"
}
],
"raw": "a?::b"
}
}

0 comments on commit e3c5667

Please sign in to comment.