adding a new comprehension 'for all key, value of object', which compiles to the naked JS for..in, including enumerable properties inherited from prototypes.

This commit is contained in:
Jeremy Ashkenas 2010-07-15 21:18:35 -04:00
parent 2a932597e4
commit 72c4efbc39
12 changed files with 177 additions and 124 deletions

View File

@ -1,6 +1,5 @@
(function(){
var CoffeeScript, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks;
var __hasProp = Object.prototype.hasOwnProperty;
fs = require('fs');
path = require('path');
helpers = require('./helpers').helpers;
@ -62,7 +61,7 @@
var _a, _b, desc, i, name, spaces, task;
puts('');
_a = tasks;
for (name in _a) { if (__hasProp.call(_a, name)) {
for (name in _a) {
task = _a[name];
spaces = 20 - name.length;
spaces = spaces > 0 ? (function() {
@ -74,7 +73,7 @@
})().join('') : '';
desc = task.description ? ("# " + task.description) : '';
puts("cake " + name + spaces + " " + desc);
}}
}
if (switches.length) {
return puts(oparse.help());
}

View File

@ -377,12 +377,23 @@
})
],
For: [
o("Statement FOR ForVariables ForSource", function() {
return new ForNode($1, $4, $3[0], $3[1]);
}), o("Expression FOR ForVariables ForSource", function() {
return new ForNode($1, $4, $3[0], $3[1]);
}), o("FOR ForVariables ForSource Block", function() {
return new ForNode($4, $3, $2[0], $2[1]);
o("Statement ForStart ForSource", function() {
$3.raw = $2.raw;
return new ForNode($1, $3, $2[0], $2[1]);
}), o("Expression ForStart ForSource", function() {
$3.raw = $2.raw;
return new ForNode($1, $3, $2[0], $2[1]);
}), o("ForStart ForSource Block", function() {
$2.raw = $1.raw;
return new ForNode($3, $2, $1[0], $1[1]);
})
],
ForStart: [
o("FOR ForVariables", function() {
return $2;
}), o("FOR ALL ForVariables", function() {
$3.raw = true;
return $3;
})
],
ForValue: [

View File

@ -1,6 +1,5 @@
(function(){
var compact, count, del, ends, extend, flatten, helpers, include, indexOf, merge, starts;
var __hasProp = Object.prototype.hasOwnProperty;
if (!(typeof process !== "undefined" && process !== null)) {
this.exports = this;
}
@ -53,26 +52,26 @@
var _a, _b, fresh, key, val;
fresh = {};
_a = options;
for (key in _a) { if (__hasProp.call(_a, key)) {
for (key in _a) {
val = _a[key];
(fresh[key] = val);
}}
}
if (overrides) {
_b = overrides;
for (key in _b) { if (__hasProp.call(_b, key)) {
for (key in _b) {
val = _b[key];
(fresh[key] = val);
}}
}
}
return fresh;
});
helpers.extend = (extend = function(object, properties) {
var _a, _b, key, val;
_a = []; _b = properties;
for (key in _b) { if (__hasProp.call(_b, key)) {
for (key in _b) {
val = _b[key];
_a.push(object[key] = val);
}}
}
return _a;
});
helpers.flatten = (flatten = function(array) {

View File

@ -83,6 +83,9 @@
if (tag === 'WHEN' && include(LINE_BREAK, this.tag())) {
tag = 'LEADING_WHEN';
}
if (id === 'all' && this.tag() === 'FOR') {
tag = 'ALL';
}
if (include(JS_FORBIDDEN, id)) {
if (forcedIdentifier) {
tag = 'STRING';

View File

@ -1356,6 +1356,7 @@
this.source = source.source;
this.guard = source.guard;
this.step = source.step;
this.raw = !!source.raw;
this.object = !!source.object;
if (this.object) {
_b = [this.index, this.name];
@ -1468,13 +1469,13 @@
} else {
varPart = (namePart || '') && (this.pattern ? namePart : ("" + (this.idt(1)) + namePart + ";\n"));
}
this.object ? (forPart = ("" + ivar + " in " + svar + ") { if (" + (utility('hasProp')) + ".call(" + svar + ", " + ivar + ")")) : null;
this.object ? this.raw ? (forPart = ("" + ivar + " in " + svar)) : (forPart = ("" + ivar + " in " + svar + ") { if (" + (utility('hasProp')) + ".call(" + svar + ", " + ivar + ")")) : null;
body = body.compile(merge(o, {
indent: this.idt(1),
top: true
}));
vars = range ? name : ("" + name + ", " + ivar);
close = this.object ? '}}' : '}';
close = this.object && !this.raw ? '}}' : '}';
return "" + (sourcePart) + "for (" + forPart + ") {\n" + varPart + body + "\n" + this.tab + close + returnResult;
};
return ForNode;

File diff suppressed because one or more lines are too long

View File

@ -57,7 +57,7 @@ exports.run: ->
# Display the list of Cake tasks in a format similar to `rake -T`
printTasks: ->
puts ''
for name, task of tasks
for all name, task of tasks
spaces: 20 - name.length
spaces: if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
desc: if task.description then "# $task.description" else ''

View File

@ -419,9 +419,14 @@ grammar: {
# Comprehensions can either be normal, with a block of expressions to execute,
# or postfix, with a single expression.
For: [
o "Statement FOR ForVariables ForSource", -> new ForNode $1, $4, $3[0], $3[1]
o "Expression FOR ForVariables ForSource", -> new ForNode $1, $4, $3[0], $3[1]
o "FOR ForVariables ForSource Block", -> new ForNode $4, $3, $2[0], $2[1]
o "Statement ForStart ForSource", -> $3.raw: $2.raw; new ForNode $1, $3, $2[0], $2[1]
o "Expression ForStart ForSource", -> $3.raw: $2.raw; new ForNode $1, $3, $2[0], $2[1]
o "ForStart ForSource Block", -> $2.raw: $1.raw; new ForNode $3, $2, $1[0], $1[1]
]
ForStart: [
o "FOR ForVariables", -> $2
o "FOR ALL ForVariables", -> $3.raw: true; $3
]
# An array of all accepted values for a variable inside the loop. This
@ -448,9 +453,9 @@ grammar: {
o "OF Expression", -> {source: $2, object: true}
o "IN Expression WHEN Expression", -> {source: $2, guard: $4}
o "OF Expression WHEN Expression", -> {source: $2, guard: $4, object: true}
o "IN Expression BY Expression", -> {source: $2, step: $4}
o "IN Expression BY Expression", -> {source: $2, step: $4}
o "IN Expression WHEN Expression BY Expression", -> {source: $2, guard: $4, step: $6}
o "IN Expression BY Expression WHEN Expression", -> {source: $2, step: $4, guard: $6}
o "IN Expression BY Expression WHEN Expression", -> {source: $2, step: $4, guard: $6}
]
# The CoffeeScript switch/when/else block replaces the JavaScript

View File

@ -44,14 +44,14 @@ helpers.count: count: (string, letter) ->
# options hash to propagate down the tree without polluting other branches.
helpers.merge: merge: (options, overrides) ->
fresh: {}
(fresh[key]: val) for key, val of options
(fresh[key]: val) for key, val of overrides if overrides
(fresh[key]: val) for all key, val of options
(fresh[key]: val) for all key, val of overrides if overrides
fresh
# Extend a source object with the properties of another object (shallow copy).
# We use this to simulate Node's deprecated `process.mixin`
helpers.extend: extend: (object, properties) ->
(object[key]: val) for key, val of properties
(object[key]: val) for all key, val of properties
# Return a completely flattened version of an array. Handy for getting a
# list of `children` from the nodes.

View File

@ -87,6 +87,7 @@ exports.Lexer: class Lexer
tag: 'IDENTIFIER'
tag: id.toUpperCase() if include(JS_KEYWORDS, id) or (not forcedIdentifier and include(COFFEE_KEYWORDS, id))
tag: 'LEADING_WHEN' if tag is 'WHEN' and include LINE_BREAK, @tag()
tag: 'ALL' if id is 'all' and @tag() is 'FOR'
if include(JS_FORBIDDEN, id)
if forcedIdentifier
tag: 'STRING'

View File

@ -1226,6 +1226,7 @@ exports.ForNode: class ForNode extends BaseNode
@source: source.source
@guard: source.guard
@step: source.step
@raw: !!source.raw
@object: !!source.object
[@name, @index]: [@index, @name] if @object
@pattern: @name instanceof ValueNode
@ -1291,10 +1292,13 @@ exports.ForNode: class ForNode extends BaseNode
else
varPart: (namePart or '') and (if @pattern then namePart else "${@idt(1)}$namePart;\n")
if @object
forPart: "$ivar in $svar) { if (${utility('hasProp')}.call($svar, $ivar)"
if @raw
forPart: "$ivar in $svar"
else
forPart: "$ivar in $svar) { if (${utility('hasProp')}.call($svar, $ivar)"
body: body.compile(merge(o, {indent: @idt(1), top: true}))
vars: if range then name else "$name, $ivar"
close: if @object then '}}' else '}'
close: if @object and not @raw then '}}' else '}'
"${sourcePart}for ($forPart) {\n$varPart$body\n$@tab$close$returnResult"
#### IfNode

View File

@ -114,3 +114,17 @@ expr: ->
result: item * item for item in arguments
ok expr(2, 4, 8).join(' ') is '4 16 64'
# Fast object comprehensions over all properties, including prototypal ones.
class Cat
constructor: -> @name: 'Whiskers'
breed: 'tabby'
hair: 'cream'
whiskers: new Cat
own: value for key, value of whiskers
all: value for all key, value of whiskers
ok own.join(' ') is 'Whiskers'
ok all.sort().join(' ') is 'Whiskers cream tabby'