Merge pull request #4198 from lydell/node-types
Refactor `Literal` into several subtypes
This commit is contained in:
commit
8afb7cc95b
|
@ -299,7 +299,7 @@
|
|||
return 'end of input';
|
||||
case errorTag !== 'INDENT' && errorTag !== 'OUTDENT':
|
||||
return 'indentation';
|
||||
case errorTag !== 'IDENTIFIER' && errorTag !== 'NUMBER' && errorTag !== 'STRING' && errorTag !== 'STRING_START' && errorTag !== 'REGEX' && errorTag !== 'REGEX_START':
|
||||
case errorTag !== 'IDENTIFIER' && errorTag !== 'NUMBER' && errorTag !== 'INFINITY' && errorTag !== 'STRING' && errorTag !== 'STRING_START' && errorTag !== 'REGEX' && errorTag !== 'REGEX_START':
|
||||
return errorTag.replace(/_START$/, '').toLowerCase();
|
||||
default:
|
||||
return helpers.nameWhitespaceCharacter(errorText);
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
Line: [o('Expression'), o('Statement'), o('YieldReturn')],
|
||||
Statement: [
|
||||
o('Return'), o('Comment'), o('STATEMENT', function() {
|
||||
return new Literal($1);
|
||||
return new StatementLiteral($1);
|
||||
})
|
||||
],
|
||||
Expression: [o('Value'), o('Invocation'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class'), o('Throw'), o('Yield')],
|
||||
|
@ -66,39 +66,39 @@
|
|||
],
|
||||
Identifier: [
|
||||
o('IDENTIFIER', function() {
|
||||
return new Literal($1);
|
||||
return new IdentifierLiteral($1);
|
||||
})
|
||||
],
|
||||
AlphaNumeric: [
|
||||
o('NUMBER', function() {
|
||||
return new Literal($1);
|
||||
return new NumberLiteral($1);
|
||||
}), o('INFINITY', function() {
|
||||
return new InfinityLiteral($1);
|
||||
}), o('String')
|
||||
],
|
||||
String: [
|
||||
o('STRING', function() {
|
||||
return new Literal($1);
|
||||
return new StringLiteral($1);
|
||||
}), o('STRING_START Body STRING_END', function() {
|
||||
return new Parens($2);
|
||||
return new StringWithInterpolations($2);
|
||||
})
|
||||
],
|
||||
Regex: [
|
||||
o('REGEX', function() {
|
||||
return new Literal($1);
|
||||
return new RegexLiteral($1);
|
||||
}), o('REGEX_START Invocation REGEX_END', function() {
|
||||
return $2;
|
||||
return new RegexWithInterpolations($2.args);
|
||||
})
|
||||
],
|
||||
Literal: [
|
||||
o('AlphaNumeric'), o('JS', function() {
|
||||
return new Literal($1);
|
||||
}), o('Regex'), o('DEBUGGER', function() {
|
||||
return new Literal($1);
|
||||
}), o('UNDEFINED', function() {
|
||||
return new Undefined;
|
||||
return new PassthroughLiteral($1);
|
||||
}), o('Regex'), o('UNDEFINED', function() {
|
||||
return new UndefinedLiteral;
|
||||
}), o('NULL', function() {
|
||||
return new Null;
|
||||
return new NullLiteral;
|
||||
}), o('BOOL', function() {
|
||||
return new Bool($1);
|
||||
return new BooleanLiteral($1);
|
||||
})
|
||||
],
|
||||
Assign: [
|
||||
|
@ -228,11 +228,11 @@
|
|||
}), o('?. Identifier', function() {
|
||||
return new Access($2, 'soak');
|
||||
}), o(':: Identifier', function() {
|
||||
return [LOC(1)(new Access(new Literal('prototype'))), LOC(2)(new Access($2))];
|
||||
return [LOC(1)(new Access(new IdentifierLiteral('prototype'))), LOC(2)(new Access($2))];
|
||||
}), o('?:: Identifier', function() {
|
||||
return [LOC(1)(new Access(new Literal('prototype'), 'soak')), LOC(2)(new Access($2))];
|
||||
return [LOC(1)(new Access(new IdentifierLiteral('prototype'), 'soak')), LOC(2)(new Access($2))];
|
||||
}), o('::', function() {
|
||||
return new Access(new Literal('prototype'));
|
||||
return new Access(new IdentifierLiteral('prototype'));
|
||||
}), o('Index')
|
||||
],
|
||||
Index: [
|
||||
|
@ -293,10 +293,13 @@
|
|||
return new Call($1, $3, $2);
|
||||
}), o('Invocation OptFuncExist Arguments', function() {
|
||||
return new Call($1, $3, $2);
|
||||
}), o('SUPER', function() {
|
||||
return new Call('super', [new Splat(new Literal('arguments'))]);
|
||||
}), o('Super')
|
||||
],
|
||||
Super: [
|
||||
o('SUPER', function() {
|
||||
return new SuperCall;
|
||||
}), o('SUPER Arguments', function() {
|
||||
return new Call('super', $2);
|
||||
return new SuperCall($2);
|
||||
})
|
||||
],
|
||||
OptFuncExist: [
|
||||
|
@ -315,14 +318,14 @@
|
|||
],
|
||||
This: [
|
||||
o('THIS', function() {
|
||||
return new Value(new Literal('this'));
|
||||
return new Value(new ThisLiteral);
|
||||
}), o('@', function() {
|
||||
return new Value(new Literal('this'));
|
||||
return new Value(new ThisLiteral);
|
||||
})
|
||||
],
|
||||
ThisProperty: [
|
||||
o('@ Identifier', function() {
|
||||
return new Value(LOC(1)(new Literal('this')), [LOC(2)(new Access($2))], 'this');
|
||||
return new Value(LOC(1)(new ThisLiteral), [LOC(2)(new Access($2))], 'this');
|
||||
})
|
||||
],
|
||||
Array: [
|
||||
|
@ -441,9 +444,9 @@
|
|||
],
|
||||
Loop: [
|
||||
o('LOOP Block', function() {
|
||||
return new While(LOC(1)(new Literal('true'))).addBody($2);
|
||||
return new While(LOC(1)(new BooleanLiteral('true'))).addBody($2);
|
||||
}), o('LOOP Expression', function() {
|
||||
return new While(LOC(1)(new Literal('true'))).addBody(LOC(2)(Block.wrap([$2])));
|
||||
return new While(LOC(1)(new BooleanLiteral('true'))).addBody(LOC(2)(Block.wrap([$2])));
|
||||
})
|
||||
],
|
||||
For: [
|
||||
|
|
|
@ -84,9 +84,10 @@
|
|||
};
|
||||
|
||||
exports.some = (ref = Array.prototype.some) != null ? ref : function(fn) {
|
||||
var e, i, len1;
|
||||
for (i = 0, len1 = this.length; i < len1; i++) {
|
||||
e = this[i];
|
||||
var e, i, len1, ref1;
|
||||
ref1 = this;
|
||||
for (i = 0, len1 = ref1.length; i < len1; i++) {
|
||||
e = ref1[i];
|
||||
if (fn(e)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -109,8 +109,6 @@
|
|||
if (indexOf.call(JS_FORBIDDEN, id) >= 0) {
|
||||
if (forcedIdentifier) {
|
||||
tag = 'IDENTIFIER';
|
||||
id = new String(id);
|
||||
id.reserved = true;
|
||||
} else if (indexOf.call(RESERVED, id) >= 0) {
|
||||
this.error("reserved word '" + id + "'", {
|
||||
length: id.length
|
||||
|
@ -137,6 +135,7 @@
|
|||
return 'BOOL';
|
||||
case 'break':
|
||||
case 'continue':
|
||||
case 'debugger':
|
||||
return 'STATEMENT';
|
||||
default:
|
||||
return tag;
|
||||
|
@ -159,7 +158,7 @@
|
|||
};
|
||||
|
||||
Lexer.prototype.numberToken = function() {
|
||||
var binaryLiteral, lexedLength, match, number, octalLiteral;
|
||||
var binaryLiteral, lexedLength, match, number, numberValue, octalLiteral, tag;
|
||||
if (!(match = NUMBER.exec(this.chunk))) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -183,12 +182,16 @@
|
|||
});
|
||||
}
|
||||
if (octalLiteral = /^0o([0-7]+)/.exec(number)) {
|
||||
number = '0x' + parseInt(octalLiteral[1], 8).toString(16);
|
||||
numberValue = parseInt(octalLiteral[1], 8);
|
||||
number = "0x" + (numberValue.toString(16));
|
||||
} else if (binaryLiteral = /^0b([01]+)/.exec(number)) {
|
||||
numberValue = parseInt(binaryLiteral[1], 2);
|
||||
number = "0x" + (numberValue.toString(16));
|
||||
} else {
|
||||
numberValue = parseFloat(number);
|
||||
}
|
||||
if (binaryLiteral = /^0b([01]+)/.exec(number)) {
|
||||
number = '0x' + parseInt(binaryLiteral[1], 2).toString(16);
|
||||
}
|
||||
this.token('NUMBER', number, 0, lexedLength);
|
||||
tag = numberValue === Infinity ? 'INFINITY' : 'NUMBER';
|
||||
this.token(tag, number, 0, lexedLength);
|
||||
return lexedLength;
|
||||
};
|
||||
|
||||
|
@ -504,7 +507,7 @@
|
|||
tag = value;
|
||||
ref2 = this.tokens, prev = ref2[ref2.length - 1];
|
||||
if (value === '=' && prev) {
|
||||
if (!prev[1].reserved && (ref3 = prev[1], indexOf.call(JS_FORBIDDEN, ref3) >= 0)) {
|
||||
if (prev.variable && (ref3 = prev[1], indexOf.call(JS_FORBIDDEN, ref3) >= 0)) {
|
||||
if (prev.origin) {
|
||||
prev = prev.origin;
|
||||
}
|
||||
|
@ -920,6 +923,8 @@
|
|||
|
||||
exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED;
|
||||
|
||||
exports.JS_FORBIDDEN = JS_FORBIDDEN;
|
||||
|
||||
BOM = 65279;
|
||||
|
||||
IDENTIFIER = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+)([^\n\S]*:(?!:))?/;
|
||||
|
@ -1000,7 +1005,7 @@
|
|||
|
||||
CALLABLE = ['IDENTIFIER', ')', ']', '?', '@', 'THIS', 'SUPER'];
|
||||
|
||||
INDEXABLE = CALLABLE.concat(['NUMBER', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END', 'BOOL', 'NULL', 'UNDEFINED', '}', '::']);
|
||||
INDEXABLE = CALLABLE.concat(['NUMBER', 'INFINITY', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END', 'BOOL', 'NULL', 'UNDEFINED', '}', '::']);
|
||||
|
||||
NOT_REGEX = INDEXABLE.concat(['++', '--']);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Generated by CoffeeScript 1.10.0
|
||||
(function() {
|
||||
var Access, Arr, Assign, Base, Block, Call, Class, Code, CodeFragment, Comment, Existence, Expansion, Extends, For, HEXNUM, IDENTIFIER, IS_REGEX, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, YieldReturn, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isComplexOrAssignable, isLiteralArguments, isLiteralThis, locationDataToString, merge, multident, parseNum, ref1, ref2, some, starts, throwSyntaxError, unfoldSoak, utility,
|
||||
var Access, Arr, Assign, Base, Block, BooleanLiteral, Call, Class, Code, CodeFragment, Comment, Existence, Expansion, Extends, For, IdentifierLiteral, If, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, RESERVED, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, SuperCall, Switch, TAB, THIS, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isComplexOrAssignable, isLiteralArguments, isLiteralThis, locationDataToString, merge, multident, ref1, ref2, some, starts, throwSyntaxError, unfoldSoak, utility,
|
||||
extend1 = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
hasProp = {}.hasOwnProperty,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
Scope = require('./scope').Scope;
|
||||
|
||||
ref1 = require('./lexer'), RESERVED = ref1.RESERVED, STRICT_PROSCRIBED = ref1.STRICT_PROSCRIBED;
|
||||
ref1 = require('./lexer'), RESERVED = ref1.RESERVED, STRICT_PROSCRIBED = ref1.STRICT_PROSCRIBED, JS_FORBIDDEN = ref1.JS_FORBIDDEN;
|
||||
|
||||
ref2 = require('./helpers'), compact = ref2.compact, flatten = ref2.flatten, extend = ref2.extend, merge = ref2.merge, del = ref2.del, starts = ref2.starts, ends = ref2.ends, some = ref2.some, addLocationDataFn = ref2.addLocationDataFn, locationDataToString = ref2.locationDataToString, throwSyntaxError = ref2.throwSyntaxError;
|
||||
|
||||
|
@ -95,14 +95,14 @@
|
|||
func = new Code([], Block.wrap([this]));
|
||||
args = [];
|
||||
if ((argumentsNode = this.contains(isLiteralArguments)) || this.contains(isLiteralThis)) {
|
||||
args = [new Literal('this')];
|
||||
args = [new ThisLiteral];
|
||||
if (argumentsNode) {
|
||||
meth = 'apply';
|
||||
args.push(new Literal('arguments'));
|
||||
args.push(new IdentifierLiteral('arguments'));
|
||||
} else {
|
||||
meth = 'call';
|
||||
}
|
||||
func = new Value(func, [new Access(new Literal(meth))]);
|
||||
func = new Value(func, [new Access(new IdentifierLiteral(meth))]);
|
||||
}
|
||||
parts = (new Call(func, args)).compileNode(o);
|
||||
if (func.isGenerator || ((ref3 = func.base) != null ? ref3.isGenerator : void 0)) {
|
||||
|
@ -116,7 +116,7 @@
|
|||
var complex, ref, sub;
|
||||
complex = isComplex != null ? isComplex(this) : this.isComplex();
|
||||
if (complex) {
|
||||
ref = new Literal(o.scope.freeVariable('ref'));
|
||||
ref = new IdentifierLiteral(o.scope.freeVariable('ref'));
|
||||
sub = new Assign(ref, this);
|
||||
if (level) {
|
||||
return [sub.compileToFragments(o, level), [this.makeCode(ref.value)]];
|
||||
|
@ -240,6 +240,8 @@
|
|||
|
||||
Base.prototype.isAssignable = NO;
|
||||
|
||||
Base.prototype.isNumber = NO;
|
||||
|
||||
Base.prototype.unwrap = THIS;
|
||||
|
||||
Base.prototype.unfoldSoak = NO;
|
||||
|
@ -522,30 +524,111 @@
|
|||
this.value = value1;
|
||||
}
|
||||
|
||||
Literal.prototype.makeReturn = function() {
|
||||
if (this.isStatement()) {
|
||||
return this;
|
||||
} else {
|
||||
return Literal.__super__.makeReturn.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
Literal.prototype.isAssignable = function() {
|
||||
return IDENTIFIER.test(this.value);
|
||||
};
|
||||
|
||||
Literal.prototype.isStatement = function() {
|
||||
var ref3;
|
||||
return (ref3 = this.value) === 'break' || ref3 === 'continue' || ref3 === 'debugger';
|
||||
};
|
||||
|
||||
Literal.prototype.isComplex = NO;
|
||||
|
||||
Literal.prototype.assigns = function(name) {
|
||||
return name === this.value;
|
||||
};
|
||||
|
||||
Literal.prototype.jumps = function(o) {
|
||||
Literal.prototype.compileNode = function(o) {
|
||||
return [this.makeCode(this.value)];
|
||||
};
|
||||
|
||||
Literal.prototype.toString = function() {
|
||||
return " " + (this.isStatement() ? Literal.__super__.toString.apply(this, arguments) : this.constructor.name) + ": " + this.value;
|
||||
};
|
||||
|
||||
return Literal;
|
||||
|
||||
})(Base);
|
||||
|
||||
exports.NumberLiteral = NumberLiteral = (function(superClass1) {
|
||||
extend1(NumberLiteral, superClass1);
|
||||
|
||||
function NumberLiteral() {
|
||||
return NumberLiteral.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
return NumberLiteral;
|
||||
|
||||
})(Literal);
|
||||
|
||||
exports.InfinityLiteral = InfinityLiteral = (function(superClass1) {
|
||||
extend1(InfinityLiteral, superClass1);
|
||||
|
||||
function InfinityLiteral() {
|
||||
return InfinityLiteral.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
InfinityLiteral.prototype.compileNode = function() {
|
||||
return [this.makeCode('Infinity')];
|
||||
};
|
||||
|
||||
return InfinityLiteral;
|
||||
|
||||
})(NumberLiteral);
|
||||
|
||||
exports.StringLiteral = StringLiteral = (function(superClass1) {
|
||||
extend1(StringLiteral, superClass1);
|
||||
|
||||
function StringLiteral() {
|
||||
return StringLiteral.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
return StringLiteral;
|
||||
|
||||
})(Literal);
|
||||
|
||||
exports.RegexLiteral = RegexLiteral = (function(superClass1) {
|
||||
extend1(RegexLiteral, superClass1);
|
||||
|
||||
function RegexLiteral() {
|
||||
return RegexLiteral.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
return RegexLiteral;
|
||||
|
||||
})(Literal);
|
||||
|
||||
exports.PassthroughLiteral = PassthroughLiteral = (function(superClass1) {
|
||||
extend1(PassthroughLiteral, superClass1);
|
||||
|
||||
function PassthroughLiteral() {
|
||||
return PassthroughLiteral.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
return PassthroughLiteral;
|
||||
|
||||
})(Literal);
|
||||
|
||||
exports.IdentifierLiteral = IdentifierLiteral = (function(superClass1) {
|
||||
extend1(IdentifierLiteral, superClass1);
|
||||
|
||||
function IdentifierLiteral() {
|
||||
return IdentifierLiteral.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
IdentifierLiteral.prototype.isAssignable = function() {
|
||||
var ref3;
|
||||
return ref3 = this.value, indexOf.call(RESERVED, ref3) < 0;
|
||||
};
|
||||
|
||||
return IdentifierLiteral;
|
||||
|
||||
})(Literal);
|
||||
|
||||
exports.StatementLiteral = StatementLiteral = (function(superClass1) {
|
||||
extend1(StatementLiteral, superClass1);
|
||||
|
||||
function StatementLiteral() {
|
||||
return StatementLiteral.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
StatementLiteral.prototype.isStatement = YES;
|
||||
|
||||
StatementLiteral.prototype.makeReturn = THIS;
|
||||
|
||||
StatementLiteral.prototype.jumps = function(o) {
|
||||
if (this.value === 'break' && !((o != null ? o.loop : void 0) || (o != null ? o.block : void 0))) {
|
||||
return this;
|
||||
}
|
||||
|
@ -554,77 +637,67 @@
|
|||
}
|
||||
};
|
||||
|
||||
Literal.prototype.compileNode = function(o) {
|
||||
var answer, code, ref3;
|
||||
code = this.value === 'this' ? ((ref3 = o.scope.method) != null ? ref3.bound : void 0) ? o.scope.method.context : this.value : this.value.reserved ? "\"" + this.value + "\"" : this.value;
|
||||
answer = this.isStatement() ? "" + this.tab + code + ";" : code;
|
||||
return [this.makeCode(answer)];
|
||||
StatementLiteral.prototype.compileNode = function(o) {
|
||||
return [this.makeCode("" + this.tab + this.value + ";")];
|
||||
};
|
||||
|
||||
Literal.prototype.toString = function() {
|
||||
return ' "' + this.value + '"';
|
||||
};
|
||||
return StatementLiteral;
|
||||
|
||||
return Literal;
|
||||
})(Literal);
|
||||
|
||||
})(Base);
|
||||
exports.ThisLiteral = ThisLiteral = (function(superClass1) {
|
||||
extend1(ThisLiteral, superClass1);
|
||||
|
||||
exports.Undefined = (function(superClass1) {
|
||||
extend1(Undefined, superClass1);
|
||||
|
||||
function Undefined() {
|
||||
return Undefined.__super__.constructor.apply(this, arguments);
|
||||
function ThisLiteral() {
|
||||
ThisLiteral.__super__.constructor.call(this, 'this');
|
||||
}
|
||||
|
||||
Undefined.prototype.isAssignable = NO;
|
||||
ThisLiteral.prototype.compileNode = function(o) {
|
||||
var code, ref3;
|
||||
code = ((ref3 = o.scope.method) != null ? ref3.bound : void 0) ? o.scope.method.context : this.value;
|
||||
return [this.makeCode(code)];
|
||||
};
|
||||
|
||||
Undefined.prototype.isComplex = NO;
|
||||
return ThisLiteral;
|
||||
|
||||
Undefined.prototype.compileNode = function(o) {
|
||||
})(Literal);
|
||||
|
||||
exports.UndefinedLiteral = UndefinedLiteral = (function(superClass1) {
|
||||
extend1(UndefinedLiteral, superClass1);
|
||||
|
||||
function UndefinedLiteral() {
|
||||
UndefinedLiteral.__super__.constructor.call(this, 'undefined');
|
||||
}
|
||||
|
||||
UndefinedLiteral.prototype.compileNode = function(o) {
|
||||
return [this.makeCode(o.level >= LEVEL_ACCESS ? '(void 0)' : 'void 0')];
|
||||
};
|
||||
|
||||
return Undefined;
|
||||
return UndefinedLiteral;
|
||||
|
||||
})(Base);
|
||||
})(Literal);
|
||||
|
||||
exports.Null = (function(superClass1) {
|
||||
extend1(Null, superClass1);
|
||||
exports.NullLiteral = NullLiteral = (function(superClass1) {
|
||||
extend1(NullLiteral, superClass1);
|
||||
|
||||
function Null() {
|
||||
return Null.__super__.constructor.apply(this, arguments);
|
||||
function NullLiteral() {
|
||||
NullLiteral.__super__.constructor.call(this, 'null');
|
||||
}
|
||||
|
||||
Null.prototype.isAssignable = NO;
|
||||
return NullLiteral;
|
||||
|
||||
Null.prototype.isComplex = NO;
|
||||
})(Literal);
|
||||
|
||||
Null.prototype.compileNode = function() {
|
||||
return [this.makeCode("null")];
|
||||
};
|
||||
exports.BooleanLiteral = BooleanLiteral = (function(superClass1) {
|
||||
extend1(BooleanLiteral, superClass1);
|
||||
|
||||
return Null;
|
||||
|
||||
})(Base);
|
||||
|
||||
exports.Bool = (function(superClass1) {
|
||||
extend1(Bool, superClass1);
|
||||
|
||||
Bool.prototype.isAssignable = NO;
|
||||
|
||||
Bool.prototype.isComplex = NO;
|
||||
|
||||
Bool.prototype.compileNode = function() {
|
||||
return [this.makeCode(this.val)];
|
||||
};
|
||||
|
||||
function Bool(val1) {
|
||||
this.val = val1;
|
||||
function BooleanLiteral() {
|
||||
return BooleanLiteral.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
return Bool;
|
||||
return BooleanLiteral;
|
||||
|
||||
})(Base);
|
||||
})(Literal);
|
||||
|
||||
exports.Return = Return = (function(superClass1) {
|
||||
extend1(Return, superClass1);
|
||||
|
@ -730,16 +803,28 @@
|
|||
return this.hasProperties() || this.base.isAssignable();
|
||||
};
|
||||
|
||||
Value.prototype.isSimpleNumber = function() {
|
||||
return this.bareLiteral(Literal) && SIMPLENUM.test(this.base.value);
|
||||
Value.prototype.isNumber = function() {
|
||||
return this.bareLiteral(NumberLiteral);
|
||||
};
|
||||
|
||||
Value.prototype.isString = function() {
|
||||
return this.bareLiteral(Literal) && IS_STRING.test(this.base.value);
|
||||
return this.bareLiteral(StringLiteral);
|
||||
};
|
||||
|
||||
Value.prototype.isRegex = function() {
|
||||
return this.bareLiteral(Literal) && IS_REGEX.test(this.base.value);
|
||||
return this.bareLiteral(RegexLiteral);
|
||||
};
|
||||
|
||||
Value.prototype.isUndefined = function() {
|
||||
return this.bareLiteral(UndefinedLiteral);
|
||||
};
|
||||
|
||||
Value.prototype.isNull = function() {
|
||||
return this.bareLiteral(NullLiteral);
|
||||
};
|
||||
|
||||
Value.prototype.isBoolean = function() {
|
||||
return this.bareLiteral(BooleanLiteral);
|
||||
};
|
||||
|
||||
Value.prototype.isAtomic = function() {
|
||||
|
@ -755,7 +840,7 @@
|
|||
};
|
||||
|
||||
Value.prototype.isNotCallable = function() {
|
||||
return this.isSimpleNumber() || this.isString() || this.isRegex() || this.isArray() || this.isRange() || this.isSplice() || this.isObject();
|
||||
return this.isNumber() || this.isString() || this.isRegex() || this.isArray() || this.isRange() || this.isSplice() || this.isObject() || this.isUndefined() || this.isNull() || this.isBoolean();
|
||||
};
|
||||
|
||||
Value.prototype.isStatement = function(o) {
|
||||
|
@ -804,14 +889,14 @@
|
|||
}
|
||||
base = new Value(this.base, this.properties.slice(0, -1));
|
||||
if (base.isComplex()) {
|
||||
bref = new Literal(o.scope.freeVariable('base'));
|
||||
bref = new IdentifierLiteral(o.scope.freeVariable('base'));
|
||||
base = new Value(new Parens(new Assign(bref, base)));
|
||||
}
|
||||
if (!name) {
|
||||
return [base, bref];
|
||||
}
|
||||
if (name.isComplex()) {
|
||||
nref = new Literal(o.scope.freeVariable('name'));
|
||||
nref = new IdentifierLiteral(o.scope.freeVariable('name'));
|
||||
name = new Index(new Assign(nref, name.index));
|
||||
nref = new Index(nref);
|
||||
}
|
||||
|
@ -823,7 +908,7 @@
|
|||
this.base.front = this.front;
|
||||
props = this.properties;
|
||||
fragments = this.base.compileToFragments(o, (props.length ? LEVEL_ACCESS : null));
|
||||
if ((this.base instanceof Parens || props.length) && SIMPLENUM.test(fragmentsToText(fragments))) {
|
||||
if (props.length && SIMPLENUM.test(fragmentsToText(fragments))) {
|
||||
fragments.push(this.makeCode('.'));
|
||||
}
|
||||
for (j = 0, len1 = props.length; j < len1; j++) {
|
||||
|
@ -851,7 +936,7 @@
|
|||
fst = new Value(_this.base, _this.properties.slice(0, i));
|
||||
snd = new Value(_this.base, _this.properties.slice(i));
|
||||
if (fst.isComplex()) {
|
||||
ref = new Literal(o.scope.freeVariable('ref'));
|
||||
ref = new IdentifierLiteral(o.scope.freeVariable('ref'));
|
||||
fst = new Parens(new Assign(ref, fst));
|
||||
snd.base = ref;
|
||||
}
|
||||
|
@ -896,14 +981,13 @@
|
|||
exports.Call = Call = (function(superClass1) {
|
||||
extend1(Call, superClass1);
|
||||
|
||||
function Call(variable, args1, soak) {
|
||||
function Call(variable1, args1, soak) {
|
||||
this.variable = variable1;
|
||||
this.args = args1 != null ? args1 : [];
|
||||
this.soak = soak;
|
||||
this.isNew = false;
|
||||
this.isSuper = variable === 'super';
|
||||
this.variable = this.isSuper ? null : variable;
|
||||
if (variable instanceof Value && variable.isNotCallable()) {
|
||||
variable.error("literal is not a function");
|
||||
if (this.variable instanceof Value && this.variable.isNotCallable()) {
|
||||
this.variable.error("literal is not a function");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -920,53 +1004,17 @@
|
|||
return this;
|
||||
};
|
||||
|
||||
Call.prototype.superReference = function(o) {
|
||||
var accesses, base, bref, klass, method, name, nref, variable;
|
||||
method = o.scope.namedMethod();
|
||||
if (method != null ? method.klass : void 0) {
|
||||
klass = method.klass, name = method.name, variable = method.variable;
|
||||
if (klass.isComplex()) {
|
||||
bref = new Literal(o.scope.parent.freeVariable('base'));
|
||||
base = new Value(new Parens(new Assign(bref, klass)));
|
||||
variable.base = base;
|
||||
variable.properties.splice(0, klass.properties.length);
|
||||
}
|
||||
if (name.isComplex() || (name instanceof Index && name.index.isAssignable())) {
|
||||
nref = new Literal(o.scope.parent.freeVariable('name'));
|
||||
name = new Index(new Assign(nref, name.index));
|
||||
variable.properties.pop();
|
||||
variable.properties.push(name);
|
||||
}
|
||||
accesses = [new Access(new Literal('__super__'))];
|
||||
if (method["static"]) {
|
||||
accesses.push(new Access(new Literal('constructor')));
|
||||
}
|
||||
accesses.push(nref != null ? new Index(nref) : name);
|
||||
return (new Value(bref != null ? bref : klass, accesses)).compile(o);
|
||||
} else if (method != null ? method.ctor : void 0) {
|
||||
return method.name + ".__super__.constructor";
|
||||
} else {
|
||||
return this.error('cannot call super outside of an instance method.');
|
||||
}
|
||||
};
|
||||
|
||||
Call.prototype.superThis = function(o) {
|
||||
var method;
|
||||
method = o.scope.method;
|
||||
return (method && !method.klass && method.context) || "this";
|
||||
};
|
||||
|
||||
Call.prototype.unfoldSoak = function(o) {
|
||||
var call, ifn, j, left, len1, list, ref3, ref4, rite;
|
||||
if (this.soak) {
|
||||
if (this.variable) {
|
||||
if (this instanceof SuperCall) {
|
||||
left = new Literal(this.superReference(o));
|
||||
rite = new Value(left);
|
||||
} else {
|
||||
if (ifn = unfoldSoak(o, this, 'variable')) {
|
||||
return ifn;
|
||||
}
|
||||
ref3 = new Value(this.variable).cacheReference(o), left = ref3[0], rite = ref3[1];
|
||||
} else {
|
||||
left = new Literal(this.superReference(o));
|
||||
rite = new Value(left);
|
||||
}
|
||||
rite = new Call(rite, this.args);
|
||||
rite.isNew = this.isNew;
|
||||
|
@ -1025,7 +1073,7 @@
|
|||
compiledArgs.push.apply(compiledArgs, arg.compileToFragments(o, LEVEL_LIST));
|
||||
}
|
||||
fragments = [];
|
||||
if (this.isSuper) {
|
||||
if (this instanceof SuperCall) {
|
||||
preface = this.superReference(o) + (".call(" + (this.superThis(o)));
|
||||
if (compiledArgs.length) {
|
||||
preface += ", ";
|
||||
|
@ -1045,7 +1093,7 @@
|
|||
|
||||
Call.prototype.compileSplat = function(o, splatArgs) {
|
||||
var answer, base, fun, idt, name, ref;
|
||||
if (this.isSuper) {
|
||||
if (this instanceof SuperCall) {
|
||||
return [].concat(this.makeCode((this.superReference(o)) + ".apply(" + (this.superThis(o)) + ", "), splatArgs, this.makeCode(")"));
|
||||
}
|
||||
if (this.isNew) {
|
||||
|
@ -1077,6 +1125,68 @@
|
|||
|
||||
})(Base);
|
||||
|
||||
exports.SuperCall = SuperCall = (function(superClass1) {
|
||||
extend1(SuperCall, superClass1);
|
||||
|
||||
function SuperCall(args) {
|
||||
SuperCall.__super__.constructor.call(this, null, args != null ? args : [new Splat(new IdentifierLiteral('arguments'))]);
|
||||
this.isBare = args != null;
|
||||
}
|
||||
|
||||
SuperCall.prototype.superReference = function(o) {
|
||||
var accesses, base, bref, klass, method, name, nref, variable;
|
||||
method = o.scope.namedMethod();
|
||||
if (method != null ? method.klass : void 0) {
|
||||
klass = method.klass, name = method.name, variable = method.variable;
|
||||
if (klass.isComplex()) {
|
||||
bref = new IdentifierLiteral(o.scope.parent.freeVariable('base'));
|
||||
base = new Value(new Parens(new Assign(bref, klass)));
|
||||
variable.base = base;
|
||||
variable.properties.splice(0, klass.properties.length);
|
||||
}
|
||||
if (name.isComplex() || (name instanceof Index && name.index.isAssignable())) {
|
||||
nref = new IdentifierLiteral(o.scope.parent.freeVariable('name'));
|
||||
name = new Index(new Assign(nref, name.index));
|
||||
variable.properties.pop();
|
||||
variable.properties.push(name);
|
||||
}
|
||||
accesses = [new Access(new IdentifierLiteral('__super__'))];
|
||||
if (method["static"]) {
|
||||
accesses.push(new Access(new IdentifierLiteral('constructor')));
|
||||
}
|
||||
accesses.push(nref != null ? new Index(nref) : name);
|
||||
return (new Value(bref != null ? bref : klass, accesses)).compile(o);
|
||||
} else if (method != null ? method.ctor : void 0) {
|
||||
return method.name + ".__super__.constructor";
|
||||
} else {
|
||||
return this.error('cannot call super outside of an instance method.');
|
||||
}
|
||||
};
|
||||
|
||||
SuperCall.prototype.superThis = function(o) {
|
||||
var method;
|
||||
method = o.scope.method;
|
||||
return (method && !method.klass && method.context) || "this";
|
||||
};
|
||||
|
||||
return SuperCall;
|
||||
|
||||
})(Call);
|
||||
|
||||
exports.RegexWithInterpolations = RegexWithInterpolations = (function(superClass1) {
|
||||
extend1(RegexWithInterpolations, superClass1);
|
||||
|
||||
function RegexWithInterpolations(args) {
|
||||
if (args == null) {
|
||||
args = [];
|
||||
}
|
||||
RegexWithInterpolations.__super__.constructor.call(this, new Value(new IdentifierLiteral('RegExp')), args, false);
|
||||
}
|
||||
|
||||
return RegexWithInterpolations;
|
||||
|
||||
})(Call);
|
||||
|
||||
exports.Extends = Extends = (function(superClass1) {
|
||||
extend1(Extends, superClass1);
|
||||
|
||||
|
@ -1107,15 +1217,18 @@
|
|||
Access.prototype.children = ['name'];
|
||||
|
||||
Access.prototype.compileToFragments = function(o) {
|
||||
var name;
|
||||
var name, node, ref3;
|
||||
name = this.name.compileToFragments(o);
|
||||
if (IDENTIFIER.test(fragmentsToText(name))) {
|
||||
name.unshift(this.makeCode("."));
|
||||
node = this.name.unwrap();
|
||||
if (node instanceof IdentifierLiteral) {
|
||||
if (ref3 = node.value, indexOf.call(JS_FORBIDDEN, ref3) >= 0) {
|
||||
return [this.makeCode('["')].concat(slice.call(name), [this.makeCode('"]')]);
|
||||
} else {
|
||||
return [this.makeCode('.')].concat(slice.call(name));
|
||||
}
|
||||
} else {
|
||||
name.unshift(this.makeCode("["));
|
||||
name.push(this.makeCode("]"));
|
||||
return [this.makeCode('[')].concat(slice.call(name), [this.makeCode(']')]);
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
Access.prototype.isComplex = NO;
|
||||
|
@ -1158,7 +1271,7 @@
|
|||
}
|
||||
|
||||
Range.prototype.compileVariables = function(o) {
|
||||
var isComplex, ref3, ref4, ref5, ref6, step;
|
||||
var isComplex, ref3, ref4, ref5, step;
|
||||
o = merge(o, {
|
||||
top: true
|
||||
});
|
||||
|
@ -1168,10 +1281,9 @@
|
|||
if (step = del(o, 'step')) {
|
||||
ref5 = this.cacheToCodeFragments(step.cache(o, LEVEL_LIST, isComplex)), this.step = ref5[0], this.stepVar = ref5[1];
|
||||
}
|
||||
ref6 = [this.fromVar.match(NUMBER), this.toVar.match(NUMBER)], this.fromNum = ref6[0], this.toNum = ref6[1];
|
||||
if (this.stepVar) {
|
||||
return this.stepNum = this.stepVar.match(NUMBER);
|
||||
}
|
||||
this.fromNum = this.from.isNumber() ? Number(this.fromVar) : null;
|
||||
this.toNum = this.to.isNumber() ? Number(this.toVar) : null;
|
||||
return this.stepNum = (step != null ? step.isNumber() : void 0) ? Number(this.stepVar) : null;
|
||||
};
|
||||
|
||||
Range.prototype.compileNode = function(o) {
|
||||
|
@ -1182,7 +1294,7 @@
|
|||
if (!o.index) {
|
||||
return this.compileArray(o);
|
||||
}
|
||||
known = this.fromNum && this.toNum;
|
||||
known = (this.fromNum != null) && (this.toNum != null);
|
||||
idx = del(o, 'index');
|
||||
idxName = del(o, 'name');
|
||||
namedIndex = idxName && idxName !== idx;
|
||||
|
@ -1194,7 +1306,7 @@
|
|||
varPart += ", " + this.step;
|
||||
}
|
||||
ref3 = [idx + " <" + this.equals, idx + " >" + this.equals], lt = ref3[0], gt = ref3[1];
|
||||
condPart = this.stepNum ? parseNum(this.stepNum[0]) > 0 ? lt + " " + this.toVar : gt + " " + this.toVar : known ? ((ref4 = [parseNum(this.fromNum[0]), parseNum(this.toNum[0])], from = ref4[0], to = ref4[1], ref4), from <= to ? lt + " " + to : gt + " " + to) : (cond = this.stepVar ? this.stepVar + " > 0" : this.fromVar + " <= " + this.toVar, cond + " ? " + lt + " " + this.toVar + " : " + gt + " " + this.toVar);
|
||||
condPart = this.stepNum != null ? this.stepNum > 0 ? lt + " " + this.toVar : gt + " " + this.toVar : known ? ((ref4 = [this.fromNum, this.toNum], from = ref4[0], to = ref4[1], ref4), from <= to ? lt + " " + to : gt + " " + to) : (cond = this.stepVar ? this.stepVar + " > 0" : this.fromVar + " <= " + this.toVar, cond + " ? " + lt + " " + this.toVar + " : " + gt + " " + this.toVar);
|
||||
stepPart = this.stepVar ? idx + " += " + this.stepVar : known ? namedIndex ? from <= to ? "++" + idx : "--" + idx : from <= to ? idx + "++" : idx + "--" : namedIndex ? cond + " ? ++" + idx + " : --" + idx : cond + " ? " + idx + "++ : " + idx + "--";
|
||||
if (namedIndex) {
|
||||
varPart = idxName + " = " + varPart;
|
||||
|
@ -1206,11 +1318,12 @@
|
|||
};
|
||||
|
||||
Range.prototype.compileArray = function(o) {
|
||||
var args, body, cond, hasArgs, i, idt, j, post, pre, range, ref3, ref4, result, results, vars;
|
||||
if (this.fromNum && this.toNum && Math.abs(this.fromNum - this.toNum) <= 20) {
|
||||
var args, body, cond, hasArgs, i, idt, j, known, post, pre, range, ref3, ref4, result, results, vars;
|
||||
known = (this.fromNum != null) && (this.toNum != null);
|
||||
if (known && Math.abs(this.fromNum - this.toNum) <= 20) {
|
||||
range = (function() {
|
||||
results = [];
|
||||
for (var j = ref3 = +this.fromNum, ref4 = +this.toNum; ref3 <= ref4 ? j <= ref4 : j >= ref4; ref3 <= ref4 ? j++ : j--){ results.push(j); }
|
||||
for (var j = ref3 = this.fromNum, ref4 = this.toNum; ref3 <= ref4 ? j <= ref4 : j >= ref4; ref3 <= ref4 ? j++ : j--){ results.push(j); }
|
||||
return results;
|
||||
}).apply(this);
|
||||
if (this.exclusive) {
|
||||
|
@ -1224,7 +1337,7 @@
|
|||
});
|
||||
result = o.scope.freeVariable('results');
|
||||
pre = "\n" + idt + result + " = [];";
|
||||
if (this.fromNum && this.toNum) {
|
||||
if (known) {
|
||||
o.index = i;
|
||||
body = fragmentsToText(this.compileNode(o));
|
||||
} else {
|
||||
|
@ -1264,7 +1377,7 @@
|
|||
compiled = to.compileToFragments(o, LEVEL_PAREN);
|
||||
compiledText = fragmentsToText(compiled);
|
||||
if (!(!this.range.exclusive && +compiledText === -1)) {
|
||||
toStr = ', ' + (this.range.exclusive ? compiledText : SIMPLENUM.test(compiledText) ? "" + (+compiledText + 1) : (compiled = to.compileToFragments(o, LEVEL_ACCESS), "+" + (fragmentsToText(compiled)) + " + 1 || 9e9"));
|
||||
toStr = ', ' + (this.range.exclusive ? compiledText : to.isNumber() ? "" + (+compiledText + 1) : (compiled = to.compileToFragments(o, LEVEL_ACCESS), "+" + (fragmentsToText(compiled)) + " + 1 || 9e9"));
|
||||
}
|
||||
}
|
||||
return [this.makeCode(".slice(" + (fragmentsToText(fromCompiled)) + (toStr || '') + ")")];
|
||||
|
@ -1347,7 +1460,7 @@
|
|||
} else {
|
||||
ref3 = prop.base.cache(o), key = ref3[0], value = ref3[1];
|
||||
}
|
||||
prop = new Assign(new Value(new Literal(oref), [new Access(key)]), value);
|
||||
prop = new Assign(new Value(new IdentifierLiteral(oref), [new Access(key)]), value);
|
||||
}
|
||||
}
|
||||
if (indent) {
|
||||
|
@ -1464,17 +1577,24 @@
|
|||
|
||||
Class.prototype.children = ['variable', 'parent', 'body'];
|
||||
|
||||
Class.prototype.defaultClassVariableName = '_Class';
|
||||
|
||||
Class.prototype.determineName = function() {
|
||||
var decl, ref3, tail;
|
||||
var name, node, ref3, tail;
|
||||
if (!this.variable) {
|
||||
return null;
|
||||
return this.defaultClassVariableName;
|
||||
}
|
||||
ref3 = this.variable.properties, tail = ref3[ref3.length - 1];
|
||||
decl = tail ? tail instanceof Access && tail.name.value : this.variable.base.value;
|
||||
if (indexOf.call(STRICT_PROSCRIBED, decl) >= 0) {
|
||||
this.variable.error("class variable name may not be " + decl);
|
||||
node = tail ? tail instanceof Access && tail.name : this.variable.base;
|
||||
if (!(node instanceof IdentifierLiteral)) {
|
||||
return this.defaultClassVariableName;
|
||||
}
|
||||
name = node.value;
|
||||
if (indexOf.call(JS_FORBIDDEN, name) >= 0) {
|
||||
return "_" + name;
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
return decl && (decl = IDENTIFIER.test(decl) && decl);
|
||||
};
|
||||
|
||||
Class.prototype.setContext = function(name) {
|
||||
|
@ -1482,7 +1602,7 @@
|
|||
if (node.classBody) {
|
||||
return false;
|
||||
}
|
||||
if (node instanceof Literal && node.value === 'this') {
|
||||
if (node instanceof ThisLiteral) {
|
||||
return node.value = name;
|
||||
} else if (node instanceof Code) {
|
||||
if (node.bound) {
|
||||
|
@ -1497,7 +1617,7 @@
|
|||
ref3 = this.boundFuncs;
|
||||
for (j = 0, len1 = ref3.length; j < len1; j++) {
|
||||
bvar = ref3[j];
|
||||
lhs = (new Value(new Literal("this"), [new Access(bvar)])).compile(o);
|
||||
lhs = (new Value(new ThisLiteral, [new Access(bvar)])).compile(o);
|
||||
this.ctor.body.unshift(new Literal(lhs + " = " + (utility('bind', o)) + "(" + lhs + ", this)"));
|
||||
}
|
||||
};
|
||||
|
@ -1524,14 +1644,14 @@
|
|||
assign = this.ctor = func;
|
||||
} else {
|
||||
this.externalCtor = o.classScope.freeVariable('class');
|
||||
assign = new Assign(new Literal(this.externalCtor), func);
|
||||
assign = new Assign(new IdentifierLiteral(this.externalCtor), func);
|
||||
}
|
||||
} else {
|
||||
if (assign.variable["this"]) {
|
||||
func["static"] = true;
|
||||
} else {
|
||||
acc = base.isComplex() ? new Index(base) : new Access(base);
|
||||
assign.variable = new Value(new Literal(name), [new Access(new Literal('prototype')), acc]);
|
||||
assign.variable = new Value(new IdentifierLiteral(name), [new Access(new IdentifierLiteral('prototype')), acc]);
|
||||
if (func instanceof Code && func.bound) {
|
||||
this.boundFuncs.push(base);
|
||||
func.bound = false;
|
||||
|
@ -1606,11 +1726,8 @@
|
|||
if (argumentsNode = this.body.contains(isLiteralArguments)) {
|
||||
argumentsNode.error("Class bodies shouldn't reference arguments");
|
||||
}
|
||||
name = this.determineName() || '_Class';
|
||||
if (name.reserved) {
|
||||
name = "_" + name;
|
||||
}
|
||||
lname = new Literal(name);
|
||||
name = this.determineName();
|
||||
lname = new IdentifierLiteral(name);
|
||||
func = new Code([], Block.wrap([this.body]));
|
||||
args = [];
|
||||
o.classScope = func.makeScope(o.scope);
|
||||
|
@ -1622,7 +1739,7 @@
|
|||
this.body.spaced = true;
|
||||
this.body.expressions.push(lname);
|
||||
if (this.parent) {
|
||||
superClass = new Literal(o.classScope.freeVariable('superClass', {
|
||||
superClass = new IdentifierLiteral(o.classScope.freeVariable('superClass', {
|
||||
reserve: false
|
||||
}));
|
||||
this.body.expressions.unshift(new Extends(lname, superClass));
|
||||
|
@ -1674,7 +1791,7 @@
|
|||
};
|
||||
|
||||
Assign.prototype.compileNode = function(o) {
|
||||
var answer, compiledName, isValue, j, name, properties, prototype, ref3, ref4, ref5, ref6, ref7, val, varBase;
|
||||
var answer, compiledName, isValue, j, name, properties, prototype, ref3, ref4, ref5, ref6, ref7, ref8, val, varBase;
|
||||
if (isValue = this.variable instanceof Value) {
|
||||
if (this.variable.isArray() || this.variable.isObject()) {
|
||||
return this.compilePatternMatch(o);
|
||||
|
@ -1722,6 +1839,10 @@
|
|||
}
|
||||
compiledName = this.variable.compileToFragments(o, LEVEL_LIST);
|
||||
if (this.context === 'object') {
|
||||
if (ref8 = fragmentsToText(compiledName), indexOf.call(JS_FORBIDDEN, ref8) >= 0) {
|
||||
compiledName.unshift(this.makeCode('"'));
|
||||
compiledName.push(this.makeCode('"'));
|
||||
}
|
||||
return compiledName.concat(this.makeCode(": "), val);
|
||||
}
|
||||
answer = compiledName.concat(this.makeCode(" " + (this.context || '=') + " "), val);
|
||||
|
@ -1763,9 +1884,9 @@
|
|||
defaultValue = obj.value;
|
||||
obj = obj.variable;
|
||||
}
|
||||
idx = isObject ? obj["this"] ? obj.properties[0].name : obj : new Literal(0);
|
||||
idx = isObject ? obj["this"] ? obj.properties[0].name : obj : new NumberLiteral(0);
|
||||
}
|
||||
acc = IDENTIFIER.test(idx.unwrap().value);
|
||||
acc = idx.unwrap() instanceof IdentifierLiteral;
|
||||
value = new Value(value);
|
||||
value.properties.push(new (acc ? Access : Index)(idx));
|
||||
if (ref5 = obj.unwrap().value, indexOf.call(RESERVED, ref5) >= 0) {
|
||||
|
@ -1782,7 +1903,7 @@
|
|||
vvarText = fragmentsToText(vvar);
|
||||
assigns = [];
|
||||
expandedIdx = false;
|
||||
if (!IDENTIFIER.test(vvarText) || this.variable.assigns(vvarText)) {
|
||||
if (!(value.unwrap() instanceof IdentifierLiteral) || this.variable.assigns(vvarText)) {
|
||||
assigns.push([this.makeCode((ref = o.scope.freeVariable('ref')) + " = ")].concat(slice.call(vvar)));
|
||||
vvar = [this.makeCode(ref)];
|
||||
vvarText = ref;
|
||||
|
@ -1837,7 +1958,7 @@
|
|||
idx = isObject ? obj["this"] ? obj.properties[0].name : obj : new Literal(expandedIdx || idx);
|
||||
}
|
||||
name = obj.unwrap().value;
|
||||
acc = IDENTIFIER.test(idx.unwrap().value);
|
||||
acc = idx.unwrap() instanceof IdentifierLiteral;
|
||||
val = new Value(new Literal(vvarText), [new (acc ? Access : Index)(idx)]);
|
||||
if (defaultValue) {
|
||||
val = new Op('?', val, defaultValue);
|
||||
|
@ -1865,7 +1986,7 @@
|
|||
Assign.prototype.compileConditional = function(o) {
|
||||
var fragments, left, ref3, right;
|
||||
ref3 = this.variable.cacheReference(o), left = ref3[0], right = ref3[1];
|
||||
if (!left.properties.length && left.base instanceof Literal && left.base.value !== "this" && !o.scope.check(left.base.value)) {
|
||||
if (!left.properties.length && left.base instanceof Literal && !(left.base instanceof ThisLiteral) && !o.scope.check(left.base.value)) {
|
||||
this.variable.error("the variable \"" + left.base.value + "\" can't be assigned with " + this.context + " because it has not been declared before");
|
||||
}
|
||||
if (indexOf.call(this.context, "?") >= 0) {
|
||||
|
@ -1899,7 +2020,7 @@
|
|||
fromDecl = fromRef = '0';
|
||||
}
|
||||
if (to) {
|
||||
if (from instanceof Value && from.isSimpleNumber() && to instanceof Value && to.isSimpleNumber()) {
|
||||
if ((from != null ? from.isNumber() : void 0) && to.isNumber()) {
|
||||
to = to.compile(o) - fromRef;
|
||||
if (!exclusive) {
|
||||
to += 1;
|
||||
|
@ -1957,8 +2078,8 @@
|
|||
}
|
||||
if (this.bound && !this.context) {
|
||||
this.context = '_this';
|
||||
wrapper = new Code([new Param(new Literal(this.context))], new Block([this]));
|
||||
boundfunc = new Call(wrapper, [new Literal('this')]);
|
||||
wrapper = new Code([new Param(new IdentifierLiteral(this.context))], new Block([this]));
|
||||
boundfunc = new Call(wrapper, [new ThisLiteral]);
|
||||
boundfunc.updateLocationDataIfMissing(this.locationData);
|
||||
return boundfunc.compileNode(o);
|
||||
}
|
||||
|
@ -1998,7 +2119,7 @@
|
|||
results.push(p.asReference(o));
|
||||
}
|
||||
return results;
|
||||
}).call(this))), new Value(new Literal('arguments')));
|
||||
}).call(this))), new Value(new IdentifierLiteral('arguments')));
|
||||
break;
|
||||
}
|
||||
ref7 = this.params;
|
||||
|
@ -2129,12 +2250,12 @@
|
|||
node = this.name;
|
||||
if (node["this"]) {
|
||||
name = node.properties[0].name.value;
|
||||
if (name.reserved) {
|
||||
if (indexOf.call(JS_FORBIDDEN, name) >= 0) {
|
||||
name = "_" + name;
|
||||
}
|
||||
node = new Literal(o.scope.freeVariable(name));
|
||||
node = new IdentifierLiteral(o.scope.freeVariable(name));
|
||||
} else if (node.isComplex()) {
|
||||
node = new Literal(o.scope.freeVariable('arg'));
|
||||
node = new IdentifierLiteral(o.scope.freeVariable('arg'));
|
||||
}
|
||||
node = new Value(node);
|
||||
if (this.splat) {
|
||||
|
@ -2347,7 +2468,7 @@
|
|||
}
|
||||
if (this.guard) {
|
||||
if (body.expressions.length > 1) {
|
||||
body.expressions.unshift(new If((new Parens(this.guard)).invert(), new Literal("continue")));
|
||||
body.expressions.unshift(new If((new Parens(this.guard)).invert(), new StatementLiteral("continue")));
|
||||
} else {
|
||||
if (this.guard) {
|
||||
body = Block.wrap([new If(this.guard, body)]);
|
||||
|
@ -2408,7 +2529,10 @@
|
|||
|
||||
Op.prototype.children = ['first', 'second'];
|
||||
|
||||
Op.prototype.isSimpleNumber = NO;
|
||||
Op.prototype.isNumber = function() {
|
||||
var ref3;
|
||||
return this.isUnary() && ((ref3 = this.operator) === '+' || ref3 === '-') && this.first instanceof Value && this.first.isNumber();
|
||||
};
|
||||
|
||||
Op.prototype.isYield = function() {
|
||||
var ref3;
|
||||
|
@ -2420,8 +2544,7 @@
|
|||
};
|
||||
|
||||
Op.prototype.isComplex = function() {
|
||||
var ref3;
|
||||
return !(this.isUnary() && ((ref3 = this.operator) === '+' || ref3 === '-') && this.first instanceof Value && this.first.isSimpleNumber());
|
||||
return !this.isNumber();
|
||||
};
|
||||
|
||||
Op.prototype.isChainable = function() {
|
||||
|
@ -2540,7 +2663,7 @@
|
|||
Op.prototype.compileExistence = function(o) {
|
||||
var fst, ref;
|
||||
if (this.first.isComplex()) {
|
||||
ref = new Literal(o.scope.freeVariable('ref'));
|
||||
ref = new IdentifierLiteral(o.scope.freeVariable('ref'));
|
||||
fst = new Parens(new Assign(ref, this.first));
|
||||
} else {
|
||||
fst = this.first;
|
||||
|
@ -2606,13 +2729,13 @@
|
|||
|
||||
Op.prototype.compilePower = function(o) {
|
||||
var pow;
|
||||
pow = new Value(new Literal('Math'), [new Access(new Literal('pow'))]);
|
||||
pow = new Value(new IdentifierLiteral('Math'), [new Access(new IdentifierLiteral('pow'))]);
|
||||
return new Call(pow, [this.first, this.second]).compileToFragments(o);
|
||||
};
|
||||
|
||||
Op.prototype.compileFloorDivision = function(o) {
|
||||
var div, floor;
|
||||
floor = new Value(new Literal('Math'), [new Access(new Literal('floor'))]);
|
||||
floor = new Value(new IdentifierLiteral('Math'), [new Access(new IdentifierLiteral('floor'))]);
|
||||
div = new Op('/', this.first, this.second);
|
||||
return new Call(floor, [div]).compileToFragments(o);
|
||||
};
|
||||
|
@ -2740,7 +2863,7 @@
|
|||
tryPart = this.attempt.compileToFragments(o, LEVEL_TOP);
|
||||
catchPart = this.recovery ? (generatedErrorVariableName = o.scope.freeVariable('error', {
|
||||
reserve: false
|
||||
}), placeholder = new Literal(generatedErrorVariableName), this.errorVariable ? this.recovery.unshift(new Assign(this.errorVariable, placeholder)) : void 0, [].concat(this.makeCode(" catch ("), placeholder.compileToFragments(o), this.makeCode(") {\n"), this.recovery.compileToFragments(o, LEVEL_TOP), this.makeCode("\n" + this.tab + "}"))) : !(this.ensure || this.recovery) ? (generatedErrorVariableName = o.scope.freeVariable('error', {
|
||||
}), placeholder = new IdentifierLiteral(generatedErrorVariableName), this.errorVariable ? this.recovery.unshift(new Assign(this.errorVariable, placeholder)) : void 0, [].concat(this.makeCode(" catch ("), placeholder.compileToFragments(o), this.makeCode(") {\n"), this.recovery.compileToFragments(o, LEVEL_TOP), this.makeCode("\n" + this.tab + "}"))) : !(this.ensure || this.recovery) ? (generatedErrorVariableName = o.scope.freeVariable('error', {
|
||||
reserve: false
|
||||
}), [this.makeCode(" catch (" + generatedErrorVariableName + ") {}")]) : [];
|
||||
ensurePart = this.ensure ? [].concat(this.makeCode(" finally {\n"), this.ensure.compileToFragments(o, LEVEL_TOP), this.makeCode("\n" + this.tab + "}")) : [];
|
||||
|
@ -2789,7 +2912,7 @@
|
|||
var cmp, cnj, code, ref3;
|
||||
this.expression.front = this.front;
|
||||
code = this.expression.compile(o, LEVEL_OP);
|
||||
if (IDENTIFIER.test(code) && !o.scope.check(code)) {
|
||||
if (this.expression.unwrap() instanceof IdentifierLiteral && !o.scope.check(code)) {
|
||||
ref3 = this.negated ? ['===', '||'] : ['!==', '&&'], cmp = ref3[0], cnj = ref3[1];
|
||||
code = "typeof " + code + " " + cmp + " \"undefined\" " + cnj + " " + code + " " + cmp + " null";
|
||||
} else {
|
||||
|
@ -2839,6 +2962,17 @@
|
|||
|
||||
})(Base);
|
||||
|
||||
exports.StringWithInterpolations = StringWithInterpolations = (function(superClass1) {
|
||||
extend1(StringWithInterpolations, superClass1);
|
||||
|
||||
function StringWithInterpolations() {
|
||||
return StringWithInterpolations.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
return StringWithInterpolations;
|
||||
|
||||
})(Parens);
|
||||
|
||||
exports.For = For = (function(superClass1) {
|
||||
extend1(For, superClass1);
|
||||
|
||||
|
@ -2899,7 +3033,9 @@
|
|||
kvarAssign = kvar !== ivar ? kvar + " = " : "";
|
||||
if (this.step && !this.range) {
|
||||
ref4 = this.cacheToCodeFragments(this.step.cache(o, LEVEL_LIST, isComplexOrAssignable)), step = ref4[0], stepVar = ref4[1];
|
||||
stepNum = stepVar.match(NUMBER);
|
||||
if (this.step.isNumber()) {
|
||||
stepNum = Number(stepVar);
|
||||
}
|
||||
}
|
||||
if (this.pattern) {
|
||||
name = ivar;
|
||||
|
@ -2917,7 +3053,7 @@
|
|||
}));
|
||||
} else {
|
||||
svar = this.source.compile(o, LEVEL_LIST);
|
||||
if ((name || this.own) && !IDENTIFIER.test(svar)) {
|
||||
if ((name || this.own) && !(this.source.unwrap() instanceof IdentifierLiteral)) {
|
||||
defPart += "" + this.tab + (ref = scope.freeVariable('ref')) + " = " + svar + ";\n";
|
||||
svar = ref;
|
||||
}
|
||||
|
@ -2928,7 +3064,8 @@
|
|||
if (step !== stepVar) {
|
||||
defPart += "" + this.tab + step + ";\n";
|
||||
}
|
||||
if (!(this.step && stepNum && (down = parseNum(stepNum[0]) < 0))) {
|
||||
down = stepNum < 0;
|
||||
if (!(this.step && (stepNum != null) && down)) {
|
||||
lvar = scope.freeVariable('len');
|
||||
}
|
||||
declare = "" + kvarAssign + ivar + " = 0, " + lvar + " = " + svar + ".length";
|
||||
|
@ -2936,7 +3073,7 @@
|
|||
compare = ivar + " < " + lvar;
|
||||
compareDown = ivar + " >= 0";
|
||||
if (this.step) {
|
||||
if (stepNum) {
|
||||
if (stepNum != null) {
|
||||
if (down) {
|
||||
compare = compareDown;
|
||||
declare = declareDown;
|
||||
|
@ -2959,7 +3096,7 @@
|
|||
}
|
||||
if (this.guard) {
|
||||
if (body.expressions.length > 1) {
|
||||
body.expressions.unshift(new If((new Parens(this.guard)).invert(), new Literal("continue")));
|
||||
body.expressions.unshift(new If((new Parens(this.guard)).invert(), new StatementLiteral("continue")));
|
||||
} else {
|
||||
if (this.guard) {
|
||||
body = Block.wrap([new If(this.guard, body)]);
|
||||
|
@ -3003,7 +3140,7 @@
|
|||
continue;
|
||||
}
|
||||
fn = ((ref8 = val.base) != null ? ref8.unwrapAll() : void 0) || val;
|
||||
ref = new Literal(o.scope.freeVariable('fn'));
|
||||
ref = new IdentifierLiteral(o.scope.freeVariable('fn'));
|
||||
base = new Value(ref);
|
||||
if (val.base) {
|
||||
ref9 = [base, val], val.base = ref9[0], base = ref9[1];
|
||||
|
@ -3264,18 +3401,8 @@
|
|||
|
||||
TAB = ' ';
|
||||
|
||||
IDENTIFIER = /^(?!\d)[$\w\x7f-\uffff]+$/;
|
||||
|
||||
SIMPLENUM = /^[+-]?\d+$/;
|
||||
|
||||
HEXNUM = /^[+-]?0x[\da-f]+/i;
|
||||
|
||||
NUMBER = /^[+-]?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)$/i;
|
||||
|
||||
IS_STRING = /^['"]/;
|
||||
|
||||
IS_REGEX = /^\//;
|
||||
|
||||
utility = function(name, o) {
|
||||
var ref, root;
|
||||
root = o.scope.root;
|
||||
|
@ -3293,22 +3420,12 @@
|
|||
return code.replace(/\s+$/, '');
|
||||
};
|
||||
|
||||
parseNum = function(x) {
|
||||
if (x == null) {
|
||||
return 0;
|
||||
} else if (x.match(HEXNUM)) {
|
||||
return parseInt(x, 16);
|
||||
} else {
|
||||
return parseFloat(x);
|
||||
}
|
||||
};
|
||||
|
||||
isLiteralArguments = function(node) {
|
||||
return node instanceof Literal && node.value === 'arguments' && !node.asKey;
|
||||
};
|
||||
|
||||
isLiteralThis = function(node) {
|
||||
return (node instanceof Literal && node.value === 'this' && !node.asKey) || (node instanceof Code && node.bound) || (node instanceof Call && node.isSuper);
|
||||
return (node instanceof ThisLiteral && !node.asKey) || (node instanceof Code && node.bound) || node instanceof SuperCall;
|
||||
};
|
||||
|
||||
isComplexOrAssignable = function(node) {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -487,7 +487,7 @@
|
|||
|
||||
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
|
||||
|
||||
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'STRING_START', 'JS', 'REGEX', 'REGEX_START', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'YIELD', 'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
|
||||
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'JS', 'REGEX', 'REGEX_START', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'YIELD', 'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
|
||||
|
||||
IMPLICIT_UNSPACED_CALL = ['+', '-'];
|
||||
|
||||
|
|
|
@ -251,7 +251,7 @@ parser.yy.parseError = (message, {token}) ->
|
|||
'end of input'
|
||||
when errorTag in ['INDENT', 'OUTDENT']
|
||||
'indentation'
|
||||
when errorTag in ['IDENTIFIER', 'NUMBER', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START']
|
||||
when errorTag in ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START']
|
||||
errorTag.replace(/_START$/, '').toLowerCase()
|
||||
else
|
||||
helpers.nameWhitespaceCharacter errorText
|
||||
|
|
|
@ -96,7 +96,7 @@ grammar =
|
|||
Statement: [
|
||||
o 'Return'
|
||||
o 'Comment'
|
||||
o 'STATEMENT', -> new Literal $1
|
||||
o 'STATEMENT', -> new StatementLiteral $1
|
||||
]
|
||||
|
||||
# All the different types of expressions in our language. The basic unit of
|
||||
|
@ -135,36 +135,36 @@ grammar =
|
|||
|
||||
# A literal identifier, a variable name or property.
|
||||
Identifier: [
|
||||
o 'IDENTIFIER', -> new Literal $1
|
||||
o 'IDENTIFIER', -> new IdentifierLiteral $1
|
||||
]
|
||||
|
||||
# Alphanumerics are separated from the other **Literal** matchers because
|
||||
# they can also serve as keys in object literals.
|
||||
AlphaNumeric: [
|
||||
o 'NUMBER', -> new Literal $1
|
||||
o 'NUMBER', -> new NumberLiteral $1
|
||||
o 'INFINITY', -> new InfinityLiteral $1
|
||||
o 'String'
|
||||
]
|
||||
|
||||
String: [
|
||||
o 'STRING', -> new Literal $1
|
||||
o 'STRING_START Body STRING_END', -> new Parens $2
|
||||
o 'STRING', -> new StringLiteral $1
|
||||
o 'STRING_START Body STRING_END', -> new StringWithInterpolations $2
|
||||
]
|
||||
|
||||
Regex: [
|
||||
o 'REGEX', -> new Literal $1
|
||||
o 'REGEX_START Invocation REGEX_END', -> $2
|
||||
o 'REGEX', -> new RegexLiteral $1
|
||||
o 'REGEX_START Invocation REGEX_END', -> new RegexWithInterpolations $2.args
|
||||
]
|
||||
|
||||
# All of our immediate values. Generally these can be passed straight
|
||||
# through and printed to JavaScript.
|
||||
Literal: [
|
||||
o 'AlphaNumeric'
|
||||
o 'JS', -> new Literal $1
|
||||
o 'JS', -> new PassthroughLiteral $1
|
||||
o 'Regex'
|
||||
o 'DEBUGGER', -> new Literal $1
|
||||
o 'UNDEFINED', -> new Undefined
|
||||
o 'NULL', -> new Null
|
||||
o 'BOOL', -> new Bool $1
|
||||
o 'UNDEFINED', -> new UndefinedLiteral
|
||||
o 'NULL', -> new NullLiteral
|
||||
o 'BOOL', -> new BooleanLiteral $1
|
||||
]
|
||||
|
||||
# Assignment of a variable, property, or index to a value.
|
||||
|
@ -299,9 +299,9 @@ grammar =
|
|||
Accessor: [
|
||||
o '. Identifier', -> new Access $2
|
||||
o '?. Identifier', -> new Access $2, 'soak'
|
||||
o ':: Identifier', -> [LOC(1)(new Access new Literal('prototype')), LOC(2)(new Access $2)]
|
||||
o '?:: Identifier', -> [LOC(1)(new Access new Literal('prototype'), 'soak'), LOC(2)(new Access $2)]
|
||||
o '::', -> new Access new Literal 'prototype'
|
||||
o ':: Identifier', -> [LOC(1)(new Access new IdentifierLiteral('prototype')), LOC(2)(new Access $2)]
|
||||
o '?:: Identifier', -> [LOC(1)(new Access new IdentifierLiteral('prototype'), 'soak'), LOC(2)(new Access $2)]
|
||||
o '::', -> new Access new IdentifierLiteral 'prototype'
|
||||
o 'Index'
|
||||
]
|
||||
|
||||
|
@ -348,8 +348,12 @@ grammar =
|
|||
Invocation: [
|
||||
o 'Value OptFuncExist Arguments', -> new Call $1, $3, $2
|
||||
o 'Invocation OptFuncExist Arguments', -> new Call $1, $3, $2
|
||||
o 'SUPER', -> new Call 'super', [new Splat new Literal 'arguments']
|
||||
o 'SUPER Arguments', -> new Call 'super', $2
|
||||
o 'Super'
|
||||
]
|
||||
|
||||
Super: [
|
||||
o 'SUPER', -> new SuperCall
|
||||
o 'SUPER Arguments', -> new SuperCall $2
|
||||
]
|
||||
|
||||
# An optional existence check on a function.
|
||||
|
@ -366,13 +370,13 @@ grammar =
|
|||
|
||||
# A reference to the *this* current object.
|
||||
This: [
|
||||
o 'THIS', -> new Value new Literal 'this'
|
||||
o '@', -> new Value new Literal 'this'
|
||||
o 'THIS', -> new Value new ThisLiteral
|
||||
o '@', -> new Value new ThisLiteral
|
||||
]
|
||||
|
||||
# A reference to a property on *this*.
|
||||
ThisProperty: [
|
||||
o '@ Identifier', -> new Value LOC(1)(new Literal('this')), [LOC(2)(new Access($2))], 'this'
|
||||
o '@ Identifier', -> new Value LOC(1)(new ThisLiteral), [LOC(2)(new Access($2))], 'this'
|
||||
]
|
||||
|
||||
# The array literal.
|
||||
|
@ -473,8 +477,8 @@ grammar =
|
|||
]
|
||||
|
||||
Loop: [
|
||||
o 'LOOP Block', -> new While(LOC(1) new Literal 'true').addBody $2
|
||||
o 'LOOP Expression', -> new While(LOC(1) new Literal 'true').addBody LOC(2) Block.wrap [$2]
|
||||
o 'LOOP Block', -> new While(LOC(1) new BooleanLiteral 'true').addBody $2
|
||||
o 'LOOP Expression', -> new While(LOC(1) new BooleanLiteral 'true').addBody LOC(2) Block.wrap [$2]
|
||||
]
|
||||
|
||||
# Array, object, and range comprehensions, at the most generic level.
|
||||
|
|
|
@ -142,8 +142,6 @@ exports.Lexer = class Lexer
|
|||
if id in JS_FORBIDDEN
|
||||
if forcedIdentifier
|
||||
tag = 'IDENTIFIER'
|
||||
id = new String id
|
||||
id.reserved = yes
|
||||
else if id in RESERVED
|
||||
@error "reserved word '#{id}'", length: id.length
|
||||
|
||||
|
@ -156,7 +154,8 @@ exports.Lexer = class Lexer
|
|||
when '==', '!=' then 'COMPARE'
|
||||
when '&&', '||' then 'LOGIC'
|
||||
when 'true', 'false' then 'BOOL'
|
||||
when 'break', 'continue' then 'STATEMENT'
|
||||
when 'break', 'continue', \
|
||||
'debugger' then 'STATEMENT'
|
||||
else tag
|
||||
|
||||
tagToken = @token tag, id, 0, idLength
|
||||
|
@ -187,10 +186,15 @@ exports.Lexer = class Lexer
|
|||
else if /^0\d+/.test number
|
||||
@error "octal literal '#{number}' must be prefixed with '0o'", length: lexedLength
|
||||
if octalLiteral = /^0o([0-7]+)/.exec number
|
||||
number = '0x' + parseInt(octalLiteral[1], 8).toString 16
|
||||
if binaryLiteral = /^0b([01]+)/.exec number
|
||||
number = '0x' + parseInt(binaryLiteral[1], 2).toString 16
|
||||
@token 'NUMBER', number, 0, lexedLength
|
||||
numberValue = parseInt(octalLiteral[1], 8)
|
||||
number = "0x#{numberValue.toString 16}"
|
||||
else if binaryLiteral = /^0b([01]+)/.exec number
|
||||
numberValue = parseInt(binaryLiteral[1], 2)
|
||||
number = "0x#{numberValue.toString 16}"
|
||||
else
|
||||
numberValue = parseFloat(number)
|
||||
tag = if numberValue is Infinity then 'INFINITY' else 'NUMBER'
|
||||
@token tag, number, 0, lexedLength
|
||||
lexedLength
|
||||
|
||||
# Matches strings, including multi-line strings, as well as heredocs, with or without
|
||||
|
@ -408,7 +412,7 @@ exports.Lexer = class Lexer
|
|||
tag = value
|
||||
[..., prev] = @tokens
|
||||
if value is '=' and prev
|
||||
if not prev[1].reserved and prev[1] in JS_FORBIDDEN
|
||||
if prev.variable and prev[1] in JS_FORBIDDEN
|
||||
prev = prev.origin if prev.origin
|
||||
@error "reserved word '#{prev[1]}' can't be assigned", prev[2]
|
||||
if prev[1] in ['||', '&&']
|
||||
|
@ -786,6 +790,7 @@ JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED)
|
|||
|
||||
exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED)
|
||||
exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED
|
||||
exports.JS_FORBIDDEN = JS_FORBIDDEN
|
||||
|
||||
# The character code of the nasty Microsoft madness otherwise known as the BOM.
|
||||
BOM = 65279
|
||||
|
@ -918,7 +923,7 @@ BOOL = ['TRUE', 'FALSE']
|
|||
# of a function invocation or indexing operation.
|
||||
CALLABLE = ['IDENTIFIER', ')', ']', '?', '@', 'THIS', 'SUPER']
|
||||
INDEXABLE = CALLABLE.concat [
|
||||
'NUMBER', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END'
|
||||
'NUMBER', 'INFINITY', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END'
|
||||
'BOOL', 'NULL', 'UNDEFINED', '}', '::'
|
||||
]
|
||||
|
||||
|
|
376
src/nodes.coffee
376
src/nodes.coffee
|
@ -6,7 +6,7 @@
|
|||
Error.stackTraceLimit = Infinity
|
||||
|
||||
{Scope} = require './scope'
|
||||
{RESERVED, STRICT_PROSCRIBED} = require './lexer'
|
||||
{RESERVED, STRICT_PROSCRIBED, JS_FORBIDDEN} = require './lexer'
|
||||
|
||||
# Import the helpers we plan to use.
|
||||
{compact, flatten, extend, merge, del, starts, ends, some,
|
||||
|
@ -82,13 +82,13 @@ exports.Base = class Base
|
|||
func = new Code [], Block.wrap [this]
|
||||
args = []
|
||||
if (argumentsNode = @contains isLiteralArguments) or @contains isLiteralThis
|
||||
args = [new Literal 'this']
|
||||
args = [new ThisLiteral]
|
||||
if argumentsNode
|
||||
meth = 'apply'
|
||||
args.push new Literal 'arguments'
|
||||
args.push new IdentifierLiteral 'arguments'
|
||||
else
|
||||
meth = 'call'
|
||||
func = new Value func, [new Access new Literal meth]
|
||||
func = new Value func, [new Access new IdentifierLiteral meth]
|
||||
parts = (new Call func, args).compileNode o
|
||||
if func.isGenerator or func.base?.isGenerator
|
||||
parts.unshift @makeCode "(yield* "
|
||||
|
@ -105,7 +105,7 @@ exports.Base = class Base
|
|||
cache: (o, level, isComplex) ->
|
||||
complex = if isComplex? then isComplex this else @isComplex()
|
||||
if complex
|
||||
ref = new Literal o.scope.freeVariable 'ref'
|
||||
ref = new IdentifierLiteral o.scope.freeVariable 'ref'
|
||||
sub = new Assign ref, this
|
||||
if level then [sub.compileToFragments(o, level), [@makeCode(ref.value)]] else [sub, ref]
|
||||
else
|
||||
|
@ -181,6 +181,7 @@ exports.Base = class Base
|
|||
isComplex : YES
|
||||
isChainable : NO
|
||||
isAssignable : NO
|
||||
isNumber : NO
|
||||
|
||||
unwrap : THIS
|
||||
unfoldSoak : NO
|
||||
|
@ -380,59 +381,70 @@ exports.Block = class Block extends Base
|
|||
|
||||
#### Literal
|
||||
|
||||
# Literals are static values that can be passed through directly into
|
||||
# JavaScript without translation, such as: strings, numbers,
|
||||
# `Literal` is a base class for static values that can be passed through
|
||||
# directly into JavaScript without translation, such as: strings, numbers,
|
||||
# `true`, `false`, `null`...
|
||||
exports.Literal = class Literal extends Base
|
||||
constructor: (@value) ->
|
||||
|
||||
makeReturn: ->
|
||||
if @isStatement() then this else super
|
||||
|
||||
isAssignable: ->
|
||||
IDENTIFIER.test @value
|
||||
|
||||
isStatement: ->
|
||||
@value in ['break', 'continue', 'debugger']
|
||||
|
||||
isComplex: NO
|
||||
|
||||
assigns: (name) ->
|
||||
name is @value
|
||||
|
||||
compileNode: (o) ->
|
||||
[@makeCode @value]
|
||||
|
||||
toString: ->
|
||||
" #{if @isStatement() then super else @constructor.name}: #{@value}"
|
||||
|
||||
exports.NumberLiteral = class NumberLiteral extends Literal
|
||||
|
||||
exports.InfinityLiteral = class InfinityLiteral extends NumberLiteral
|
||||
compileNode: ->
|
||||
[@makeCode 'Infinity']
|
||||
|
||||
exports.StringLiteral = class StringLiteral extends Literal
|
||||
|
||||
exports.RegexLiteral = class RegexLiteral extends Literal
|
||||
|
||||
exports.PassthroughLiteral = class PassthroughLiteral extends Literal
|
||||
|
||||
exports.IdentifierLiteral = class IdentifierLiteral extends Literal
|
||||
isAssignable: -> @value not in RESERVED
|
||||
|
||||
exports.StatementLiteral = class StatementLiteral extends Literal
|
||||
isStatement: YES
|
||||
|
||||
makeReturn: THIS
|
||||
|
||||
jumps: (o) ->
|
||||
return this if @value is 'break' and not (o?.loop or o?.block)
|
||||
return this if @value is 'continue' and not o?.loop
|
||||
|
||||
compileNode: (o) ->
|
||||
code = if @value is 'this'
|
||||
if o.scope.method?.bound then o.scope.method.context else @value
|
||||
else if @value.reserved
|
||||
"\"#{@value}\""
|
||||
else
|
||||
@value
|
||||
answer = if @isStatement() then "#{@tab}#{code};" else code
|
||||
[@makeCode answer]
|
||||
[@makeCode "#{@tab}#{@value};"]
|
||||
|
||||
toString: ->
|
||||
' "' + @value + '"'
|
||||
exports.ThisLiteral = class ThisLiteral extends Literal
|
||||
constructor: ->
|
||||
super 'this'
|
||||
|
||||
compileNode: (o) ->
|
||||
code = if o.scope.method?.bound then o.scope.method.context else @value
|
||||
[@makeCode code]
|
||||
|
||||
exports.UndefinedLiteral = class UndefinedLiteral extends Literal
|
||||
constructor: ->
|
||||
super 'undefined'
|
||||
|
||||
class exports.Undefined extends Base
|
||||
isAssignable: NO
|
||||
isComplex: NO
|
||||
compileNode: (o) ->
|
||||
[@makeCode if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0']
|
||||
|
||||
class exports.Null extends Base
|
||||
isAssignable: NO
|
||||
isComplex: NO
|
||||
compileNode: -> [@makeCode "null"]
|
||||
exports.NullLiteral = class NullLiteral extends Literal
|
||||
constructor: ->
|
||||
super 'null'
|
||||
|
||||
class exports.Bool extends Base
|
||||
isAssignable: NO
|
||||
isComplex: NO
|
||||
compileNode: -> [@makeCode @val]
|
||||
constructor: (@val) ->
|
||||
exports.BooleanLiteral = class BooleanLiteral extends Literal
|
||||
|
||||
#### Return
|
||||
|
||||
|
@ -498,16 +510,20 @@ exports.Value = class Value extends Base
|
|||
isRange : -> @bareLiteral(Range)
|
||||
isComplex : -> @hasProperties() or @base.isComplex()
|
||||
isAssignable : -> @hasProperties() or @base.isAssignable()
|
||||
isSimpleNumber : -> @bareLiteral(Literal) and SIMPLENUM.test @base.value
|
||||
isString : -> @bareLiteral(Literal) and IS_STRING.test @base.value
|
||||
isRegex : -> @bareLiteral(Literal) and IS_REGEX.test @base.value
|
||||
isNumber : -> @bareLiteral(NumberLiteral)
|
||||
isString : -> @bareLiteral(StringLiteral)
|
||||
isRegex : -> @bareLiteral(RegexLiteral)
|
||||
isUndefined : -> @bareLiteral(UndefinedLiteral)
|
||||
isNull : -> @bareLiteral(NullLiteral)
|
||||
isBoolean : -> @bareLiteral(BooleanLiteral)
|
||||
isAtomic : ->
|
||||
for node in @properties.concat @base
|
||||
return no if node.soak or node instanceof Call
|
||||
yes
|
||||
|
||||
isNotCallable : -> @isSimpleNumber() or @isString() or @isRegex() or
|
||||
@isArray() or @isRange() or @isSplice() or @isObject()
|
||||
isNotCallable : -> @isNumber() or @isString() or @isRegex() or
|
||||
@isArray() or @isRange() or @isSplice() or @isObject() or
|
||||
@isUndefined() or @isNull() or @isBoolean()
|
||||
|
||||
isStatement : (o) -> not @properties.length and @base.isStatement o
|
||||
assigns : (name) -> not @properties.length and @base.assigns name
|
||||
|
@ -539,11 +555,11 @@ exports.Value = class Value extends Base
|
|||
return [this, this] # `a` `a.b`
|
||||
base = new Value @base, @properties[...-1]
|
||||
if base.isComplex() # `a().b`
|
||||
bref = new Literal o.scope.freeVariable 'base'
|
||||
bref = new IdentifierLiteral o.scope.freeVariable 'base'
|
||||
base = new Value new Parens new Assign bref, base
|
||||
return [base, bref] unless name # `a()`
|
||||
if name.isComplex() # `a[b()]`
|
||||
nref = new Literal o.scope.freeVariable 'name'
|
||||
nref = new IdentifierLiteral o.scope.freeVariable 'name'
|
||||
name = new Index new Assign nref, name.index
|
||||
nref = new Index nref
|
||||
[base.add(name), new Value(bref or base.base, [nref or name])]
|
||||
|
@ -556,7 +572,7 @@ exports.Value = class Value extends Base
|
|||
@base.front = @front
|
||||
props = @properties
|
||||
fragments = @base.compileToFragments o, (if props.length then LEVEL_ACCESS else null)
|
||||
if (@base instanceof Parens or props.length) and SIMPLENUM.test fragmentsToText fragments
|
||||
if props.length and SIMPLENUM.test fragmentsToText fragments
|
||||
fragments.push @makeCode '.'
|
||||
for prop in props
|
||||
fragments.push (prop.compileToFragments o)...
|
||||
|
@ -573,7 +589,7 @@ exports.Value = class Value extends Base
|
|||
fst = new Value @base, @properties[...i]
|
||||
snd = new Value @base, @properties[i..]
|
||||
if fst.isComplex()
|
||||
ref = new Literal o.scope.freeVariable 'ref'
|
||||
ref = new IdentifierLiteral o.scope.freeVariable 'ref'
|
||||
fst = new Parens new Assign ref, fst
|
||||
snd.base = ref
|
||||
return new If new Existence(fst), snd, soak: on
|
||||
|
@ -597,15 +613,12 @@ exports.Comment = class Comment extends Base
|
|||
|
||||
#### Call
|
||||
|
||||
# Node for a function invocation. Takes care of converting `super()` calls into
|
||||
# calls against the prototype's function of the same name.
|
||||
# Node for a function invocation.
|
||||
exports.Call = class Call extends Base
|
||||
constructor: (variable, @args = [], @soak) ->
|
||||
constructor: (@variable, @args = [], @soak) ->
|
||||
@isNew = false
|
||||
@isSuper = variable is 'super'
|
||||
@variable = if @isSuper then null else variable
|
||||
if variable instanceof Value and variable.isNotCallable()
|
||||
variable.error "literal is not a function"
|
||||
if @variable instanceof Value and @variable.isNotCallable()
|
||||
@variable.error "literal is not a function"
|
||||
|
||||
children: ['variable', 'args']
|
||||
|
||||
|
@ -618,45 +631,15 @@ exports.Call = class Call extends Base
|
|||
@isNew = true
|
||||
this
|
||||
|
||||
# Grab the reference to the superclass's implementation of the current
|
||||
# method.
|
||||
superReference: (o) ->
|
||||
method = o.scope.namedMethod()
|
||||
if method?.klass
|
||||
{klass, name, variable} = method
|
||||
if klass.isComplex()
|
||||
bref = new Literal o.scope.parent.freeVariable 'base'
|
||||
base = new Value new Parens new Assign bref, klass
|
||||
variable.base = base
|
||||
variable.properties.splice 0, klass.properties.length
|
||||
if name.isComplex() or (name instanceof Index and name.index.isAssignable())
|
||||
nref = new Literal o.scope.parent.freeVariable 'name'
|
||||
name = new Index new Assign nref, name.index
|
||||
variable.properties.pop()
|
||||
variable.properties.push name
|
||||
accesses = [new Access new Literal '__super__']
|
||||
accesses.push new Access new Literal 'constructor' if method.static
|
||||
accesses.push if nref? then new Index nref else name
|
||||
(new Value bref ? klass, accesses).compile o
|
||||
else if method?.ctor
|
||||
"#{method.name}.__super__.constructor"
|
||||
else
|
||||
@error 'cannot call super outside of an instance method.'
|
||||
|
||||
# The appropriate `this` value for a `super` call.
|
||||
superThis : (o) ->
|
||||
method = o.scope.method
|
||||
(method and not method.klass and method.context) or "this"
|
||||
|
||||
# Soaked chained invocations unfold into if/else ternary structures.
|
||||
unfoldSoak: (o) ->
|
||||
if @soak
|
||||
if @variable
|
||||
return ifn if ifn = unfoldSoak o, this, 'variable'
|
||||
[left, rite] = new Value(@variable).cacheReference o
|
||||
else
|
||||
if this instanceof SuperCall
|
||||
left = new Literal @superReference o
|
||||
rite = new Value left
|
||||
else
|
||||
return ifn if ifn = unfoldSoak o, this, 'variable'
|
||||
[left, rite] = new Value(@variable).cacheReference o
|
||||
rite = new Call rite, @args
|
||||
rite.isNew = @isNew
|
||||
left = new Literal "typeof #{ left.compile o } === \"function\""
|
||||
|
@ -692,7 +675,7 @@ exports.Call = class Call extends Base
|
|||
compiledArgs.push (arg.compileToFragments o, LEVEL_LIST)...
|
||||
|
||||
fragments = []
|
||||
if @isSuper
|
||||
if this instanceof SuperCall
|
||||
preface = @superReference(o) + ".call(#{@superThis(o)}"
|
||||
if compiledArgs.length then preface += ", "
|
||||
fragments.push @makeCode preface
|
||||
|
@ -711,7 +694,7 @@ exports.Call = class Call extends Base
|
|||
#
|
||||
# splatArgs is an array of CodeFragments to put into the 'apply'.
|
||||
compileSplat: (o, splatArgs) ->
|
||||
if @isSuper
|
||||
if this instanceof SuperCall
|
||||
return [].concat @makeCode("#{ @superReference o }.apply(#{@superThis(o)}, "),
|
||||
splatArgs, @makeCode(")")
|
||||
|
||||
|
@ -745,6 +728,54 @@ exports.Call = class Call extends Base
|
|||
answer = answer.concat fun
|
||||
answer = answer.concat @makeCode(".apply(#{ref}, "), splatArgs, @makeCode(")")
|
||||
|
||||
#### Super
|
||||
|
||||
# Takes care of converting `super()` calls into calls against the prototype's
|
||||
# function of the same name.
|
||||
exports.SuperCall = class SuperCall extends Call
|
||||
constructor: (args) ->
|
||||
super null, args ? [new Splat new IdentifierLiteral 'arguments']
|
||||
# Allow to recognize a bare `super` call without parentheses and arguments.
|
||||
@isBare = args?
|
||||
|
||||
# Grab the reference to the superclass's implementation of the current
|
||||
# method.
|
||||
superReference: (o) ->
|
||||
method = o.scope.namedMethod()
|
||||
if method?.klass
|
||||
{klass, name, variable} = method
|
||||
if klass.isComplex()
|
||||
bref = new IdentifierLiteral o.scope.parent.freeVariable 'base'
|
||||
base = new Value new Parens new Assign bref, klass
|
||||
variable.base = base
|
||||
variable.properties.splice 0, klass.properties.length
|
||||
if name.isComplex() or (name instanceof Index and name.index.isAssignable())
|
||||
nref = new IdentifierLiteral o.scope.parent.freeVariable 'name'
|
||||
name = new Index new Assign nref, name.index
|
||||
variable.properties.pop()
|
||||
variable.properties.push name
|
||||
accesses = [new Access new IdentifierLiteral '__super__']
|
||||
accesses.push new Access new IdentifierLiteral 'constructor' if method.static
|
||||
accesses.push if nref? then new Index nref else name
|
||||
(new Value bref ? klass, accesses).compile o
|
||||
else if method?.ctor
|
||||
"#{method.name}.__super__.constructor"
|
||||
else
|
||||
@error 'cannot call super outside of an instance method.'
|
||||
|
||||
# The appropriate `this` value for a `super` call.
|
||||
superThis : (o) ->
|
||||
method = o.scope.method
|
||||
(method and not method.klass and method.context) or "this"
|
||||
|
||||
#### RegexWithInterpolations
|
||||
|
||||
# Regexes with interpolations are in fact just a variation of a `Call` (a
|
||||
# `RegExp()` call to be precise) with a `StringWithInterpolations` inside.
|
||||
exports.RegexWithInterpolations = class RegexWithInterpolations extends Call
|
||||
constructor: (args = []) ->
|
||||
super (new Value new IdentifierLiteral 'RegExp'), args, false
|
||||
|
||||
#### Extends
|
||||
|
||||
# Node to extend an object's prototype with an ancestor object.
|
||||
|
@ -772,12 +803,14 @@ exports.Access = class Access extends Base
|
|||
|
||||
compileToFragments: (o) ->
|
||||
name = @name.compileToFragments o
|
||||
if IDENTIFIER.test fragmentsToText name
|
||||
name.unshift @makeCode "."
|
||||
node = @name.unwrap()
|
||||
if node instanceof IdentifierLiteral
|
||||
if node.value in JS_FORBIDDEN
|
||||
[@makeCode('["'), name..., @makeCode('"]')]
|
||||
else
|
||||
[@makeCode('.'), name...]
|
||||
else
|
||||
name.unshift @makeCode "["
|
||||
name.push @makeCode "]"
|
||||
name
|
||||
[@makeCode('['), name..., @makeCode(']')]
|
||||
|
||||
isComplex: NO
|
||||
|
||||
|
@ -818,8 +851,9 @@ exports.Range = class Range extends Base
|
|||
[@fromC, @fromVar] = @cacheToCodeFragments @from.cache o, LEVEL_LIST, isComplex
|
||||
[@toC, @toVar] = @cacheToCodeFragments @to.cache o, LEVEL_LIST, isComplex
|
||||
[@step, @stepVar] = @cacheToCodeFragments step.cache o, LEVEL_LIST, isComplex if step = del o, 'step'
|
||||
[@fromNum, @toNum] = [@fromVar.match(NUMBER), @toVar.match(NUMBER)]
|
||||
@stepNum = @stepVar.match(NUMBER) if @stepVar
|
||||
@fromNum = if @from.isNumber() then Number @fromVar else null
|
||||
@toNum = if @to.isNumber() then Number @toVar else null
|
||||
@stepNum = if step?.isNumber() then Number @stepVar else null
|
||||
|
||||
# When compiled normally, the range returns the contents of the *for loop*
|
||||
# needed to iterate over the values in the range. Used by comprehensions.
|
||||
|
@ -828,7 +862,7 @@ exports.Range = class Range extends Base
|
|||
return @compileArray(o) unless o.index
|
||||
|
||||
# Set up endpoints.
|
||||
known = @fromNum and @toNum
|
||||
known = @fromNum? and @toNum?
|
||||
idx = del o, 'index'
|
||||
idxName = del o, 'name'
|
||||
namedIndex = idxName and idxName isnt idx
|
||||
|
@ -838,10 +872,10 @@ exports.Range = class Range extends Base
|
|||
[lt, gt] = ["#{idx} <#{@equals}", "#{idx} >#{@equals}"]
|
||||
|
||||
# Generate the condition.
|
||||
condPart = if @stepNum
|
||||
if parseNum(@stepNum[0]) > 0 then "#{lt} #{@toVar}" else "#{gt} #{@toVar}"
|
||||
condPart = if @stepNum?
|
||||
if @stepNum > 0 then "#{lt} #{@toVar}" else "#{gt} #{@toVar}"
|
||||
else if known
|
||||
[from, to] = [parseNum(@fromNum[0]), parseNum(@toNum[0])]
|
||||
[from, to] = [@fromNum, @toNum]
|
||||
if from <= to then "#{lt} #{to}" else "#{gt} #{to}"
|
||||
else
|
||||
cond = if @stepVar then "#{@stepVar} > 0" else "#{@fromVar} <= #{@toVar}"
|
||||
|
@ -870,15 +904,16 @@ exports.Range = class Range extends Base
|
|||
|
||||
# When used as a value, expand the range into the equivalent array.
|
||||
compileArray: (o) ->
|
||||
if @fromNum and @toNum and Math.abs(@fromNum - @toNum) <= 20
|
||||
range = [+@fromNum..+@toNum]
|
||||
known = @fromNum? and @toNum?
|
||||
if known and Math.abs(@fromNum - @toNum) <= 20
|
||||
range = [@fromNum..@toNum]
|
||||
range.pop() if @exclusive
|
||||
return [@makeCode "[#{ range.join(', ') }]"]
|
||||
idt = @tab + TAB
|
||||
i = o.scope.freeVariable 'i', single: true
|
||||
result = o.scope.freeVariable 'results'
|
||||
pre = "\n#{idt}#{result} = [];"
|
||||
if @fromNum and @toNum
|
||||
if known
|
||||
o.index = i
|
||||
body = fragmentsToText @compileNode o
|
||||
else
|
||||
|
@ -915,7 +950,7 @@ exports.Slice = class Slice extends Base
|
|||
if not (not @range.exclusive and +compiledText is -1)
|
||||
toStr = ', ' + if @range.exclusive
|
||||
compiledText
|
||||
else if SIMPLENUM.test compiledText
|
||||
else if to.isNumber()
|
||||
"#{+compiledText + 1}"
|
||||
else
|
||||
compiled = to.compileToFragments o, LEVEL_ACCESS
|
||||
|
@ -975,7 +1010,7 @@ exports.Obj = class Obj extends Base
|
|||
value = prop.value
|
||||
else
|
||||
[key, value] = prop.base.cache o
|
||||
prop = new Assign (new Value (new Literal oref), [new Access key]), value
|
||||
prop = new Assign (new Value (new IdentifierLiteral oref), [new Access key]), value
|
||||
if indent then answer.push @makeCode indent
|
||||
answer.push prop.compileToFragments(o, LEVEL_TOP)...
|
||||
if join then answer.push @makeCode join
|
||||
|
@ -1034,24 +1069,26 @@ exports.Class = class Class extends Base
|
|||
|
||||
children: ['variable', 'parent', 'body']
|
||||
|
||||
defaultClassVariableName: '_Class'
|
||||
|
||||
# Figure out the appropriate name for the constructor function of this class.
|
||||
determineName: ->
|
||||
return null unless @variable
|
||||
return @defaultClassVariableName unless @variable
|
||||
[..., tail] = @variable.properties
|
||||
decl = if tail
|
||||
tail instanceof Access and tail.name.value
|
||||
node = if tail
|
||||
tail instanceof Access and tail.name
|
||||
else
|
||||
@variable.base.value
|
||||
if decl in STRICT_PROSCRIBED
|
||||
@variable.error "class variable name may not be #{decl}"
|
||||
decl and= IDENTIFIER.test(decl) and decl
|
||||
@variable.base
|
||||
return @defaultClassVariableName unless node instanceof IdentifierLiteral
|
||||
name = node.value
|
||||
if name in JS_FORBIDDEN then "_#{name}" else name
|
||||
|
||||
# For all `this`-references and bound functions in the class definition,
|
||||
# `this` is the Class being constructed.
|
||||
setContext: (name) ->
|
||||
@body.traverseChildren false, (node) ->
|
||||
return false if node.classBody
|
||||
if node instanceof Literal and node.value is 'this'
|
||||
if node instanceof ThisLiteral
|
||||
node.value = name
|
||||
else if node instanceof Code
|
||||
node.context = name if node.bound
|
||||
|
@ -1060,7 +1097,7 @@ exports.Class = class Class extends Base
|
|||
# constructor.
|
||||
addBoundFunctions: (o) ->
|
||||
for bvar in @boundFuncs
|
||||
lhs = (new Value (new Literal "this"), [new Access bvar]).compile o
|
||||
lhs = (new Value (new ThisLiteral), [new Access bvar]).compile o
|
||||
@ctor.body.unshift new Literal "#{lhs} = #{utility 'bind', o}(#{lhs}, this)"
|
||||
return
|
||||
|
||||
|
@ -1082,13 +1119,13 @@ exports.Class = class Class extends Base
|
|||
assign = @ctor = func
|
||||
else
|
||||
@externalCtor = o.classScope.freeVariable 'class'
|
||||
assign = new Assign new Literal(@externalCtor), func
|
||||
assign = new Assign new IdentifierLiteral(@externalCtor), func
|
||||
else
|
||||
if assign.variable.this
|
||||
func.static = yes
|
||||
else
|
||||
acc = if base.isComplex() then new Index base else new Access base
|
||||
assign.variable = new Value(new Literal(name), [(new Access new Literal 'prototype'), acc])
|
||||
assign.variable = new Value(new IdentifierLiteral(name), [(new Access new IdentifierLiteral 'prototype'), acc])
|
||||
if func instanceof Code and func.bound
|
||||
@boundFuncs.push base
|
||||
func.bound = no
|
||||
|
@ -1145,9 +1182,8 @@ exports.Class = class Class extends Base
|
|||
if argumentsNode = @body.contains isLiteralArguments
|
||||
argumentsNode.error "Class bodies shouldn't reference arguments"
|
||||
|
||||
name = @determineName() or '_Class'
|
||||
name = "_#{name}" if name.reserved
|
||||
lname = new Literal name
|
||||
name = @determineName()
|
||||
lname = new IdentifierLiteral name
|
||||
func = new Code [], Block.wrap [@body]
|
||||
args = []
|
||||
o.classScope = func.makeScope o.scope
|
||||
|
@ -1161,7 +1197,7 @@ exports.Class = class Class extends Base
|
|||
@body.expressions.push lname
|
||||
|
||||
if @parent
|
||||
superClass = new Literal o.classScope.freeVariable 'superClass', reserve: no
|
||||
superClass = new IdentifierLiteral o.classScope.freeVariable 'superClass', reserve: no
|
||||
@body.expressions.unshift new Extends lname, superClass
|
||||
func.params.push new Param superClass
|
||||
args.push @parent
|
||||
|
@ -1227,7 +1263,13 @@ exports.Assign = class Assign extends Base
|
|||
val = @value.compileToFragments o, LEVEL_LIST
|
||||
@variable.front = true if isValue and @variable.base instanceof Obj
|
||||
compiledName = @variable.compileToFragments o, LEVEL_LIST
|
||||
return (compiledName.concat @makeCode(": "), val) if @context is 'object'
|
||||
|
||||
if @context is 'object'
|
||||
if fragmentsToText(compiledName) in JS_FORBIDDEN
|
||||
compiledName.unshift @makeCode '"'
|
||||
compiledName.push @makeCode '"'
|
||||
return compiledName.concat @makeCode(": "), val
|
||||
|
||||
answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val
|
||||
if o.level <= LEVEL_LIST then answer else @wrapInBraces answer
|
||||
|
||||
|
@ -1263,8 +1305,8 @@ exports.Assign = class Assign extends Base
|
|||
if obj.this then obj.properties[0].name else obj
|
||||
else
|
||||
# A regular array pattern-match.
|
||||
new Literal 0
|
||||
acc = IDENTIFIER.test idx.unwrap().value
|
||||
new NumberLiteral 0
|
||||
acc = idx.unwrap() instanceof IdentifierLiteral
|
||||
value = new Value value
|
||||
value.properties.push new (if acc then Access else Index) idx
|
||||
if obj.unwrap().value in RESERVED
|
||||
|
@ -1276,7 +1318,7 @@ exports.Assign = class Assign extends Base
|
|||
assigns = []
|
||||
expandedIdx = false
|
||||
# Make vvar into a simple variable if it isn't already.
|
||||
if not IDENTIFIER.test(vvarText) or @variable.assigns(vvarText)
|
||||
if value.unwrap() not instanceof IdentifierLiteral or @variable.assigns(vvarText)
|
||||
assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...]
|
||||
vvar = [@makeCode ref]
|
||||
vvarText = ref
|
||||
|
@ -1324,7 +1366,7 @@ exports.Assign = class Assign extends Base
|
|||
# A regular array pattern-match.
|
||||
new Literal expandedIdx or idx
|
||||
name = obj.unwrap().value
|
||||
acc = IDENTIFIER.test idx.unwrap().value
|
||||
acc = idx.unwrap() instanceof IdentifierLiteral
|
||||
val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx]
|
||||
val = new Op '?', val, defaultValue if defaultValue
|
||||
if name? and name in RESERVED
|
||||
|
@ -1341,7 +1383,7 @@ exports.Assign = class Assign extends Base
|
|||
[left, right] = @variable.cacheReference o
|
||||
# Disallow conditional assignment of undefined variables.
|
||||
if not left.properties.length and left.base instanceof Literal and
|
||||
left.base.value != "this" and not o.scope.check left.base.value
|
||||
left.base not instanceof ThisLiteral and not o.scope.check left.base.value
|
||||
@variable.error "the variable \"#{left.base.value}\" can't be assigned with #{@context} because it has not been declared before"
|
||||
if "?" in @context
|
||||
o.isExistentialEquals = true
|
||||
|
@ -1366,8 +1408,7 @@ exports.Assign = class Assign extends Base
|
|||
else
|
||||
fromDecl = fromRef = '0'
|
||||
if to
|
||||
if from instanceof Value and from.isSimpleNumber() and
|
||||
to instanceof Value and to.isSimpleNumber()
|
||||
if from?.isNumber() and to.isNumber()
|
||||
to = to.compile(o) - fromRef
|
||||
to += 1 unless exclusive
|
||||
else
|
||||
|
@ -1413,8 +1454,8 @@ exports.Code = class Code extends Base
|
|||
# Handle bound functions early.
|
||||
if @bound and not @context
|
||||
@context = '_this'
|
||||
wrapper = new Code [new Param new Literal @context], new Block [this]
|
||||
boundfunc = new Call(wrapper, [new Literal 'this'])
|
||||
wrapper = new Code [new Param new IdentifierLiteral @context], new Block [this]
|
||||
boundfunc = new Call(wrapper, [new ThisLiteral])
|
||||
boundfunc.updateLocationDataIfMissing @locationData
|
||||
return boundfunc.compileNode(o)
|
||||
|
||||
|
@ -1431,7 +1472,7 @@ exports.Code = class Code extends Base
|
|||
for p in @params when p not instanceof Expansion and p.name.value
|
||||
o.scope.add p.name.value, 'var', yes
|
||||
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
|
||||
new Value new Literal 'arguments'
|
||||
new Value new IdentifierLiteral 'arguments'
|
||||
break
|
||||
for param in @params
|
||||
if param.isComplex()
|
||||
|
@ -1502,10 +1543,10 @@ exports.Param = class Param extends Base
|
|||
node = @name
|
||||
if node.this
|
||||
name = node.properties[0].name.value
|
||||
name = "_#{name}" if name.reserved
|
||||
node = new Literal o.scope.freeVariable name
|
||||
name = "_#{name}" if name in JS_FORBIDDEN
|
||||
node = new IdentifierLiteral o.scope.freeVariable name
|
||||
else if node.isComplex()
|
||||
node = new Literal o.scope.freeVariable 'arg'
|
||||
node = new IdentifierLiteral o.scope.freeVariable 'arg'
|
||||
node = new Value node
|
||||
node = new Splat node if @splat
|
||||
node.updateLocationDataIfMissing @locationData
|
||||
|
@ -1663,7 +1704,7 @@ exports.While = class While extends Base
|
|||
set = "#{@tab}#{rvar} = [];\n"
|
||||
if @guard
|
||||
if body.expressions.length > 1
|
||||
body.expressions.unshift new If (new Parens @guard).invert(), new Literal "continue"
|
||||
body.expressions.unshift new If (new Parens @guard).invert(), new StatementLiteral "continue"
|
||||
else
|
||||
body = Block.wrap [new If @guard, body] if @guard
|
||||
body = [].concat @makeCode("\n"), (body.compileToFragments o, LEVEL_TOP), @makeCode("\n#{@tab}")
|
||||
|
@ -1705,7 +1746,9 @@ exports.Op = class Op extends Base
|
|||
|
||||
children: ['first', 'second']
|
||||
|
||||
isSimpleNumber: NO
|
||||
isNumber: ->
|
||||
@isUnary() and @operator in ['+', '-'] and
|
||||
@first instanceof Value and @first.isNumber()
|
||||
|
||||
isYield: ->
|
||||
@operator in ['yield', 'yield*']
|
||||
|
@ -1714,8 +1757,7 @@ exports.Op = class Op extends Base
|
|||
not @second
|
||||
|
||||
isComplex: ->
|
||||
not (@isUnary() and @operator in ['+', '-'] and
|
||||
@first instanceof Value and @first.isSimpleNumber())
|
||||
not @isNumber()
|
||||
|
||||
# Am I capable of
|
||||
# [Python-style comparison chaining](http://docs.python.org/reference/expressions.html#notin)?
|
||||
|
@ -1806,7 +1848,7 @@ exports.Op = class Op extends Base
|
|||
# Keep reference to the left expression, unless this an existential assignment
|
||||
compileExistence: (o) ->
|
||||
if @first.isComplex()
|
||||
ref = new Literal o.scope.freeVariable 'ref'
|
||||
ref = new IdentifierLiteral o.scope.freeVariable 'ref'
|
||||
fst = new Parens new Assign ref, @first
|
||||
else
|
||||
fst = @first
|
||||
|
@ -1849,11 +1891,11 @@ exports.Op = class Op extends Base
|
|||
|
||||
compilePower: (o) ->
|
||||
# Make a Math.pow call
|
||||
pow = new Value new Literal('Math'), [new Access new Literal 'pow']
|
||||
pow = new Value new IdentifierLiteral('Math'), [new Access new IdentifierLiteral 'pow']
|
||||
new Call(pow, [@first, @second]).compileToFragments o
|
||||
|
||||
compileFloorDivision: (o) ->
|
||||
floor = new Value new Literal('Math'), [new Access new Literal 'floor']
|
||||
floor = new Value new IdentifierLiteral('Math'), [new Access new IdentifierLiteral 'floor']
|
||||
div = new Op '/', @first, @second
|
||||
new Call(floor, [div]).compileToFragments o
|
||||
|
||||
|
@ -1926,7 +1968,7 @@ exports.Try = class Try extends Base
|
|||
|
||||
catchPart = if @recovery
|
||||
generatedErrorVariableName = o.scope.freeVariable 'error', reserve: no
|
||||
placeholder = new Literal generatedErrorVariableName
|
||||
placeholder = new IdentifierLiteral generatedErrorVariableName
|
||||
@recovery.unshift new Assign @errorVariable, placeholder if @errorVariable
|
||||
[].concat @makeCode(" catch ("), placeholder.compileToFragments(o), @makeCode(") {\n"),
|
||||
@recovery.compileToFragments(o, LEVEL_TOP), @makeCode("\n#{@tab}}")
|
||||
|
@ -1975,7 +2017,7 @@ exports.Existence = class Existence extends Base
|
|||
compileNode: (o) ->
|
||||
@expression.front = @front
|
||||
code = @expression.compile o, LEVEL_OP
|
||||
if IDENTIFIER.test(code) and not o.scope.check code
|
||||
if @expression.unwrap() instanceof IdentifierLiteral and not o.scope.check code
|
||||
[cmp, cnj] = if @negated then ['===', '||'] else ['!==', '&&']
|
||||
code = "typeof #{code} #{cmp} \"undefined\" #{cnj} #{code} #{cmp} null"
|
||||
else
|
||||
|
@ -2008,6 +2050,13 @@ exports.Parens = class Parens extends Base
|
|||
(expr instanceof For and expr.returns))
|
||||
if bare then fragments else @wrapInBraces fragments
|
||||
|
||||
#### StringWithInterpolations
|
||||
|
||||
# Strings with interpolations are in fact just a variation of `Parens` with
|
||||
# string concatenation inside.
|
||||
|
||||
exports.StringWithInterpolations = class StringWithInterpolations extends Parens
|
||||
|
||||
#### For
|
||||
|
||||
# CoffeeScript's replacement for the *for* loop is our array and object
|
||||
|
@ -2054,7 +2103,7 @@ exports.For = class For extends While
|
|||
kvarAssign = if kvar isnt ivar then "#{kvar} = " else ""
|
||||
if @step and not @range
|
||||
[step, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST, isComplexOrAssignable
|
||||
stepNum = stepVar.match NUMBER
|
||||
stepNum = Number stepVar if @step.isNumber()
|
||||
name = ivar if @pattern
|
||||
varPart = ''
|
||||
guardPart = ''
|
||||
|
@ -2065,20 +2114,21 @@ exports.For = class For extends While
|
|||
{index: ivar, name, @step, isComplex: isComplexOrAssignable}
|
||||
else
|
||||
svar = @source.compile o, LEVEL_LIST
|
||||
if (name or @own) and not IDENTIFIER.test svar
|
||||
if (name or @own) and @source.unwrap() not instanceof IdentifierLiteral
|
||||
defPart += "#{@tab}#{ref = scope.freeVariable 'ref'} = #{svar};\n"
|
||||
svar = ref
|
||||
if name and not @pattern
|
||||
namePart = "#{name} = #{svar}[#{kvar}]"
|
||||
if not @object
|
||||
defPart += "#{@tab}#{step};\n" if step isnt stepVar
|
||||
lvar = scope.freeVariable 'len' unless @step and stepNum and down = (parseNum(stepNum[0]) < 0)
|
||||
down = stepNum < 0
|
||||
lvar = scope.freeVariable 'len' unless @step and stepNum? and down
|
||||
declare = "#{kvarAssign}#{ivar} = 0, #{lvar} = #{svar}.length"
|
||||
declareDown = "#{kvarAssign}#{ivar} = #{svar}.length - 1"
|
||||
compare = "#{ivar} < #{lvar}"
|
||||
compareDown = "#{ivar} >= 0"
|
||||
if @step
|
||||
if stepNum
|
||||
if stepNum?
|
||||
if down
|
||||
compare = compareDown
|
||||
declare = declareDown
|
||||
|
@ -2095,7 +2145,7 @@ exports.For = class For extends While
|
|||
body.makeReturn rvar
|
||||
if @guard
|
||||
if body.expressions.length > 1
|
||||
body.expressions.unshift new If (new Parens @guard).invert(), new Literal "continue"
|
||||
body.expressions.unshift new If (new Parens @guard).invert(), new StatementLiteral "continue"
|
||||
else
|
||||
body = Block.wrap [new If @guard, body] if @guard
|
||||
if @pattern
|
||||
|
@ -2124,7 +2174,7 @@ exports.For = class For extends While
|
|||
val.properties.length is 1 and
|
||||
val.properties[0].name?.value in ['call', 'apply'])
|
||||
fn = val.base?.unwrapAll() or val
|
||||
ref = new Literal o.scope.freeVariable 'fn'
|
||||
ref = new IdentifierLiteral o.scope.freeVariable 'fn'
|
||||
base = new Value ref
|
||||
if val.base
|
||||
[val.base, base] = [base, val]
|
||||
|
@ -2317,17 +2367,7 @@ LEVEL_ACCESS = 6 # ...[0]
|
|||
# Tabs are two spaces for pretty printing.
|
||||
TAB = ' '
|
||||
|
||||
IDENTIFIER = /// ^ (?!\d) [$\w\x7f-\uffff]+ $ ///
|
||||
SIMPLENUM = /^[+-]?\d+$/
|
||||
HEXNUM = /^[+-]?0x[\da-f]+/i
|
||||
NUMBER = ///^[+-]?(?:
|
||||
0x[\da-f]+ | # hex
|
||||
\d*\.?\d+ (?:e[+-]?\d+)? # decimal
|
||||
)$///i
|
||||
|
||||
# Is a literal value a string/regex?
|
||||
IS_STRING = /^['"]/
|
||||
IS_REGEX = /^\//
|
||||
SIMPLENUM = /^[+-]?\d+$/
|
||||
|
||||
# Helper Functions
|
||||
# ----------------
|
||||
|
@ -2346,23 +2386,13 @@ multident = (code, tab) ->
|
|||
code = code.replace /\n/g, '$&' + tab
|
||||
code.replace /\s+$/, ''
|
||||
|
||||
# Parse a number (+- decimal/hexadecimal)
|
||||
# Examples: 0, -1, 1, 2e3, 2e-3, -0xfe, 0xfe
|
||||
parseNum = (x) ->
|
||||
if not x?
|
||||
0
|
||||
else if x.match HEXNUM
|
||||
parseInt x, 16
|
||||
else
|
||||
parseFloat x
|
||||
|
||||
isLiteralArguments = (node) ->
|
||||
node instanceof Literal and node.value is 'arguments' and not node.asKey
|
||||
|
||||
isLiteralThis = (node) ->
|
||||
(node instanceof Literal and node.value is 'this' and not node.asKey) or
|
||||
(node instanceof ThisLiteral and not node.asKey) or
|
||||
(node instanceof Code and node.bound) or
|
||||
(node instanceof Call and node.isSuper)
|
||||
node instanceof SuperCall
|
||||
|
||||
isComplexOrAssignable = (node) -> node.isComplex() or node.isAssignable?()
|
||||
|
||||
|
|
|
@ -483,7 +483,7 @@ IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@
|
|||
|
||||
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
||||
IMPLICIT_CALL = [
|
||||
'IDENTIFIER', 'NUMBER', 'STRING', 'STRING_START', 'JS', 'REGEX', 'REGEX_START'
|
||||
'IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'JS', 'REGEX', 'REGEX_START'
|
||||
'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL'
|
||||
'UNDEFINED', 'UNARY', 'YIELD', 'UNARY_MATH', 'SUPER', 'THROW'
|
||||
'@', '->', '=>', '[', '(', '{', '--', '++'
|
||||
|
|
|
@ -832,14 +832,14 @@ test "#4130: unassignable in destructured param", ->
|
|||
}) ->
|
||||
console.log "Oh hello!"
|
||||
''', '''
|
||||
[stdin]:2:12: error: "null" cannot be assigned
|
||||
[stdin]:2:12: error: assignment to a reserved word: null
|
||||
@param : null
|
||||
^^^^
|
||||
'''
|
||||
assertErrorFormat '''
|
||||
({a: null}) ->
|
||||
''', '''
|
||||
[stdin]:1:6: error: "null" cannot be assigned
|
||||
[stdin]:1:6: error: assignment to a reserved word: null
|
||||
({a: null}) ->
|
||||
^^^^
|
||||
'''
|
||||
|
|
|
@ -86,6 +86,15 @@ test "self-referencing functions", ->
|
|||
changeMe()
|
||||
eq changeMe, 2
|
||||
|
||||
test "#2009: don't touch `` `this` ``", ->
|
||||
nonceA = {}
|
||||
nonceB = {}
|
||||
fn = null
|
||||
(->
|
||||
fn = => this is nonceA and `this` is nonceB
|
||||
).call nonceA
|
||||
ok fn.call nonceB
|
||||
|
||||
|
||||
# Parameter List Features
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ eq Number::toString, 42['toString']
|
|||
|
||||
eq Number::toString, 42.toString
|
||||
|
||||
eq Number::toString, 2e308['toString'] # Infinity
|
||||
|
||||
|
||||
# Non-Integer Literals
|
||||
|
||||
|
@ -74,3 +76,10 @@ test "#2224: hex literals with 0b or B or E", ->
|
|||
eq 176, 0x0b0
|
||||
eq 177, 0x0B1
|
||||
eq 225, 0xE1
|
||||
|
||||
test "Infinity", ->
|
||||
eq Infinity, CoffeeScript.eval "0b#{Array(1024 + 1).join('1')}"
|
||||
eq Infinity, CoffeeScript.eval "0o#{Array(342 + 1).join('7')}"
|
||||
eq Infinity, CoffeeScript.eval "0x#{Array(256 + 1).join('f')}"
|
||||
eq Infinity, CoffeeScript.eval Array(500 + 1).join('9')
|
||||
eq Infinity, 2e308
|
||||
|
|
Loading…
Reference in New Issue