Skip to content

Commit

Permalink
Add support for <| and super references
Browse files Browse the repository at this point in the history
  • Loading branch information
zaach committed Aug 13, 2011
1 parent 3f42f35 commit d9952e6
Show file tree
Hide file tree
Showing 7 changed files with 359 additions and 40 deletions.
10 changes: 10 additions & 0 deletions lib/grammar.y
Expand Up @@ -195,6 +195,8 @@ PrimaryExpr
PrimaryExprNoBrace
: THISTOKEN
{ $$ = yy.Node('ThisExpression'); }
| SUPER
{ $$ = yy.Node('SuperExpression'); }
| Literal
| ArrayLiteral
| IDENT
Expand Down Expand Up @@ -247,6 +249,10 @@ MemberExpr
{ $$ = yy.Node('NewExpression',$MemberExpr,$Arguments,yy.loc([@$,@3])); }
| MemberExpr '.' ObjectLiteral
{ $$ = yy.Node('MemberExpression',$1,$3,false,yy.loc([@$,@3])); }
| MemberExpr PROTOTYPEFOR ObjectLiteral
{ $$ = yy.Node('PrototypeForExpression',$1,$3,yy.loc([@$,@3])); }
| MemberExpr PROTOTYPEFOR FunctionExpr
{ $$ = yy.Node('PrototypeForExpression',$1,$3,yy.loc([@$,@3])); }
;

ObjectLiteral
Expand All @@ -266,6 +272,10 @@ MemberExprNoBF
{ $$ = yy.Node('NewExpression',$MemberExpr,$Arguments,yy.loc([@$,@3])); }
| MemberExprNoBF '.' ObjectLiteral
{ $$ = yy.Node('MemberExpression',$1,$3,false,yy.loc([@$,@3])); }
| MemberExprNoBF PROTOTYPEFOR ObjectLiteral
{ $$ = yy.Node('PrototypeForExpression',$1,$3,yy.loc([@$,@3])); }
| MemberExprNoBF PROTOTYPEFOR FunctionExpr
{ $$ = yy.Node('PrototypeForExpression',$1,$3,yy.loc([@$,@3])); }
;

