diff --git a/Cakefile b/Cakefile index a16a3cfe..a291d6b3 100644 --- a/Cakefile +++ b/Cakefile @@ -1,5 +1,5 @@ fs: require 'fs' -helpers: require('./lib/helpers').helpers +{helpers}: require('./lib/helpers') CoffeeScript: require './lib/coffee-script' {spawn, exec}: require('child_process') diff --git a/README b/README index c17aa615..758d0bc0 100644 --- a/README +++ b/README @@ -50,7 +50,7 @@ Mathieu D'Amours (matehat) Chris Hoffman (cehoffman) Jason Huggins (hugs) - Tim Jones (Tesco) + Timothy Jones (Tesco) Chris Lloyd (chrislloyd) Matt Lyon (mattly) Jeff Olson (olsonjeffery) diff --git a/lib/cake.js b/lib/cake.js index 635f28ab..7ac8d84e 100755 --- a/lib/cake.js +++ b/lib/cake.js @@ -21,6 +21,7 @@ // Mixin the top-level Cake functions for Cakefiles to use directly. helpers.extend(global, { // Define a Cake task with a short name, an optional sentence description, + // and the function to run as the action itself.: // Define a Cake task with a short name, an optional sentence description, // and the function to run as the action itself. task: function task(name, description, action) { var _a; @@ -38,11 +39,13 @@ }, // Define an option that the Cakefile accepts. The parsed options hash, // containing all of the command-line options passed, will be made available + // as the first argument to the action.: // Define an option that the Cakefile accepts. The parsed options hash, + // containing all of the command-line options passed, will be made available // as the first argument to the action. option: function option(letter, flag, description) { return switches.push([letter, flag, description]); }, - // Invoke another task in the current Cakefile. + // Invoke another task in the current Cakefile.: // Invoke another task in the current Cakefile. invoke: function invoke(name) { if (!(tasks[name])) { no_such_task(name); diff --git a/lib/grammar.js b/lib/grammar.js index 640d6223..cd273792 100644 --- a/lib/grammar.js +++ b/lib/grammar.js @@ -48,6 +48,7 @@ // `Expression`. grammar = { // The **Root** is the top-level node in the syntax tree. Since we parse bottom-up, + // all parsing must end here.: // The **Root** is the top-level node in the syntax tree. Since we parse bottom-up, // all parsing must end here. Root: [o("", function() { return new Expressions(); @@ -55,16 +56,16 @@ return new Expressions(); }), o("Body"), o("Block TERMINATOR") ], - // Any list of statements and expressions, seperated by line breaks or semicolons. + // Any list of statements and expressions, seperated by line breaks or semicolons.: // Any list of statements and expressions, seperated by line breaks or semicolons. Body: [o("Line", function() { return Expressions.wrap([$1]); }), o("Body TERMINATOR Line", function() { return $1.push($3); }), o("Body TERMINATOR") ], - // Expressions and statements, which make up a line in a body. + // Expressions and statements, which make up a line in a body.: // Expressions and statements, which make up a line in a body. Line: [o("Expression"), o("Statement")], - // Pure statements which cannot be expressions. + // Pure statements which cannot be expressions.: // Pure statements which cannot be expressions. Statement: [o("Return"), o("Throw"), o("BREAK", function() { return new LiteralNode($1); }), o("CONTINUE", function() { @@ -74,10 +75,15 @@ // All the different types of expressions in our language. The basic unit of // CoffeeScript is the **Expression** -- everything that can be an expression // is one. Expressions serve as the building blocks of many other rules, making + // them somewhat circular.: // All the different types of expressions in our language. The basic unit of + // CoffeeScript is the **Expression** -- everything that can be an expression + // is one. Expressions serve as the building blocks of many other rules, making // them somewhat circular. Expression: [o("Value"), o("Call"), o("Curry"), o("Code"), o("Operation"), o("Assign"), o("If"), o("Try"), o("While"), o("For"), o("Switch"), o("Extends"), o("Class"), o("Splat"), o("Existence"), o("Comment"), o("Extension")], // A an indented block of expressions. Note that the [Rewriter](rewriter.html) // will convert some postfix forms into blocks for us, by adjusting the + // token stream.: // A an indented block of expressions. Note that the [Rewriter](rewriter.html) + // will convert some postfix forms into blocks for us, by adjusting the // token stream. Block: [o("INDENT Body OUTDENT", function() { return $2; @@ -87,12 +93,13 @@ return Expressions.wrap([$2]); }) ], - // A literal identifier, a variable name or property. + // A literal identifier, a variable name or property.: // A literal identifier, a variable name or property. Identifier: [o("IDENTIFIER", function() { return new LiteralNode($1); }) ], // Alphanumerics are separated from the other **Literal** matchers because + // they can also serve as keys in object literals.: // Alphanumerics are separated from the other **Literal** matchers because // they can also serve as keys in object literals. AlphaNumeric: [o("NUMBER", function() { return new LiteralNode($1); @@ -101,6 +108,7 @@ }) ], // All of our immediate values. These can (in general), be passed straight + // through and printed to JavaScript.: // All of our immediate values. These can (in general), be passed straight // through and printed to JavaScript. Literal: [o("AlphaNumeric"), o("JS", function() { return new LiteralNode($1); @@ -120,24 +128,23 @@ return new LiteralNode(false); }) ], - // Assignment of a variable, property, or index to a value. + // Assignment of a variable, property, or index to a value.: // Assignment of a variable, property, or index to a value. Assign: [o("Assignable ASSIGN Expression", function() { return new AssignNode($1, $3); }) ], // Assignment when it happens within an object literal. The difference from + // the ordinary **Assign** is that these allow numbers and strings as keys.: // Assignment when it happens within an object literal. The difference from // the ordinary **Assign** is that these allow numbers and strings as keys. AssignObj: [o("Identifier", function() { return new ValueNode($1); - }), o("AlphaNumeric", function() { - return new ValueNode($1); - }), o("Identifier ASSIGN Expression", function() { + }), o("AlphaNumeric"), o("Identifier ASSIGN Expression", function() { return new AssignNode(new ValueNode($1), $3, 'object'); }), o("AlphaNumeric ASSIGN Expression", function() { return new AssignNode(new ValueNode($1), $3, 'object'); }), o("Comment") ], - // A return statement from a function body. + // A return statement from a function body.: // A return statement from a function body. Return: [o("RETURN Expression", function() { return new ReturnNode($2); }), o("RETURN", function() { @@ -146,18 +153,22 @@ ], // A comment. Because CoffeeScript passes comments through to JavaScript, we // have to parse comments like any other construct, and identify all of the + // positions in which they can occur in the grammar.: // A comment. Because CoffeeScript passes comments through to JavaScript, we + // have to parse comments like any other construct, and identify all of the // positions in which they can occur in the grammar. Comment: [o("COMMENT", function() { return new CommentNode($1); }) ], - // [The existential operator](http://jashkenas.github.com/coffee-script/#existence). + // [The existential operator](http://jashkenas.github.com/coffee-script/#existence).: // [The existential operator](http://jashkenas.github.com/coffee-script/#existence). Existence: [o("Expression ?", function() { return new ExistenceNode($1); }) ], // The **Code** node is the function literal. It's defined by an indented block // of **Expressions** preceded by a function arrow, with an optional parameter + // list.: // The **Code** node is the function literal. It's defined by an indented block + // of **Expressions** preceded by a function arrow, with an optional parameter // list. Code: [o("PARAM_START ParamList PARAM_END FuncGlyph Block", function() { return new CodeNode($2, $5, $4); @@ -166,6 +177,7 @@ }) ], // CoffeeScript has two different symbols for functions. `->` is for ordinary + // functions, and `=>` is for functions bound to the current value of *this*.: // CoffeeScript has two different symbols for functions. `->` is for ordinary // functions, and `=>` is for functions bound to the current value of *this*. FuncGlyph: [o("->", function() { return 'func'; @@ -173,9 +185,9 @@ return 'boundfunc'; }) ], - // An optional, trailing comma. + // An optional, trailing comma.: // An optional, trailing comma. OptComma: [o(''), o(',')], - // The list of parameters that a function accepts can be of any length. + // The list of parameters that a function accepts can be of any length.: // The list of parameters that a function accepts can be of any length. ParamList: [o("", function() { return []; }), o("Param", function() { @@ -185,6 +197,7 @@ }) ], // A single parameter in a function definition can be ordinary, or a splat + // that hoovers up the remaining arguments.: // A single parameter in a function definition can be ordinary, or a splat // that hoovers up the remaining arguments. Param: [o("PARAM", function() { return new LiteralNode($1); @@ -192,12 +205,12 @@ return new SplatNode($1); }) ], - // A splat that occurs outside of a parameter list. + // A splat that occurs outside of a parameter list.: // A splat that occurs outside of a parameter list. Splat: [o("Expression . . .", function() { return new SplatNode($1); }) ], - // Variables and properties that can be assigned to. + // Variables and properties that can be assigned to.: // Variables and properties that can be assigned to. SimpleAssignable: [o("Identifier", function() { return new ValueNode($1); }), o("Value Accessor", function() { @@ -206,7 +219,7 @@ return new ValueNode($1, [$2]); }), o("ThisProperty") ], - // Everything that can be assigned to. + // Everything that can be assigned to.: // Everything that can be assigned to. Assignable: [o("SimpleAssignable"), o("Array", function() { return new ValueNode($1); }), o("Object", function() { @@ -214,6 +227,7 @@ }) ], // The types of things that can be treated as values -- assigned to, invoked + // as functions, indexed into, named as a class, etc.: // The types of things that can be treated as values -- assigned to, invoked // as functions, indexed into, named as a class, etc. Value: [o("Assignable"), o("Literal", function() { return new ValueNode($1); @@ -226,6 +240,7 @@ }) ], // The general group of accessors into an object, by property, by prototype + // or by array index or slice.: // The general group of accessors into an object, by property, by prototype // or by array index or slice. Accessor: [o("PROPERTY_ACCESS Identifier", function() { return new AccessorNode($2); @@ -239,19 +254,20 @@ return new SliceNode($1); }) ], - // Indexing into an object or array using bracket notation. + // Indexing into an object or array using bracket notation.: // Indexing into an object or array using bracket notation. Index: [o("INDEX_START Expression INDEX_END", function() { return new IndexNode($2); }), o("SOAKED_INDEX_START Expression SOAKED_INDEX_END", function() { return new IndexNode($2, 'soak'); }) ], - // In CoffeeScript, an object literal is simply a list of assignments. + // In CoffeeScript, an object literal is simply a list of assignments.: // In CoffeeScript, an object literal is simply a list of assignments. Object: [o("{ AssignList OptComma }", function() { return new ObjectNode($2); }) ], // Assignment of properties within an object literal can be separated by + // comma, as in JavaScript, or simply by newline.: // Assignment of properties within an object literal can be separated by // comma, as in JavaScript, or simply by newline. AssignList: [o("", function() { return []; @@ -268,6 +284,7 @@ }) ], // Class definitions have optional bodies of prototype property assignments, + // and optional references to the superclass.: // Class definitions have optional bodies of prototype property assignments, // and optional references to the superclass. Class: [o("CLASS SimpleAssignable", function() { return new ClassNode($2); @@ -279,14 +296,14 @@ return new ClassNode($2, $4, $6); }) ], - // Assignments that can happen directly inside a class declaration. + // Assignments that can happen directly inside a class declaration.: // Assignments that can happen directly inside a class declaration. ClassAssign: [o("AssignObj", function() { return $1; }), o("ThisProperty ASSIGN Expression", function() { return new AssignNode(new ValueNode($1), $3, 'this'); }) ], - // A list of assignments to a class. + // A list of assignments to a class.: // A list of assignments to a class. ClassBody: [o("", function() { return []; }), o("ClassAssign", function() { @@ -296,72 +313,76 @@ }) ], // The three flavors of function call: normal, object instantiation with `new`, + // and calling `super()`: // The three flavors of function call: normal, object instantiation with `new`, // and calling `super()` Call: [o("Invocation"), o("NEW Invocation", function() { return $2.new_instance(); }), o("Super") ], - // Binds a function call to a context and/or arguments. + // Binds a function call to a context and/or arguments.: // Binds a function call to a context and/or arguments. Curry: [o("Value <- Arguments", function() { return new CurryNode($1, $3); }) ], // Extending an object by setting its prototype chain to reference a parent + // object.: // Extending an object by setting its prototype chain to reference a parent // object. Extends: [o("SimpleAssignable EXTENDS Value", function() { return new ExtendsNode($1, $3); }) ], - // Ordinary function invocation, or a chained series of calls. + // Ordinary function invocation, or a chained series of calls.: // Ordinary function invocation, or a chained series of calls. Invocation: [o("Value Arguments", function() { return new CallNode($1, $2); }), o("Invocation Arguments", function() { return new CallNode($1, $2); }) ], - // The list of arguments to a function call. + // The list of arguments to a function call.: // The list of arguments to a function call. Arguments: [o("CALL_START ArgList OptComma CALL_END", function() { return $2; }) ], - // Calling super. + // Calling super.: // Calling super. Super: [o("SUPER CALL_START ArgList OptComma CALL_END", function() { return new CallNode('super', $3); }) ], - // A reference to the *this* current object. + // A reference to the *this* current object.: // A reference to the *this* current object. This: [o("THIS", function() { return new ValueNode(new LiteralNode('this')); }), o("@", function() { return new ValueNode(new LiteralNode('this')); }) ], - // A reference to a property on *this*. + // A reference to a property on *this*.: // A reference to a property on *this*. ThisProperty: [o("@ Identifier", function() { return new ValueNode(new LiteralNode('this'), [new AccessorNode($2)]); }) ], - // The CoffeeScript range literal. + // The CoffeeScript range literal.: // The CoffeeScript range literal. Range: [o("[ Expression . . Expression ]", function() { return new RangeNode($2, $5); }), o("[ Expression . . . Expression ]", function() { return new RangeNode($2, $6, true); }) ], - // The slice literal. + // The slice literal.: // The slice literal. Slice: [o("INDEX_START Expression . . Expression INDEX_END", function() { return new RangeNode($2, $5); }), o("INDEX_START Expression . . . Expression INDEX_END", function() { return new RangeNode($2, $6, true); }) ], - // The array literal. + // The array literal.: // The array literal. Array: [o("[ ArgList OptComma ]", function() { return new ArrayNode($2); }) ], // The **ArgList** is both the list of objects passed into a function call, // as well as the contents of an array literal + // (i.e. comma-separated expressions). Newlines work as well.: // The **ArgList** is both the list of objects passed into a function call, + // as well as the contents of an array literal // (i.e. comma-separated expressions). Newlines work as well. ArgList: [o("", function() { return []; @@ -381,6 +402,8 @@ ], // Just simple, comma-separated, required arguments (no fancy syntax). We need // this to be separate from the **ArgList** for use in **Switch** blocks, where + // having the newlines wouldn't make sense.: // Just simple, comma-separated, required arguments (no fancy syntax). We need + // this to be separate from the **ArgList** for use in **Switch** blocks, where // having the newlines wouldn't make sense. SimpleArgs: [o("Expression"), o("SimpleArgs , Expression", function() { if ($1 instanceof Array) { @@ -390,7 +413,7 @@ } }) ], - // The variants of *try/catch/finally* exception handling blocks. + // The variants of *try/catch/finally* exception handling blocks.: // The variants of *try/catch/finally* exception handling blocks. Try: [o("TRY Block Catch", function() { return new TryNode($2, $3[0], $3[1]); }), o("TRY Block FINALLY Block", function() { @@ -399,12 +422,12 @@ return new TryNode($2, $3[0], $3[1], $5); }) ], - // A catch clause names its error and runs a block of code. + // A catch clause names its error and runs a block of code.: // A catch clause names its error and runs a block of code. Catch: [o("CATCH Identifier Block", function() { return [$2, $3]; }) ], - // Throw an exception object. + // Throw an exception object.: // Throw an exception object. Throw: [o("THROW Expression", function() { return new ThrowNode($2); }) @@ -412,15 +435,19 @@ // Parenthetical expressions. Note that the **Parenthetical** is a **Value**, // not an **Expression**, so if you need to use an expression in a place // where only values are accepted, wrapping it in parentheses will always do + // the trick.: // Parenthetical expressions. Note that the **Parenthetical** is a **Value**, + // not an **Expression**, so if you need to use an expression in a place + // where only values are accepted, wrapping it in parentheses will always do // the trick. Parenthetical: [o("( Line )", function() { return new ParentheticalNode($2); }) ], // A language extension to CoffeeScript from the outside. We simply pass + // it through unaltered.: // A language extension to CoffeeScript from the outside. We simply pass // it through unaltered. Extension: [o("EXTENSION")], - // The condition portion of a while loop. + // The condition portion of a while loop.: // The condition portion of a while loop. WhileSource: [o("WHILE Expression", function() { return new WhileNode($2); }), o("WHILE Expression WHEN Expression", function() { @@ -430,6 +457,7 @@ }) ], // The while loop can either be normal, with a block of expressions to execute, + // or postfix, with a single expression. There is no do..while.: // The while loop can either be normal, with a block of expressions to execute, // or postfix, with a single expression. There is no do..while. While: [o("WhileSource Block", function() { return $1.add_body($2); @@ -441,6 +469,8 @@ ], // Array, object, and range comprehensions, at the most generic level. // Comprehensions can either be normal, with a block of expressions to execute, + // or postfix, with a single expression.: // Array, object, and range comprehensions, at the most generic level. + // Comprehensions can either be normal, with a block of expressions to execute, // or postfix, with a single expression. For: [o("Statement FOR ForVariables ForSource", function() { return new ForNode($1, $4, $3[0], $3[1]); @@ -452,6 +482,8 @@ ], // An array or range comprehension has variables for the current element and // (optional) reference to the current index. Or, *key, value*, in the case + // of object comprehensions.: // An array or range comprehension has variables for the current element and + // (optional) reference to the current index. Or, *key, value*, in the case // of object comprehensions. ForVariables: [o("Identifier", function() { return [$1]; @@ -461,6 +493,8 @@ ], // The source of a comprehension is an array or object with an optional filter // clause. If it's an array comprehension, you can also choose to step through + // in fixed-size increments.: // The source of a comprehension is an array or object with an optional filter + // clause. If it's an array comprehension, you can also choose to step through // in fixed-size increments. ForSource: [o("IN Expression", function() { return { @@ -502,6 +536,7 @@ }) ], // The CoffeeScript switch/when/else block replaces the JavaScript + // switch/case/default by compiling into an if-else chain.: // The CoffeeScript switch/when/else block replaces the JavaScript // switch/case/default by compiling into an if-else chain. Switch: [o("SWITCH Expression INDENT Whens OUTDENT", function() { return $4.rewrite_condition($2); @@ -510,12 +545,13 @@ }) ], // The inner list of whens is left recursive. At code-generation time, the + // IfNode will rewrite them into a proper chain.: // The inner list of whens is left recursive. At code-generation time, the // IfNode will rewrite them into a proper chain. Whens: [o("When"), o("Whens When", function() { return $1.push($2); }) ], - // An individual **When** clause, with action. + // An individual **When** clause, with action.: // An individual **When** clause, with action. When: [o("LEADING_WHEN SimpleArgs Block", function() { return new IfNode($2, $3, null, { statement: true @@ -531,6 +567,8 @@ ], // The most basic form of *if* is a condition and an action. The following // if-related rules are broken up along these lines in order to avoid + // ambiguity.: // The most basic form of *if* is a condition and an action. The following + // if-related rules are broken up along these lines in order to avoid // ambiguity. IfStart: [o("IF Expression Block", function() { return new IfNode($2, $3); @@ -538,17 +576,18 @@ return $1.add_else($2); }) ], - // An **IfStart** can optionally be followed by an else block. + // An **IfStart** can optionally be followed by an else block.: // An **IfStart** can optionally be followed by an else block. IfBlock: [o("IfStart"), o("IfStart ELSE Block", function() { return $1.add_else($3); }) ], - // An *else if* continuation of the *if* expression. + // An *else if* continuation of the *if* expression.: // An *else if* continuation of the *if* expression. ElsIf: [o("ELSE IF Expression Block", function() { return (new IfNode($3, $4)).force_statement(); }) ], // The full complement of *if* expressions, including postfix one-liner + // *if* and *unless*.: // The full complement of *if* expressions, including postfix one-liner // *if* and *unless*. If: [o("IfBlock"), o("Statement IF Expression", function() { return new IfNode($3, Expressions.wrap([$1]), null, { @@ -575,6 +614,11 @@ // are defined at the bottom of the page. It would be shorter if we could // combine most of these rules into a single generic *Operand OpSymbol Operand* // -type rule, but in order to make the precedence binding possible, separate + // rules are necessary.: // Arithmetic and logical operators, working on one or more operands. + // Here they are grouped by order of precedence. The actual precedence rules + // are defined at the bottom of the page. It would be shorter if we could + // combine most of these rules into a single generic *Operand OpSymbol Operand* + // -type rule, but in order to make the precedence binding possible, separate // rules are necessary. Operation: [o("! Expression", function() { return new OpNode('!', $2); diff --git a/lib/nodes.js b/lib/nodes.js index 232acb50..d8e518fb 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -752,6 +752,9 @@ join = ''; } indent = prop instanceof CommentNode ? '' : this.idt(1); + if (!(prop instanceof AssignNode)) { + prop = new AssignNode(prop, prop, 'object'); + } return indent + prop.compile(o) + join; }).call(this)); } @@ -1714,6 +1717,8 @@ ClosureNode = (exports.ClosureNode = { // Wrap the expressions body, unless it contains a pure statement, // in which case, no dice. If the body mentions `this` or `arguments`, + // then make sure that the closure wrapper preserves the original values.: // Wrap the expressions body, unless it contains a pure statement, + // in which case, no dice. If the body mentions `this` or `arguments`, // then make sure that the closure wrapper preserves the original values. wrap: function wrap(expressions, statement) { var args, call, func, mentions_args, mentions_this, meth; @@ -1749,12 +1754,15 @@ UTILITIES = { // Correctly set up a prototype chain for inheritance, including a reference // to the superclass for `super()` calls. See: + // [goog.inherits](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.source.html#line1206).: // Correctly set up a prototype chain for inheritance, including a reference + // to the superclass for `super()` calls. See: // [goog.inherits](http://closure-library.googlecode.com/svn/docs/closure_goog_base.js.source.html#line1206). __extends: "function(child, parent) {\n var ctor = function(){ };\n ctor.prototype = parent.prototype;\n child.__superClass__ = parent.prototype;\n child.prototype = new ctor();\n child.prototype.constructor = child;\n }", // Bind a function to a calling context, optionally including curried arguments. + // See [Underscore's implementation](http://jashkenas.github.com/coffee-script/documentation/docs/underscore.html#section-47).: // Bind a function to a calling context, optionally including curried arguments. // See [Underscore's implementation](http://jashkenas.github.com/coffee-script/documentation/docs/underscore.html#section-47). __bind: "function(func, obj, args) {\n return function() {\n return func.apply(obj || {}, args ? args.concat(__slice.call(arguments, 0)) : arguments);\n };\n }", - // Shortcuts to speed up the lookup time for native functions. + // Shortcuts to speed up the lookup time for native functions.: // Shortcuts to speed up the lookup time for native functions. __hasProp: 'Object.prototype.hasOwnProperty', __slice: 'Array.prototype.slice' }; diff --git a/lib/parser.js b/lib/parser.js index 99f4498c..7216421a 100755 --- a/lib/parser.js +++ b/lib/parser.js @@ -103,7 +103,7 @@ case 46:this.$ = new AssignNode($$[$0-3+1-1], $$[$0-3+3-1]); break; case 47:this.$ = new ValueNode($$[$0-1+1-1]); break; -case 48:this.$ = new ValueNode($$[$0-1+1-1]); +case 48:this.$ = $$[$0-1+1-1]; break; case 49:this.$ = new AssignNode(new ValueNode($$[$0-3+1-1]), $$[$0-3+3-1], 'object'); break; diff --git a/src/grammar.coffee b/src/grammar.coffee index 0a8d9500..dd5c8b01 100644 --- a/src/grammar.coffee +++ b/src/grammar.coffee @@ -148,7 +148,7 @@ grammar: { # the ordinary **Assign** is that these allow numbers and strings as keys. AssignObj: [ o "Identifier", -> new ValueNode $1 - o "AlphaNumeric", -> new ValueNode $1 + o "AlphaNumeric" o "Identifier ASSIGN Expression", -> new AssignNode new ValueNode($1), $3, 'object' o "AlphaNumeric ASSIGN Expression", -> new AssignNode new ValueNode($1), $3, 'object' o "Comment" diff --git a/src/nodes.coffee b/src/nodes.coffee index 5d73d818..5740c8ad 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -542,6 +542,7 @@ exports.ObjectNode: class ObjectNode extends BaseNode join: "\n" if (prop is last_noncom) or (prop instanceof CommentNode) join: '' if i is @properties.length - 1 indent: if prop instanceof CommentNode then '' else @idt 1 + prop: new AssignNode prop, prop, 'object' unless prop instanceof AssignNode indent + prop.compile(o) + join props: props.join('') inner: if props then '\n' + props + '\n' + @idt() else ''