diff --git a/lib/coffee-script/lexer.js b/lib/coffee-script/lexer.js index e5fb6f40f6..3d7f561503 100644 --- a/lib/coffee-script/lexer.js +++ b/lib/coffee-script/lexer.js @@ -9,6 +9,8 @@ exports.Lexer = Lexer = (function() { + Lexer.__name__ = 'Lexer'; + function Lexer() {} Lexer.prototype.tokenize = function(code, opts) { diff --git a/lib/coffee-script/nodes.js b/lib/coffee-script/nodes.js index bb7c18133c..c3a30c3b12 100644 --- a/lib/coffee-script/nodes.js +++ b/lib/coffee-script/nodes.js @@ -32,6 +32,8 @@ exports.Base = Base = (function() { + Base.__name__ = 'Base'; + function Base() {} Base.prototype.compile = function(o, lvl) { @@ -208,6 +210,8 @@ __extends(Block, _super); + Block.__name__ = 'Block'; + function Block(nodes) { this.expressions = compact(flatten(nodes || [])); } @@ -428,6 +432,8 @@ __extends(Literal, _super); + Literal.__name__ = 'Literal'; + function Literal(value) { this.value = value; } @@ -486,6 +492,8 @@ __extends(Return, _super); + Return.__name__ = 'Return'; + function Return(expr) { if (expr && !expr.unwrap().isUndefined) { this.expression = expr; @@ -522,6 +530,8 @@ __extends(Value, _super); + Value.__name__ = 'Value'; + function Value(base, props, tag) { if (!props && base instanceof Value) { return base; @@ -688,6 +698,8 @@ __extends(Comment, _super); + Comment.__name__ = 'Comment'; + function Comment(comment) { this.comment = comment; } @@ -713,6 +725,8 @@ __extends(Call, _super); + Call.__name__ = 'Call'; + function Call(variable, args, soak) { this.args = args != null ? args : []; this.soak = soak; @@ -898,6 +912,8 @@ __extends(Extends, _super); + Extends.__name__ = 'Extends'; + function Extends(child, parent) { this.child = child; this.parent = parent; @@ -917,6 +933,8 @@ __extends(Access, _super); + Access.__name__ = 'Access'; + function Access(name, tag) { this.name = name; this.name.asKey = true; @@ -945,6 +963,8 @@ __extends(Index, _super); + Index.__name__ = 'Index'; + function Index(index) { this.index = index; } @@ -967,6 +987,8 @@ __extends(Range, _super); + Range.__name__ = 'Range'; + Range.prototype.children = ['from', 'to']; function Range(from, to, tag) { @@ -1068,6 +1090,8 @@ __extends(Slice, _super); + Slice.__name__ = 'Slice'; + Slice.prototype.children = ['range']; function Slice(range) { @@ -1094,6 +1118,8 @@ __extends(Obj, _super); + Obj.__name__ = 'Obj'; + function Obj(props, generated) { this.generated = generated != null ? generated : false; this.objects = this.properties = props || []; @@ -1181,6 +1207,8 @@ __extends(Arr, _super); + Arr.__name__ = 'Arr'; + function Arr(objs) { this.objects = objs || []; } @@ -1235,6 +1263,8 @@ __extends(Class, _super); + Class.__name__ = 'Class'; + function Class(variable, parent, body) { this.variable = variable; this.parent = parent; @@ -1396,6 +1426,9 @@ if (!(this.ctor instanceof Code)) { this.body.expressions.unshift(this.ctor); } + if (decl) { + this.body.expressions.unshift(new Assign(new Value(new Literal(name), [new Access(new Literal('__name__'))]), new Literal("'" + name + "'"))); + } this.body.expressions.push(lname); (_ref2 = this.body.expressions).unshift.apply(_ref2, this.directives); this.addBoundFunctions(o); @@ -1422,6 +1455,8 @@ __extends(Assign, _super); + Assign.__name__ = 'Assign'; + function Assign(variable, value, context, options) { var forbidden, name, _ref2; this.variable = variable; @@ -1642,6 +1677,8 @@ __extends(Code, _super); + Code.__name__ = 'Code'; + function Code(params, body, tag) { this.params = params || []; this.body = body || new Block; @@ -1801,6 +1838,8 @@ __extends(Param, _super); + Param.__name__ = 'Param'; + function Param(name, value, splat) { var _ref2; this.name = name; @@ -1887,6 +1926,8 @@ __extends(Splat, _super); + Splat.__name__ = 'Splat'; + Splat.prototype.children = ['name']; Splat.prototype.isAssignable = YES; @@ -1957,6 +1998,8 @@ __extends(While, _super); + While.__name__ = 'While'; + function While(condition, options) { this.condition = (options != null ? options.invert : void 0) ? condition.invert() : condition; this.guard = options != null ? options.guard : void 0; @@ -2038,6 +2081,8 @@ __extends(Op, _super); + Op.__name__ = 'Op'; + function Op(op, first, second, flip) { if (op === 'in') { return new In(first, second); @@ -2230,6 +2275,8 @@ __extends(In, _super); + In.__name__ = 'In'; + function In(object, array) { this.object = object; this.array = array; @@ -2310,6 +2357,8 @@ __extends(Try, _super); + Try.__name__ = 'Try'; + function Try(attempt, error, recovery, ensure) { this.attempt = attempt; this.error = error; @@ -2367,6 +2416,8 @@ __extends(Throw, _super); + Throw.__name__ = 'Throw'; + function Throw(expression) { this.expression = expression; } @@ -2391,6 +2442,8 @@ __extends(Existence, _super); + Existence.__name__ = 'Existence'; + function Existence(expression) { this.expression = expression; } @@ -2424,6 +2477,8 @@ __extends(Parens, _super); + Parens.__name__ = 'Parens'; + function Parens(body) { this.body = body; } @@ -2462,6 +2517,8 @@ __extends(For, _super); + For.__name__ = 'For'; + function For(body, source) { var _ref2; this.source = source.source, this.guard = source.guard, this.step = source.step, this.name = source.name, this.index = source.index; @@ -2619,6 +2676,8 @@ __extends(Switch, _super); + Switch.__name__ = 'Switch'; + function Switch(subject, cases, otherwise) { this.subject = subject; this.cases = cases; @@ -2704,6 +2763,8 @@ __extends(If, _super); + If.__name__ = 'If'; + function If(condition, body, options) { this.body = body; if (options == null) { diff --git a/lib/coffee-script/optparse.js b/lib/coffee-script/optparse.js index 288cffb568..9864e05a54 100644 --- a/lib/coffee-script/optparse.js +++ b/lib/coffee-script/optparse.js @@ -4,6 +4,8 @@ exports.OptionParser = OptionParser = (function() { + OptionParser.__name__ = 'OptionParser'; + function OptionParser(rules, banner) { this.banner = banner; this.rules = buildRules(rules); diff --git a/lib/coffee-script/parser.js b/lib/coffee-script/parser.js index 9bb9e75a52..397504a2b9 100755 --- a/lib/coffee-script/parser.js +++ b/lib/coffee-script/parser.js @@ -1,6 +1,5 @@ /* Jison generated parser */ var parser = (function(){ -undefined var parser = {trace: function trace() { }, yy: {}, symbols_: {"error":2,"Root":3,"Body":4,"Block":5,"TERMINATOR":6,"Line":7,"Expression":8,"Statement":9,"Return":10,"Comment":11,"STATEMENT":12,"Value":13,"Invocation":14,"Code":15,"Operation":16,"Assign":17,"If":18,"Try":19,"While":20,"For":21,"Switch":22,"Class":23,"Throw":24,"INDENT":25,"OUTDENT":26,"Identifier":27,"IDENTIFIER":28,"AlphaNumeric":29,"NUMBER":30,"STRING":31,"Literal":32,"JS":33,"REGEX":34,"DEBUGGER":35,"BOOL":36,"Assignable":37,"=":38,"AssignObj":39,"ObjAssignable":40,":":41,"ThisProperty":42,"RETURN":43,"HERECOMMENT":44,"PARAM_START":45,"ParamList":46,"PARAM_END":47,"FuncGlyph":48,"->":49,"=>":50,"OptComma":51,",":52,"Param":53,"ParamVar":54,"...":55,"Array":56,"Object":57,"Splat":58,"SimpleAssignable":59,"Accessor":60,"Parenthetical":61,"Range":62,"This":63,".":64,"?.":65,"::":66,"Index":67,"INDEX_START":68,"IndexValue":69,"INDEX_END":70,"INDEX_SOAK":71,"Slice":72,"{":73,"AssignList":74,"}":75,"CLASS":76,"EXTENDS":77,"OptFuncExist":78,"Arguments":79,"SUPER":80,"FUNC_EXIST":81,"CALL_START":82,"CALL_END":83,"ArgList":84,"THIS":85,"@":86,"[":87,"]":88,"RangeDots":89,"..":90,"Arg":91,"SimpleArgs":92,"TRY":93,"Catch":94,"FINALLY":95,"CATCH":96,"THROW":97,"(":98,")":99,"WhileSource":100,"WHILE":101,"WHEN":102,"UNTIL":103,"Loop":104,"LOOP":105,"ForBody":106,"FOR":107,"ForStart":108,"ForSource":109,"ForVariables":110,"OWN":111,"ForValue":112,"FORIN":113,"FOROF":114,"BY":115,"SWITCH":116,"Whens":117,"ELSE":118,"When":119,"LEADING_WHEN":120,"IfBlock":121,"IF":122,"POST_IF":123,"UNARY":124,"-":125,"+":126,"--":127,"++":128,"?":129,"MATH":130,"SHIFT":131,"COMPARE":132,"LOGIC":133,"RELATION":134,"COMPOUND_ASSIGN":135,"$accept":0,"$end":1}, @@ -77,7 +76,9 @@ break; case 33:this.$ = (function () { var val; val = new yy.Literal($$[$0]); - if ($$[$0] === 'undefined') val.isUndefined = true; + if ($$[$0] === 'undefined') { + val.isUndefined = true; + } return val; }()); break; @@ -473,103 +474,190 @@ parseError: function parseError(str, hash) { throw new Error(str); }, parse: function parse(input) { - var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; + var self = this, + stack = [0], + vstack = [null], // semantic value stack + lstack = [], // location stack + table = this.table, + yytext = '', + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; + + //this.reductionCount = this.shiftCount = 0; + this.lexer.setInput(input); this.lexer.yy = this.yy; this.yy.lexer = this.lexer; - if (typeof this.lexer.yylloc == "undefined") + if (typeof this.lexer.yylloc == 'undefined') this.lexer.yylloc = {}; var yyloc = this.lexer.yylloc; lstack.push(yyloc); - if (typeof this.yy.parseError === "function") + + if (typeof this.yy.parseError === 'function') this.parseError = this.yy.parseError; - function popStack(n) { - stack.length = stack.length - 2 * n; + + function popStack (n) { + stack.length = stack.length - 2*n; vstack.length = vstack.length - n; lstack.length = lstack.length - n; } + function lex() { var token; - token = self.lexer.lex() || 1; - if (typeof token !== "number") { + token = self.lexer.lex() || 1; // $end = 1 + // if token isn't its numeric value, convert + if (typeof token !== 'number') { token = self.symbols_[token] || token; } return token; } - var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; + + var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; while (true) { - state = stack[stack.length - 1]; + // retreive state number from top of stack + state = stack[stack.length-1]; + + // use default actions if available if (this.defaultActions[state]) { action = this.defaultActions[state]; } else { if (symbol == null) symbol = lex(); + // read action for current state and first input action = table[state] && table[state][symbol]; } - if (typeof action === "undefined" || !action.length || !action[0]) { + + // handle parse error + _handle_error: + if (typeof action === 'undefined' || !action.length || !action[0]) { + if (!recovering) { + // Report error expected = []; - for (p in table[state]) - if (this.terminals_[p] && p > 2) { - expected.push("'" + this.terminals_[p] + "'"); - } - var errStr = ""; + for (p in table[state]) if (this.terminals_[p] && p > 2) { + expected.push("'"+this.terminals_[p]+"'"); + } + var errStr = ''; if (this.lexer.showPosition) { - errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + this.terminals_[symbol] + "'"; + errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'"; } else { - errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); + errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + + (symbol == 1 /*EOF*/ ? "end of input" : + ("'"+(this.terminals_[symbol] || symbol)+"'")); } - this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + this.parseError(errStr, + {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); } - } - if (action[0] instanceof Array && action.length > 1) { - throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); - } - switch (action[0]) { - case 1: - stack.push(symbol); - vstack.push(this.lexer.yytext); - lstack.push(this.lexer.yylloc); - stack.push(action[1]); - symbol = null; - if (!preErrorSymbol) { + + // just recovered from another error + if (recovering == 3) { + if (symbol == EOF) { + throw new Error(errStr || 'Parsing halted.'); + } + + // discard current lookahead and grab another yyleng = this.lexer.yyleng; yytext = this.lexer.yytext; yylineno = this.lexer.yylineno; yyloc = this.lexer.yylloc; - if (recovering > 0) - recovering--; - } else { - symbol = preErrorSymbol; - preErrorSymbol = null; - } - break; - case 2: - len = this.productions_[action[1]][1]; - yyval.$ = vstack[vstack.length - len]; - yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; - r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); - if (typeof r !== "undefined") { - return r; + symbol = lex(); } - if (len) { - stack = stack.slice(0, -1 * len * 2); - vstack = vstack.slice(0, -1 * len); - lstack = lstack.slice(0, -1 * len); + + // try to recover from error + while (1) { + // check for error recovery rule in this state + if ((TERROR.toString()) in table[state]) { + break; + } + if (state == 0) { + throw new Error(errStr || 'Parsing halted.'); + } + popStack(1); + state = stack[stack.length-1]; } - stack.push(this.productions_[action[1]][0]); - vstack.push(yyval.$); - lstack.push(yyval._$); - newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; - stack.push(newState); - break; - case 3: - return true; + + preErrorSymbol = symbol; // save the lookahead token + symbol = TERROR; // insert generic error symbol as new lookahead + state = stack[stack.length-1]; + action = table[state] && table[state][TERROR]; + recovering = 3; // allow 3 real symbols to be shifted before reporting a new error + } + + // this shouldn't happen, unless resolve defaults are off + if (action[0] instanceof Array && action.length > 1) { + throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); } + + switch (action[0]) { + + case 1: // shift + //this.shiftCount++; + + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); // push state + symbol = null; + if (!preErrorSymbol) { // normal execution/no error + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { // error just occurred, resume old lookahead f/ before error + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + + case 2: // reduce + //this.reductionCount++; + + len = this.productions_[action[1]][1]; + + // perform semantic action + yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 + // default location, uses first token for firsts, last for lasts + yyval._$ = { + first_line: lstack[lstack.length-(len||1)].first_line, + last_line: lstack[lstack.length-1].last_line, + first_column: lstack[lstack.length-(len||1)].first_column, + last_column: lstack[lstack.length-1].last_column + }; + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + + if (typeof r !== 'undefined') { + return r; + } + + // pop off stack + if (len) { + stack = stack.slice(0,-1*len*2); + vstack = vstack.slice(0, -1*len); + lstack = lstack.slice(0, -1*len); + } + + stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) + vstack.push(yyval.$); + lstack.push(yyval._$); + // goto new state = table[STATE][NONTERMINAL] + newState = table[stack[stack.length-2]][stack[stack.length-1]]; + stack.push(newState); + break; + + case 3: // accept + return true; + } + } + return true; -} -}; +}}; +undefined return parser; })(); if (typeof require !== 'undefined' && typeof exports !== 'undefined') { diff --git a/lib/coffee-script/rewriter.js b/lib/coffee-script/rewriter.js index 0ec2105226..8cce056a90 100644 --- a/lib/coffee-script/rewriter.js +++ b/lib/coffee-script/rewriter.js @@ -6,6 +6,8 @@ exports.Rewriter = (function() { + Rewriter.__name__ = 'Rewriter'; + function Rewriter() {} Rewriter.prototype.rewrite = function(tokens) { diff --git a/lib/coffee-script/scope.js b/lib/coffee-script/scope.js index 8b13543723..0f210e0812 100644 --- a/lib/coffee-script/scope.js +++ b/lib/coffee-script/scope.js @@ -6,6 +6,8 @@ exports.Scope = Scope = (function() { + Scope.__name__ = 'Scope'; + Scope.root = null; function Scope(parent, expressions, method) { diff --git a/src/nodes.coffee b/src/nodes.coffee index 225844595c..a709f84b0d 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -966,6 +966,8 @@ exports.Class = class Class extends Base @ensureConstructor name @body.spaced = yes @body.expressions.unshift @ctor unless @ctor instanceof Code + if decl + @body.expressions.unshift new Assign (new Value (new Literal name), [new Access new Literal '__name__']), (new Literal "'#{name}'") @body.expressions.push lname @body.expressions.unshift @directives... @addBoundFunctions o diff --git a/test/classes.coffee b/test/classes.coffee index c60f296a5e..4af600d23a 100644 --- a/test/classes.coffee +++ b/test/classes.coffee @@ -676,3 +676,11 @@ test "#2052: classes should work in strict mode", -> class A catch e ok no + +test '#494: Named classes', -> + class A + eq A.__name__, 'A' + class A.B + eq A.B.__name__, 'B' + class A.B['C'] + ok A.B.C.__name__ isnt 'C'