Skip to content
Browse files

Add support for spread arguments. Implementation inspired by CoffeeSc…

…ript.
  • Loading branch information...
1 parent 53c82de commit 9f495b57e2b9a7a431e0b4b10d8fec21eaa1a7f7 @wycats committed Oct 16, 2011
Showing with 94 additions and 7 deletions.
  1. +6 −0 lib/grammar.y
  2. +1 −0 lib/lexer.l
  3. +7 −0 lib/nodes.js
  4. +80 −7 lib/stringify.js
View
6 lib/grammar.y
@@ -303,6 +303,10 @@ ArgumentList
{ $$ = [$1]; }
| ArgumentList ',' AssignmentExpr
{ $$ = $1; $$.push($3); }
+ | ArgumentList ',' SPREAD AssignmentExpr
+ { $$ = $1; $$.push(yy.Node('Spread', $4, yy.loc(@4))); }
+ | SPREAD AssignmentExpr
+ { $$ = [yy.Node('Spread', $2, yy.loc(@2))]; }
;
LeftHandSideExpr
@@ -1081,6 +1085,8 @@ FormalParameterList
{ $$ = $1; $$.push(yy.Node('Identifier', $3,yy.loc(@3))); }
| FormalParameterList ',' Pattern
{ $$ = $1; $$.push($3); }
+ | FormalParameterList ',' SPREAD IDENT
+ { $$ = $1; $$.push(yy.Node('Spread', $4, yy.loc(@4))); }
;
FunctionBody
View
1 lib/lexer.l
@@ -58,6 +58,7 @@ RCLASS "["({BSL}|[^\\\]])*"]"
"(" return '('
")" return ')'
"," return ','
+"..."/((#{IDS})+(#{IDS}|[0-9])*) return 'SPREAD'
"." return '.'
";" yy.ASI = false; return ';'
":" return ':'
View
7 lib/nodes.js
@@ -297,6 +297,13 @@ def('ArrayPattern', function (elements, loc) {
this.loc = loc;
});
+// Extras
+
+def('Spread', function (name, loc) {
+ this.name = name;
+ this.loc = loc;
+})
+
return def;
}
View
87 lib/stringify.js
@@ -3,6 +3,8 @@
(function () {
"use strict";
+var Reflect = require("./reflect");
+
var indentChar = " ";
function assertEq (val, expected) {
@@ -67,9 +69,14 @@ function params(arr, indent) {
}
function args(arr, indent) {
+ return "(" + argBody(arr, indent) + ")";
return "(" + values(arr, function (x){return expr(x, indent, 2, false)}).join(", ") + ")";
}
+function argBody(arr, indent) {
+ return values(arr, function (x){return expr(x, indent, 2, false)}).join(", ");
+}
+
function functionDeclaration(init, id, n, indent) {
// name is ordinarily an identifier, but literals are also legal for
// getters and setters: ({get 1() {}})
@@ -291,8 +298,8 @@ function expr(n, indent, cprec, noIn) {
case "ConditionalExpression":
return wrapExpr(expr(n.test, indent, 4, noIn) +
- "?" + expr(n.consequent, indent, 0, noIn) +
- ":" + expr(n.alternate, indent, 3, noIn),
+ " ? " + expr(n.consequent, indent, 0, noIn) +
+ " : " + expr(n.alternate, indent, 3, noIn),
cprec, 4);
case "Identifier":
@@ -316,9 +323,11 @@ function expr(n, indent, cprec, noIn) {
}
case "CallExpression":
- return wrapExpr(expr(n.callee, indent, 17, false) +
- args(n.arguments, indent),
- cprec, 18);
+ return decaf_handleCallSpread(n, indent, function() {;
+ return wrapExpr(expr(n.callee, indent, 17, false) +
+ args(n.arguments, indent),
+ cprec, 18);
+ });
case "NewExpression":
return (n.arguments.length == 0
@@ -447,6 +456,11 @@ function expr(n, indent, cprec, noIn) {
temp.push(xmlData(x, indent));
return "<>" + temp.join('') + "</>";
+ // extras
+
+ case "Spread":
+ return "..." + n.name;
+
default:
return unexpected(n);
}
@@ -598,6 +612,9 @@ function sourceElement(n, indent) {
case "FunctionDeclaration":
assertEq(n.id.type, "Identifier");
+
+ decaf_handleFunctionDeclSpread(n)
+
return (indent +
functionDeclaration("function", n.id, n, indent) +
(n.expression ? ";\n" : "\n"));
@@ -610,9 +627,65 @@ function sourceElement(n, indent) {
}
}
+/**
+ * Extensions
+ */
+
+function decaf_handleFunctionDeclSpread(n) {
+ var len = n.params.length - 1, params = n.params, spread, ast;
+
+ if (params[len].type === 'Spread') {
+ spread = params.pop();
+ // TODO: save off AST
+ ast = Reflect.parse("var " + spread.name + " = " + len + " < arguments.length ? [].slice.call(arguments, " + len + ") : []");
+ n.body.body.unshift(ast.body[0]);
+ }
+}
+
+function decaf_handleCallSpread(n, indent, callback) {
+ var len = n.arguments.length - 1, params = n.arguments, spread, ast;
+
+ if (params[len] && params[len].type === 'Spread') {
+ spread = params.pop();
+
+ var toString = function(expression) { return expr(expression, indent, 17, false); };
+
+ var spreadName = toString(spread.name);
+
+ // more parameters than just the spread
+ if (params.length) {
+ var argString = "[" + argBody(params, indent) + "].concat([].slice.call(" + spreadName + ")";
+ } else {
+ var argString = spreadName;
+ }
+
+ var callee = n.callee,
+ object1 = callee && callee.object,
+ object2 = object1 && object1.object;
+
+ // x.y.z
+ if (object2) {
+ // TODO: More robust variable system
+ var calleeString = "(var _ref=" + toString(object1) + ")." + toString(callee.property);
+ var thisString = "_ref";
+ // x.y
+ } else if (object1) {
+ var calleeString = toString(callee);
+ var thisString = toString(object1);
+ } else {
+ var calleeString = toString(callee);
+ var thisString = "undefined";
+ }
+
+ return calleeString + ".apply(" + thisString + ", " + argString + ")";
+ }
+
+ return callback()
+}
+
function stringify(n, newIndentChar) {
- if (n.type != "Program")
- throw new TypeError("argument must be a Program parse node");
+ //if (n.type != "Program")
+ //throw new TypeError("argument must be a Program parse node");
if (newIndentChar) indentChar = newIndentChar;
return values(n.body, function (x){return sourceElement(x, "")}).join("");
}

0 comments on commit 9f495b5

Please sign in to comment.
Something went wrong with that request. Please try again.