NewExpr
Expand Down
3 changes: 2 additions & 1 deletion lib/lexer.l
Expand Up @@ -68,6 +68,7 @@ RCLASS "["({BSL}|[^\\\]])*"]"
"&=" return 'ANDEQUAL'
"|=" return 'OREQUAL'
"^=" return 'XOREQUAL'
"<|" return 'PROTOTYPEFOR'
"<<=" return 'LSHIFTEQUAL'
">>=" return 'RSHIFTEQUAL'
">>>=" return 'URSHIFTEQUAL'
Expand Down Expand Up @@ -130,7 +131,7 @@ RCLASS "["({BSL}|[^\\\]])*"]"
"export" return 'EXPORT'
"extends" return 'EXTENDS'
"import" return 'IMPORT'
"super" return 'SUPERTOKEN'
"super" return 'SUPER'
"implements" return 'IMPLEMENTS'
"interface" return 'INTERFACE'
"let" return 'LET'
Expand Down
8 changes: 8 additions & 0 deletions lib/nodes.js
Expand Up @@ -66,6 +66,8 @@ def('Literal', function (val, loc) {
// "this" expression node
def('ThisExpression', defaultIni);

def('SuperExpression', defaultIni);

// Var statement node
def('VariableDeclaration', function (kind, declarations, loc) {
this.kind = kind;
Expand Down Expand Up @@ -242,6 +244,12 @@ def('MemberExpression', function (object, property, computed, loc) {
this.loc = loc;
});

def('PrototypeForExpression', function (proto, object, loc) {
this.proto = proto;
this.object = object;
this.loc = loc;
});

// debugger node
def('DebuggerStatement', defaultIni);

Expand Down
8 changes: 8 additions & 0 deletions lib/stringify.js
Expand Up @@ -329,6 +329,9 @@ function expr(n, indent, cprec, noIn) {
case "ThisExpression":
return "this";

case "SuperExpression":
return "super";

case "MemberExpression":
return wrapExpr(expr(n.object, indent, 17, false) +
(n.computed
Expand All @@ -338,6 +341,11 @@ function expr(n, indent, cprec, noIn) {
: "." + expr(n.property, indent, 18, false)),
cprec, 18);

case "PrototypeOfExpression":
return wrapExpr(expr(n.proto, indent, 17, false) +
" <| " + expr(n.object, indent, 18, false),
cprec, 18);

case "UnaryExpression":
case "UpdateExpression":
{
Expand Down
236 changes: 202 additions & 34 deletions lib/transform.js
Expand Up @@ -2,45 +2,208 @@ var JSONSelect = require("JSONSelect");

exports.transform = function (ast, b /*builder*/) {

var superSelector = JSONSelect.compile(':has(:root > .type:val("SuperExpression"))');

// object extend .{ operator
JSONSelect.forEach(':has(:root > .property .type:val("ObjectExpression"))', ast,
function (match) {
var parent = match.parent;
var name = match.object.type == 'Identifier' ?
match.object.name :
'$IIFEparam';
//var exp = b.callExpression(
//buildFunction(name, match.property),
//[match.object]
//);
var exp = buildExtend(match.object, match.property);

parent.replace(match, exp);
}
function (match) {
var parent = match.parent;
var hasSuper = 0;
superSelector.forEach(match.property, function (sup) {
hasSuper++;
superExpr(sup);
});

if (hasSuper) {
var wrapExp = buildExtend(b.identifier("__Extendee__"), match.property);
var exp = wrapSuper(match.object, wrapExp, superExtender());
} else {
var exp = buildExtend(match.object, match.property);
}

parent.replace(match, exp);
}
);

function buildFunction (name, property) {
return b.functionExpression(
null,
[b.identifier(name)],
b.blockStatement(
buildBody(name, property.properties)
.concat(b.returnStatement(b.identifier(name)))
// prototype for <| operator
JSONSelect.forEach(':has(:root > .type:val("PrototypeForExpression"))', ast,
function (match) {
var parent = match.parent;

var hasSuper = 0;
superSelector.forEach(match.object, function (sup) {
hasSuper++;
superExpr(sup);
});

var isFun = match.object.type === 'FunctionExpression';

if (hasSuper) {
var wrapExp = buildPrototypeFor(b.identifier(isFun ? "__Proto__" : "__Super__"), match.object);
var exp = wrapSuper(match.proto, wrapExp, isFun ? superProto() : []);
} else {
var exp = buildPrototypeFor(match.proto, match.object);
}
parent.replace(match, exp);
}
);

function superExpr (sup) {
var parent = sup.parent;
var grandparent = parent.parent;

if (grandparent.type == 'CallExpression' &&
grandparent.callee === parent) {
superCall(sup);
} else if (parent.type == 'ExpressionStatement' ||
parent.type == 'AssignmentExpression' ||
parent.type == 'UpdateExpression' ||
grandparent.operator == 'delete') {
sup.parent.replace(sup, b.thisExpression());
} else if (grandparent.type == 'AssignmentExpression') {
//superSet(sup);
if (grandparent.operator == '=') {
sup.parent.parent.parent.replace(sup.parent.parent, getSetCall("set", sup, sup.parent.parent.right));
} else {
sup.parent.replace(sup, b.thisExpression());
}
} else {
//superGet(sup);
sup.parent.parent.replace(sup.parent, getSetCall("get", sup));
}
}

// replace super.meth() with __Super__.meth.call(this)
function superCall (sup) {
var mem = sup.parent;
var call = mem.parent;
mem.replace(sup, b.identifier("__Super__"));
call.replace(mem,
b.memberExpression(mem, b.identifier("call"), false));
call.arguments.unshift(b.thisExpression());
}

function getSetCall (getset, sup, val) {
return b.callExpression(
b.identifier("__"+getset),
[ b.identifier("__Super__"), propName(sup.parent), b.thisExpression() ].concat(val ? [val] : [])
)
}

function propName (member) {
return member.computed ? member.property : b.literal(member.property.name);
}



// for inline getter checks
function superGet (sup) {
var member = sup.parent;
member.parent.replace(member, b.conditionalExpression(
b.memberExpression(
getProp(member.property),
b.identifier("get"),
false
),
getSetPropCall("get", member.property),
b.memberExpression(b.identifier("__Super__"), member.property, member.computed)
));
}

// for inline setter checks
function superSet (sup) {
var member = sup.parent;
var assign = member.parent;
var val = assign.right;
var key = member.property;
assign.parent.replace(assign, b.conditionalExpression(
b.memberExpression(
getProp(key),
b.identifier("get"),
false
),
getSetPropCall("set", key, val),
(member.replace(sup,b.thisExpression()),assign)
));
}

// used for inlining getter/setter checks
function getProp (prop) {
return b.callExpression(
b.memberExpression(
b.identifier("Object"),
b.identifier("getOwnPropertyDescriptor"),
false
),
false, false
);
[ b.identifier("__Super__"), prop.name ? b.literal(prop.name) : prop ]
)
}
// used for inlining getter/setter checks
function getSetPropCall (getset, prop, val) {
return b.callExpression(
b.memberExpression(
b.memberExpression(
getProp(prop),
b.identifier(getset),
false
),
b.identifier("call"),
false
),
[ b.thisExpression() ].concat(val ? [val] : [])
)
}

function buildBody (name, properties) {
return properties.map(function (prop) {
return expStmt(b.assignmentExpression(
"=",
b.memberExpression(b.identifier(name), prop.key, prop.key.type === 'Literal'),
prop.value
));
});
function superProto () {
return [b.variableDeclaration(
"var",
[ b.variableDeclarator(
b.identifier("__Proto__"),
b.identifier("__Super__")
) ]
),b.expressionStatement(
b.assignmentExpression("=",
b.identifier("__Super__"),
b.memberExpression(
b.identifier("__Super__"),
b.identifier("prototype"),
false)
)
)];
}

function buildGetSet (name, prop) {
function superExtender () {
return [b.variableDeclaration(
"var",
[ b.variableDeclarator(
b.identifier("__Extendee__"),
b.identifier("__Super__")
) ]
), b.expressionStatement(b.assignmentExpression(
"=",
b.identifier("__Super__"),
b.callExpression(
b.memberExpression(
b.identifier("Object"),
b.identifier("getPrototypeOf"),
false
),
[ b.identifier("__Super__") ]
)
))]
}

function wrapSuper (super, exp, pre) {
return b.callExpression(
b.functionExpression(
null,
[ b.identifier("__Super__") ],
b.blockStatement(
(pre||[]).concat([b.returnStatement(exp)])
),
false, false),
[super]
);
}

function expStmt (expr) {
Expand All @@ -49,11 +212,16 @@ exports.transform = function (ast, b /*builder*/) {

function buildExtend (obj, extension) {
return b.callExpression(
b.memberExpression(
b.identifier("Object"),
b.identifier("extend")),
b.identifier("extend"),
[obj, extension]
);
}

function buildPrototypeFor (proto, obj) {
return b.callExpression(
b.identifier("prototypeFor"),
[proto, obj]
);
}
};

0 comments on commit d9952e6

Please sign in to comment.