From 05a3cf386a7563ef610ffd4ee5cc1527d6b5b1c7 Mon Sep 17 00:00:00 2001 From: mohayonao Date: Sat, 3 May 2014 06:34:07 +0900 Subject: [PATCH] refactoring - mv klass.js - add klass/constructors.js, move to separate file - add klass/utils.js, move to separate file - mv compiler.js - add compiler/scope.js, move to separate file - add compiler/marker.js, move to separete file - add compiler/node.js, move to separate file - add compiler/lexer.js, move to separate file - fix compiler/marker - remove parser#peek - build v0.0.19 --- build/scscript.js | 20484 ++++++++-------- package.json | 2 +- src/sc/lang/classlib.js | 4 +- src/sc/lang/compiler.js | 187 +- src/sc/lang/{ => compiler}/codegen.js | 4 +- src/sc/lang/{ => compiler}/codegen_test.js | 15 +- src/sc/lang/compiler/compiler.js | 89 + src/sc/lang/{ => compiler}/compiler_test.js | 20 +- src/sc/lang/compiler/lexer.js | 727 + src/sc/lang/compiler/marker.js | 89 + src/sc/lang/compiler/marker_test.js | 174 + src/sc/lang/compiler/node.js | 157 + src/sc/lang/{ => compiler}/parser.js | 1554 +- src/sc/lang/{ => compiler}/parser_test.js | 0 src/sc/lang/compiler/sc.js | 1 + src/sc/lang/compiler/scope.js | 99 + src/sc/lang/fn.js | 2 - src/sc/lang/fn_test.js | 2 +- src/sc/lang/installer.js | 3 +- src/sc/lang/iterator.js | 2 +- src/sc/lang/klass.js | 281 +- .../constructors.js} | 4 +- .../constructors_test.js} | 2 +- src/sc/lang/klass/klass.js | 278 + src/sc/lang/{ => klass}/klass_test.js | 0 .../lang/{klass-utils.js => klass/utils.js} | 6 +- .../utils_test.js} | 4 +- 27 files changed, 12175 insertions(+), 12015 deletions(-) rename src/sc/lang/{ => compiler}/codegen.js (99%) rename src/sc/lang/{ => compiler}/codegen_test.js (93%) create mode 100644 src/sc/lang/compiler/compiler.js rename src/sc/lang/{ => compiler}/compiler_test.js (99%) create mode 100644 src/sc/lang/compiler/lexer.js create mode 100644 src/sc/lang/compiler/marker.js create mode 100644 src/sc/lang/compiler/marker_test.js create mode 100644 src/sc/lang/compiler/node.js rename src/sc/lang/{ => compiler}/parser.js (50%) rename src/sc/lang/{ => compiler}/parser_test.js (100%) create mode 100644 src/sc/lang/compiler/sc.js create mode 100644 src/sc/lang/compiler/scope.js rename src/sc/lang/{klass-constructors.js => klass/constructors.js} (98%) rename src/sc/lang/{klass-constructors_test.js => klass/constructors_test.js} (98%) create mode 100644 src/sc/lang/klass/klass.js rename src/sc/lang/{ => klass}/klass_test.js (100%) rename src/sc/lang/{klass-utils.js => klass/utils.js} (87%) rename src/sc/lang/{klass-utils_test.js => klass/utils_test.js} (97%) diff --git a/build/scscript.js b/build/scscript.js index 6fc04b2..d41d1aa 100644 --- a/build/scscript.js +++ b/build/scscript.js @@ -1,7 +1,7 @@ (function(global) { "use strict"; -var sc = { VERSION: "0.0.18" }; +var sc = { VERSION: "0.0.19" }; // src/sc/sc.js (function(sc) { @@ -24,13421 +24,13407 @@ var sc = { VERSION: "0.0.18" }; })(sc); -// src/sc/libs/random.js +// src/sc/lang/compiler/compiler.js (function(sc) { - var random = {}; - - function RandGen(seed) { - this.setSeed(seed); - } - - RandGen.prototype.setSeed = function(seed) { - if (typeof seed !== "number") { - seed = Date.now(); - } - seed += ~(seed << 15); - seed ^= seed >>> 10; - seed += seed << 3; - seed ^= seed >>> 6; - seed += ~(seed << 11); - seed ^= seed >>> 16; - - this.x = 1243598713 ^ seed; - this.y = 3093459404 ^ seed; - this.z = 1821928721 ^ seed; - - return this; - }; + var compiler = {}; - RandGen.prototype.trand = function() { - this.x = ((this.x & 4294967294) << 12) ^ (((this.x << 13) ^ this.x) >>> 19); - this.y = ((this.y & 4294967288) << 4) ^ (((this.y << 2) ^ this.y) >>> 25); - this.z = ((this.z & 4294967280) << 17) ^ (((this.z << 3) ^ this.z) >>> 11); - return this.x ^ this.y ^ this.z; + compiler.Token = { + CharLiteral: "Char", + EOF: "", + FalseLiteral: "False", + FloatLiteral: "Float", + Identifier: "Identifier", + IntegerLiteral: "Integer", + Keyword: "Keyword", + Label: "Label", + NilLiteral: "Nil", + Punctuator: "Punctuator", + StringLiteral: "String", + SymbolLiteral: "Symbol", + TrueLiteral: "True" }; - RandGen.prototype.next = function() { - return (this.trand() >>> 0) / 4294967296; + compiler.Syntax = { + AssignmentExpression: "AssignmentExpression", + BinaryExpression: "BinaryExpression", + BlockExpression: "BlockExpression", + CallExpression: "CallExpression", + FunctionExpression: "FunctionExpression", + GlobalExpression: "GlobalExpression", + Identifier: "Identifier", + ListExpression: "ListExpression", + Label: "Label", + Literal: "Literal", + ObjectExpression: "ObjectExpression", + Program: "Program", + ThisExpression: "ThisExpression", + UnaryExpression: "UnaryExpression", + VariableDeclaration: "VariableDeclaration", + VariableDeclarator: "VariableDeclarator" }; - RandGen.prototype.RandGen = RandGen; - - random = { - RandGen: RandGen, - current: new RandGen(), - next: function() { - return random.current.next(); - }, - setSeed: function(seed) { - return random.current.setSeed(seed); - } + compiler.Message = { + ArgumentAlreadyDeclared: "argument '%0' already declared", + InvalidLHSInAssignment: "invalid left-hand side in assignment", + NotImplemented: "not implemented %0", + UnexpectedEOS: "unexpected end of input", + UnexpectedIdentifier: "unexpected identifier", + UnexpectedChar: "unexpected char", + UnexpectedLabel: "unexpected label", + UnexpectedNumber: "unexpected number", + UnexpectedString: "unexpected string", + UnexpectedSymbol: "unexpected symbol", + UnexpectedToken: "unexpected token %0", + VariableAlreadyDeclared: "variable '%0' already declared", + VariableNotDefined: "variable '%0' not defined" }; - sc.libs.random = random; - -})(sc); - -// src/sc/libs/mathlib.js -(function(sc) { - - var rand = sc.libs.random; - var mathlib = {}; - - mathlib.rand = function(a) { - return rand.next() * a; + compiler.Keywords = { + var: "keyword", + arg: "keyword", + const: "keyword", + this: "function", + thisThread: "function", + thisProcess: "function", + thisFunction: "function", + thisFunctionDef: "function", }; - mathlib["+"] = function(a, b) { - return a + b; - }; + sc.lang.compiler = compiler; - mathlib["-"] = function(a, b) { - return a - b; - }; + var SCScript = sc.SCScript; - mathlib["*"] = function(a, b) { - return a * b; + SCScript.tokenize = function(source, opts) { + opts = opts || /* istanbul ignore next */ {}; + opts.tokens = true; + return sc.lang.parser.parse(source, opts).tokens || /* istanbul ignore next */ []; }; - mathlib["/"] = function(a, b) { - return a / b; + SCScript.parse = function(source, opts) { + return sc.lang.parser.parse(source, opts); }; - mathlib.mod = function(a, b) { - if (a === 0 || b === 0) { - return 0; - } - if ((a > 0 && b < 0) || (a < 0 && b > 0)) { - return b + a % b; - } - return a % b; + SCScript.compile = function(source, opts) { + var ast = SCScript.parse(source, opts); + return sc.lang.codegen.compile(ast, opts); }; - mathlib.div = function(a, b) { - if (b === 0) { - return a|0; - } - return (a / b)|0; - }; +})(sc); - mathlib.pow = function(a, b) { - return Math.pow(a, b); - }; +// src/sc/lang/compiler/scope.js +(function(sc) { - mathlib.min = Math.min; - mathlib.max = Math.max; + var Message = sc.lang.compiler.Message; - mathlib.bitAnd = function(a, b) { - return a & b; - }; + function Scope(methods) { + var f = function(parent) { + this.parent = parent; + this.stack = []; + }; - mathlib.bitOr = function(a, b) { - return a | b; - }; + function F() {} + F.prototype = Scope; + f.prototype = new F(); - mathlib.bitXor = function(a, b) { - return a ^ b; - }; + Object.keys(methods).forEach(function(key) { + f.prototype[key] = methods[key]; + }); - var gcd = function(a, b) { - var t; + return f; + } - a = a|0; - b = b|0; + Scope.add = function(type, id, scope) { + var peek = this.stack[this.stack.length - 1]; + var vars, args, declared, stmt, indent; - while (b !== 0) { - t = a % b; - a = b; - b = t; + if (scope) { + vars = scope.vars; + args = scope.args; + declared = scope.declared; + stmt = scope.stmt; + indent = scope.indent; + } else { + vars = peek.vars; + args = peek.args; + declared = peek.declared; + stmt = peek.stmt; + indent = peek.indent; } - return Math.abs(a); - }; - - mathlib.lcm = function(a, b) { - if (a === 0 && b === 0) { - return 0; + if (args[id]) { + this.parent.throwError({}, Message.ArgumentAlreadyDeclared, id); } - return Math.abs((a|0) * (b|0)) / gcd(a, b); - }; - - mathlib.gcd = function(a, b) { - return gcd(a, b); - }; - - mathlib.round = function(a, b) { - return b === 0 ? a : Math.round(a / b) * b; - }; - - mathlib.roundUp = function(a, b) { - return b === 0 ? a : Math.ceil(a / b) * b; - }; - - mathlib.trunc = function(a, b) { - return b === 0 ? a : Math.floor(a / b) * b; - }; - mathlib.atan2 = Math.atan2; + if (vars[id] && id.charAt(0) !== "_") { + this.parent.throwError({}, Message.VariableAlreadyDeclared, id); + } - mathlib.hypot = function(a, b) { - return Math.sqrt((a * a) + (b * b)); + switch (type) { + case "var": + if (!vars[id]) { + this.add_delegate(stmt, id, indent, peek, scope); + vars[id] = true; + delete declared[id]; + } + break; + case "arg": + args[id] = true; + delete declared[id]; + break; + } }; - mathlib.hypotApx = function(a, b) { - var x = Math.abs(a); - var y = Math.abs(b); - var minxy = Math.min(x, y); - return x + y - (Math.sqrt(2) - 1) * minxy; + Scope.add_delegate = function() { }; - mathlib.leftShift = function(a, b) { - if (b < 0) { - return a >> -b; - } - return a << b; + Scope.end = function() { + this.stack.pop(); }; - mathlib.rightShift = function(a, b) { - if (b < 0) { - return a << -b; - } - return a >> b; - }; + Scope.getDeclaredVariable = function() { + var peek = this.stack[this.stack.length - 1]; + var declared = {}; - mathlib.unsignedRightShift = function(a, b) { - if (b < 0) { - return (a << -b) >>> 0; + if (peek) { + Array.prototype.concat.apply([], [ + peek.declared, peek.args, peek.vars + ].map(Object.keys)).forEach(function(key) { + declared[key] = true; + }); } - return a >>> b; - }; - mathlib.ring1 = function(a, b) { - return a * b + a; + return declared; }; - mathlib.ring2 = function(a, b) { - return a * b + a + b; + Scope.find = function(id) { + var peek = this.stack[this.stack.length - 1]; + return peek.vars[id] || peek.args[id] || peek.declared[id]; }; - mathlib.ring3 = function(a, b) { - return a * a * b; + Scope.peek = function() { + return this.stack[this.stack.length - 1]; }; - mathlib.ring4 = function(a, b) { - return a * a * b - a * b * b; - }; + sc.lang.compiler.Scope = Scope; - mathlib.difsqr = function(a, b) { - return a * a - b * b; - }; +})(sc); - mathlib.sumsqr = function(a, b) { - return a * a + b * b; - }; +// src/sc/lang/compiler/codegen.js +(function(sc) { - mathlib.sqrsum = function(a, b) { - return (a + b) * (a + b); + var codegen = {}; + var Syntax = sc.lang.compiler.Syntax; + var Token = sc.lang.compiler.Token; + var Message = sc.lang.compiler.Message; + var SegmentedMethod = { + idle : true, + sleep: true, + wait : true, + yield: true }; - mathlib.sqrdif = function(a, b) { - return (a - b) * (a - b); - }; + var Scope = sc.lang.compiler.Scope({ + add_delegate: function(stmt, id, indent, peek, scope) { + if (stmt.vars.length === 0) { + this._addNewVariableStatement(stmt, id, indent); + } else { + this._appendVariable(stmt, id); + } + if (scope) { + peek.declared[id] = true; + } + }, + _addNewVariableStatement: function(stmt, id, indent) { + stmt.head.push(indent, "var "); + stmt.vars.push($id(id)); + if (id.charAt(0) !== "_") { + stmt.vars.push(" = $SC.Nil()"); + } + stmt.tail.push(";", "\n"); + }, + _appendVariable: function(stmt, id) { + stmt.vars.push( + ", ", $id(id) + ); + if (id.charAt(0) !== "_") { + stmt.vars.push(" = $SC.Nil()"); + } + }, + begin: function(stream, args) { + var declared = this.getDeclaredVariable(); + var stmt = { head: [], vars: [], tail: [] }; + var i, imax; - mathlib.absdif = function(a, b) { - return Math.abs(a - b); - }; + this.stack.push({ + vars : {}, + args : {}, + declared: declared, + indent : this.parent.base, + stmt : stmt + }); - mathlib.thresh = function(a, b) { - return a < b ? 0 : a; - }; + for (i = 0, imax = args.length; i < imax; i++) { + this.add("arg", args[i]); + } - mathlib.amclip = function(a, b) { - return a * 0.5 * (b + Math.abs(b)); - }; + stream.push(stmt.head, stmt.vars, stmt.tail); + } + }); - mathlib.scaleneg = function(a, b) { - b = 0.5 * b + 0.5; - return (Math.abs(a) - a) * b + a; + function CodeGen(opts) { + this.opts = opts || {}; + this.base = ""; + this.state = { + calledSegmentedMethod: false, + syncBlockScope: null + }; + this.scope = new Scope(this); + if (typeof this.opts.bare === "undefined") { + this.opts.bare = false; + } + } + + CodeGen.prototype.toSourceNodeWhenNeeded = function(generated) { + if (Array.isArray(generated)) { + return this.flattenToString(generated); + } + return generated; }; - mathlib.clip2 = function(a, b) { - return Math.max(-b, Math.min(a, b)); + CodeGen.prototype.flattenToString = function(list) { + var i, imax, e, result = ""; + for (i = 0, imax = list.length; i < imax; ++i) { + e = list[i]; + result += Array.isArray(e) ? this.flattenToString(e) : e; + } + return result; }; - mathlib.fold2 = function(a, b) { - var x, c, range, range2; + CodeGen.prototype.addIndent = function(stmt) { + return [ this.base, stmt ]; + }; - if (b === 0) { - return 0; - } + CodeGen.prototype.generate = function(node, opts) { + var result; - x = a + b; - if (a >= b) { - a = b + b - a; - if (a >= -b) { - return a; - } - } else if (a < -b) { - a = -b - b - a; - if (a < b) { - return a; - } + if (Array.isArray(node)) { + result = [ + "(", this.stitchWith(node, ", ", function(item) { + return this.generate(item, opts); + }), ")" + ]; + } else if (node && node.type) { + result = this[node.type](node, opts); + result = this.toSourceNodeWhenNeeded(result, node); + } else if (typeof node === "string") { + result = $id(node); } else { - return a; + result = node; } - range = b + b; - range2 = range + range; - c = x - range2 * Math.floor(x / range2); + return result; + }; - if (c >= range) { - c = range2 - c; - } + CodeGen.prototype.withFunction = function(args, fn) { + var result; + var argItems, base, body; - return c - b; - }; + argItems = this.stitchWith(args, ", ", function(item) { + return this.generate(item); + }); - mathlib.wrap2 = function(a, b) { - var range; + result = [ "function(", argItems, ") {\n" ]; - if (b === 0) { - return 0; - } + base = this.base; + this.base += " "; - if (a >= b) { - range = b + b; - a -= range; - if (a < b) { - return a; - } - } else if (a < -b) { - range = b + b; - a += range; - if (a >= -b) { - return a; - } - } else { - return a; - } + this.scope.begin(result, args); - return a - range * Math.floor((a + b) / range); - }; + body = fn.call(this); - mathlib.excess = function(a, b) { - return a - Math.max(-b, Math.min(a, b)); - }; + if (body.length) { + result.push(body); + } else { + result.push(this.base, "return $SC.Nil();"); + } - mathlib.firstArg = function(a) { - return a; - }; + this.scope.end(); - mathlib.rrand = function(a, b) { - return a + rand.next() * (b - a); - }; + this.base = base; - mathlib.exprand = function(a, b) { - return a * Math.exp(Math.log(b / a) * rand.next()); - }; + result.push("\n", this.base, "}"); - mathlib.clip = function(val, lo, hi) { - return Math.max(lo, Math.min(val, hi)); + return result; }; - mathlib.iwrap = function(val, lo, hi) { - var range; + CodeGen.prototype.withIndent = function(fn) { + var base, result; - range = hi - lo + 1; - val -= range * Math.floor((val - lo) / range); + base = this.base; + this.base += " "; + result = fn.call(this); + this.base = base; - return val; + return result; }; - mathlib.wrap = function(val, lo, hi) { - var range; + CodeGen.prototype.insertArrayElement = function(elements) { + var result, items; - if (hi === lo) { - return lo; - } + result = [ "[", "]" ]; - range = (hi - lo); - if (val >= hi) { - val -= range; - if (val < hi) { - return val; - } - } else if (val < lo) { - val += range; - if (val >= lo) { - return val; - } - } else { - return val; + if (elements.length) { + items = this.withIndent(function() { + return this.stitchWith(elements, "\n", function(item) { + return [ this.base, this.generate(item), "," ]; + }); + }); + result.splice(1, 0, "\n", items, "\n", this.base); } - return val - range * Math.floor((val - lo) / range); + return result; }; - mathlib.ifold = function(val, lo, hi) { - var x, range1, range2; - - range1 = hi - lo; - range2 = range1 * 2; - x = val - lo; - x -= range2 * Math.floor(x / range2); + CodeGen.prototype.insertKeyValueElement = function(keyValues, with_comma) { + var result = []; - if (x >= range1) { - return range2 - x + lo; + if (keyValues) { + if (with_comma) { + result.push(", "); + } + result.push( + "{ ", this.stitchWith(Object.keys(keyValues), ", ", function(key) { + return [ key, ": ", this.generate(keyValues[key]) ]; + }), " }" + ); } - return x + lo; + return result; }; - mathlib.fold = function(val, lo, hi) { - var x, range1, range2; + CodeGen.prototype.stitchWith = function(elements, bond, fn) { + var result, item; + var count, i, imax; - if (hi === lo) { - return lo; - } - - if (val >= hi) { - val = (hi * 2) - val; - if (val >= lo) { - return val; - } - } else if (val < lo) { - val = (lo * 2) - val; - if (val < hi) { - return val; + result = []; + for (i = count = 0, imax = elements.length; i < imax; ++i) { + if (count) { + result.push(bond); } - } else { - return val; - } - range1 = hi - lo; - range2 = range1 * 2; - x = val - lo; - x -= range2 * Math.floor(x / range2); + item = fn.call(this, elements[i], i); - if (x >= range1) { - return range2 - x + lo; + if (typeof item !== "undefined") { + result.push(item); + count += 1; + } } - return x + lo; + return result; }; - mathlib.clip_idx = function(index, len) { - return Math.max(0, Math.min(index, len - 1)); - }; + CodeGen.prototype.throwError = function(obj, messageFormat) { + var args, message; - mathlib.wrap_idx = function(index, len) { - index = index % len; - if (index < 0) { - index += len; - } - return index; - }; + args = Array.prototype.slice.call(arguments, 2); + message = messageFormat.replace(/%(\d)/g, function(whole, index) { + return args[index]; + }); - mathlib.fold_idx = function(index, len) { - var len2 = len * 2 - 2; + throw new Error(message); + }; - index = (index|0) % len2; - if (index < 0) { - index += len2; - } - if (len <= index) { - return len2 - index; + CodeGen.prototype.AssignmentExpression = function(node) { + if (Array.isArray(node.left)) { + return this._DestructuringAssignment(node); } - return index; - }; - sc.libs.mathlib = mathlib; + return this._SimpleAssignment(node); + }; -})(sc); + CodeGen.prototype._SimpleAssignment = function(node) { + var result = []; + var opts; -// src/sc/lang/compiler.js -(function(sc) { + opts = { right: node.right, used: false }; - var compiler = {}; + result.push(this.generate(node.left, opts)); - compiler.Token = { - CharLiteral: "Char", - EOF: "", - FalseLiteral: "False", - FloatLiteral: "Float", - Identifier: "Identifier", - IntegerLiteral: "Integer", - Keyword: "Keyword", - Label: "Label", - NilLiteral: "Nil", - Punctuator: "Punctuator", - StringLiteral: "String", - SymbolLiteral: "Symbol", - TrueLiteral: "True" - }; + if (!opts.used) { + result.push(" " + node.operator + " ", this.generate(opts.right)); + } - compiler.Syntax = { - AssignmentExpression: "AssignmentExpression", - BinaryExpression: "BinaryExpression", - BlockExpression: "BlockExpression", - CallExpression: "CallExpression", - FunctionExpression: "FunctionExpression", - GlobalExpression: "GlobalExpression", - Identifier: "Identifier", - ListExpression: "ListExpression", - Label: "Label", - Literal: "Literal", - ObjectExpression: "ObjectExpression", - Program: "Program", - ThisExpression: "ThisExpression", - UnaryExpression: "UnaryExpression", - VariableDeclaration: "VariableDeclaration", - VariableDeclarator: "VariableDeclarator" + return result; }; - var Message = compiler.Message = { - ArgumentAlreadyDeclared: "argument '%0' already declared", - InvalidLHSInAssignment: "invalid left-hand side in assignment", - NotImplemented: "not implemented %0", - UnexpectedEOS: "unexpected end of input", - UnexpectedIdentifier: "unexpected identifier", - UnexpectedChar: "unexpected char", - UnexpectedLabel: "unexpected label", - UnexpectedNumber: "unexpected number", - UnexpectedString: "unexpected string", - UnexpectedSymbol: "unexpected symbol", - UnexpectedToken: "unexpected token %0", - VariableAlreadyDeclared: "variable '%0' already declared", - VariableNotDefined: "variable '%0' not defined" - }; + CodeGen.prototype._DestructuringAssignment = function(node) { + var elements = node.left; + var operator = node.operator; + var assignments; - compiler.Keywords = { - var: "keyword", - arg: "keyword", - const: "keyword", - this: "function", - thisThread: "function", - thisProcess: "function", - thisFunction: "function", - thisFunctionDef: "function", - }; + this.scope.add("var", "_ref"); - var Scope = (function() { - function Scope(methods) { - var f = function(parent) { - this.parent = parent; - this.stack = []; - }; + assignments = this.withIndent(function() { + var result, lastUsedIndex; - function F() {} - F.prototype = Scope; - f.prototype = new F(); + lastUsedIndex = elements.length; - Object.keys(methods).forEach(function(key) { - f.prototype[key] = methods[key]; - }); + result = [ + this.stitchWith(elements, ",\n", function(item, i) { + return this.addIndent(this._Assign( + item, operator, "_ref.at($SC.Integer(" + i + "))" + )); + }) + ]; - return f; - } + if (node.remain) { + result.push(",\n", this.addIndent(this._Assign( + node.remain, operator, "_ref.copyToEnd($SC.Integer(" + lastUsedIndex + "))" + ))); + } - Scope.add = function(type, id, scope) { - var peek = this.stack[this.stack.length - 1]; - var vars, args, declared, stmt, indent; + return result; + }); - if (scope) { - vars = scope.vars; - args = scope.args; - declared = scope.declared; - stmt = scope.stmt; - indent = scope.indent; - } else { - vars = peek.vars; - args = peek.args; - declared = peek.declared; - stmt = peek.stmt; - indent = peek.indent; - } + return [ + "(_ref = ", this.generate(node.right), ",\n", + assignments , ",\n", + this.addIndent("_ref)") + ]; + }; - if (args[id]) { - this.parent.throwError({}, Message.ArgumentAlreadyDeclared, id); - } + CodeGen.prototype._Assign = function(left, operator, right) { + var result = []; + var opts; - if (vars[id] && id.charAt(0) !== "_") { - this.parent.throwError({}, Message.VariableAlreadyDeclared, id); - } + opts = { right: right, used: false }; - switch (type) { - case "var": - if (!vars[id]) { - this.add_delegate(stmt, id, indent, peek, scope); - vars[id] = true; - delete declared[id]; - } - break; - case "arg": - args[id] = true; - delete declared[id]; - break; - } - }; + result.push(this.generate(left, opts)); - Scope.add_delegate = function() { - }; + if (!opts.used) { + result.push(" " + operator + " ", right); + } - Scope.end = function() { - this.stack.pop(); - }; + return result; + }; - Scope.getDeclaredVariable = function() { - var peek = this.stack[this.stack.length - 1]; - var declared = {}; + CodeGen.prototype.BinaryExpression = function(node) { + var operator = node.operator; - if (peek) { - Array.prototype.concat.apply([], [ - peek.declared, peek.args, peek.vars - ].map(Object.keys)).forEach(function(key) { - declared[key] = true; - }); - } + if (operator === "===" || operator === "!==") { + return this._EqualityOperator(node); + } - return declared; - }; + return this._BinaryExpression(node); + }; - Scope.find = function(id) { - var peek = this.stack[this.stack.length - 1]; - return peek.vars[id] || peek.args[id] || peek.declared[id]; - }; + CodeGen.prototype._EqualityOperator = function(node) { + return [ + "$SC.Boolean(", + this.generate(node.left), " " + node.operator + " ", this.generate(node.right), + ")" + ]; + }; - Scope.peek = function() { - return this.stack[this.stack.length - 1]; - }; + CodeGen.prototype._BinaryExpression = function(node) { + var result, operator, ch; - return Scope; - })(); + result = [ this.generate(node.left) ]; + operator = node.operator; - compiler.Scope = Scope; + ch = operator.charCodeAt(0); - sc.lang.compiler = compiler; + if (0x61 <= ch && ch <= 0x7a) { + result.push(".", operator); + } else { + result.push(" ['", operator, "'] "); + } - var SCScript = sc.SCScript; + result.push("(", this.generate(node.right)); + if (node.adverb) { + result.push(", ", this.generate(node.adverb)); + } + result.push(")"); - SCScript.tokenize = function(source, opts) { - opts = opts || /* istanbul ignore next */ {}; - opts.tokens = true; - return sc.lang.parser.parse(source, opts).tokens || /* istanbul ignore next */ []; + return result; }; - SCScript.parse = function(source, opts) { - return sc.lang.parser.parse(source, opts); - }; + CodeGen.prototype.BlockExpression = function(node) { + var body = this.withFunction([], function() { + return this._Statements(node.body); + }); - SCScript.compile = function(source, opts) { - var ast = SCScript.parse(source, opts); - return sc.lang.codegen.compile(ast, opts); + return [ "(", body, ")()" ]; }; -})(sc); + CodeGen.prototype.CallExpression = function(node) { + if (isSegmentedMethod(node)) { + this.state.calledSegmentedMethod = true; + } -// src/sc/lang/parser.js -(function(sc) { + if (node.args.expand) { + return this._ExpandCall(node); + } - var parser = {}; + return this._SimpleCall(node); + }; - var Token = sc.lang.compiler.Token; - var Syntax = sc.lang.compiler.Syntax; - var Message = sc.lang.compiler.Message; - var Keywords = sc.lang.compiler.Keywords; + CodeGen.prototype._SimpleCall = function(node) { + var args; + var list; + var hasActualArgument; - var binaryPrecedenceDefaults = { - "?" : 1, - "??" : 1, - "!?" : 1, - "->" : 2, - "||" : 3, - "&&" : 4, - "|" : 5, - "&" : 6, - "==" : 7, - "!=" : 7, - "===": 7, - "!==": 7, - "<" : 8, - ">" : 8, - "<=" : 8, - ">=" : 8, - "<<" : 9, - ">>" : 9, - "+>>": 9, - "+" : 10, - "-" : 10, - "*" : 11, - "/" : 11, - "%" : 11, - "!" : 12, + list = node.args.list; + hasActualArgument = !!list.length; + + args = [ + this.stitchWith(list, ", ", function(item) { + return this.generate(item); + }), + this.insertKeyValueElement(node.args.keywords, hasActualArgument) + ]; + + return [ + this.generate(node.callee), ".", node.method.name, "(", args, ")" + ]; }; - function char2num(ch) { - var n = ch.charCodeAt(0); + CodeGen.prototype._ExpandCall = function(node) { + var result; - if (48 <= n && n <= 57) { - return n - 48; - } - if (65 <= n && n <= 90) { - return n - 55; - } - return n - 87; // if (97 <= n && n <= 122) - } + this.scope.add("var", "_ref"); - function isNumber(ch) { - return "0" <= ch && ch <= "9"; - } + result = [ + "(_ref = ", + this.generate(node.callee), + ", _ref." + node.method.name + ".apply(_ref, ", + this.insertArrayElement(node.args.list), ".concat(", + this.generate(node.args.expand), ".asArray()._", + this.insertKeyValueElement(node.args.keywords, true), + ")))" + ]; - var Scope = sc.lang.compiler.Scope({ - begin: function() { - var declared = this.getDeclaredVariable(); + return result; + }; - this.stack.push({ - vars: {}, - args: {}, - declared: declared - }); - } - }); + CodeGen.prototype.GlobalExpression = function(node) { + return "$SC.Global." + node.id.name; + }; - function LocationMarker(parser) { - this.parser = parser; - this.marker = [ - parser.index, - parser.lineNumber, - parser.index - parser.lineStart - ]; - } + CodeGen.prototype.FunctionExpression = function(node) { + var fn, info; - LocationMarker.prototype.apply = function(node) { - var parser = this.parser; - var marker = this.marker; + info = getInformationOfFunction(node); - /* istanbul ignore else */ - if (this.parser.opts.range) { - node.range = [ marker[0], parser.index ]; - } - /* istanbul ignore else */ - if (this.parser.opts.loc) { - node.loc = { - start: { - line: marker[1], - column: marker[2] - }, - end: { - line: parser.lineNumber, - column: parser.index - parser.lineStart - } - }; + if (!isSegmentedBlock(node)) { + fn = CodeGen.prototype._SimpleFunction; + } else { + fn = CodeGen.prototype._SegmentedFunction; } + + return [ + fn.call(this, node, info.args), + this._FunctionMetadata(info), ")" + ]; }; - function SCParser(source, opts) { - /* istanbul ignore next */ - if (typeof source !== "string") { - if (typeof source === "undefined") { - source = ""; - } - source = String(source); + var format_argument = function(node) { + switch (node.valueType) { + case Token.NilLiteral : return "nil"; + case Token.TrueLiteral : return "true"; + case Token.FalseLiteral : return "false"; + case Token.CharLiteral : return "$" + node.value; + case Token.SymbolLiteral: return "\\" + node.value; } - source = source.replace(/\r\n?/g, "\n"); - this.source = source; - this.opts = opts; - this.length = source.length; - this.index = 0; - this.lineNumber = this.length ? 1 : 0; - this.lineStart = 0; - this.reverted = null; - this.scope = new Scope(this); - this.marker = []; - this.tokens = opts.tokens ? [] : null; - this.errors = opts.tolerant ? [] : null; - this.state = { - closedFunction: false, - disallowGenerator: false, - innerElements: false, - immutableList: false, - underscore: [] - }; - } - - SCParser.prototype.parse = function() { - return this.parseProgram(); + switch (node.value) { + case "Infinity" : return "inf"; + case "-Infinity": return "-inf"; + } + return node.value; }; - SCParser.prototype.skipComment = function() { - var source = this.source; - var length = this.length; - var index = this.index; - var ch; + CodeGen.prototype._FunctionMetadata = function(info) { + var keys, vals; + var args, result; - LOOP: while (index < length) { - ch = source.charAt(index); + keys = info.keys; + vals = info.vals; - if (ch === " " || ch === "\t") { - index += 1; - continue; - } + if (keys.length === 0 && !info.remain && !info.closed) { + return []; + } - if (ch === "\n") { - index += 1; - this.lineNumber += 1; - this.lineStart = index; - continue; - } + args = this.stitchWith(keys, "; ", function(item, i) { + var result = [ keys[i] ]; - if (ch === "/") { - ch = source.charAt(index + 1); - if (ch === "/") { - index = this.skipLineComment(index + 2); - continue; - } - if (ch === "*") { - index = this.skipBlockComment(index + 2); - continue; + if (vals[i]) { + if (vals[i].type === Syntax.ListExpression) { + result.push("=[ ", this.stitchWith(vals[i].elements, ", ", function(item) { + return format_argument(item); + }), " ]"); + } else { + result.push("=", format_argument(vals[i])); } } - break; - } - - this.index = index; - }; + return result; + }); - SCParser.prototype.skipLineComment = function(index) { - var source = this.source; - var length = this.length; - var ch; + result = [ ", '", args ]; - while (index < length) { - ch = source.charAt(index); - index += 1; - if (ch === "\n") { - this.lineNumber += 1; - this.lineStart = index; - break; + if (info.remain) { + if (keys.length) { + result.push("; "); } + result.push("*" + info.remain); } + result.push("'"); - return index; - }; - - SCParser.prototype.skipBlockComment = function(index) { - var source = this.source; - var length = this.length; - var ch, depth; - - depth = 1; - while (index < length) { - ch = source.charAt(index); - - if (ch === "\n") { - this.lineNumber += 1; - this.lineStart = index; - } else { - ch = ch + source.charAt(index + 1); - if (ch === "/*") { - depth += 1; - index += 1; - } else if (ch === "*/") { - depth -= 1; - index += 1; - if (depth === 0) { - return index + 1; - } - } - } - - index += 1; + if (info.closed) { + result.push(", true"); } - this.throwError({}, Message.UnexpectedToken, "ILLEGAL"); - return index; + return result; }; - SCParser.prototype.collectToken = function() { - var loc, token, source, t; + CodeGen.prototype._SimpleFunction = function(node, args) { + var body; - this.skipComment(); + body = this.withFunction(args, function() { + return this._Statements(node.body); + }); - loc = { - start: { - line: this.lineNumber, - column: this.index - this.lineStart - } - }; + return [ "$SC.Function(", body ]; + }; - token = this.advance(); + CodeGen.prototype._SegmentedFunction = function(node, args) { + var fargs, body, assignArguments; - loc.end = { - line: this.lineNumber, - column: this.index - this.lineStart + fargs = args.map(function(_, i) { + return "_arg" + i; + }); + + assignArguments = function(item, i) { + return "$" + args[i] + " = " + fargs[i]; }; - if (token.type !== Token.EOF) { - source = this.source; - t = { - type: token.type, - value: source.slice(token.range[0], token.range[1]) - }; - if (this.opts.range) { - t.range = [ token.range[0], token.range[1] ]; - } - if (this.opts.loc) { - t.loc = loc; + body = this.withFunction([], function() { + var result = []; + var fragments = [], syncBlockScope; + var elements = node.body; + var i, imax; + var functionBodies; + + for (i = 0, imax = args.length; i < imax; ++i) { + this.scope.add("var", args[i]); } - this.tokens.push(t); - } - return token; - }; + syncBlockScope = this.state.syncBlockScope; + this.state.syncBlockScope = this.scope.peek(); - SCParser.prototype.advance = function() { - var ch, token; + functionBodies = this.withIndent(function() { + var fragments = []; + var i = 0, imax = elements.length; + var lastIndex = imax - 1; - this.skipComment(); + fragments.push("\n"); - if (this.length <= this.index) { - return this.EOFToken(); - } + var loop = function() { + var fragments = []; + var stmt; + var count = 0; - ch = this.source.charAt(this.index); + while (i < imax) { + if (i === 0) { + if (args.length) { + stmt = this.stitchWith(args, "; ", assignArguments); + fragments.push([ this.addIndent(stmt), ";", "\n" ]); + } + } else if (count) { + fragments.push("\n"); + } - // Symbol literal starts with back slash - if (ch === "\\") { - return this.scanSymbolLiteral(); - } + this.state.calledSegmentedMethod = false; + stmt = this.generate(elements[i]); - // Char literal starts with dollar - if (ch === "$") { - return this.scanCharLiteral(); - } + if (stmt.length) { + if (i === lastIndex || this.state.calledSegmentedMethod) { + stmt = [ "return ", stmt ]; + } + fragments.push([ this.addIndent(stmt), ";" ]); + count += 1; + } - // String literal starts with single quote or double quote - if (ch === "'" || ch === "\"") { - return this.scanStringLiteral(); - } + i += 1; + if (this.state.calledSegmentedMethod) { + break; + } + } - // for partial application - if (ch === "_") { - return this.scanUnderscore(); - } + return fragments; + }; - if (ch === "-") { - token = this.scanNegativeNumericLiteral(); - if (token) { - return token; - } + while (i < imax) { + if (i) { + fragments.push(",", "\n", this.addIndent(this.withFunction([], loop))); + } else { + fragments.push(this.addIndent(this.withFunction(fargs, loop))); + } + } + + fragments.push("\n"); + + return fragments; + }); + + fragments.push("return [", functionBodies, this.addIndent("];")); + + result.push([ this.addIndent(fragments) ]); + + this.state.syncBlockScope = syncBlockScope; + + return result; + }); + + return [ "$SC.SegFunction(", body ]; + }; + + CodeGen.prototype.Identifier = function(node, opts) { + var name = node.name; + + if (isClassName(name)) { + return "$SC('" + name + "')"; } - // Identifier starts with alphabet - if (("A" <= ch && ch <= "Z") || ("a" <= ch && ch <= "z")) { - return this.scanIdentifier(); + if (this.scope.find(name)) { + return $id(name); } - // Number literal starts with number - if (isNumber(ch)) { - return this.scanNumericLiteral(); + if (name.length === 1) { + return this._InterpreterVariable(node, opts); } - return this.scanPunctuator(); + this.throwError(null, Message.VariableNotDefined, name); }; - SCParser.prototype.expect = function(value) { - var token = this.lex(); - if (token.type !== Token.Punctuator || token.value !== value) { - this.throwUnexpected(token, value); + CodeGen.prototype._InterpreterVariable = function(node, opts) { + var name; + + if (opts) { + // setter + name = [ + "$this." + node.name + "_(", this.generate(opts.right), ")" + ]; + opts.used = true; + } else { + // getter + name = "$this." + node.name + "()"; } + + return name; }; - SCParser.prototype.peek = function() { - var index, lineNumber, lineStart; + CodeGen.prototype.ListExpression = function(node) { + var result; - index = this.index; - lineNumber = this.lineNumber; - lineStart = this.lineStart; + result = [ + "$SC.Array(", + this.insertArrayElement(node.elements), + ]; - if (this.opts.tokens) { - this.lookahead = this.collectToken(); - } else { - this.lookahead = this.advance(); + if (node.immutable) { + result.push(", ", "true"); } - this.index = index; - this.lineNumber = lineNumber; - this.lineStart = lineStart; - }; + result.push(")"); - SCParser.prototype.lex = function(saved) { - var that = this; - var token = this.lookahead; + return result; + }; - if (saved) { - saved = [ this.lookahead, this.index, this.lineNumber, this.lineStart ]; + CodeGen.prototype.Literal = function(node) { + switch (node.valueType) { + case Token.IntegerLiteral: + return "$SC.Integer(" + node.value + ")"; + case Token.FloatLiteral: + return "$SC.Float(" + node.value + ")"; + case Token.CharLiteral: + return "$SC.Char('" + node.value + "')"; + case Token.SymbolLiteral: + return "$SC.Symbol('" + node.value + "')"; + case Token.StringLiteral: + return "$SC.String('" + node.value + "')"; + case Token.TrueLiteral: + return "$SC.True()"; + case Token.FalseLiteral: + return "$SC.False()"; } - this.index = token.range[1]; - this.lineNumber = token.lineNumber; - this.lineStart = token.lineStart; + return "$SC.Nil()"; + }; - if (this.opts.tokens) { - this.lookahead = this.collectToken(); + CodeGen.prototype.ObjectExpression = function(node) { + return [ + "$SC.Event(", this.insertArrayElement(node.elements), ")" + ]; + }; + + CodeGen.prototype.Program = function(node) { + var result, body; + + if (node.body.length) { + body = this.withFunction([ "this", "SC" ], function() { + return this._Statements(node.body); + }); + + result = [ "(", body, ")" ]; + + if (!this.opts.bare) { + result = [ "SCScript", result, ";" ]; + } } else { - this.lookahead = this.advance(); + result = []; } - this.index = token.range[1]; - this.lineNumber = token.lineNumber; - this.lineStart = token.lineStart; + return result; + }; - if (saved) { - token.restore = function() { - that.lookahead = saved[0]; - that.index = saved[1]; - that.lineNumber = saved[2]; - that.lineStart = saved[3]; - if (that.tokens) { - that.tokens.pop(); - } - }; + CodeGen.prototype.ThisExpression = function(node) { + if (node.name === "this") { + return "$this"; } - return token; + return [ "$this." + node.name + "()" ]; }; - SCParser.prototype.EOFToken = function() { - return { - type: Token.EOF, - value: "", - lineNumber: this.lineNumber, - lineStart: this.lineStart, - range: [ this.index, this.index ] - }; + CodeGen.prototype.UnaryExpression = function(node) { + /* istanbul ignore else */ + if (node.operator === "`") { + return [ "$SC.Ref(", this.generate(node.arg), ")" ]; + } + + /* istanbul ignore next */ + throw new Error("Unknown UnaryExpression: " + node.operator); }; - SCParser.prototype.scanCharLiteral = function() { - var start, value; + CodeGen.prototype.VariableDeclaration = function(node) { + var scope = this.state.syncBlockScope; - start = this.index; - value = this.source.charAt(this.index + 1); + return this.stitchWith(node.declarations, ", ", function(item) { + this.scope.add("var", item.id.name, scope); - this.index += 2; + if (!item.init) { + return; + } - return { - type : Token.CharLiteral, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; + return [ this.generate(item.id), " = ", this.generate(item.init) ]; + }); }; - SCParser.prototype.scanIdentifier = function() { - var source = this.source.slice(this.index); - var start = this.index; - var value, type; + CodeGen.prototype._Statements = function(elements) { + var lastIndex = elements.length - 1; - value = /^[a-zA-Z][a-zA-Z0-9_]*/.exec(source)[0]; + return this.stitchWith(elements, "\n", function(item, i) { + var stmt; - this.index += value.length; + stmt = this.generate(item); - if (this.source.charAt(this.index) === ":") { - this.index += 1; - return { - type: Token.Label, - value: value, - lineNumber: this.lineNumber, - lineStart: this.lineStart, - range: [ start, this.index ] - }; - } else if (this.isKeyword(value)) { - type = Token.Keyword; - } else { - switch (value) { - case "inf": - type = Token.FloatLiteral; - value = "Infinity"; - break; - case "pi": - type = Token.FloatLiteral; - value = String(Math.PI); - break; - case "nil": - type = Token.NilLiteral; - value = "null"; - break; - case "true": - type = Token.TrueLiteral; - break; - case "false": - type = Token.FalseLiteral; - break; - default: - type = Token.Identifier; - break; + if (stmt.length === 0) { + return; } - } - return { - type: type, - value: value, - lineNumber: this.lineNumber, - lineStart: this.lineStart, - range: [ start, this.index ] - }; - }; + if (i === lastIndex) { + stmt = [ "return ", stmt ]; + } - SCParser.prototype.scanNumericLiteral = function(neg) { - return this.scanNAryNumberLiteral(neg) || this.scanDecimalNumberLiteral(neg); + return [ this.addIndent(stmt), ";" ]; + }); }; - SCParser.prototype.scanNegativeNumericLiteral = function() { - var token, ch1, ch2, ch3; - var start, value = null; - - start = this.index; - ch1 = this.source.charAt(this.index + 1); - - if (isNumber(ch1)) { - this.index += 1; - token = this.scanNumericLiteral(true); - token.range[0] = start; - return token; - } - - ch2 = this.source.charAt(this.index + 2); - ch3 = this.source.charAt(this.index + 3); - - if (ch1 + ch2 === "pi") { - this.index += 3; - value = String(-Math.PI); - } else if (ch1 + ch2 + ch3 === "inf") { - this.index += 4; - value = "-Infinity"; - } + var $id = function(id) { + var ch = id.charAt(0); - if (value !== null) { - return { - type : Token.FloatLiteral, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; + if (ch !== "_" && ch !== "$") { + id = "$" + id; } - return null; + return id; }; - SCParser.prototype.scanNAryNumberLiteral = function(neg) { - var re, start, items; - var base, integer, frac, pi; - var value, type; - - re = /^(\d+)r((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+)(?:\.((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+))?/; - start = this.index; - items = re.exec(this.source.slice(this.index)); - - if (!items) { - return; - } - - base = items[1].replace(/^0+(?=\d)/g, "")|0; - integer = items[2].replace(/(^0+(?=\d)|_)/g, ""); - frac = items[3] && items[3].replace(/_/g, ""); - - if (!frac && base < 26 && integer.substr(-2) === "pi") { - integer = integer.substr(0, integer.length - 2); - pi = true; - } - - type = Token.IntegerLiteral; - value = this.calcNBasedInteger(integer, base); - - if (frac) { - type = Token.FloatLiteral; - value += this.calcNBasedFrac(frac, base); - } + var getInformationOfFunction = function(node) { + var args = []; + var keys, vals, remain; + var list, i, imax; - if (neg) { - value *= -1; - } + keys = []; + vals = []; + remain = null; - if (pi) { - type = Token.FloatLiteral; - value = value * Math.PI; + if (node.args) { + list = node.args.list; + for (i = 0, imax = list.length; i < imax; ++i) { + args.push(list[i].id.name); + keys.push(list[i].id.name); + vals.push(list[i].init); + } + if (node.args.remain) { + remain = node.args.remain.name; + args.push(remain); + } } - if (type === Token.FloatLiteral && value === (value|0)) { - value = value + ".0"; - } else { - value = String(value); + if (node.partial) { + keys = []; } - this.index += items[0].length; - return { - type : type, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] + args : args, + keys : keys, + vals : vals, + remain: remain, + closed: node.closed }; }; - SCParser.prototype.char2num = function(ch, base) { - var x = char2num(ch, base); - if (x >= base) { - this.throwError({}, Message.UnexpectedToken, ch); - } - return x; + var isClassName = function(name) { + var ch0 = name.charAt(0); + return "A" <= ch0 && ch0 <= "Z"; }; - SCParser.prototype.calcNBasedInteger = function(integer, base) { - var value, i, imax; + var isNode = function(node, key) { + return key !== "range" && key !== "loc" && typeof node[key] === "object"; + }; - for (i = value = 0, imax = integer.length; i < imax; ++i) { - value *= base; - value += this.char2num(integer[i], base); + var isSegmentedBlock = function(node) { + if (isSegmentedMethod(node)) { + return true; } - - return value; + return Object.keys(node).some(function(key) { + if (isNode(node, key)) { + return isSegmentedBlock(node[key]); + } + return false; + }); }; - SCParser.prototype.calcNBasedFrac = function(frac, base) { - var value, i, imax; - - for (i = value = 0, imax = frac.length; i < imax; ++i) { - value += this.char2num(frac[i], base) * Math.pow(base, -(i + 1)); - } + var isSegmentedMethod = function(node) { + return node.type === Syntax.CallExpression && !!SegmentedMethod[node.method.name]; + }; - return value; + codegen.compile = function(ast, opts) { + return new CodeGen(opts).generate(ast); }; - SCParser.prototype.scanDecimalNumberLiteral = function(neg) { - var re, start, items, integer, frac, pi; - var value, type; + sc.lang.codegen = codegen; - re = /^((?:\d(?:_(?=\d))?)+((?:\.(?:\d(?:_(?=\d))?)+)?(?:e[-+]?(?:\d(?:_(?=\d))?)+)?))(pi)?/; - start = this.index; - items = re.exec(this.source.slice(this.index)); +})(sc); - integer = items[1]; - frac = items[2]; - pi = items[3]; +// src/sc/lang/compiler/node.js +(function(sc) { - type = (frac || pi) ? Token.FloatLiteral : Token.IntegerLiteral; - value = +integer.replace(/(^0+(?=\d)|_)/g, ""); + var Syntax = sc.lang.compiler.Syntax; - if (neg) { - value *= -1; - } + var Node = { + createAssignmentExpression: function(operator, left, right, remain) { + var node = { + type: Syntax.AssignmentExpression, + operator: operator, + left: left, + right: right + }; + if (remain) { + node.remain = remain; + } + return node; + }, + createBinaryExpression: function(operator, left, right) { + var node = { + type: Syntax.BinaryExpression, + operator: operator.value, + left: left, + right: right + }; + if (operator.adverb) { + node.adverb = operator.adverb; + } + return node; + }, + createBlockExpression: function(body) { + return { + type: Syntax.BlockExpression, + body: body + }; + }, + createCallExpression: function(callee, method, args, stamp) { + var node; + + node = { + type: Syntax.CallExpression, + callee: callee, + method: method, + args : args, + }; - if (pi) { - value = value * Math.PI; - } + if (stamp) { + node.stamp = stamp; + } - if (type === Token.FloatLiteral && value === (value|0)) { - value = value + ".0"; - } else { - value = String(value); - } - - this.index += items[0].length; - - return { - type : type, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - }; - - SCParser.prototype.scanPunctuator = function() { - var re, start, items; - - re = /^(\.{1,3}|[(){}[\]:;,~#`]|[-+*\/%<=>!?&|@]+)/; - start = this.index; - items = re.exec(this.source.slice(this.index)); - - if (items) { - this.index += items[0].length; + return node; + }, + createGlobalExpression: function(id) { return { - type : Token.Punctuator, - value: items[0], - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] + type: Syntax.GlobalExpression, + id: id }; - } - - this.throwError({}, Message.UnexpectedToken, this.source.charAt(this.index)); - - this.index = this.length; - - return this.EOFToken(); - }; - - SCParser.prototype.scanStringLiteral = function() { - var source, start; - var length, index; - var quote, ch, value, type; - - source = this.source; - length = this.length; - index = start = this.index; - quote = source.charAt(start); - type = (quote === '"') ? Token.StringLiteral : Token.SymbolLiteral; + }, + createFunctionExpression: function(args, body, closed, partial, blocklist) { + var node; - index += 1; - while (index < length) { - ch = source.charAt(index); - index += 1; - if (ch === quote) { - value = source.substr(start + 1, index - start - 2); - value = value.replace(/\n/g, "\\n"); - this.index = index; - return { - type : type, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - } else if (ch === "\n") { - this.lineNumber += 1; - this.lineStart = index; - } else if (ch === "\\") { - index += 1; + node = { + type: Syntax.FunctionExpression, + body: body + }; + if (args) { + node.args = args; + } + if (closed) { + node.closed = true; + } + if (partial) { + node.partial = true; + } + if (blocklist) { + node.blocklist = true; + } + return node; + }, + createIdentifier: function(name) { + return { + type: Syntax.Identifier, + name: name + }; + }, + createLabel: function(name) { + return { + type: Syntax.Label, + name: name + }; + }, + createListExpression: function(elements, immutable) { + var node = { + type: Syntax.ListExpression, + elements: elements + }; + if (immutable) { + node.immutable = !!immutable; + } + return node; + }, + createLiteral: function(token) { + return { + type: Syntax.Literal, + value: token.value, + valueType: token.type + }; + }, + createObjectExpression: function(elements) { + return { + type: Syntax.ObjectExpression, + elements: elements + }; + }, + createProgram: function(body) { + return { + type: Syntax.Program, + body: body + }; + }, + createThisExpression: function(name) { + return { + type: Syntax.ThisExpression, + name: name + }; + }, + createUnaryExpression: function(operator, arg) { + return { + type: Syntax.UnaryExpression, + operator: operator, + arg: arg + }; + }, + createVariableDeclaration: function(declarations, kind) { + return { + type: Syntax.VariableDeclaration, + declarations: declarations, + kind: kind + }; + }, + createVariableDeclarator: function(id, init) { + var node = { + type: Syntax.VariableDeclarator, + id: id + }; + if (init) { + node.init = init; } + return node; } - - this.index = index; - this.throwError({}, Message.UnexpectedToken, "ILLEGAL"); - - return this.EOFToken(); }; - SCParser.prototype.scanSymbolLiteral = function() { - var re, start, items; - var value; + sc.lang.compiler.node = Node; - re = /^\\([a-z_]\w*)?/i; - start = this.index; - items = re.exec(this.source.slice(this.index)); +})(sc); - value = items[1]; +// src/sc/lang/compiler/marker.js +(function(sc) { - this.index += items[0].length; + function Marker(lexer, locItems) { + this.lexer = lexer; + this.startLocItems = locItems; + this.endLocItems = null; + } - return { - type : Token.SymbolLiteral, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - }; + Marker.create = function(lexer, node) { + var locItems; - SCParser.prototype.scanUnderscore = function() { - var start = this.index; + if (!lexer.opts.loc && !lexer.opts.range) { + return nopMarker; + } - this.index += 1; + if (node) { + locItems = [ node.range[0], node.loc.start.line, node.loc.start.column ]; + } else { + lexer.skipComment(); + locItems = lexer.getLocItems(); + } - return { - type: Token.Identifier, - value: "_", - lineNumber: this.lineNumber, - lineStart: this.lineStart, - range: [ start, this.index ] - }; + return new Marker(lexer, locItems); }; - SCParser.prototype.createAssignmentExpression = function(operator, left, right, remain) { - var node = { - type: Syntax.AssignmentExpression, - operator: operator, - left: left, - right: right - }; - if (remain) { - node.remain = remain; - } - return node; - }; + Marker.prototype.update = function(node) { + var locItems; - SCParser.prototype.createBinaryExpression = function(operator, left, right) { - var node = { - type: Syntax.BinaryExpression, - operator: operator.value, - left: left, - right: right - }; - if (operator.adverb) { - node.adverb = operator.adverb; + if (node) { + locItems = [ node.range[1], node.loc.end.line, node.loc.end.column ]; + } else { + locItems = this.lexer.getLocItems(); } - return node; - }; + this.endLocItems = locItems; - SCParser.prototype.createBlockExpression = function(body) { - return { - type: Syntax.BlockExpression, - body: body - }; + return this; }; - SCParser.prototype.createCallExpression = function(callee, method, args, stamp) { - var node; + Marker.prototype.apply = function(node, force) { + var startLocItems, endLocItems; - node = { - type: Syntax.CallExpression, - callee: callee, - method: method, - args : args, - }; + if (Array.isArray(node)) { + return node; + } - if (stamp) { - node.stamp = stamp; + if (force || !node.range || !node.loc) { + startLocItems = this.startLocItems; + if (this.endLocItems) { + endLocItems = this.endLocItems; + } else { + endLocItems = this.startLocItems; + } + /* istanbul ignore else */ + if (this.lexer.opts.range) { + node.range = [ startLocItems[0], endLocItems[0] ]; + } + /* istanbul ignore else */ + if (this.lexer.opts.loc) { + node.loc = { + start: { + line : startLocItems[1], + column: startLocItems[2] + }, + end: { + line : endLocItems[1], + column: endLocItems[2] + } + }; + } } return node; }; - SCParser.prototype.createGlobalExpression = function(id) { - return { - type: Syntax.GlobalExpression, - id: id - }; + var nopMarker = { + apply: function(node) { + return node; + }, + update: function() { + return this; + } }; - SCParser.prototype.createFunctionExpression = function(args, body, closed, partial, blocklist) { - var node; + sc.lang.compiler.marker = Marker; - node = { - type: Syntax.FunctionExpression, - body: body - }; - if (args) { - node.args = args; - } - if (closed) { - node.closed = true; - } - if (partial) { - node.partial = true; - } - if (blocklist) { - node.blocklist = true; - } - return node; - }; +})(sc); - SCParser.prototype.createIdentifier = function(name) { - return { - type: Syntax.Identifier, - name: name - }; - }; +// src/sc/lang/compiler/lexer.js +(function(sc) { - SCParser.prototype.createLabel = function(name) { - return { - type: Syntax.Label, - name: name - }; - }; + var Token = sc.lang.compiler.Token; + var Message = sc.lang.compiler.Message; + var Keywords = sc.lang.compiler.Keywords; - SCParser.prototype.createListExpression = function(elements, immutable) { - var node = { - type: Syntax.ListExpression, - elements: elements - }; - if (immutable) { - node.immutable = !!immutable; + function Lexer(source, opts) { + /* istanbul ignore next */ + if (typeof source !== "string") { + if (typeof source === "undefined") { + source = ""; + } + source = String(source); } - return node; - }; + source = source.replace(/\r\n?/g, "\n"); + this.source = source; + this.opts = opts; + this.length = source.length; + this.index = 0; + this.lineNumber = this.length ? 1 : 0; + this.lineStart = 0; + this.reverted = null; + this.tokens = opts.tokens ? [] : null; + this.errors = opts.tolerant ? [] : null; - SCParser.prototype.createLiteral = function(token) { - return { - type: Syntax.Literal, - value: token.value, - valueType: token.type - }; - }; + this.peek(); + } + + function char2num(ch) { + var n = ch.charCodeAt(0); - SCParser.prototype.createLocationMarker = function() { - if (this.opts.loc || this.opts.range) { - this.skipComment(); - return new LocationMarker(this); + if (48 <= n && n <= 57) { + return n - 48; } - }; + if (65 <= n && n <= 90) { + return n - 55; + } + return n - 87; // if (97 <= n && n <= 122) + } - SCParser.prototype.createObjectExpression = function(elements) { - return { - type: Syntax.ObjectExpression, - elements: elements - }; - }; + function isNumber(ch) { + return "0" <= ch && ch <= "9"; + } - SCParser.prototype.createProgram = function(body) { - return { - type: Syntax.Program, - body: body - }; - }; + Lexer.prototype.skipComment = function() { + var source = this.source; + var length = this.length; + var index = this.index; + var ch; - SCParser.prototype.createThisExpression = function(name) { - return { - type: Syntax.ThisExpression, - name: name - }; - }; + LOOP: while (index < length) { + ch = source.charAt(index); - SCParser.prototype.createUnaryExpression = function(operator, arg) { - return { - type: Syntax.UnaryExpression, - operator: operator, - arg: arg - }; - }; + if (ch === " " || ch === "\t") { + index += 1; + continue; + } - SCParser.prototype.createVariableDeclaration = function(declarations, kind) { - return { - type: Syntax.VariableDeclaration, - declarations: declarations, - kind: kind - }; - }; - - SCParser.prototype.createVariableDeclarator = function(id, init) { - var node = { - type: Syntax.VariableDeclarator, - id: id - }; - if (init) { - node.init = init; - } - return node; - }; + if (ch === "\n") { + index += 1; + this.lineNumber += 1; + this.lineStart = index; + continue; + } - SCParser.prototype.isClassName = function(node) { - var name, ch; + if (ch === "/") { + ch = source.charAt(index + 1); + if (ch === "/") { + index = this.skipLineComment(index + 2); + continue; + } + if (ch === "*") { + index = this.skipBlockComment(index + 2); + continue; + } + } - if (node.type === Syntax.Identifier) { - name = node.value || node.name; - ch = name.charAt(0); - return "A" <= ch && ch <= "Z"; + break; } - return false; + this.index = index; }; - SCParser.prototype.isKeyword = function(value) { - return !!Keywords[value] || false; - }; + Lexer.prototype.skipLineComment = function(index) { + var source = this.source; + var length = this.length; + var ch; - SCParser.prototype.isLeftHandSide = function(expr) { - switch (expr.type) { - case Syntax.Identifier: - case Syntax.GlobalExpression: - return true; + while (index < length) { + ch = source.charAt(index); + index += 1; + if (ch === "\n") { + this.lineNumber += 1; + this.lineStart = index; + break; + } } - return false; - }; - - SCParser.prototype._match = function(value, type) { - var token = this.lookahead; - return token.type === type && token.value === value; - }; - SCParser.prototype.match = function(value) { - return this._match(value, Token.Punctuator); + return index; }; - SCParser.prototype.matchKeyword = function(value) { - return this._match(value, Token.Keyword); - }; + Lexer.prototype.skipBlockComment = function(index) { + var source = this.source; + var length = this.length; + var ch, depth; - SCParser.prototype.matchAny = function(list) { - var value, i, imax; + depth = 1; + while (index < length) { + ch = source.charAt(index); - if (this.lookahead.type === Token.Punctuator) { - value = this.lookahead.value; - for (i = 0, imax = list.length; i < imax; ++i) { - if (list[i] === value) { - return value; + if (ch === "\n") { + this.lineNumber += 1; + this.lineStart = index; + } else { + ch = ch + source.charAt(index + 1); + if (ch === "/*") { + depth += 1; + index += 1; + } else if (ch === "*/") { + depth -= 1; + index += 1; + if (depth === 0) { + return index + 1; + } } } - } - return null; - }; - - SCParser.prototype.withScope = function(fn) { - var result; - - this.scope.begin(); - result = fn.call(this); - this.scope.end(); + index += 1; + } + this.throwError({}, Message.UnexpectedToken, "ILLEGAL"); - return result; + return index; }; - // 1. Program - SCParser.prototype.parseProgram = function() { - var node; + Lexer.prototype.collectToken = function() { + var loc, token, source, t; this.skipComment(); - this.markStart(); - this.peek(); - - node = this.withScope(function() { - var body; - body = this.parseFunctionBody(""); - if (body.length === 1 && body[0].type === Syntax.BlockExpression) { - body = body[0].body; + loc = { + start: { + line: this.lineNumber, + column: this.index - this.lineStart } + }; - return this.createProgram(body); - }); - - return this.markEnd(node); - }; - - // 2. Function - // 2.1 Function Expression - SCParser.prototype.parseFunctionExpression = function(closed, blocklist) { - var node; + token = this.advance(); - node = this.withScope(function() { - var args, body; + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart + }; - if (this.match("|")) { - args = this.parseFunctionArgument("|"); - } else if (this.matchKeyword("arg")) { - args = this.parseFunctionArgument(";"); + if (token.type !== Token.EOF) { + source = this.source; + t = { + type: token.type, + value: source.slice(token.range[0], token.range[1]) + }; + if (this.opts.range) { + t.range = [ token.range[0], token.range[1] ]; } - body = this.parseFunctionBody("}"); - - return this.createFunctionExpression(args, body, closed, false, blocklist); - }); + if (this.opts.loc) { + t.loc = loc; + } + this.tokens.push(t); + } - return node; + return token; }; - // 2.2 Function Argument - SCParser.prototype.parseFunctionArgument = function(expect) { - var args = { list: [] }, lookahead; + Lexer.prototype.advance = function() { + var ch, token; - this.lex(); + this.skipComment(); - if (!this.match("...")) { - do { - args.list.push(this.parseFunctionArgumentElement()); - if (!this.match(",")) { - break; - } - this.lex(); - } while (this.lookahead.type !== Token.EOF); + if (this.length <= this.index) { + return this.EOFToken(); } - if (this.match("...")) { - this.lex(); - lookahead = this.lookahead; - args.remain = this.parseVariableIdentifier(); - this.scope.add("arg", args.remain.name); + ch = this.source.charAt(this.index); + + // Symbol literal starts with back slash + if (ch === "\\") { + return this.scanSymbolLiteral(); } - this.expect(expect); + // Char literal starts with dollar + if (ch === "$") { + return this.scanCharLiteral(); + } - return args; - }; + // String literal starts with single quote or double quote + if (ch === "'" || ch === "\"") { + return this.scanStringLiteral(); + } - SCParser.prototype._parseArgVarElement = function(type, method) { - var init = null, id; + // for partial application + if (ch === "_") { + return this.scanUnderscore(); + } - this.skipComment(); - this.markStart(); - id = this.parseVariableIdentifier(); - this.scope.add(type, id.name); + if (ch === "-") { + token = this.scanNegativeNumericLiteral(); + if (token) { + return token; + } + } - if (this.match("=")) { - this.lex(); - init = this[method](); + // Identifier starts with alphabet + if (("A" <= ch && ch <= "Z") || ("a" <= ch && ch <= "z")) { + return this.scanIdentifier(); } - return this.markEnd(this.createVariableDeclarator(id, init)); - }; + // Number literal starts with number + if (isNumber(ch)) { + return this.scanNumericLiteral(); + } - SCParser.prototype.parseFunctionArgumentElement = function() { - var node = this._parseArgVarElement("arg", "parseArgumentableValue"); + return this.scanPunctuator(); + }; - if (node.init && !isValidArgumentValue(node.init)) { - this.throwUnexpected(this.lookahead); + Lexer.prototype.expect = function(value) { + var token = this.lex(); + if (token.type !== Token.Punctuator || token.value !== value) { + this.throwUnexpected(token, value); } - - return node; }; - // 2.3 Function Body - SCParser.prototype.parseFunctionBody = function(match) { - var elements = []; + Lexer.prototype.peek = function() { + var index, lineNumber, lineStart; - while (this.matchKeyword("var")) { - elements.push(this.parseVariableDeclaration()); - } + index = this.index; + lineNumber = this.lineNumber; + lineStart = this.lineStart; - while (this.lookahead.type !== Token.EOF && !this.match(match)) { - elements.push(this.parseExpression()); - if (this.lookahead.type !== Token.EOF && !this.match(match)) { - this.expect(";"); - } else { - break; - } + if (this.opts.tokens) { + this.lookahead = this.collectToken(); + } else { + this.lookahead = this.advance(); } - return elements; + this.index = index; + this.lineNumber = lineNumber; + this.lineStart = lineStart; }; - // 3. Variable Declarations - SCParser.prototype.parseVariableDeclaration = function() { - var declaration; + Lexer.prototype.lex = function(saved) { + var that = this; + var token = this.lookahead; - this.skipComment(); - this.markStart(); + if (saved) { + saved = [ this.lookahead, this.index, this.lineNumber, this.lineStart ]; + } - this.lex(); // var + this.index = token.range[1]; + this.lineNumber = token.lineNumber; + this.lineStart = token.lineStart; - declaration = this.markEnd( - this.createVariableDeclaration( - this.parseVariableDeclarationList(), "var" - ) - ); + if (this.opts.tokens) { + this.lookahead = this.collectToken(); + } else { + this.lookahead = this.advance(); + } - this.expect(";"); + this.index = token.range[1]; + this.lineNumber = token.lineNumber; + this.lineStart = token.lineStart; - return declaration; + if (saved) { + token.revert = function() { + that.lookahead = saved[0]; + that.index = saved[1]; + that.lineNumber = saved[2]; + that.lineStart = saved[3]; + if (that.tokens) { + that.tokens.pop(); + } + }; + } + + return token; }; - SCParser.prototype.parseVariableDeclarationList = function() { - var list = []; + Lexer.prototype.EOFToken = function() { + return { + type: Token.EOF, + value: "", + lineNumber: this.lineNumber, + lineStart: this.lineStart, + range: [ this.index, this.index ] + }; + }; - do { - list.push(this.parseVariableDeclarationElement()); - if (!this.match(",")) { - break; - } - this.lex(); - } while (this.lookahead.type !== Token.EOF); + Lexer.prototype.scanCharLiteral = function() { + var start, value; - return list; - }; + start = this.index; + value = this.source.charAt(this.index + 1); - SCParser.prototype.parseVariableDeclarationElement = function() { - return this._parseArgVarElement("var", "parseAssignmentExpression"); - }; + this.index += 2; - // 4. Expression - SCParser.prototype.parseExpression = function(node) { - return this.parseAssignmentExpression(node); + return { + type : Token.CharLiteral, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; }; - // 4.1 Expressions - SCParser.prototype.parseExpressions = function(node) { - var nodes = []; + Lexer.prototype.scanIdentifier = function() { + var source = this.source.slice(this.index); + var start = this.index; + var value, type; - if (node) { - nodes.push(node); - this.lex(); - } + value = /^[a-zA-Z][a-zA-Z0-9_]*/.exec(source)[0]; - while (this.lookahead.type !== Token.EOF && !this.matchAny([ ",", ")", "]", ".." ])) { - this.skipComment(); - this.markStart(); - node = this.parseAssignmentExpression(); - this.markEnd(node); - nodes.push(node); - if (this.match(";")) { - this.lex(); + this.index += value.length; + + if (this.source.charAt(this.index) === ":") { + this.index += 1; + return { + type: Token.Label, + value: value, + lineNumber: this.lineNumber, + lineStart: this.lineStart, + range: [ start, this.index ] + }; + } else if (this.isKeyword(value)) { + type = Token.Keyword; + } else { + switch (value) { + case "inf": + type = Token.FloatLiteral; + value = "Infinity"; + break; + case "pi": + type = Token.FloatLiteral; + value = String(Math.PI); + break; + case "nil": + type = Token.NilLiteral; + value = "null"; + break; + case "true": + type = Token.TrueLiteral; + break; + case "false": + type = Token.FalseLiteral; + break; + default: + type = Token.Identifier; + break; } } - if (nodes.length === 0) { - this.throwUnexpected(this.lookahead); - } + return { + type: type, + value: value, + lineNumber: this.lineNumber, + lineStart: this.lineStart, + range: [ start, this.index ] + }; + }; - return nodes.length === 1 ? nodes[0] : nodes; + Lexer.prototype.scanNumericLiteral = function(neg) { + return this.scanNAryNumberLiteral(neg) || this.scanDecimalNumberLiteral(neg); }; - // 4.2 Assignment Expression - SCParser.prototype.parseAssignmentExpression = function(node) { - var token; + Lexer.prototype.scanNegativeNumericLiteral = function() { + var token, ch1, ch2, ch3; + var start, value = null; - if (node) { - return this.parsePartialExpression(node); + start = this.index; + ch1 = this.source.charAt(this.index + 1); + + if (isNumber(ch1)) { + this.index += 1; + token = this.scanNumericLiteral(true); + token.range[0] = start; + return token; } - this.skipComment(); - this.markStart(); + ch2 = this.source.charAt(this.index + 2); + ch3 = this.source.charAt(this.index + 3); - if (this.match("#")) { - token = this.lex(true); - if (this.matchAny([ "[", "{" ])) { - token.restore(); - } else { - node = this.parseDestructuringAssignmentExpression(); - } + if (ch1 + ch2 === "pi") { + this.index += 3; + value = String(-Math.PI); + } else if (ch1 + ch2 + ch3 === "inf") { + this.index += 4; + value = "-Infinity"; } - if (!node) { - node = this.parseSimpleAssignmentExpression(); + if (value !== null) { + return { + type : Token.FloatLiteral, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; } - return this.markEnd(node); + return null; }; - SCParser.prototype.parseDestructuringAssignmentExpression = function() { - var node, left, right, token; + Lexer.prototype.scanNAryNumberLiteral = function(neg) { + var re, start, items; + var base, integer, frac, pi; + var value, type; - left = this.parseDestructuringAssignmentLeft(); - token = this.lookahead; - this.expect("="); + re = /^(\d+)r((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+)(?:\.((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+))?/; + start = this.index; + items = re.exec(this.source.slice(this.index)); - right = this.parseAssignmentExpression(); - node = this.createAssignmentExpression( - token.value, left.list, right, left.remain - ); + if (!items) { + return; + } - return node; - }; + base = items[1].replace(/^0+(?=\d)/g, "")|0; + integer = items[2].replace(/(^0+(?=\d)|_)/g, ""); + frac = items[3] && items[3].replace(/_/g, ""); - SCParser.prototype.parseSimpleAssignmentExpression = function() { - var node, left, right, token; + if (!frac && base < 26 && integer.substr(-2) === "pi") { + integer = integer.substr(0, integer.length - 2); + pi = true; + } - node = left = this.parsePartialExpression(); + type = Token.IntegerLiteral; + value = this.calcNBasedInteger(integer, base); - if (this.match("=")) { - if (node.type === Syntax.CallExpression) { - token = this.lex(); - right = this.parseAssignmentExpression(); - left.method.name = this.getAssignMethod(left.method.name); - left.args.list = node.args.list.concat(right); - /* istanbul ignore else */ - if (this.opts.range) { - left.range[1] = this.index; - } - /* istanbul ignore else */ - if (this.opts.loc) { - left.loc.end = { - line: this.lineNumber, - column: this.index - this.lineStart - }; - } - node = left; - } else { - // TODO: fix - if (!this.isLeftHandSide(left)) { - this.throwError({}, Message.InvalidLHSInAssignment); - } + if (frac) { + type = Token.FloatLiteral; + value += this.calcNBasedFrac(frac, base); + } - token = this.lex(); - right = this.parseAssignmentExpression(); - node = this.createAssignmentExpression( - token.value, left, right - ); - } + if (neg) { + value *= -1; } - return node; + if (pi) { + type = Token.FloatLiteral; + value = value * Math.PI; + } + + if (type === Token.FloatLiteral && value === (value|0)) { + value = value + ".0"; + } else { + value = String(value); + } + + this.index += items[0].length; + + return { + type : type, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; }; - SCParser.prototype.getAssignMethod = function(methodName) { - switch (methodName) { - case "at": - return "put"; - case "copySeries": - return "putSeries"; + Lexer.prototype.char2num = function(ch, base) { + var x = char2num(ch, base); + if (x >= base) { + this.throwError({}, Message.UnexpectedToken, ch); } - return methodName + "_"; + return x; }; - SCParser.prototype.parseDestructuringAssignmentLeft = function() { - var params = { list: [] }, element; + Lexer.prototype.calcNBasedInteger = function(integer, base) { + var value, i, imax; - do { - element = this.parseLeftHandSideExpression(); - if (!this.isLeftHandSide(element)) { - this.throwError({}, Message.InvalidLHSInAssignment); - } - params.list.push(element); - if (this.match(",")) { - this.lex(); - } else if (this.match("...")) { - this.lex(); - params.remain = this.parseLeftHandSideExpression(); - if (!this.isLeftHandSide(params.remain)) { - this.throwError({}, Message.InvalidLHSInAssignment); - } - break; - } - } while (this.lookahead.type !== Token.EOF && !this.match("=")); + for (i = value = 0, imax = integer.length; i < imax; ++i) { + value *= base; + value += this.char2num(integer[i], base); + } - return params; + return value; }; - // 4.3 Partial Expression - SCParser.prototype.parsePartialExpression = function(node) { - var underscore, x, y; - - if (this.state.innerElements) { - node = this.parseBinaryExpression(node); - } else { - underscore = this.state.underscore; - this.state.underscore = []; - - node = this.parseBinaryExpression(node); + Lexer.prototype.calcNBasedFrac = function(frac, base) { + var value, i, imax; - if (this.state.underscore.length) { - node = this.withScope(function() { - var args, i, imax; + for (i = value = 0, imax = frac.length; i < imax; ++i) { + value += this.char2num(frac[i], base) * Math.pow(base, -(i + 1)); + } - args = new Array(this.state.underscore.length); - for (i = 0, imax = args.length; i < imax; ++i) { - x = this.state.underscore[i]; - y = this.createVariableDeclarator(x); - /* istanbul ignore else */ - if (x.range) { - y.range = x.range; - } - /* istanbul ignore else */ - if (x.loc) { - y.loc = x.loc; - } - args[i] = y; - this.scope.add("arg", this.state.underscore[i].name); - } + return value; + }; - return this.createFunctionExpression( - { list: args }, [ node ], false, true, false - ); - }); - } + Lexer.prototype.scanDecimalNumberLiteral = function(neg) { + var re, start, items, integer, frac, pi; + var value, type; - this.state.underscore = underscore; - } + re = /^((?:\d(?:_(?=\d))?)+((?:\.(?:\d(?:_(?=\d))?)+)?(?:e[-+]?(?:\d(?:_(?=\d))?)+)?))(pi)?/; + start = this.index; + items = re.exec(this.source.slice(this.index)); - return node; - }; + integer = items[1]; + frac = items[2]; + pi = items[3]; - // 4.4 Conditional Expression - // 4.5 Binary Expression - SCParser.prototype.parseBinaryExpression = function(node) { - var marker, left, token, prec; + type = (frac || pi) ? Token.FloatLiteral : Token.IntegerLiteral; + value = +integer.replace(/(^0+(?=\d)|_)/g, ""); - this.skipComment(); + if (neg) { + value *= -1; + } - marker = this.createLocationMarker(); - left = this.parseUnaryExpression(node); - token = this.lookahead; + if (pi) { + value = value * Math.PI; + } - prec = this.binaryPrecedence(token); - if (prec === 0) { - if (node) { - return this.parseUnaryExpression(node); - } - return left; + if (type === Token.FloatLiteral && value === (value|0)) { + value = value + ".0"; + } else { + value = String(value); } - this.lex(); - token.prec = prec; - token.adverb = this.parseAdverb(); + this.index += items[0].length; - return this.sortByBinaryPrecedence(left, token, marker); + return { + type : type, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; }; - SCParser.prototype.sortByBinaryPrecedence = function(left, operator, marker) { - var expr; - var prec, token; - var markers, i; - var right, stack; + Lexer.prototype.scanPunctuator = function() { + var re, start, items; - markers = [ marker, this.createLocationMarker() ]; - right = this.parseUnaryExpression(); + re = /^(\.{1,3}|[(){}[\]:;,~#`]|[-+*\/%<=>!?&|@]+)/; + start = this.index; + items = re.exec(this.source.slice(this.index)); - stack = [ left, operator, right ]; + if (items) { + this.index += items[0].length; + return { + type : Token.Punctuator, + value: items[0], + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + } - while ((prec = this.binaryPrecedence(this.lookahead)) > 0) { - // Reduce: make a binary expression from the three topmost entries. - while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { - right = stack.pop(); - operator = stack.pop(); - left = stack.pop(); - expr = this.createBinaryExpression(operator, left, right); - markers.pop(); + this.throwError({}, Message.UnexpectedToken, this.source.charAt(this.index)); - marker = markers.pop(); - /* istanbul ignore else */ - if (marker) { - marker.apply(expr); - } - stack.push(expr); - markers.push(marker); - } + this.index = this.length; - // Shift. - token = this.lex(); - token.prec = prec; - token.adverb = this.parseAdverb(); + return this.EOFToken(); + }; - stack.push(token); - markers.push(this.createLocationMarker()); - expr = this.parseUnaryExpression(); - stack.push(expr); - } + Lexer.prototype.scanStringLiteral = function() { + var source, start; + var length, index; + var quote, ch, value, type; - // Final reduce to clean-up the stack. - i = stack.length - 1; - expr = stack[i]; - markers.pop(); - while (i > 1) { - expr = this.createBinaryExpression(stack[i - 1], stack[i - 2], expr); - i -= 2; - marker = markers.pop(); - /* istanbul ignore else */ - if (marker) { - marker.apply(expr); + source = this.source; + length = this.length; + index = start = this.index; + quote = source.charAt(start); + type = (quote === '"') ? Token.StringLiteral : Token.SymbolLiteral; + + index += 1; + while (index < length) { + ch = source.charAt(index); + index += 1; + if (ch === quote) { + value = source.substr(start + 1, index - start - 2); + value = value.replace(/\n/g, "\\n"); + this.index = index; + return { + type : type, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + } else if (ch === "\n") { + this.lineNumber += 1; + this.lineStart = index; + } else if (ch === "\\") { + index += 1; } } - return expr; - }; + this.index = index; + this.throwError({}, Message.UnexpectedToken, "ILLEGAL"); - SCParser.prototype.binaryPrecedence = function(token) { - var table, prec = 0; + return this.EOFToken(); + }; - if (this.opts.binaryPrecedence) { - if (typeof this.opts.binaryPrecedence === "object") { - table = this.opts.binaryPrecedence; - } else { - table = binaryPrecedenceDefaults; - } - } else { - table = {}; - } - switch (token.type) { - case Token.Punctuator: - if (token.value !== "=") { - if (table.hasOwnProperty(token.value)) { - prec = table[token.value]; - } else if (/^[-+*\/%<=>!?&|@]+$/.test(token.value)) { - prec = 255; - } - } - break; - case Token.Label: - prec = 255; - break; - } + Lexer.prototype.scanSymbolLiteral = function() { + var re, start, items; + var value; - return prec; - }; + re = /^\\([a-z_]\w*)?/i; + start = this.index; + items = re.exec(this.source.slice(this.index)); - SCParser.prototype.parseAdverb = function() { - var adverb, lookahead; + value = items[1]; - if (this.match(".")) { - this.lex(); + this.index += items[0].length; - lookahead = this.lookahead; - adverb = this.parsePrimaryExpression(); + return { + type : Token.SymbolLiteral, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + }; - if (adverb.type === Syntax.Literal) { - return adverb; - } + Lexer.prototype.scanUnderscore = function() { + var start = this.index; - if (adverb.type === Syntax.Identifier) { - adverb.type = Syntax.Literal; - adverb.value = adverb.name; - adverb.valueType = Token.SymbolLiteral; - delete adverb.name; - return adverb; - } + this.index += 1; - this.throwUnexpected(lookahead); - } + return { + type: Token.Identifier, + value: "_", + lineNumber: this.lineNumber, + lineStart: this.lineStart, + range: [ start, this.index ] + }; + }; - return null; + Lexer.prototype.isKeyword = function(value) { + return !!Keywords[value] || false; }; - // 4.6 Unary Expressions - SCParser.prototype.parseUnaryExpression = function(node) { - var token, expr; + Lexer.prototype.match = function(value) { + return this.lookahead.value === value; + }; - this.markStart(); + Lexer.prototype.matchAny = function(list) { + var value, i, imax; - if (this.match("`")) { - token = this.lex(); - expr = this.parseUnaryExpression(); - expr = this.createUnaryExpression(token.value, expr); - } else { - expr = this.parseLeftHandSideExpression(node); + value = this.lookahead.value; + for (i = 0, imax = list.length; i < imax; ++i) { + if (list[i] === value) { + return value; + } } - return this.markEnd(expr); + return null; }; - // 4.7 LeftHandSide Expressions - SCParser.prototype.parseLeftHandSideExpression = function(node) { - var marker, expr, prev, lookahead; - var blocklist, stamp; + Lexer.prototype.getLocItems = function() { + return [ this.index, this.lineNumber, this.index - this.lineStart ]; + }; - this.skipComment(); + Lexer.prototype.throwError = function(token, messageFormat) { + var args, message; + var error, index, lineNumber, column; + var prev; - marker = this.createLocationMarker(); - expr = this.parsePrimaryExpression(node); + args = Array.prototype.slice.call(arguments, 2); + message = messageFormat.replace(/%(\d)/g, function(whole, index) { + return args[index]; + }); - blocklist = false; + if (typeof token.lineNumber === "number") { + index = token.range[0]; + lineNumber = token.lineNumber; + column = token.range[0] - token.lineStart + 1; + } else { + index = this.index; + lineNumber = this.lineNumber; + column = this.index - this.lineStart + 1; + } - while ((stamp = this.matchAny([ "(", "{", "#", "[", "." ])) !== null) { - lookahead = this.lookahead; - if ((prev === "{" && (stamp !== "#" && stamp !== "{")) || (prev === "(" && stamp === "(")) { - this.throwUnexpected(lookahead); - } - switch (stamp) { - case "(": - expr = this.parseLeftHandSideParenthesis(expr); - break; - case "#": - expr = this.parseLeftHandSideClosedBrace(expr); - break; - case "{": - expr = this.parseLeftHandSideBrace(expr); - break; - case "[": - expr = this.parseLeftHandSideBracket(expr); - break; - case ".": - expr = this.parseLeftHandSideDot(expr); - break; - } + error = new Error("Line " + lineNumber + ": " + message); + error.index = index; + error.lineNumber = lineNumber; + error.column = column; + error.description = message; - /* istanbul ignore else */ - if (marker) { - marker.apply(expr); + if (this.errors) { + prev = this.errors[this.errors.length - 1]; + if (!(prev && error.index <= prev.index)) { + this.errors.push(error); } - prev = stamp; + } else { + throw error; } - - return expr; }; - SCParser.prototype.parseLeftHandSideParenthesis = function(expr) { - if (this.isClassName(expr)) { - return this.parseLeftHandSideClassNew(expr); + Lexer.prototype.throwUnexpected = function(token) { + switch (token.type) { + case Token.EOF: + this.throwError(token, Message.UnexpectedEOS); + break; + case Token.FloatLiteral: + case Token.IntegerLiteral: + this.throwError(token, Message.UnexpectedNumber); + break; + case Token.CharLiteral: + this.throwError(token, Message.UnexpectedChar); + break; + case Token.StringLiteral: + this.throwError(token, Message.UnexpectedString); + break; + case Token.SymbolLiteral: + this.throwError(token, Message.UnexpectedSymbol); + break; + case Token.Identifier: + this.throwError(token, Message.UnexpectedIdentifier); + break; + default: + this.throwError(token, Message.UnexpectedToken, token.value); + break; } - - return this.parseLeftHandSideMethodCall(expr); }; - SCParser.prototype.parseLeftHandSideClassNew = function(expr) { - var method, args; + sc.lang.compiler.lexer = Lexer; - method = this.markTouch(this.createIdentifier("new")); - args = this.parseCallArgument(); +})(sc); + +// src/sc/lang/compiler/parser.js +(function(sc) { - return this.createCallExpression(expr, method, args, "("); + var parser = {}; + + var Token = sc.lang.compiler.Token; + var Syntax = sc.lang.compiler.Syntax; + var Message = sc.lang.compiler.Message; + var Keywords = sc.lang.compiler.Keywords; + var Lexer = sc.lang.compiler.lexer; + var Marker = sc.lang.compiler.marker; + var Node = sc.lang.compiler.node; + + var binaryPrecedenceDefaults = { + "?" : 1, + "??" : 1, + "!?" : 1, + "->" : 2, + "||" : 3, + "&&" : 4, + "|" : 5, + "&" : 6, + "==" : 7, + "!=" : 7, + "===": 7, + "!==": 7, + "<" : 8, + ">" : 8, + "<=" : 8, + ">=" : 8, + "<<" : 9, + ">>" : 9, + "+>>": 9, + "+" : 10, + "-" : 10, + "*" : 11, + "/" : 11, + "%" : 11, + "!" : 12, }; - SCParser.prototype.parseLeftHandSideMethodCall = function(expr) { - var method, args, lookahead; + var Scope = sc.lang.compiler.Scope({ + begin: function() { + var declared = this.getDeclaredVariable(); - if (expr.type !== Syntax.Identifier) { - this.throwUnexpected(this.lookahead); + this.stack.push({ + vars: {}, + args: {}, + declared: declared + }); } + }); - lookahead = this.lookahead; - args = this.parseCallArgument(); + function SCParser(source, opts) { + var binaryPrecedence; - method = expr; - expr = args.list.shift(); + this.opts = opts || /* istanbul ignore next */ {}; + this.lexer = new Lexer(source, opts); + this.scope = new Scope(this); + this.state = { + closedFunction: false, + disallowGenerator: false, + innerElements: false, + immutableList: false, + underscore: [] + }; - if (!expr) { - if (args.expand) { - expr = args.expand; - delete args.expand; + if (this.opts.binaryPrecedence) { + if (typeof this.opts.binaryPrecedence === "object") { + binaryPrecedence = this.opts.binaryPrecedence; } else { - this.throwUnexpected(lookahead); + binaryPrecedence = binaryPrecedenceDefaults; } } - // max(0, 1) -> 0.max(1) - return this.createCallExpression(expr, method, args, "("); - }; + this.binaryPrecedence = binaryPrecedence || {}; + } - SCParser.prototype.parseLeftHandSideClosedBrace = function(expr) { - this.lex(); - if (!this.match("{")) { - this.throwUnexpected(this.lookahead); + Object.defineProperty(SCParser.prototype, "lookahead", { + get: function() { + return this.lexer.lookahead; } + }); - this.state.closedFunction = true; - expr = this.parseLeftHandSideBrace(expr); - this.state.closedFunction = false; + SCParser.prototype.lex = function() { + return this.lexer.lex(); + }; - return expr; + SCParser.prototype.expect = function(value) { + return this.lexer.expect(value); }; - SCParser.prototype.parseLeftHandSideBrace = function(expr) { - var method, lookahead, disallowGenerator, node; + SCParser.prototype.match = function(value) { + return this.lexer.match(value); + }; - if (expr.type === Syntax.CallExpression && expr.stamp && expr.stamp !== "(") { - this.throwUnexpected(this.lookahead); - } - if (expr.type === Syntax.Identifier) { - if (this.isClassName(expr)) { - method = this.markTouch(this.createIdentifier("new")); - expr = this.createCallExpression(expr, method, { list: [] }, "{"); - } else { - expr = this.createCallExpression(null, expr, { list: [] }); - } - } - lookahead = this.lookahead; - disallowGenerator = this.state.disallowGenerator; - this.state.disallowGenerator = true; - node = this.parseBraces(true); - this.state.disallowGenerator = disallowGenerator; + SCParser.prototype.matchAny = function(list) { + return this.lexer.matchAny(list); + }; - // TODO: refactoring - if (expr.callee === null) { - expr.callee = node; - node = expr; - } else { - expr.args.list.push(node); - } + SCParser.prototype.throwError = function() { + return this.lexer.throwError.apply(this.lexer, arguments); + }; - return expr; + SCParser.prototype.throwUnexpected = function(token) { + return this.lexer.throwUnexpected(token); }; - SCParser.prototype.parseLeftHandSideBracket = function(expr) { - if (expr.type === Syntax.CallExpression && expr.stamp === "(") { - this.throwUnexpected(this.lookahead); - } + SCParser.prototype.withScope = function(fn) { + var result; - if (this.isClassName(expr)) { - expr = this.parseLeftHandSideNewFrom(expr); - } else { - expr = this.parseLeftHandSideListAt(expr); - } + this.scope.begin(); + result = fn.call(this); + this.scope.end(); - return expr; + return result; }; - SCParser.prototype.parseLeftHandSideNewFrom = function(expr) { - var node, method; + SCParser.prototype.parse = function() { + return this.parseProgram(); + }; - method = this.markTouch(this.createIdentifier("newFrom")); + // 1. Program + SCParser.prototype.parseProgram = function() { + var node, marker; - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); + + node = this.withScope(function() { + var body; + + body = this.parseFunctionBody(""); + if (body.length === 1 && body[0].type === Syntax.BlockExpression) { + body = body[0].body; + } - node = this.markEnd(this.parseListInitialiser()); + return Node.createProgram(body); + }); - return this.createCallExpression(expr, method, { list: [ node ] }, "["); + return marker.update().apply(node); }; - SCParser.prototype.parseLeftHandSideListAt = function(expr) { - var indexes, method; + // 2. Function + // 2.1 Function Expression + SCParser.prototype.parseFunctionExpression = function(closed, blocklist) { + var node; - method = this.markTouch(this.createIdentifier("at")); + node = this.withScope(function() { + var args, body; - indexes = this.parseListIndexer(); - if (indexes) { - if (indexes.length === 3) { - method.name = "copySeries"; + if (this.match("|")) { + args = this.parseFunctionArgument("|"); + } else if (this.match("arg")) { + args = this.parseFunctionArgument(";"); } - } else { - this.throwUnexpected(this.lookahead); - } + body = this.parseFunctionBody("}"); + + return Node.createFunctionExpression(args, body, closed, false, blocklist); + }); - return this.createCallExpression(expr, method, { list: indexes }, "["); + return node; }; - SCParser.prototype.parseLeftHandSideDot = function(expr) { - var method, args; + // 2.2 Function Argument + SCParser.prototype.parseFunctionArgument = function(expect) { + var args = { list: [] }; this.lex(); - if (this.match("(")) { - // expr.() - return this.parseLeftHandSideDotValue(expr); - } else if (this.match("[")) { - // expr.[0] - return this.parseLeftHandSideDotBracket(expr); - } - - method = this.parseProperty(); - if (this.match("(")) { - // expr.method(args) - args = this.parseCallArgument(); - return this.createCallExpression(expr, method, args); + if (!this.match("...")) { + do { + args.list.push(this.parseFunctionArgumentElement()); + if (!this.match(",")) { + break; + } + this.lex(); + } while (this.lookahead.type !== Token.EOF); } - // expr.method - return this.createCallExpression(expr, method, { list: [] }); - }; - - SCParser.prototype.parseLeftHandSideDotValue = function(expr) { - var method, args; - - method = this.markTouch(this.createIdentifier("value")); - args = this.parseCallArgument(); - - return this.createCallExpression(expr, method, args, "."); - }; - - SCParser.prototype.parseLeftHandSideDotBracket = function(expr) { - var save, method; - - save = expr; - method = this.markTouch(this.createIdentifier("value")); - expr = this.markTouch(this.createCallExpression(expr, method, { list: [] }, ".")); - - /* istanbul ignore else */ - if (this.opts.range) { - expr.range[0] = save.range[0]; + if (this.match("...")) { + this.lex(); + args.remain = this.parseVariableIdentifier(); + this.scope.add("arg", args.remain.name); } - /* istanbul ignore else */ - if (this.opts.loc) { - expr.loc.start = save.loc.start; - } + this.expect(expect); - return this.parseLeftHandSideListAt(expr); + return args; }; - SCParser.prototype.parseCallArgument = function() { - var args, node, hasKeyword, lookahead; + SCParser.prototype._parseArgVarElement = function(type, method) { + var init = null, id; + var marker, declarator; - args = { list: [] }; - hasKeyword = false; + marker = Marker.create(this.lexer); - this.expect("("); + id = this.parseVariableIdentifier(); + this.scope.add(type, id.name); - while (this.lookahead.type !== Token.EOF && !this.match(")")) { - lookahead = this.lookahead; - if (!hasKeyword) { - if (this.match("*")) { - this.lex(); - args.expand = this.parseExpressions(); - hasKeyword = true; - } else if (lookahead.type === Token.Label) { - this.parseCallArgumentKeyword(args); - hasKeyword = true; - } else { - node = this.parseExpressions(); - args.list.push(node); - } - } else { - if (lookahead.type !== Token.Label) { - this.throwUnexpected(lookahead); - } - this.parseCallArgumentKeyword(args); - } - if (this.match(")")) { - break; - } - this.expect(","); + if (this.match("=")) { + this.lex(); + init = this[method](); } - this.expect(")"); + declarator = Node.createVariableDeclarator(id, init); - return args; + return marker.update().apply(declarator); }; - SCParser.prototype.parseCallArgumentKeyword = function(args) { - var key, value; + SCParser.prototype.parseFunctionArgumentElement = function() { + var node = this._parseArgVarElement("arg", "parseArgumentableValue"); - key = this.lex().value; - value = this.parseExpressions(); - if (!args.keywords) { - args.keywords = {}; + if (node.init && !isValidArgumentValue(node.init)) { + this.throwUnexpected(this.lookahead); } - args.keywords[key] = value; + + return node; }; - SCParser.prototype.parseListIndexer = function() { - var node = null; + // 2.3 Function Body + SCParser.prototype.parseFunctionBody = function(match) { + var elements = []; - this.expect("["); + while (this.match("var")) { + elements.push(this.parseVariableDeclaration()); + } - if (!this.match("]")) { - if (this.match("..")) { - // [..last] / [..] - node = this.parseListIndexerWithoutFirst(); + while (this.lookahead.type !== Token.EOF && !this.match(match)) { + elements.push(this.parseExpression()); + if (this.lookahead.type !== Token.EOF && !this.match(match)) { + this.expect(";"); } else { - // [first] / [first..last] / [first, second..last] - node = this.parseListIndexerWithFirst(); + break; } } - this.expect("]"); + return elements; + }; - if (node === null) { - this.throwUnexpected({ value: "]" }); - } + // 3. Variable Declarations + SCParser.prototype.parseVariableDeclaration = function() { + var declaration; + var marker; - return node; - }; + marker = Marker.create(this.lexer); - SCParser.prototype.parseListIndexerWithoutFirst = function() { - var last; + this.lex(); // var - this.lex(); + declaration = Node.createVariableDeclaration( + this.parseVariableDeclarationList(), "var" + ); - if (!this.match("]")) { - last = this.parseExpressions(); + declaration = marker.update().apply(declaration); - // [..last] - return [ null, null, last ]; - } + this.expect(";"); - // [..] - return [ null, null, null ]; + return declaration; }; - SCParser.prototype.parseListIndexerWithFirst = function() { - var first = null; + SCParser.prototype.parseVariableDeclarationList = function() { + var list = []; - if (!this.match(",")) { - first = this.parseExpressions(); - } else { - this.throwUnexpected(this.lookahead); - } + do { + list.push(this.parseVariableDeclarationElement()); + if (!this.match(",")) { + break; + } + this.lex(); + } while (this.lookahead.type !== Token.EOF); - if (this.match("..")) { - return this.parseListIndexerWithoutSecond(first); - } else if (this.match(",")) { - return this.parseListIndexerWithSecond(first); - } + return list; + }; - // [first] - return [ first ]; + SCParser.prototype.parseVariableDeclarationElement = function() { + return this._parseArgVarElement("var", "parseAssignmentExpression"); }; - SCParser.prototype.parseListIndexerWithoutSecond = function(first) { - var last = null; + // 4. Expression + SCParser.prototype.parseExpression = function(node) { + return this.parseAssignmentExpression(node); + }; - this.lex(); + // 4.1 Expressions + SCParser.prototype.parseExpressions = function(node) { + var nodes = []; - if (!this.match("]")) { - last = this.parseExpressions(); + if (node) { + nodes.push(node); + this.lex(); } - // [first..last] - return [ first, null, last ]; - }; + while (this.lookahead.type !== Token.EOF && !this.matchAny([ ",", ")", "]", ".." ])) { + var marker; - SCParser.prototype.parseListIndexerWithSecond = function(first) { - var second, last = null; + marker = Marker.create(this.lexer); + node = this.parseAssignmentExpression(); + node = marker.update().apply(node); - this.lex(); + nodes.push(node); - second = this.parseExpressions(); - if (this.match("..")) { - this.lex(); - if (!this.match("]")) { - last = this.parseExpressions(); + if (this.match(";")) { + this.lex(); } - } else { + } + + if (nodes.length === 0) { this.throwUnexpected(this.lookahead); } - // [first, second..last] - return [ first, second, last ]; + return nodes.length === 1 ? nodes[0] : nodes; }; - SCParser.prototype.parseProperty = function() { - var token; - - this.skipComment(); - this.markStart(); - token = this.lex(); + // 4.2 Assignment Expression + SCParser.prototype.parseAssignmentExpression = function(node) { + var token, marker; - if (token.type !== Token.Identifier || this.isClassName(token)) { - this.throwUnexpected(token); + if (node) { + return this.parsePartialExpression(node); } - return this.markEnd(this.createIdentifier(token.value)); - }; + marker = Marker.create(this.lexer); - // 4.8 Primary Expressions - SCParser.prototype.parseArgumentableValue = function() { - var expr, stamp; + if (this.match("#")) { + token = this.lexer.lex(true); + if (this.matchAny([ "[", "{" ])) { + token.revert(); + } else { + node = this.parseDestructuringAssignmentExpression(); + } + } - this.skipComment(); - this.markStart(); + if (!node) { + node = this.parseSimpleAssignmentExpression(); + } - stamp = this.matchAny([ "(", "{", "[", "#" ]) || this.lookahead.type; + return marker.update().apply(node); + }; - switch (stamp) { - case "#": - expr = this.parsePrimaryHashedExpression(); - break; - case Token.CharLiteral: - case Token.FloatLiteral: - case Token.FalseLiteral: - case Token.IntegerLiteral: - case Token.NilLiteral: - case Token.SymbolLiteral: - case Token.TrueLiteral: - expr = this.createLiteral(this.lex()); - break; - } + SCParser.prototype.parseDestructuringAssignmentExpression = function() { + var node, left, right, token; - if (!expr) { - expr = {}; - this.throwUnexpected(this.lex()); - } + left = this.parseDestructuringAssignmentLeft(); + token = this.lookahead; + this.expect("="); - return this.markEnd(expr); + right = this.parseAssignmentExpression(); + node = Node.createAssignmentExpression( + token.value, left.list, right, left.remain + ); + + return node; }; - SCParser.prototype.parsePrimaryExpression = function(node) { - var expr, stamp; + SCParser.prototype.parseSimpleAssignmentExpression = function() { + var node, left, right, token, marker; - if (node) { - return node; - } + node = left = this.parsePartialExpression(); - this.skipComment(); - this.markStart(); + if (this.match("=")) { + if (node.type === Syntax.CallExpression) { + marker = Marker.create(this.lexer, left); - if (this.match("~")) { - this.lex(); - expr = this.createGlobalExpression(this.parseIdentifier()); - } else { - stamp = this.matchAny([ "(", "{", "[", "#" ]) || this.lookahead.type; - switch (stamp) { - case "(": - expr = this.parseParentheses(); - break; - case "{": - expr = this.parseBraces(); - break; - case "[": - expr = this.parseListInitialiser(); - break; - case Token.Keyword: - expr = this.parsePrimaryKeywordExpression(); - break; - case Token.Identifier: - expr = this.parsePrimaryIdentifier(); - break; - case Token.StringLiteral: - expr = this.parsePrimaryStringExpression(); - break; - default: - // case "#": - // case Token.CharLiteral: - // case Token.FloatLiteral: - // case Token.FalseLiteral: - // case Token.IntegerLiteral: - // case Token.NilLiteral: - // case Token.SymbolLiteral: - // case Token.TrueLiteral: - expr = this.parseArgumentableValue(stamp); - break; + token = this.lex(); + right = this.parseAssignmentExpression(); + left.method.name = renameGetterToSetter(left.method.name); + left.args.list = node.args.list.concat(right); + + node = marker.update().apply(left, true); + } else { + if (!isLeftHandSide(left)) { + this.throwError({}, Message.InvalidLHSInAssignment); + } + + token = this.lex(); + right = this.parseAssignmentExpression(); + node = Node.createAssignmentExpression( + token.value, left, right + ); } } - return this.markEnd(expr); + return node; }; - SCParser.prototype.parsePrimaryHashedExpression = function() { - var expr, lookahead; - - lookahead = this.lookahead; - - this.lex(); + SCParser.prototype.parseDestructuringAssignmentLeft = function() { + var params = { list: [] }, element; - switch (this.matchAny([ "[", "{" ])) { - case "[": - expr = this.parsePrimaryImmutableListExpression(lookahead); - break; - case "{": - expr = this.parsePrimaryClosedFunctionExpression(); - break; - default: - expr = {}; - this.throwUnexpected(this.lookahead); - break; - } + do { + element = this.parseLeftHandSideExpression(); + if (!isLeftHandSide(element)) { + this.throwError({}, Message.InvalidLHSInAssignment); + } + params.list.push(element); + if (this.match(",")) { + this.lex(); + } else if (this.match("...")) { + this.lex(); + params.remain = this.parseLeftHandSideExpression(); + if (!isLeftHandSide(params.remain)) { + this.throwError({}, Message.InvalidLHSInAssignment); + } + break; + } + } while (this.lookahead.type !== Token.EOF && !this.match("=")); - return expr; + return params; }; - SCParser.prototype.parsePrimaryImmutableListExpression = function(lookahead) { - var expr; - - if (this.state.immutableList) { - this.throwUnexpected(lookahead); - } - - this.state.immutableList = true; - expr = this.parseListInitialiser(); - this.state.immutableList = false; + // 4.3 Partial Expression + SCParser.prototype.parsePartialExpression = function(node) { + var underscore, x, y; - return expr; - }; + if (this.state.innerElements) { + node = this.parseBinaryExpression(node); + } else { + underscore = this.state.underscore; + this.state.underscore = []; - SCParser.prototype.parsePrimaryClosedFunctionExpression = function() { - var expr, disallowGenerator, closedFunction; + node = this.parseBinaryExpression(node); - disallowGenerator = this.state.disallowGenerator; - closedFunction = this.state.closedFunction; + if (this.state.underscore.length) { + node = this.withScope(function() { + var args, i, imax; - this.state.disallowGenerator = true; - this.state.closedFunction = true; - expr = this.parseBraces(); - this.state.closedFunction = closedFunction; - this.state.disallowGenerator = disallowGenerator; + args = new Array(this.state.underscore.length); + for (i = 0, imax = args.length; i < imax; ++i) { + x = this.state.underscore[i]; + y = Node.createVariableDeclarator(x); + args[i] = Marker.create(this.lexer, x).update(x).apply(y); + this.scope.add("arg", this.state.underscore[i].name); + } - return expr; - }; + return Node.createFunctionExpression( + { list: args }, [ node ], false, true, false + ); + }); + } - SCParser.prototype.parsePrimaryKeywordExpression = function() { - if (Keywords[this.lookahead.value] === "keyword") { - this.throwUnexpected(this.lookahead); + this.state.underscore = underscore; } - return this.createThisExpression(this.lex().value); + return node; }; - SCParser.prototype.parsePrimaryIdentifier = function() { - var expr, lookahead; - - lookahead = this.lookahead; + // 4.4 Conditional Expression + // 4.5 Binary Expression + SCParser.prototype.parseBinaryExpression = function(node) { + var marker, left, token, prec; - expr = this.parseIdentifier(); + marker = Marker.create(this.lexer); + left = this.parseUnaryExpression(node); + token = this.lookahead; - if (expr.name === "_") { - expr.name = "$_" + this.state.underscore.length.toString(); - this.state.underscore.push(expr); + prec = calcBinaryPrecedence(token, this.binaryPrecedence); + if (prec === 0) { + if (node) { + return this.parseUnaryExpression(node); + } + return left; } + this.lex(); - return expr; - }; + token.prec = prec; + token.adverb = this.parseAdverb(); - SCParser.prototype.isInterpolatedString = function(value) { - var re = /(^|[^\x5c])#\{/; - return re.test(value); + return this.sortByBinaryPrecedence(left, token, marker); }; - SCParser.prototype.parsePrimaryStringExpression = function() { - var token; - - token = this.lex(); + SCParser.prototype.sortByBinaryPrecedence = function(left, operator, marker) { + var expr; + var prec, token; + var markers, i; + var right, stack; - if (this.isInterpolatedString(token.value)) { - return this.parseInterpolatedString(token.value); - } + markers = [ marker, Marker.create(this.lexer) ]; + right = this.parseUnaryExpression(); - return this.createLiteral(token); - }; + stack = [ left, operator, right ]; - SCParser.prototype.parseInterpolatedString = function(value) { - var len, items; - var index1, index2, code, parser; + while ((prec = calcBinaryPrecedence(this.lookahead, this.binaryPrecedence)) > 0) { + // Reduce: make a binary expression from the three topmost entries. + while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { + right = stack.pop(); + operator = stack.pop(); + left = stack.pop(); + expr = Node.createBinaryExpression(operator, left, right); + markers.pop(); - len = value.length; - items = []; + marker = markers.pop(); + marker.update().apply(expr); - index1 = 0; + stack.push(expr); - do { - index2 = findString$InterpolatedString(value, index1); - if (index2 >= len) { - break; - } - code = value.substr(index1, index2 - index1); - if (code) { - items.push('"' + code + '"'); + markers.push(marker); } - index1 = index2 + 2; - index2 = findExpression$InterpolatedString(value, index1, items); - - code = value.substr(index1, index2 - index1); - if (code) { - items.push("(" + code + ").asString"); - } + // Shift. + token = this.lex(); + token.prec = prec; + token.adverb = this.parseAdverb(); - index1 = index2 + 1; - } while (index1 < len); + stack.push(token); - if (index1 < len) { - items.push('"' + value.substr(index1) + '"'); + markers.push(Marker.create(this.lexer)); + expr = this.parseUnaryExpression(); + stack.push(expr); } - code = items.join("++"); - parser = new SCParser(code, {}); - parser.peek(); + // Final reduce to clean-up the stack. + i = stack.length - 1; + expr = stack[i]; + markers.pop(); + while (i > 1) { + expr = Node.createBinaryExpression(stack[i - 1], stack[i - 2], expr); + i -= 2; + marker = markers.pop(); + marker.update().apply(expr); + } - return parser.parseExpression(); + return expr; }; - var findString$InterpolatedString = function(value, index) { - var len, ch; + SCParser.prototype.parseAdverb = function() { + var adverb, lookahead; - len = value.length; + if (this.match(".")) { + this.lex(); - while (index < len) { - ch = value.charAt(index); - if (ch === "#") { - if (value.charAt(index + 1) === "{") { - break; - } - } else if (ch === "\\") { - index += 1; + lookahead = this.lookahead; + adverb = this.parsePrimaryExpression(); + + if (adverb.type === Syntax.Literal) { + return adverb; } - index += 1; + + if (adverb.type === Syntax.Identifier) { + adverb.type = Syntax.Literal; + adverb.value = adverb.name; + adverb.valueType = Token.SymbolLiteral; + delete adverb.name; + return adverb; + } + + this.throwUnexpected(lookahead); } - return index; + return null; }; - var findExpression$InterpolatedString = function(value, index) { - var len, depth, ch; + // 4.6 Unary Expressions + SCParser.prototype.parseUnaryExpression = function(node) { + var token, expr; + var marker; - len = value.length; + marker = Marker.create(this.lexer); - depth = 0; - while (index < len) { - ch = value.charAt(index); - if (ch === "}") { - if (depth === 0) { - break; - } - depth -= 1; - } else if (ch === "{") { - depth += 1; - } - index += 1; + if (this.match("`")) { + token = this.lex(); + expr = this.parseUnaryExpression(); + expr = Node.createUnaryExpression(token.value, expr); + } else { + expr = this.parseLeftHandSideExpression(node); } - return index; + return marker.update().apply(expr); }; - // ( ... ) - SCParser.prototype.parseParentheses = function() { - var marker, expr, generator, items; + // 4.7 LeftHandSide Expressions + SCParser.prototype.parseLeftHandSideExpression = function(node) { + var marker, expr, prev, lookahead; + var blocklist, stamp; - this.skipComment(); + marker = Marker.create(this.lexer); + expr = this.parsePrimaryExpression(node); - marker = this.createLocationMarker(); - this.expect("("); + blocklist = false; - if (this.match(":")) { - this.lex(); - generator = true; - } + while ((stamp = this.matchAny([ "(", "{", "#", "[", "." ])) !== null) { + lookahead = this.lookahead; + if ((prev === "{" && (stamp !== "#" && stamp !== "{")) || (prev === "(" && stamp === "(")) { + this.throwUnexpected(lookahead); + } + switch (stamp) { + case "(": + expr = this.parseLeftHandSideParenthesis(expr); + break; + case "#": + expr = this.parseLeftHandSideClosedBrace(expr); + break; + case "{": + expr = this.parseLeftHandSideBrace(expr); + break; + case "[": + expr = this.parseLeftHandSideBracket(expr); + break; + case ".": + expr = this.parseLeftHandSideDot(expr); + break; + } + marker.update().apply(expr, true); - if (this.lookahead.type === Token.Label) { - expr = this.parseObjectInitialiser(); - } else if (this.matchKeyword("var")) { - expr = this.withScope(function() { - var body; - body = this.parseFunctionBody(")"); - return this.createBlockExpression(body); - }); - } else if (this.match("..")) { - expr = this.parseSeriesInitialiser(null, generator); - } else if (this.match(")")) { - expr = this.createObjectExpression([]); - } else { - items = this.parseParenthesesGuess(generator, marker); - expr = items[0]; - marker = items[1]; + prev = stamp; } - this.expect(")"); + return expr; + }; - /* istanbul ignore else */ - if (marker) { - marker.apply(expr); + SCParser.prototype.parseLeftHandSideParenthesis = function(expr) { + if (isClassName(expr)) { + return this.parseLeftHandSideClassNew(expr); } - return expr; + return this.parseLeftHandSideMethodCall(expr); }; - SCParser.prototype.parseParenthesesGuess = function(generator, marker) { - var node, expr; + SCParser.prototype.parseLeftHandSideClassNew = function(expr) { + var method, args; - node = this.parseExpression(); - if (this.matchAny([ ",", ".." ])) { - expr = this.parseSeriesInitialiser(node, generator); - } else if (this.match(":")) { - expr = this.parseObjectInitialiser(node); - } else if (this.match(";")) { - expr = this.parseExpressions(node); - if (this.matchAny([ ",", ".." ])) { - expr = this.parseSeriesInitialiser(expr, generator); - } - marker = null; - } else { - expr = this.parseExpression(node); - marker = null; - } + method = Node.createIdentifier("new"); + method = Marker.create(this.lexer).apply(method); - return [ expr, marker ]; - }; + args = this.parseCallArgument(); - SCParser.prototype.parseObjectInitialiser = function(node) { - var elements = [], innerElements; + return Node.createCallExpression(expr, method, args, "("); + }; - innerElements = this.state.innerElements; - this.state.innerElements = true; + SCParser.prototype.parseLeftHandSideMethodCall = function(expr) { + var method, args, lookahead; - if (node) { - this.expect(":"); - } else { - node = this.parseLabelAsSymbol(); + if (expr.type !== Syntax.Identifier) { + this.throwUnexpected(this.lookahead); } - elements.push(node, this.parseExpression()); - if (this.match(",")) { - this.lex(); - } + lookahead = this.lookahead; + args = this.parseCallArgument(); - while (this.lookahead.type !== Token.EOF && !this.match(")")) { - if (this.lookahead.type === Token.Label) { - node = this.parseLabelAsSymbol(); + method = expr; + expr = args.list.shift(); + + if (!expr) { + if (args.expand) { + expr = args.expand; + delete args.expand; } else { - node = this.parseExpression(); - this.expect(":"); - } - elements.push(node, this.parseExpression()); - if (!this.match(")")) { - this.expect(","); + this.throwUnexpected(lookahead); } } - this.state.innerElements = innerElements; - - return this.createObjectExpression(elements); + // max(0, 1) -> 0.max(1) + return Node.createCallExpression(expr, method, args, "("); }; - SCParser.prototype.parseSeriesInitialiser = function(node, generator) { - var method, innerElements; - var items = []; - - innerElements = this.state.innerElements; - this.state.innerElements = true; - - method = this.markTouch(this.createIdentifier( - generator ? "seriesIter" : "series" - )); - - if (node === null) { - // (..), (..last) - items = this.parseSeriesInitialiserWithoutFirst(generator); - } else { - items = this.parseSeriesInitialiserWithFirst(node, generator); + SCParser.prototype.parseLeftHandSideClosedBrace = function(expr) { + this.lex(); + if (!this.match("{")) { + this.throwUnexpected(this.lookahead); } - this.state.innerElements = innerElements; + this.state.closedFunction = true; + expr = this.parseLeftHandSideBrace(expr); + this.state.closedFunction = false; - return this.createCallExpression(items.shift(), method, { list: items }); + return expr; }; - SCParser.prototype.parseSeriesInitialiserWithoutFirst = function(generator) { - var first, last = null; - - // (..last) - first = this.markTouch({ - type: Syntax.Literal, - value: "0", - valueType: Token.IntegerLiteral - }); + SCParser.prototype.parseLeftHandSideBrace = function(expr) { + var method, lookahead, disallowGenerator, node; - this.expect(".."); - if (this.match(")")) { - if (!generator) { - this.throwUnexpected(this.lookahead); + if (expr.type === Syntax.CallExpression && expr.stamp && expr.stamp !== "(") { + this.throwUnexpected(this.lookahead); + } + if (expr.type === Syntax.Identifier) { + if (isClassName(expr)) { + method = Node.createIdentifier("new"); + method = Marker.create(this.lexer).apply(method); + expr = Node.createCallExpression(expr, method, { list: [] }, "{"); + } else { + expr = Node.createCallExpression(null, expr, { list: [] }); } + } + lookahead = this.lookahead; + disallowGenerator = this.state.disallowGenerator; + this.state.disallowGenerator = true; + node = this.parseBraces(true); + this.state.disallowGenerator = disallowGenerator; + + // TODO: refactoring + if (expr.callee === null) { + expr.callee = node; + node = expr; } else { - last = this.parseExpressions(); + expr.args.list.push(node); } - return [ first, null, last ]; + return expr; }; - SCParser.prototype.parseSeriesInitialiserWithFirst = function(node, generator) { - var first, second = null, last = null; + SCParser.prototype.parseLeftHandSideBracket = function(expr) { + if (expr.type === Syntax.CallExpression && expr.stamp === "(") { + this.throwUnexpected(this.lookahead); + } - first = node; - if (this.match(",")) { - // (first, second .. last) - this.lex(); - second = this.parseExpressions(); - if (Array.isArray(second) && second.length === 0) { - this.throwUnexpected(this.lookahead); - } - this.expect(".."); - if (!this.match(")")) { - last = this.parseExpressions(); - } else if (!generator) { - this.throwUnexpected(this.lookahead); - } + if (isClassName(expr)) { + expr = this.parseLeftHandSideNewFrom(expr); } else { - // (first..last) - this.lex(); - if (!this.match(")")) { - last = this.parseExpressions(); - } else if (!generator) { - this.throwUnexpected(this.lookahead); - } + expr = this.parseLeftHandSideListAt(expr); } - return [ first, second, last ]; + return expr; }; - SCParser.prototype.parseListInitialiser = function() { - var elements, innerElements; + SCParser.prototype.parseLeftHandSideNewFrom = function(expr) { + var node, method; + var marker; - elements = []; + method = Node.createIdentifier("newFrom"); + method = Marker.create(this.lexer).apply(method); - innerElements = this.state.innerElements; - this.state.innerElements = true; + marker = Marker.create(this.lexer); + node = this.parseListInitialiser(); + node = marker.update().apply(node); - this.expect("["); + return Node.createCallExpression(expr, method, { list: [ node ] }, "["); + }; - while (this.lookahead.type !== Token.EOF && !this.match("]")) { - if (this.lookahead.type === Token.Label) { - elements.push(this.parseLabelAsSymbol(), this.parseExpression()); - } else { - elements.push(this.parseExpression()); - if (this.match(":")) { - this.lex(); - elements.push(this.parseExpression()); - } - } - if (!this.match("]")) { - this.expect(","); + SCParser.prototype.parseLeftHandSideListAt = function(expr) { + var indexes, method; + + method = Node.createIdentifier("at"); + method = Marker.create(this.lexer).apply(method); + + indexes = this.parseListIndexer(); + if (indexes) { + if (indexes.length === 3) { + method.name = "copySeries"; } + } else { + this.throwUnexpected(this.lookahead); } - this.expect("]"); + return Node.createCallExpression(expr, method, { list: indexes }, "["); + }; - this.state.innerElements = innerElements; + SCParser.prototype.parseLeftHandSideDot = function(expr) { + var method, args; - return this.createListExpression(elements, this.state.immutableList); - }; + this.lex(); - // { ... } - SCParser.prototype.parseBraces = function(blocklist) { - var expr; + if (this.match("(")) { + // expr.() + return this.parseLeftHandSideDotValue(expr); + } else if (this.match("[")) { + // expr.[0] + return this.parseLeftHandSideDotBracket(expr); + } - this.skipComment(); - this.markStart(); + method = this.parseProperty(); + if (this.match("(")) { + // expr.method(args) + args = this.parseCallArgument(); + return Node.createCallExpression(expr, method, args); + } - this.expect("{"); + // expr.method + return Node.createCallExpression(expr, method, { list: [] }); + }; - if (this.match(":")) { - if (!this.state.disallowGenerator) { - this.lex(); - expr = this.parseGeneratorInitialiser(); - } else { - expr = {}; - this.throwUnexpected(this.lookahead); - } - } else { - expr = this.parseFunctionExpression(this.state.closedFunction, blocklist); - } + SCParser.prototype.parseLeftHandSideDotValue = function(expr) { + var method, args; - this.expect("}"); + method = Node.createIdentifier("value"); + method = Marker.create(this.lexer).apply(method); - return this.markEnd(expr); + args = this.parseCallArgument(); + + return Node.createCallExpression(expr, method, args, "."); }; - SCParser.prototype.parseGeneratorInitialiser = function() { - this.throwError({}, Message.NotImplemented, "generator literal"); + SCParser.prototype.parseLeftHandSideDotBracket = function(expr) { + var method, marker; - this.parseExpression(); - this.expect(","); + marker = Marker.create(this.lexer, expr); - while (this.lookahead.type !== Token.EOF && !this.match("}")) { - this.parseExpression(); - if (!this.match("}")) { - this.expect(","); + method = Node.createIdentifier("value"); + method = Marker.create(this.lexer).apply(method); + + expr = Node.createCallExpression(expr, method, { list: [] }, "."); + expr = marker.update().apply(expr); + + return this.parseLeftHandSideListAt(expr); + }; + + SCParser.prototype.parseCallArgument = function() { + var args, node, hasKeyword, lookahead; + + args = { list: [] }; + hasKeyword = false; + + this.expect("("); + + while (this.lookahead.type !== Token.EOF && !this.match(")")) { + lookahead = this.lookahead; + if (!hasKeyword) { + if (this.match("*")) { + this.lex(); + args.expand = this.parseExpressions(); + hasKeyword = true; + } else if (lookahead.type === Token.Label) { + this.parseCallArgumentKeyword(args); + hasKeyword = true; + } else { + node = this.parseExpressions(); + args.list.push(node); + } + } else { + if (lookahead.type !== Token.Label) { + this.throwUnexpected(lookahead); + } + this.parseCallArgumentKeyword(args); + } + if (this.match(")")) { + break; } + this.expect(","); } - return this.createLiteral({ value: "null", valueType: Token.NilLiteral }); + this.expect(")"); + + return args; }; - SCParser.prototype.parseLabel = function() { - this.skipComment(); - this.markStart(); - return this.markEnd(this.createLabel(this.lex().value)); + SCParser.prototype.parseCallArgumentKeyword = function(args) { + var key, value; + + key = this.lex().value; + value = this.parseExpressions(); + if (!args.keywords) { + args.keywords = {}; + } + args.keywords[key] = value; }; - SCParser.prototype.parseLabelAsSymbol = function() { - var label, node; + SCParser.prototype.parseListIndexer = function() { + var node = null; - label = this.parseLabel(); - node = { - type: Syntax.Literal, - value: label.name, - valueType: Token.SymbolLiteral - }; + this.expect("["); - /* istanbul ignore else */ - if (label.range) { - node.range = label.range; + if (!this.match("]")) { + if (this.match("..")) { + // [..last] / [..] + node = this.parseListIndexerWithoutFirst(); + } else { + // [first] / [first..last] / [first, second..last] + node = this.parseListIndexerWithFirst(); + } } - /* istanbul ignore else */ - if (label.loc) { - node.loc = label.loc; + + this.expect("]"); + + if (node === null) { + this.throwUnexpected({ value: "]" }); } return node; }; - SCParser.prototype.parseIdentifier = function() { - var expr; + SCParser.prototype.parseListIndexerWithoutFirst = function() { + var last; - this.skipComment(); - this.markStart(); + this.lex(); - if (this.lookahead.type !== Syntax.Identifier) { - this.throwUnexpected(this.lookahead); - } + if (!this.match("]")) { + last = this.parseExpressions(); - expr = this.lex(); + // [..last] + return [ null, null, last ]; + } - return this.markEnd(this.createIdentifier(expr.value)); + // [..] + return [ null, null, null ]; }; - SCParser.prototype.parseVariableIdentifier = function() { - var token, value, ch; - - this.skipComment(); - this.markStart(); - - token = this.lex(); - value = token.value; + SCParser.prototype.parseListIndexerWithFirst = function() { + var first = null; - if (token.type !== Token.Identifier) { - this.throwUnexpected(token); + if (!this.match(",")) { + first = this.parseExpressions(); } else { - ch = value.charAt(0); - if (("A" <= ch && ch <= "Z") || ch === "_") { - this.throwUnexpected(token); - } + this.throwUnexpected(this.lookahead); + } + + if (this.match("..")) { + return this.parseListIndexerWithoutSecond(first); + } else if (this.match(",")) { + return this.parseListIndexerWithSecond(first); } - return this.markEnd(this.createIdentifier(value)); + // [first] + return [ first ]; }; - SCParser.prototype.markStart = function() { - /* istanbul ignore else */ - if (this.opts.loc) { - this.marker.push( - this.index - this.lineStart, - this.lineNumber - ); - } - /* istanbul ignore else */ - if (this.opts.range) { - this.marker.push( - this.index - ); + SCParser.prototype.parseListIndexerWithoutSecond = function(first) { + var last = null; + + this.lex(); + + if (!this.match("]")) { + last = this.parseExpressions(); } + + // [first..last] + return [ first, null, last ]; }; - SCParser.prototype.markEnd = function(node) { - if (Array.isArray(node) || node.range || node.loc) { - /* istanbul ignore else */ - if (this.opts.range) { - this.marker.pop(); - } - /* istanbul ignore else */ - if (this.opts.loc) { - this.marker.pop(); - this.marker.pop(); + SCParser.prototype.parseListIndexerWithSecond = function(first) { + var second, last = null; + + this.lex(); + + second = this.parseExpressions(); + if (this.match("..")) { + this.lex(); + if (!this.match("]")) { + last = this.parseExpressions(); } } else { - /* istanbul ignore else */ - if (this.opts.range) { - node.range = [ this.marker.pop(), this.index ]; - } - /* istanbul ignore else */ - if (this.opts.loc) { - node.loc = { - start: { - line: this.marker.pop(), - column: this.marker.pop() - }, - end: { - line: this.lineNumber, - column: this.index - this.lineStart - } - }; - } + this.throwUnexpected(this.lookahead); } - return node; - }; - SCParser.prototype.markTouch = function(node) { - /* istanbul ignore else */ - if (this.opts.range) { - node.range = [ this.index, this.index ]; - } - /* istanbul ignore else */ - if (this.opts.loc) { - node.loc = { - start: { - line: this.lineNumber, - column: this.index - this.lineStart - }, - end: { - line: this.lineNumber, - column: this.index - this.lineStart - } - }; - } - return node; + // [first, second..last] + return [ first, second, last ]; }; - SCParser.prototype.throwError = function(token, messageFormat) { - var args, message; - var error, index, lineNumber, column; - var prev; + SCParser.prototype.parseProperty = function() { + var token, id; + var marker; - args = Array.prototype.slice.call(arguments, 2); - message = messageFormat.replace(/%(\d)/g, function(whole, index) { - return args[index]; - }); + marker = Marker.create(this.lexer); + token = this.lex(); - if (typeof token.lineNumber === "number") { - index = token.range[0]; - lineNumber = token.lineNumber; - column = token.range[0] - token.lineStart + 1; - } else { - index = this.index; - lineNumber = this.lineNumber; - column = this.index - this.lineStart + 1; + if (token.type !== Token.Identifier || isClassName(token)) { + this.throwUnexpected(token); } - error = new Error("Line " + lineNumber + ": " + message); - error.index = index; - error.lineNumber = lineNumber; - error.column = column; - error.description = message; + id = Node.createIdentifier(token.value); - if (this.errors) { - prev = this.errors[this.errors.length - 1]; - if (!(prev && error.index <= prev.index)) { - this.errors.push(error); - } - } else { - throw error; - } + return marker.update().apply(id); }; - SCParser.prototype.throwUnexpected = function(token) { - switch (token.type) { - case Token.EOF: - this.throwError(token, Message.UnexpectedEOS); - break; - case Token.FloatLiteral: - case Token.IntegerLiteral: - this.throwError(token, Message.UnexpectedNumber); - break; - case Token.CharLiteral: - this.throwError(token, Message.UnexpectedChar); - break; - case Token.StringLiteral: - this.throwError(token, Message.UnexpectedString); + // 4.8 Primary Expressions + SCParser.prototype.parseArgumentableValue = function() { + var expr, stamp; + var marker; + + marker = Marker.create(this.lexer); + + stamp = this.matchAny([ "(", "{", "[", "#" ]) || this.lookahead.type; + + switch (stamp) { + case "#": + expr = this.parsePrimaryHashedExpression(); break; + case Token.CharLiteral: + case Token.FloatLiteral: + case Token.FalseLiteral: + case Token.IntegerLiteral: + case Token.NilLiteral: case Token.SymbolLiteral: - this.throwError(token, Message.UnexpectedSymbol); - break; - case Token.Identifier: - this.throwError(token, Message.UnexpectedIdentifier); - break; - default: - this.throwError(token, Message.UnexpectedToken, token.value); + case Token.TrueLiteral: + expr = Node.createLiteral(this.lex()); break; } + + if (!expr) { + expr = {}; + this.throwUnexpected(this.lex()); + } + + return marker.update().apply(expr); }; - function isValidArgumentValue(node) { - if (node.type === Syntax.Literal) { - return true; + SCParser.prototype.parsePrimaryExpression = function(node) { + var expr, stamp; + var marker; + + if (node) { + return node; } - if (node.type === Syntax.ListExpression) { - return node.elements.every(function(node) { - return node.type === Syntax.Literal; - }); + + marker = Marker.create(this.lexer); + + if (this.match("~")) { + this.lex(); + expr = Node.createGlobalExpression(this.parseIdentifier()); + } else { + stamp = this.matchAny([ "(", "{", "[", "#" ]) || this.lookahead.type; + switch (stamp) { + case "(": + expr = this.parseParentheses(); + break; + case "{": + expr = this.parseBraces(); + break; + case "[": + expr = this.parseListInitialiser(); + break; + case Token.Keyword: + expr = this.parsePrimaryKeywordExpression(); + break; + case Token.Identifier: + expr = this.parsePrimaryIdentifier(); + break; + case Token.StringLiteral: + expr = this.parsePrimaryStringExpression(); + break; + default: + expr = this.parseArgumentableValue(stamp); + break; + } } - return false; - } + return marker.update().apply(expr); + }; - parser.parse = function(source, opts) { - var instance, ast; + SCParser.prototype.parsePrimaryHashedExpression = function() { + var expr, lookahead; - opts = opts || /* istanbul ignore next */ {}; + lookahead = this.lookahead; - instance = new SCParser(source, opts); - ast = instance.parse(); + this.lex(); - if (!!opts.tokens && typeof instance.tokens !== "undefined") { - ast.tokens = instance.tokens; - } - if (!!opts.tolerant && typeof instance.errors !== "undefined") { - ast.errors = instance.errors; + switch (this.matchAny([ "[", "{" ])) { + case "[": + expr = this.parsePrimaryImmutableListExpression(lookahead); + break; + case "{": + expr = this.parsePrimaryClosedFunctionExpression(); + break; + default: + expr = {}; + this.throwUnexpected(this.lookahead); + break; } - return ast; + return expr; }; - sc.lang.parser = parser; + SCParser.prototype.parsePrimaryImmutableListExpression = function(lookahead) { + var expr; -})(sc); + if (this.state.immutableList) { + this.throwUnexpected(lookahead); + } -// src/sc/lang/dollarSC.js -(function(sc) { + this.state.immutableList = true; + expr = this.parseListInitialiser(); + this.state.immutableList = false; - var $SC = function(name) { - return sc.lang.klass.get(name); + return expr; }; - /* istanbul ignore next */ - var shouldBeImplementedInClassLib = function() {}; + SCParser.prototype.parsePrimaryClosedFunctionExpression = function() { + var expr, disallowGenerator, closedFunction; - $SC.Class = shouldBeImplementedInClassLib; - $SC.Integer = shouldBeImplementedInClassLib; - $SC.Float = shouldBeImplementedInClassLib; - $SC.Char = shouldBeImplementedInClassLib; - $SC.Array = shouldBeImplementedInClassLib; - $SC.String = shouldBeImplementedInClassLib; - $SC.Function = shouldBeImplementedInClassLib; - $SC.Ref = shouldBeImplementedInClassLib; - $SC.Symbol = shouldBeImplementedInClassLib; - $SC.Boolean = shouldBeImplementedInClassLib; - $SC.True = shouldBeImplementedInClassLib; - $SC.False = shouldBeImplementedInClassLib; - $SC.Nil = shouldBeImplementedInClassLib; + disallowGenerator = this.state.disallowGenerator; + closedFunction = this.state.closedFunction; - sc.lang.$SC = $SC; + this.state.disallowGenerator = true; + this.state.closedFunction = true; + expr = this.parseBraces(); + this.state.closedFunction = closedFunction; + this.state.disallowGenerator = disallowGenerator; -})(sc); + return expr; + }; -// src/sc/lang/fn.js -(function(sc) { + SCParser.prototype.parsePrimaryKeywordExpression = function() { + if (Keywords[this.lookahead.value] === "keyword") { + this.throwUnexpected(this.lookahead); + } - var slice = [].slice; - var $SC = sc.lang.$SC; + return Node.createThisExpression(this.lex().value); + }; - var _getDefaultValue = function(value) { - var ch; + SCParser.prototype.parsePrimaryIdentifier = function() { + var expr, lookahead; - switch (value) { - case "nil": - return $SC.Nil(); - case "true": - return $SC.True(); - case "false": - return $SC.False(); - case "inf": - return $SC.Float(Infinity); - case "-inf": - return $SC.Float(-Infinity); - } + lookahead = this.lookahead; - ch = value.charAt(0); - switch (ch) { - case "$": - return $SC.Char(value.charAt(1)); - case "\\": - return $SC.Symbol(value.substr(1)); - } + expr = this.parseIdentifier(); - if (value.indexOf(".") !== -1) { - return $SC.Float(+value); + if (expr.name === "_") { + expr.name = "$_" + this.state.underscore.length.toString(); + this.state.underscore.push(expr); } - return $SC.Integer(+value); + return expr; }; - var getDefaultValue = function(value) { - if (value.charAt(0) === "[") { - return $SC.Array(value.slice(1, -2).split(",").map(function(value) { - return _getDefaultValue(value.trim()); - })); - } - return _getDefaultValue(value); + SCParser.prototype.isInterpolatedString = function(value) { + var re = /(^|[^\x5c])#\{/; + return re.test(value); }; - var fn = function(func, def) { - var argItems, argNames, argVals; - var remain, wrapper; + SCParser.prototype.parsePrimaryStringExpression = function() { + var token; - argItems = def.split(/\s*;\s*/); - if (argItems[argItems.length - 1].charAt(0) === "*") { - remain = !!argItems.pop(); + token = this.lex(); + + if (this.isInterpolatedString(token.value)) { + return this.parseInterpolatedString(token.value); } - argNames = new Array(argItems.length); - argVals = new Array(argItems.length); + return Node.createLiteral(token); + }; - argItems.forEach(function(items, i) { - items = items.split("="); - argNames[i] = items[0].trim(); - argVals [i] = getDefaultValue(items[1] || "nil"); - }); + SCParser.prototype.parseInterpolatedString = function(value) { + var len, items; + var index1, index2, code, parser; - wrapper = function() { - var given, args; + len = value.length; + items = []; - given = slice.call(arguments); - args = argVals.slice(); + index1 = 0; - if (isDictionary(given[given.length - 1])) { - setKeywordArguments(args, argNames, given.pop()); + do { + index2 = findString$InterpolatedString(value, index1); + if (index2 >= len) { + break; + } + code = value.substr(index1, index2 - index1); + if (code) { + items.push('"' + code + '"'); } - copy(args, given, Math.min(argNames.length, given.length)); + index1 = index2 + 2; + index2 = findExpression$InterpolatedString(value, index1, items); - if (remain) { - args.push($SC.Array(given.slice(argNames.length))); + code = value.substr(index1, index2 - index1); + if (code) { + items.push("(" + code + ").asString"); } - return func.apply(this, args); - }; + index1 = index2 + 1; + } while (index1 < len); - wrapper._argNames = argNames; - wrapper._argVals = argVals; + if (index1 < len) { + items.push('"' + value.substr(index1) + '"'); + } - return wrapper; + code = items.join("++"); + parser = new SCParser(code, {}); + + return parser.parseExpression(); }; - var isDictionary = function(obj) { - return !!(obj && obj.constructor === Object); - }; - - var copy = function(args, given, length) { - for (var i = 0; i < length; ++i) { - if (given[i]) { - args[i] = given[i]; - } - } - }; - - var setKeywordArguments = function(args, argNames, dict) { - Object.keys(dict).forEach(function(key) { - var index = argNames.indexOf(key); - if (index !== -1) { - args[index] = dict[key]; - } - }); - }; - - sc.lang.fn = fn; - -})(sc); - -// src/sc/lang/klass.js -(function(sc) { + // ( ... ) + SCParser.prototype.parseParentheses = function() { + var marker, expr, generator; - var slice = [].slice; - var $SC = sc.lang.$SC; + marker = Marker.create(this.lexer); - var klass = {}; - var metaClasses = {}; - var classes = klass.classes = {}; + this.expect("("); - var createClassInstance = function(MetaSpec) { - var instance = new SCClass(); - instance._MetaSpec = MetaSpec; - return instance; - }; + if (this.match(":")) { + this.lex(); + generator = true; + } - var extend = function(constructor, superMetaClass) { - function F() {} - F.prototype = superMetaClass._Spec.prototype; - constructor.prototype = new F(); + if (this.lookahead.type === Token.Label) { + expr = this.parseObjectInitialiser(); + } else if (this.match("var")) { + expr = this.withScope(function() { + var body; + body = this.parseFunctionBody(")"); + return Node.createBlockExpression(body); + }); + } else if (this.match("..")) { + expr = this.parseSeriesInitialiser(null, generator); + } else if (this.match(")")) { + expr = Node.createObjectExpression([]); + } else { + expr = this.parseParenthesesGuess(generator, marker); + } - function Meta_F() {} - Meta_F.prototype = superMetaClass._MetaSpec.prototype; + this.expect(")"); - function MetaSpec() {} - MetaSpec.prototype = new Meta_F(); + marker.update().apply(expr); - constructor.metaClass = createClassInstance(MetaSpec); + return expr; }; - var def = function(className, constructor, fn, opts) { - var classMethods, instanceMethods, setMethod, spec; - - classMethods = constructor.metaClass._MetaSpec.prototype; - instanceMethods = constructor.prototype; + SCParser.prototype.parseParenthesesGuess = function(generator) { + var node, expr; - setMethod = function(methods, methodName, func) { - var bond; - if (methods.hasOwnProperty(methodName) && !opts.force) { - bond = methods === classMethods ? "." : "#"; - throw new Error( - "sc.lang.klass.refine: " + - className + bond + methodName + " is already defined." - ); + node = this.parseExpression(); + if (this.matchAny([ ",", ".." ])) { + expr = this.parseSeriesInitialiser(node, generator); + } else if (this.match(":")) { + expr = this.parseObjectInitialiser(node); + } else if (this.match(";")) { + expr = this.parseExpressions(node); + if (this.matchAny([ ",", ".." ])) { + expr = this.parseSeriesInitialiser(expr, generator); } - methods[methodName] = func; - }; - - if (typeof fn === "function") { - fn(spec = {}, klass.utils); } else { - spec = fn; + expr = this.parseExpression(node); } - Object.keys(spec).forEach(function(methodName) { - if (methodName.charCodeAt(0) === 0x24) { // u+0024 is '$' - setMethod(classMethods, methodName.substr(1), spec[methodName]); - } else { - setMethod(instanceMethods, methodName, spec[methodName]); - } - }); + return expr; }; - var throwIfInvalidArgument = function(constructor, className) { - if (typeof constructor !== "function") { - throw new Error( - "sc.lang.klass.define: " + - "first argument must be a constructor, but got: " + typeof(constructor) - ); - } - - if (typeof className !== "string") { - throw new Error( - "sc.lang.klass.define: " + - "second argument must be a string, but got: " + String(className) - ); - } - }; + SCParser.prototype.parseObjectInitialiser = function(node) { + var elements = [], innerElements; - var throwIfInvalidClassName = function(className, superClassName) { - var ch0 = className.charCodeAt(0); + innerElements = this.state.innerElements; + this.state.innerElements = true; - if (ch0 < 0x41 || 0x5a < ch0) { // faster test than !/^[A-Z]/.test(className) - throw new Error( - "sc.lang.klass.define: " + - "classname should be CamelCase, but got '" + className + "'" - ); + if (node) { + this.expect(":"); + } else { + node = this.parseLabelAsSymbol(); } + elements.push(node, this.parseExpression()); - if (metaClasses.hasOwnProperty(className)) { - throw new Error( - "sc.lang.klass.define: " + - "class '" + className + "' is already registered." - ); + if (this.match(",")) { + this.lex(); } - if (className !== "Object") { - if (!metaClasses.hasOwnProperty(superClassName)) { - throw new Error( - "sc.lang.klass.define: " + - "superclass '" + superClassName + "' is not registered." - ); + while (this.lookahead.type !== Token.EOF && !this.match(")")) { + if (this.lookahead.type === Token.Label) { + node = this.parseLabelAsSymbol(); + } else { + node = this.parseExpression(); + this.expect(":"); + } + elements.push(node, this.parseExpression()); + if (!this.match(")")) { + this.expect(","); } } - }; - var buildClass = function(className, constructor) { - var newClass, metaClass; + this.state.innerElements = innerElements; - metaClass = constructor.metaClass; + return Node.createObjectExpression(elements); + }; - newClass = new metaClass._MetaSpec(); - newClass._name = className; - newClass._Spec = constructor; - constructor.prototype.__class = newClass; - constructor.prototype.__Spec = constructor; + SCParser.prototype.parseSeriesInitialiser = function(node, generator) { + var method, innerElements; + var items = []; - metaClass._Spec = constructor; - metaClass._isMetaClass = true; - metaClass._name = "Meta_" + className; + innerElements = this.state.innerElements; + this.state.innerElements = true; - classes["Meta_" + className] = metaClass; - classes[className] = newClass; + method = Node.createIdentifier(generator ? "seriesIter" : "series"); + method = Marker.create(this.lexer).apply(method); - if (newClass.initClass) { - newClass.initClass(); + if (node === null) { + // (..), (..last) + items = this.parseSeriesInitialiserWithoutFirst(generator); + } else { + items = this.parseSeriesInitialiserWithFirst(node, generator); } - metaClasses[className] = metaClass; - }; - - klass.define = function(constructor, className, fn) { - var items, superClassName; + this.state.innerElements = innerElements; - throwIfInvalidArgument(constructor, className); + return Node.createCallExpression(items.shift(), method, { list: items }); + }; - items = className.split(":"); - className = items[0].trim(); - superClassName = (items[1] || "Object").trim(); + SCParser.prototype.parseSeriesInitialiserWithoutFirst = function(generator) { + var first, last = null; - throwIfInvalidClassName(className, superClassName); + // (..last) + first = { + type: Syntax.Literal, + value: "0", + valueType: Token.IntegerLiteral + }; + first = Marker.create(this.lexer).apply(first); - if (className !== "Object") { - extend(constructor, metaClasses[superClassName]); + this.expect(".."); + if (this.match(")")) { + if (!generator) { + this.throwUnexpected(this.lookahead); + } + } else { + last = this.parseExpressions(); } - fn = fn || {}; - - def(className, constructor, fn, {}); - - buildClass(className, constructor); + return [ first, null, last ]; }; - klass.refine = function(className, fn, opts) { - var constructor; + SCParser.prototype.parseSeriesInitialiserWithFirst = function(node, generator) { + var first, second = null, last = null; - if (!metaClasses.hasOwnProperty(className)) { - throw new Error( - "sc.lang.klass.refine: " + - "class '" + className + "' is not registered." - ); + first = node; + if (this.match(",")) { + // (first, second .. last) + this.lex(); + second = this.parseExpressions(); + if (Array.isArray(second) && second.length === 0) { + this.throwUnexpected(this.lookahead); + } + this.expect(".."); + if (!this.match(")")) { + last = this.parseExpressions(); + } else if (!generator) { + this.throwUnexpected(this.lookahead); + } + } else { + // (first..last) + this.lex(); + if (!this.match(")")) { + last = this.parseExpressions(); + } else if (!generator) { + this.throwUnexpected(this.lookahead); + } } - constructor = metaClasses[className]._Spec; - - def(className, constructor, fn, opts || {}); + return [ first, second, last ]; }; - klass.get = function(name) { - if (!classes[name]) { - throw new Error( - "sc.lang.klass.get: " + - "class '" + name + "' is not registered." - ); - } - return classes[name]; - }; - - klass.exists = function(name) { - return !!classes[name]; - }; + SCParser.prototype.parseListInitialiser = function() { + var elements, innerElements; - // basic classes - function SCObject() { - this._ = this; - } + elements = []; - function SCClass() { - this._ = this; - this._name = "Class"; - this._Spec = null; - this._isMetaClass = false; - } + innerElements = this.state.innerElements; + this.state.innerElements = true; - SCObject.metaClass = createClassInstance(function() {}); - klass.define(SCObject, "Object", { - __tag: 1, - __initializeWith__: function(className, args) { - metaClasses[className]._Spec.apply(this, args); - }, - $initClass: function() {} - }); + this.expect("["); - klass.define(SCClass, "Class"); + while (this.lookahead.type !== Token.EOF && !this.match("]")) { + if (this.lookahead.type === Token.Label) { + elements.push(this.parseLabelAsSymbol(), this.parseExpression()); + } else { + elements.push(this.parseExpression()); + if (this.match(":")) { + this.lex(); + elements.push(this.parseExpression()); + } + } + if (!this.match("]")) { + this.expect(","); + } + } - SCObject.metaClass._MetaSpec.prototype = classes.Class = createClassInstance(); - classes.Class._Spec = SCClass; - classes.Object = new SCObject.metaClass._MetaSpec(); - classes.Object._name = "Object"; - classes.Object._Spec = SCObject.metaClass._Spec; - classes.Object._Spec.prototype.__class = classes.Object; - classes.Object._Spec.prototype.__Spec = classes.Object._Spec; + this.expect("]"); - klass.refine("Object", function(spec) { - spec.$new = function() { - if (this._Spec === SCClass) { - return $SC.Nil(); - } - return new this._Spec(slice.call(arguments)); - }; + this.state.innerElements = innerElements; - spec.class = function() { - return this.__class; - }; + return Node.createListExpression(elements, this.state.immutableList); + }; - spec.isClass = function() { - return $SC.False(); - }; + // { ... } + SCParser.prototype.parseBraces = function(blocklist) { + var expr; + var marker; - spec.isKindOf = function($aClass) { - return $SC.Boolean(this instanceof $aClass._Spec); - }; + marker = Marker.create(this.lexer); - spec.isMemberOf = function($aClass) { - return $SC.Boolean(this.__class === $aClass); - }; + this.expect("{"); - spec.toString = function() { - var name = this.__class._name; - if (/^[AEIOU]/.test(name)) { - return String("an " + name); + if (this.match(":")) { + if (!this.state.disallowGenerator) { + this.lex(); + expr = this.parseGeneratorInitialiser(); } else { - return String("a " + name); + expr = {}; + this.throwUnexpected(this.lookahead); } - }; + } else { + expr = this.parseFunctionExpression(this.state.closedFunction, blocklist); + } - spec.valueOf = function() { - return this._; - }; - }); + this.expect("}"); - klass.refine("Class", function(spec) { - spec.name = function() { - return $SC.String(this._name); - }; + return marker.update().apply(expr); + }; - spec.class = function() { - if (this._isMetaClass) { - return classes.Class; - } - return $SC("Meta_" + this._name); - }; + SCParser.prototype.parseGeneratorInitialiser = function() { + this.throwError({}, Message.NotImplemented, "generator literal"); - spec.isClass = function() { - return $SC.True(); - }; + this.parseExpression(); + this.expect(","); - spec.toString = function() { - return String(this._name); - }; - }); + while (this.lookahead.type !== Token.EOF && !this.match("}")) { + this.parseExpression(); + if (!this.match("}")) { + this.expect(","); + } + } - sc.lang.klass = klass; + return Node.createLiteral({ value: "null", valueType: Token.NilLiteral }); + }; -})(sc); + SCParser.prototype.parseLabel = function() { + var label, marker; -// src/sc/lang/klass-constructors.js -(function(sc) { + marker = Marker.create(this.lexer); - var $SC = sc.lang.$SC; - var klass = sc.lang.klass; + label = Node.createLabel(this.lex().value); - function SCNil() { - this.__initializeWith__("Object"); - this._ = null; - } - klass.define(SCNil, "Nil", { - __tag: 773 - }); + return marker.update().apply(label); + }; - function SCSymbol() { - this.__initializeWith__("Object"); - this._ = ""; - } - klass.define(SCSymbol, "Symbol", { - __tag: 1027 - }); + SCParser.prototype.parseLabelAsSymbol = function() { + var marker, label, node; - function SCBoolean() { - this.__initializeWith__("Object"); - } - klass.define(SCBoolean, "Boolean"); + marker = Marker.create(this.lexer); - function SCTrue() { - this.__initializeWith__("Boolean"); - this._ = true; - } - klass.define(SCTrue, "True : Boolean", { - __tag: 775 - }); + label = this.parseLabel(); + node = { + type: Syntax.Literal, + value: label.name, + valueType: Token.SymbolLiteral + }; - function SCFalse() { - this.__initializeWith__("Boolean"); - this._ = false; - } - klass.define(SCFalse, "False : Boolean", { - __tag: 774 - }); + node = marker.update().apply(node); - function SCMagnitude() { - this.__initializeWith__("Object"); - } - klass.define(SCMagnitude, "Magnitude"); + return node; + }; - function SCChar() { - this.__initializeWith__("Magnitude"); - this._ = "\0"; - } - klass.define(SCChar, "Char : Magnitude", { - __tag: 1028 - }); + SCParser.prototype.parseIdentifier = function() { + var expr; + var marker; - function SCNumber() { - this.__initializeWith__("Magnitude"); - } - klass.define(SCNumber, "Number : Magnitude"); + marker = Marker.create(this.lexer); - function SCSimpleNumber() { - this.__initializeWith__("Number"); - } - klass.define(SCSimpleNumber, "SimpleNumber : Number"); + if (this.lookahead.type !== Syntax.Identifier) { + this.throwUnexpected(this.lookahead); + } - function SCInteger() { - this.__initializeWith__("SimpleNumber"); - this._ = 0; - } - klass.define(SCInteger, "Integer : SimpleNumber", { - __tag: 770 - }); + expr = this.lex(); + expr = Node.createIdentifier(expr.value); - function SCFloat() { - this.__initializeWith__("SimpleNumber"); - this._ = 0.0; - } - klass.define(SCFloat, "Float : SimpleNumber", { - __tag: 777 - }); + return marker.update().apply(expr); + }; - function SCCollection() { - this.__initializeWith__("Object"); - } - klass.define(SCCollection, "Collection"); + SCParser.prototype.parseVariableIdentifier = function() { + var token, value, ch; + var id, marker; - function SCSequenceableCollection() { - this.__initializeWith__("Collection"); - } - klass.define(SCSequenceableCollection, "SequenceableCollection : Collection"); + marker = Marker.create(this.lexer); - function SCArrayedCollection() { - this.__initializeWith__("SequenceableCollection"); - this._immutable = false; - this._ = []; - } - klass.define(SCArrayedCollection, "ArrayedCollection : SequenceableCollection"); + token = this.lex(); + value = token.value; - function SCRawArray() { - this.__initializeWith__("ArrayedCollection"); - } - klass.define(SCRawArray, "RawArray : ArrayedCollection"); + if (token.type !== Token.Identifier) { + this.throwUnexpected(token); + } else { + ch = value.charAt(0); + if (("A" <= ch && ch <= "Z") || ch === "_") { + this.throwUnexpected(token); + } + } - function SCArray() { - this.__initializeWith__("ArrayedCollection"); - } - klass.define(SCArray, "Array : ArrayedCollection", { - __tag: 11 - }); + id = Node.createIdentifier(value); - function SCString(value) { - this.__initializeWith__("RawArray"); - this._ = value; - } - klass.define(SCString, "String : RawArray", { - __tag: 1034 - }); + return marker.update().apply(id); + }; - function SCAbstractFunction() { - this.__initializeWith__("Object"); - } - klass.define(SCAbstractFunction, "AbstractFunction"); + var renameGetterToSetter = function(methodName) { + switch (methodName) { + case "at" : return "put"; + case "copySeries": return "putSeries"; + } + return methodName + "_"; + }; - function SCFunction() { - this.__initializeWith__("AbstractFunction"); - // istanbul ignore next - this._ = function() {}; - } - klass.define(SCFunction, "Function : AbstractFunction", { - __tag: 12 - }); + var calcBinaryPrecedence = function(token, binaryPrecedence) { + var prec = 0; - // $SC - var $nil = new SCNil(); - var $true = new SCTrue(); - var $false = new SCFalse(); - var $integers = {}; - var $floats = {}; - var $symbols = {}; - var $chars = {}; + switch (token.type) { + case Token.Punctuator: + if (token.value !== "=") { + if (binaryPrecedence.hasOwnProperty(token.value)) { + prec = binaryPrecedence[token.value]; + } else if (/^[-+*\/%<=>!?&|@]+$/.test(token.value)) { + prec = 255; + } + } + break; + case Token.Label: + prec = 255; + break; + } - $SC.Nil = function() { - return $nil; + return prec; }; - $SC.Boolean = function($value) { - return $value ? $true : $false; - }; + var isClassName = function(node) { + var name, ch; - $SC.True = function() { - return $true; - }; + if (node.type === Syntax.Identifier) { + name = node.value || node.name; + ch = name.charAt(0); + return "A" <= ch && ch <= "Z"; + } - $SC.False = function() { - return $false; + return false; }; - $SC.Integer = function(value) { - var instance; + var isLeftHandSide = function(expr) { + switch (expr.type) { + case Syntax.Identifier: + case Syntax.GlobalExpression: + return true; + } + return false; + }; - if (!global.isFinite(value)) { - return $SC.Float(+value); + var isValidArgumentValue = function(node) { + if (node.type === Syntax.Literal) { + return true; + } + if (node.type === Syntax.ListExpression) { + return node.elements.every(function(node) { + return node.type === Syntax.Literal; + }); } - value = value|0; + return false; + }; - if (!$integers.hasOwnProperty(value)) { - instance = new SCInteger(); - instance._ = value; - $integers[value] = instance; + var findString$InterpolatedString = function(value, index) { + var len, ch; + + len = value.length; + + while (index < len) { + ch = value.charAt(index); + if (ch === "#") { + if (value.charAt(index + 1) === "{") { + break; + } + } else if (ch === "\\") { + index += 1; + } + index += 1; } - return $integers[value]; + return index; }; - $SC.Float = function(value) { - var instance; + var findExpression$InterpolatedString = function(value, index) { + var len, depth, ch; - value = +value; + len = value.length; - if (!$floats.hasOwnProperty(value)) { - instance = new SCFloat(); - instance._ = value; - $floats[value] = instance; + depth = 0; + while (index < len) { + ch = value.charAt(index); + if (ch === "}") { + if (depth === 0) { + break; + } + depth -= 1; + } else if (ch === "{") { + depth += 1; + } + index += 1; } - return $floats[value]; + return index; }; - $SC.Symbol = function(value) { - var instance; - if (!$symbols.hasOwnProperty(value)) { - instance = new SCSymbol(); - instance._ = value; - $symbols[value] = instance; + parser.parse = function(source, opts) { + var instance, ast; + + opts = opts || /* istanbul ignore next */ {}; + + instance = new SCParser(source, opts); + ast = instance.parse(); + + if (!!opts.tokens && typeof instance.lexer.tokens !== "undefined") { + ast.tokens = instance.lexer.tokens; } - return $symbols[value]; + if (!!opts.tolerant && typeof instance.lexer.errors !== "undefined") { + ast.errors = instance.lexer.errors; + } + + return ast; }; - $SC.Char = function(value) { - var instance; + sc.lang.parser = parser; - value = String(value).charAt(0); +})(sc); - if (!$chars.hasOwnProperty(value)) { - instance = new SCChar(); - instance._ = value; - $chars[value] = instance; +// src/sc/libs/random.js +(function(sc) { + + var random = {}; + + function RandGen(seed) { + this.setSeed(seed); + } + + RandGen.prototype.setSeed = function(seed) { + if (typeof seed !== "number") { + seed = Date.now(); } + seed += ~(seed << 15); + seed ^= seed >>> 10; + seed += seed << 3; + seed ^= seed >>> 6; + seed += ~(seed << 11); + seed ^= seed >>> 16; - return $chars[value]; - }; + this.x = 1243598713 ^ seed; + this.y = 3093459404 ^ seed; + this.z = 1821928721 ^ seed; - $SC.Array = function(value, immutable) { - var instance = new SCArray(); - instance._ = value || []; - instance._immutable = !!immutable; - return instance; + return this; }; - $SC.String = function(value, immutable) { - var instance = new SCString(); - instance._ = String(value).split("").map($SC.Char); - instance._immutable = !!immutable; - return instance; + RandGen.prototype.trand = function() { + this.x = ((this.x & 4294967294) << 12) ^ (((this.x << 13) ^ this.x) >>> 19); + this.y = ((this.y & 4294967288) << 4) ^ (((this.y << 2) ^ this.y) >>> 25); + this.z = ((this.z & 4294967280) << 17) ^ (((this.z << 3) ^ this.z) >>> 11); + return this.x ^ this.y ^ this.z; }; - $SC.Function = function(value) { - var instance = new SCFunction(); - instance._ = value; - return instance; + RandGen.prototype.next = function() { + return (this.trand() >>> 0) / 4294967296; }; -})(sc); - -// src/sc/lang/klass-utils.js -(function(sc) { - - var $SC = sc.lang.$SC; - var klass = sc.lang.klass; + RandGen.prototype.RandGen = RandGen; - var utils = { - BOOL: function(a) { - return a.__bool__(); - }, - $nil : $SC.Nil(), - $true : $SC.True(), - $false: $SC.False(), - $int_0: $SC.Integer(0), - $int_1: $SC.Integer(1), - nop: function() { - return this; - }, - alwaysReturn$nil : $SC.Nil, - alwaysReturn$true : $SC.True, - alwaysReturn$false: $SC.False, - alwaysReturn$int_0: function() { - return utils.$int_0; - }, - alwaysReturn$int_1: function() { - return utils.$int_1; + random = { + RandGen: RandGen, + current: new RandGen(), + next: function() { + return random.current.next(); }, - getMethod: function(className, methodName) { - return klass.get(className)._Spec.prototype[methodName]; + setSeed: function(seed) { + return random.current.setSeed(seed); } }; - klass.utils = utils; + sc.libs.random = random; })(sc); -// src/sc/lang/iterator.js +// src/sc/libs/mathlib.js (function(sc) { - var iterator = {}; - var $SC = sc.lang.$SC; - var utils = sc.lang.klass.utils; - var $nil = utils.$nil; - var $int_0 = utils.$int_0; - var $int_1 = utils.$int_1; - var BOOL = utils.BOOL; + var rand = sc.libs.random; + var mathlib = {}; - var __stop__ = function() { - return null; + mathlib.rand = function(a) { + return rand.next() * a; }; - var nop_iter = { - next: __stop__ + mathlib["+"] = function(a, b) { + return a + b; }; - var one_shot_iter = function(value) { - var iter = { - next: function() { - iter.next = __stop__; - return value; - } - }; - return iter; - }; - - // TODO: async function - iterator.execute = function(iter, $function) { - var $item, ret, i = 0; - - while (($item = iter.next()) !== null) { - if (Array.isArray($item)) { - ret = $function.value($item[0], $item[1]); - } else { - ret = $function.value($item, $SC.Integer(i++)); - } - if (ret === 65535) { - break; - } - } + mathlib["-"] = function(a, b) { + return a - b; }; - iterator.object$do = one_shot_iter; - - iterator.function$while = function($function) { - var iter = { - next: function() { - if (BOOL($function.value())) { - return [ $nil, $nil ]; - } - iter.next = __stop__; - return null; - } - }; - - return iter; + mathlib["*"] = function(a, b) { + return a * b; }; - var sc_incremental_iter = function($start, $end, $step) { - var $i = $start, iter = { - next: function() { - var $ret = $i; - $i = $i ["+"] ($step); - if ($i > $end) { - iter.next = __stop__; - } - return $ret; - } - }; - return iter; + mathlib["/"] = function(a, b) { + return a / b; }; - var sc_decremental_iter = function($start, $end, $step) { - var $i = $start, iter = { - next: function() { - var $ret = $i; - $i = $i ["+"] ($step); - if ($i < $end) { - iter.next = __stop__; - } - return $ret; - } - }; - return iter; + mathlib.mod = function(a, b) { + if (a === 0 || b === 0) { + return 0; + } + if ((a > 0 && b < 0) || (a < 0 && b > 0)) { + return b + a % b; + } + return a % b; }; - var sc_numeric_iter = function($start, $end, $step) { - if ($start.valueOf() === $end.valueOf()) { - return one_shot_iter($start); - } else if ($start < $end && $step > 0) { - return sc_incremental_iter($start, $end, $step); - } else if ($start > $end && $step < 0) { - return sc_decremental_iter($start, $end, $step); + mathlib.div = function(a, b) { + if (b === 0) { + return a|0; } - return nop_iter; + return (a / b)|0; }; - iterator.number$do = function($end) { - var $start, $step; - - $start = $int_0; - $end = $end.__dec__(); - $step = $int_1; - - return sc_numeric_iter($start, $end, $step); + mathlib.pow = function(a, b) { + return Math.pow(a, b); }; - iterator.number$reverseDo = function($start) { - var $end, $step; - - $start = $start.__dec__(); - $end = $int_0; - $step = $SC.Integer(-1); + mathlib.min = Math.min; + mathlib.max = Math.max; - return sc_numeric_iter($start, $end, $step); + mathlib.bitAnd = function(a, b) { + return a & b; }; - iterator.number$for = function($start, $end) { - var $step; - - $step = ($start <= $end) ? $int_1 : $SC.Integer(-1); - - return sc_numeric_iter($start, $end, $step); + mathlib.bitOr = function(a, b) { + return a | b; }; - iterator.number$forBy = function($start, $end, $step) { - return sc_numeric_iter($start, $end, $step); + mathlib.bitXor = function(a, b) { + return a ^ b; }; - iterator.number$forSeries = function($start, $second, $last) { - var $step; - - $step = $second ["-"] ($start); + var gcd = function(a, b) { + var t; - return sc_numeric_iter($start, $last, $step); - }; + a = a|0; + b = b|0; - var js_incremental_iter = function(start, end, step, type) { - var i = start, iter = { - next: function() { - var ret = i; - i += step; - if (i > end) { - iter.next = __stop__; - } - return type(ret); - } - }; - return iter; - }; + while (b !== 0) { + t = a % b; + a = b; + b = t; + } - var js_decremental_iter = function(start, end, step, type) { - var i = start, iter = { - next: function() { - var ret = i; - i += step; - if (i < end) { - iter.next = __stop__; - } - return type(ret); - } - }; - return iter; + return Math.abs(a); }; - var js_numeric_iter = function(start, end, step, type) { - if (start === end) { - return one_shot_iter(type(start)); - } else if (start < end && step > 0) { - return js_incremental_iter(start, end, step, type); - } else if (start > end && step < 0) { - return js_decremental_iter(start, end, step, type); + mathlib.lcm = function(a, b) { + if (a === 0 && b === 0) { + return 0; } - return nop_iter; + return Math.abs((a|0) * (b|0)) / gcd(a, b); }; - var js_numeric_iter$do = function($endval, type) { - var end = type($endval.__num__()).valueOf(); - return js_numeric_iter(0, end - 1, +1, type); + mathlib.gcd = function(a, b) { + return gcd(a, b); }; - var js_numeric_iter$reverseDo = function($startval, type) { - var start = type($startval.__num__()).valueOf(); - var end = (start|0) - start; - return js_numeric_iter(start - 1, end, -1, type); + mathlib.round = function(a, b) { + return b === 0 ? a : Math.round(a / b) * b; }; - var js_numeric_iter$for = function($startval, $endval, type) { - var start = type($startval.__num__()).valueOf(); - var end = type($endval .__num__()).valueOf(); - var step = (start <= end) ? +1 : -1; + mathlib.roundUp = function(a, b) { + return b === 0 ? a : Math.ceil(a / b) * b; + }; - return js_numeric_iter(start, end, step, type); + mathlib.trunc = function(a, b) { + return b === 0 ? a : Math.floor(a / b) * b; }; - var js_numeric_iter$forBy = function($startval, $endval, $stepval, type) { - var start = type($startval.__num__()).valueOf(); - var end = type($endval .__num__()).valueOf(); - var step = type($stepval .__num__()).valueOf(); + mathlib.atan2 = Math.atan2; - return js_numeric_iter(start, end, step, type); + mathlib.hypot = function(a, b) { + return Math.sqrt((a * a) + (b * b)); }; - var js_numeric_iter$forSeries = function($startval, $second, $last, type) { - var start = type($startval.__num__()).valueOf(); - var second = type($second .__num__()).valueOf(); - var end = type($last .__num__()).valueOf(); - var step = second - start; - - return js_numeric_iter(start, end, step, type); + mathlib.hypotApx = function(a, b) { + var x = Math.abs(a); + var y = Math.abs(b); + var minxy = Math.min(x, y); + return x + y - (Math.sqrt(2) - 1) * minxy; }; - iterator.integer$do = function($endval) { - return js_numeric_iter$do($endval, $SC.Integer); + mathlib.leftShift = function(a, b) { + if (b < 0) { + return a >> -b; + } + return a << b; }; - iterator.integer$reverseDo = function($startval) { - return js_numeric_iter$reverseDo($startval, $SC.Integer); + mathlib.rightShift = function(a, b) { + if (b < 0) { + return a << -b; + } + return a >> b; }; - iterator.integer$for = function($startval, $endval) { - return js_numeric_iter$for($startval, $endval, $SC.Integer); + mathlib.unsignedRightShift = function(a, b) { + if (b < 0) { + return (a << -b) >>> 0; + } + return a >>> b; }; - iterator.integer$forBy = function($startval, $endval, $stepval) { - return js_numeric_iter$forBy($startval, $endval, $stepval, $SC.Integer); + mathlib.ring1 = function(a, b) { + return a * b + a; }; - iterator.integer$forSeries = function($startval, $second, $last) { - return js_numeric_iter$forSeries($startval, $second, $last, $SC.Integer); + mathlib.ring2 = function(a, b) { + return a * b + a + b; }; - iterator.float$do = function($endval) { - return js_numeric_iter$do($endval, $SC.Float); + mathlib.ring3 = function(a, b) { + return a * a * b; }; - iterator.float$reverseDo = function($startval) { - return js_numeric_iter$reverseDo($startval, $SC.Float); + mathlib.ring4 = function(a, b) { + return a * a * b - a * b * b; }; - iterator.float$for = function($startval, $endval) { - return js_numeric_iter$for($startval, $endval, $SC.Float); + mathlib.difsqr = function(a, b) { + return a * a - b * b; }; - iterator.float$forBy = function($startval, $endval, $stepval) { - return js_numeric_iter$forBy($startval, $endval, $stepval, $SC.Float); + mathlib.sumsqr = function(a, b) { + return a * a + b * b; }; - iterator.float$forSeries = function($startval, $second, $last) { - return js_numeric_iter$forSeries($startval, $second, $last, $SC.Float); + mathlib.sqrsum = function(a, b) { + return (a + b) * (a + b); }; - var list_iter = function(list) { - var i = 0, iter = { - next: function() { - var $ret = list[i++]; - if (i >= list.length) { - iter.next = __stop__; - } - return $ret; - } - }; - return iter; + mathlib.sqrdif = function(a, b) { + return (a - b) * (a - b); }; - var js_array_iter = function(list) { - if (list.length) { - return list_iter(list); - } - return nop_iter; + mathlib.absdif = function(a, b) { + return Math.abs(a - b); }; - iterator.array$do = function($array) { - return js_array_iter($array._.slice()); + mathlib.thresh = function(a, b) { + return a < b ? 0 : a; }; - iterator.array$reverseDo = function($array) { - return js_array_iter($array._.slice().reverse()); + mathlib.amclip = function(a, b) { + return a * 0.5 * (b + Math.abs(b)); }; - sc.lang.iterator = iterator; + mathlib.scaleneg = function(a, b) { + b = 0.5 * b + 0.5; + return (Math.abs(a) - a) * b + a; + }; -})(sc); + mathlib.clip2 = function(a, b) { + return Math.max(-b, Math.min(a, b)); + }; -// src/sc/lang/classlib.js -(function(sc) { + mathlib.fold2 = function(a, b) { + var x, c, range, range2; - sc.lang.classlib = {}; + if (b === 0) { + return 0; + } -})(sc); + x = a + b; + if (a >= b) { + a = b + b - a; + if (a >= -b) { + return a; + } + } else if (a < -b) { + a = -b - b - a; + if (a < b) { + return a; + } + } else { + return a; + } -// src/sc/lang/classlib/Core/Object.js -(function(sc) { + range = b + b; + range2 = range + range; + c = x - range2 * Math.floor(x / range2); - var slice = [].slice; - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; + if (c >= range) { + c = range2 - c; + } - sc.lang.klass.refine("Object", function(spec, utils) { - var BOOL = utils.BOOL; - var $nil = utils.$nil; - var $true = utils.$true; - var $false = utils.$false; - var $int_1 = utils.$int_1; - var SCArray = $SC("Array"); + return c - b; + }; - spec.__num__ = function() { - throw new Error("Wrong Type"); - }; + mathlib.wrap2 = function(a, b) { + var range; - spec.__int__ = function() { - return this.__num__()|0; - }; + if (b === 0) { + return 0; + } - spec.__bool__ = function() { - throw new Error("Wrong Type"); - }; + if (a >= b) { + range = b + b; + a -= range; + if (a < b) { + return a; + } + } else if (a < -b) { + range = b + b; + a += range; + if (a >= -b) { + return a; + } + } else { + return a; + } - spec.__sym__ = function() { - throw new Error("Wrong Type"); - }; + return a - range * Math.floor((a + b) / range); + }; - spec.__str__ = function() { - return String(this); - }; + mathlib.excess = function(a, b) { + return a - Math.max(-b, Math.min(a, b)); + }; - // TODO: implements $new - // TODO: implements $newCopyArgs + mathlib.firstArg = function(a) { + return a; + }; - spec.$newFrom = function() { - return this._doesNotUnderstand("newFrom"); - }; + mathlib.rrand = function(a, b) { + return a + rand.next() * (b - a); + }; - // TODO: implements dump - // TODO: implements post - // TODO: implements postln - // TODO: implements postc - // TODO: implements postcln - // TODO: implements postcs - // TODO: implements totalFree - // TODO: implements largestFreeBlock - // TODO: implements gcDumpGrey - // TODO: implements gcDumpSet - // TODO: implements gcInfo - // TODO: implements gcSanity - // TODO: implements canCallOS + mathlib.exprand = function(a, b) { + return a * Math.exp(Math.log(b / a) * rand.next()); + }; - spec.size = utils.alwaysReturn$int_0; - spec.indexedSize = utils.alwaysReturn$int_0; - spec.flatSize = utils.alwaysReturn$int_1; + mathlib.clip = function(val, lo, hi) { + return Math.max(lo, Math.min(val, hi)); + }; - spec.do = function($function) { - sc.lang.iterator.execute( - sc.lang.iterator.object$do(this), - $function - ); + mathlib.iwrap = function(val, lo, hi) { + var range; - return this; - }; + range = hi - lo + 1; + val -= range * Math.floor((val - lo) / range); - spec.generate = fn(function($function, $state) { - this.do($function); + return val; + }; - return $state; - }, "function; state"); + mathlib.wrap = function(val, lo, hi) { + var range; - // already defined: class - // already defined: isKindOf - // already defined: isMemberOf + if (hi === lo) { + return lo; + } - spec.respondsTo = fn(function($aSymbol) { - return $SC.Boolean(typeof this[$aSymbol.__sym__()] === "function"); - }, "aSymbol"); + range = (hi - lo); + if (val >= hi) { + val -= range; + if (val < hi) { + return val; + } + } else if (val < lo) { + val += range; + if (val >= lo) { + return val; + } + } else { + return val; + } - // TODO: implements performMsg + return val - range * Math.floor((val - lo) / range); + }; - spec.perform = function($selector) { - var selector, method; + mathlib.ifold = function(val, lo, hi) { + var x, range1, range2; - selector = $selector.__sym__(); - method = this[selector]; + range1 = hi - lo; + range2 = range1 * 2; + x = val - lo; + x -= range2 * Math.floor(x / range2); - if (method) { - return method.apply(this, slice.call(arguments, 1)); - } + if (x >= range1) { + return range2 - x + lo; + } - throw new Error("Message '" + selector + "' not understood."); - }; + return x + lo; + }; - spec.performList = function($selector, $arglist) { - var selector, method; + mathlib.fold = function(val, lo, hi) { + var x, range1, range2; - selector = $selector.__sym__(); - method = this[selector]; + if (hi === lo) { + return lo; + } - if (method) { - return method.apply(this, $arglist.asArray()._); + if (val >= hi) { + val = (hi * 2) - val; + if (val >= lo) { + return val; + } + } else if (val < lo) { + val = (lo * 2) - val; + if (val < hi) { + return val; } + } else { + return val; + } - throw new Error("Message '" + selector + "' not understood."); - }; + range1 = hi - lo; + range2 = range1 * 2; + x = val - lo; + x -= range2 * Math.floor(x / range2); - spec.functionPerformList = utils.nop; + if (x >= range1) { + return range2 - x + lo; + } - // TODO: implements superPerform - // TODO: implements superPerformList - // TODO: implements tryPerform - // TODO: implements multiChannelPerform - // TODO: implements performWithEnvir - // TODO: implements performKeyValuePairs + return x + lo; + }; - var copy = function(obj) { - var copied = obj; + mathlib.clip_idx = function(index, len) { + return Math.max(0, Math.min(index, len - 1)); + }; - if (Array.isArray(obj)) { - copied = obj.slice(); - } else if (obj && obj.constructor === Object) { - copied = {}; - Object.keys(obj).forEach(function(key) { - copied[key] = obj[key]; - }); - } + mathlib.wrap_idx = function(index, len) { + index = index % len; + if (index < 0) { + index += len; + } + return index; + }; - return copied; - }; + mathlib.fold_idx = function(index, len) { + var len2 = len * 2 - 2; - spec.copy = function() { - return this.shallowCopy(); - }; + index = (index|0) % len2; + if (index < 0) { + index += len2; + } + if (len <= index) { + return len2 - index; + } + return index; + }; - // TODO: implements contentsCopy + sc.libs.mathlib = mathlib; - spec.shallowCopy = function() { - var a = new this.__class._Spec(); +})(sc); - Object.keys(this).forEach(function(key) { - a[key] = copy(this[key]); - }, this); +// src/sc/lang/dollarSC.js +(function(sc) { - if (this._ === this) { - a._ = a; - } + var $SC = function(name) { + return sc.lang.klass.get(name); + }; - return a; - }; + /* istanbul ignore next */ + var shouldBeImplementedInClassLib = function() {}; - // TODO: implements copyImmutable - // TODO: implements deepCopy + $SC.Class = shouldBeImplementedInClassLib; + $SC.Integer = shouldBeImplementedInClassLib; + $SC.Float = shouldBeImplementedInClassLib; + $SC.Char = shouldBeImplementedInClassLib; + $SC.Array = shouldBeImplementedInClassLib; + $SC.String = shouldBeImplementedInClassLib; + $SC.Function = shouldBeImplementedInClassLib; + $SC.Ref = shouldBeImplementedInClassLib; + $SC.Symbol = shouldBeImplementedInClassLib; + $SC.Boolean = shouldBeImplementedInClassLib; + $SC.True = shouldBeImplementedInClassLib; + $SC.False = shouldBeImplementedInClassLib; + $SC.Nil = shouldBeImplementedInClassLib; - spec.dup = fn(function($n) { - var $this = this; - var $array, i, imax; + sc.lang.$SC = $SC; - if (BOOL($n.isSequenceableCollection())) { - return SCArray.fillND($n, $SC.Function(function() { - return $this.copy(); - })); - } +})(sc); - $array = SCArray.new($n); - for (i = 0, imax = $n.__int__(); i < imax; ++i) { - $array.add(this.copy()); - } +// src/sc/lang/fn.js +(function(sc) { - return $array; - }, "n=2"); + var slice = [].slice; + var $SC = sc.lang.$SC; - spec["!"] = function($n) { - return this.dup($n); - }; + var _getDefaultValue = function(value) { + var ch; - spec.poll = function() { - return this.value(); - }; + switch (value) { + case "nil": + return $SC.Nil(); + case "true": + return $SC.True(); + case "false": + return $SC.False(); + case "inf": + return $SC.Float(Infinity); + case "-inf": + return $SC.Float(-Infinity); + } - spec.value = utils.nop; - spec.valueArray = utils.nop; - spec.valueEnvir = utils.nop; - spec.valueArrayEnvir = utils.nop; + ch = value.charAt(0); + switch (ch) { + case "$": + return $SC.Char(value.charAt(1)); + case "\\": + return $SC.Symbol(value.substr(1)); + } - spec["=="] = function($obj) { - return this ["==="] ($obj); - }; + if (value.indexOf(".") !== -1) { + return $SC.Float(+value); + } - spec["!="] = function($obj) { - return (this ["=="] ($obj)).not(); - }; + return $SC.Integer(+value); + }; - spec["==="] = function($obj) { - return $SC.Boolean(this === $obj); - }; + var getDefaultValue = function(value) { + if (value.charAt(0) === "[") { + return $SC.Array(value.slice(1, -2).split(",").map(function(value) { + return _getDefaultValue(value.trim()); + })); + } + return _getDefaultValue(value); + }; - spec["!=="] = function($obj) { - return $SC.Boolean(this !== $obj); - }; + var fn = function(func, def) { + var argItems, argNames, argVals; + var remain, wrapper; - // TODO: implements equals - // TODO: implements compareObject - // TODO: implements instVarHash - // TODO: implements basicHash - // TODO: implements hash - // TODO: implements identityHash + argItems = def.split(/\s*;\s*/); + if (argItems[argItems.length - 1].charAt(0) === "*") { + remain = !!argItems.pop(); + } - spec["->"] = function($obj) { - return $SC("Association").new(this, $obj); - }; + argNames = new Array(argItems.length); + argVals = new Array(argItems.length); - spec.next = utils.nop; - spec.reset = utils.nop; + argItems.forEach(function(items, i) { + items = items.split("="); + argNames[i] = items[0].trim(); + argVals [i] = getDefaultValue(items[1] || "nil"); + }); - spec.first = fn(function($inval) { - this.reset(); - return this.next($inval); - }, "inval"); + wrapper = function() { + var given, args; - spec.iter = function() { - return $SC("OneShotStream").new(this); - }; + given = slice.call(arguments); + args = argVals.slice(); - spec.stop = utils.nop; - spec.free = utils.nop; - spec.clear = utils.nop; - spec.removedFromScheduler = utils.nop; - spec.isPlaying = utils.alwaysReturn$false; + if (isDictionary(given[given.length - 1])) { + setKeywordArguments(args, argNames, given.pop()); + } - spec.embedInStream = function() { - return this.yield(); + copy(args, given, Math.min(argNames.length, given.length)); + + if (remain) { + args.push($SC.Array(given.slice(argNames.length))); + } + + return func.apply(this, args); }; - // TODO: implements cyc - // TODO: implements fin - // TODO: implements repeat - // TODO: implements loop + wrapper._argNames = argNames; + wrapper._argVals = argVals; - spec.asStream = utils.nop; + return wrapper; + }; - // TODO: implements streamArg + var isDictionary = function(obj) { + return !!(obj && obj.constructor === Object); + }; - spec.eventAt = utils.alwaysReturn$nil; + var copy = function(args, given, length) { + for (var i = 0; i < length; ++i) { + if (given[i]) { + args[i] = given[i]; + } + } + }; - spec.composeEvents = fn(function($event) { - return $event.copy(); - }, "event"); + var setKeywordArguments = function(args, argNames, dict) { + Object.keys(dict).forEach(function(key) { + var index = argNames.indexOf(key); + if (index !== -1) { + args[index] = dict[key]; + } + }); + }; - spec.finishEvent = utils.nop; - spec.atLimit = utils.alwaysReturn$false; - spec.isRest = utils.alwaysReturn$false; - spec.threadPlayer = utils.nop; - spec.threadPlayer_ = utils.nop; - spec["?"] = utils.nop; - spec["??"] = utils.nop; + sc.lang.fn = fn; - spec["!?"] = function($obj) { - return $obj.value(this); - }; +})(sc); - spec.isNil = utils.alwaysReturn$false; - spec.notNil = utils.alwaysReturn$true; - spec.isNumber = utils.alwaysReturn$false; - spec.isInteger = utils.alwaysReturn$false; - spec.isFloat = utils.alwaysReturn$false; - spec.isSequenceableCollection = utils.alwaysReturn$false; - spec.isCollection = utils.alwaysReturn$false; - spec.isArray = utils.alwaysReturn$false; - spec.isString = utils.alwaysReturn$false; - spec.containsSeqColl = utils.alwaysReturn$false; - spec.isValidUGenInput = utils.alwaysReturn$false; - spec.isException = utils.alwaysReturn$false; - spec.isFunction = utils.alwaysReturn$false; +// src/sc/lang/klass/klass.js +(function(sc) { - spec.matchItem = fn(function($item) { - return this ["==="] ($item); - }, "item"); + var slice = [].slice; + var $SC = sc.lang.$SC; - spec.trueAt = utils.alwaysReturn$false; + var klass = {}; + var metaClasses = {}; + var classes = klass.classes = {}; - spec.falseAt = fn(function($key) { - return this.trueAt($key).not(); - }, "key"); + var createClassInstance = function(MetaSpec) { + var instance = new SCClass(); + instance._MetaSpec = MetaSpec; + return instance; + }; - // TODO: implements pointsTo - // TODO: implements mutable - // TODO: implements frozen - // TODO: implements halt - // TODO: implements primitiveFailed - // TODO: implements reportError - // TODO: implements subclassResponsibility - spec._subclassResponsibility = function(methodName) { - throw new Error("RECEIVER " + String(this) + ": " + - "'" + methodName + "' should have been implemented by subclass"); - }; + var extend = function(constructor, superMetaClass) { + function F() {} + F.prototype = superMetaClass._Spec.prototype; + constructor.prototype = new F(); - // TODO: implements doesNotUnderstand - spec._doesNotUnderstand = function(methodName) { - throw new Error("RECEIVER " + this.__str__() + ": " + - "Message '" + methodName + "' not understood."); - }; + function Meta_F() {} + Meta_F.prototype = superMetaClass._MetaSpec.prototype; - // TODO: implements shouldNotImplement - // TODO: implements outOfContextReturn - // TODO: implements immutableError - // TODO: implements deprecated - // TODO: implements mustBeBoolean - // TODO: implements notYetImplemented - // TODO: implements dumpBackTrace - // TODO: implements getBackTrace - // TODO: implements throw + function MetaSpec() {} + MetaSpec.prototype = new Meta_F(); - spec.species = function() { - return this.class(); - }; + constructor.metaClass = createClassInstance(MetaSpec); + }; - spec.asCollection = function() { - return $SC.Array([ this ]); - }; + var def = function(className, constructor, fn, opts) { + var classMethods, instanceMethods, setMethod, spec; - spec.asSymbol = function() { - return this.asString().asSymbol(); - }; + classMethods = constructor.metaClass._MetaSpec.prototype; + instanceMethods = constructor.prototype; - spec.asString = function() { - return $SC.String(String(this)); + setMethod = function(methods, methodName, func) { + var bond; + if (methods.hasOwnProperty(methodName) && !opts.force) { + bond = methods === classMethods ? "." : "#"; + throw new Error( + "sc.lang.klass.refine: " + + className + bond + methodName + " is already defined." + ); + } + methods[methodName] = func; }; - // TODO: implements asCompileString - // TODO: implements cs - // TODO: implements printClassNameOn - // TODO: implements printOn - // TODO: implements storeOn - // TODO: implements storeParamsOn - // TODO: implements simplifyStoreArgs - // TODO: implements storeArgs - // TODO: implements storeModifiersOn + if (typeof fn === "function") { + fn(spec = {}, klass.utils); + } else { + spec = fn; + } - spec.as = fn(function($aSimilarClass) { - return $aSimilarClass.newFrom(this); - }, "aSimilarClass"); + Object.keys(spec).forEach(function(methodName) { + if (methodName.charCodeAt(0) === 0x24) { // u+0024 is '$' + setMethod(classMethods, methodName.substr(1), spec[methodName]); + } else { + setMethod(instanceMethods, methodName, spec[methodName]); + } + }); + }; - spec.dereference = utils.nop; + var throwIfInvalidArgument = function(constructor, className) { + if (typeof constructor !== "function") { + throw new Error( + "sc.lang.klass.define: " + + "first argument must be a constructor, but got: " + typeof(constructor) + ); + } - spec.reference = function() { - return $SC.Ref(this); - }; + if (typeof className !== "string") { + throw new Error( + "sc.lang.klass.define: " + + "second argument must be a string, but got: " + String(className) + ); + } + }; - spec.asRef = function() { - return $SC.Ref(this); - }; + var throwIfInvalidClassName = function(className, superClassName) { + var ch0 = className.charCodeAt(0); - spec.asArray = function() { - return this.asCollection().asArray(); - }; + if (ch0 < 0x41 || 0x5a < ch0) { // faster test than !/^[A-Z]/.test(className) + throw new Error( + "sc.lang.klass.define: " + + "classname should be CamelCase, but got '" + className + "'" + ); + } - spec.asSequenceableCollection = function() { - return this.asArray(); - }; + if (metaClasses.hasOwnProperty(className)) { + throw new Error( + "sc.lang.klass.define: " + + "class '" + className + "' is already registered." + ); + } - spec.rank = utils.alwaysReturn$int_0; + if (className !== "Object") { + if (!metaClasses.hasOwnProperty(superClassName)) { + throw new Error( + "sc.lang.klass.define: " + + "superclass '" + superClassName + "' is not registered." + ); + } + } + }; - spec.deepCollect = fn(function($depth, $function, $index, $rank) { - return $function.value(this, $index, $rank); - }, "depth; function; index; rank"); + var buildClass = function(className, constructor) { + var newClass, metaClass; - spec.deepDo = fn(function($depth, $function, $index, $rank) { - $function.value(this, $index, $rank); - return this; - }, "depth; function; index; rank"); + metaClass = constructor.metaClass; - spec.slice = utils.nop; - spec.shape = utils.alwaysReturn$nil; - spec.unbubble = utils.nop; + newClass = new metaClass._MetaSpec(); + newClass._name = className; + newClass._Spec = constructor; + constructor.prototype.__class = newClass; + constructor.prototype.__Spec = constructor; - spec.bubble = fn(function($depth, $levels) { - var levels, a; + metaClass._Spec = constructor; + metaClass._isMetaClass = true; + metaClass._name = "Meta_" + className; - levels = $levels.__int__(); - if (levels <= 1) { - a = [ this ]; - } else { - a = [ - this.bubble($depth, $SC.Integer(levels - 1)) - ]; - } + classes["Meta_" + className] = metaClass; + classes[className] = newClass; - return $SC.Array(a); - }, "depth; levels"); + if (newClass.initClass) { + newClass.initClass(); + } - spec.obtain = fn(function($index, $default) { - if ($index.__num__() === 0) { - return this; - } else { - return $default; - } - }, "index; defaults"); + metaClasses[className] = metaClass; + }; - spec.instill = fn(function($index, $item, $default) { - if ($index.__num__() === 0) { - return $item; - } else { - return this.asArray().instill($index, $item, $default); - } - }, "index; item; default"); + klass.define = function(constructor, className, fn) { + var items, superClassName; - spec.addFunc = fn(function($$functions) { - return $SC("FunctionList").new(this ["++"] ($$functions)); - }, "*functions"); + throwIfInvalidArgument(constructor, className); - spec.removeFunc = function($function) { - if (this === $function) { - return $nil; - } - return this; - }; + items = className.split(":"); + className = items[0].trim(); + superClassName = (items[1] || "Object").trim(); - spec.replaceFunc = fn(function($find, $replace) { - if (this === $find) { - return $replace; - } - return this; - }, "find; replace"); + throwIfInvalidClassName(className, superClassName); - // TODO: implements addFuncTo - // TODO: implements removeFuncFrom + if (className !== "Object") { + extend(constructor, metaClasses[superClassName]); + } - spec.while = fn(function($body) { - var $this = this; + fn = fn || {}; - $SC.Function(function() { - return $this.value(); - }).while($SC.Function(function() { - return $body.value(); - })); + def(className, constructor, fn, {}); - return this; - }, "body"); + buildClass(className, constructor); + }; - spec.switch = function() { - var args, i, imax; + klass.refine = function(className, fn, opts) { + var constructor; - args = slice.call(arguments); - for (i = 0, imax = args.length >> 1; i < imax; i++) { - if (BOOL(this ["=="] (args[i * 2]))) { - return args[i * 2 + 1].value(); - } - } + if (!metaClasses.hasOwnProperty(className)) { + throw new Error( + "sc.lang.klass.refine: " + + "class '" + className + "' is not registered." + ); + } - if (args.length % 2 === 1) { - return args[args.length - 1].value(); - } + constructor = metaClasses[className]._Spec; - return $nil; - }; + def(className, constructor, fn, opts || {}); + }; - spec.yield = function() { - // TODO: implements yield - }; + klass.get = function(name) { + if (!classes[name]) { + throw new Error( + "sc.lang.klass.get: " + + "class '" + name + "' is not registered." + ); + } + return classes[name]; + }; - // TODO: implements alwaysYield - // TODO: implements yieldAndReset - // TODO: implements idle - // TODO: implements $initClass - // TODO: implements dependants - // TODO: implements changed - // TODO: implements addDependant - // TODO: implements removeDependant - // TODO: implements release - // TODO: implements releaseDependants - // TODO: implements update - // TODO: implements addUniqueMethod - // TODO: implements removeUniqueMethods - // TODO: implements removeUniqueMethod - // TODO: implements inspect - // TODO: implements inspectorClass - // TODO: implements inspector - // TODO: implements crash - // TODO: implements stackDepth - // TODO: implements dumpStack - // TODO: implements dumpDetailedBackTrace - // TODO: implements freeze + klass.exists = function(name) { + return !!classes[name]; + }; - spec["&"] = function($that) { - return this.bitAnd($that); - }; + // basic classes + function SCObject() { + this._ = this; + } - spec["|"] = function($that) { - return this.bitOr($that); + function SCClass() { + this._ = this; + this._name = "Class"; + this._Spec = null; + this._isMetaClass = false; + } + + SCObject.metaClass = createClassInstance(function() {}); + klass.define(SCObject, "Object", { + __tag: 1, + __initializeWith__: function(className, args) { + metaClasses[className]._Spec.apply(this, args); + }, + $initClass: function() {} + }); + + klass.define(SCClass, "Class"); + + SCObject.metaClass._MetaSpec.prototype = classes.Class = createClassInstance(); + classes.Class._Spec = SCClass; + classes.Object = new SCObject.metaClass._MetaSpec(); + classes.Object._name = "Object"; + classes.Object._Spec = SCObject.metaClass._Spec; + classes.Object._Spec.prototype.__class = classes.Object; + classes.Object._Spec.prototype.__Spec = classes.Object._Spec; + + klass.refine("Object", function(spec) { + spec.$new = function() { + if (this._Spec === SCClass) { + return $SC.Nil(); + } + return new this._Spec(slice.call(arguments)); }; - spec["%"] = function($that) { - return this.mod($that); + spec.class = function() { + return this.__class; }; - spec["**"] = function($that) { - return this.pow($that); + spec.isClass = function() { + return $SC.False(); }; - spec["<<"] = function($that) { - return this.leftShift($that); + spec.isKindOf = function($aClass) { + return $SC.Boolean(this instanceof $aClass._Spec); }; - spec[">>"] = function($that) { - return this.rightShift($that); + spec.isMemberOf = function($aClass) { + return $SC.Boolean(this.__class === $aClass); }; - spec["+>>"] = function($that) { - return this.unsignedRightShift($that); + spec.toString = function() { + var name = this.__class._name; + if (/^[AEIOU]/.test(name)) { + return String("an " + name); + } else { + return String("a " + name); + } }; - spec[" $end) { + iter.next = __stop__; + } + return $ret; + } }; + return iter; + }; - spec.tanh = function() { - return this.composeUnaryOp($SC.Symbol("tanh")); + var sc_decremental_iter = function($start, $end, $step) { + var $i = $start, iter = { + next: function() { + var $ret = $i; + $i = $i ["+"] ($step); + if ($i < $end) { + iter.next = __stop__; + } + return $ret; + } }; + return iter; + }; - spec.rand = function() { - return this.composeUnaryOp($SC.Symbol("rand")); - }; + var sc_numeric_iter = function($start, $end, $step) { + if ($start.valueOf() === $end.valueOf()) { + return one_shot_iter($start); + } else if ($start < $end && $step > 0) { + return sc_incremental_iter($start, $end, $step); + } else if ($start > $end && $step < 0) { + return sc_decremental_iter($start, $end, $step); + } + return nop_iter; + }; - spec.rand2 = function() { - return this.composeUnaryOp($SC.Symbol("rand2")); - }; + iterator.number$do = function($end) { + var $start, $step; - spec.linrand = function() { - return this.composeUnaryOp($SC.Symbol("linrand")); - }; + $start = $int_0; + $end = $end.__dec__(); + $step = $int_1; - spec.bilinrand = function() { - return this.composeUnaryOp($SC.Symbol("bilinrand")); - }; + return sc_numeric_iter($start, $end, $step); + }; - spec.sum3rand = function() { - return this.composeUnaryOp($SC.Symbol("sum3rand")); - }; + iterator.number$reverseDo = function($start) { + var $end, $step; - spec.distort = function() { - return this.composeUnaryOp($SC.Symbol("distort")); - }; + $start = $start.__dec__(); + $end = $int_0; + $step = $SC.Integer(-1); - spec.softclip = function() { - return this.composeUnaryOp($SC.Symbol("softclip")); - }; + return sc_numeric_iter($start, $end, $step); + }; - spec.coin = function() { - return this.composeUnaryOp($SC.Symbol("coin")); - }; + iterator.number$for = function($start, $end) { + var $step; - spec.even = function() { - return this.composeUnaryOp($SC.Symbol("even")); - }; + $step = ($start <= $end) ? $int_1 : $SC.Integer(-1); - spec.odd = function() { - return this.composeUnaryOp($SC.Symbol("odd")); - }; + return sc_numeric_iter($start, $end, $step); + }; - spec.rectWindow = function() { - return this.composeUnaryOp($SC.Symbol("rectWindow")); - }; + iterator.number$forBy = function($start, $end, $step) { + return sc_numeric_iter($start, $end, $step); + }; - spec.hanWindow = function() { - return this.composeUnaryOp($SC.Symbol("hanWindow")); - }; + iterator.number$forSeries = function($start, $second, $last) { + var $step; - spec.welWindow = function() { - return this.composeUnaryOp($SC.Symbol("welWindow")); - }; + $step = $second ["-"] ($start); - spec.triWindow = function() { - return this.composeUnaryOp($SC.Symbol("triWindow")); - }; + return sc_numeric_iter($start, $last, $step); + }; - spec.scurve = function() { - return this.composeUnaryOp($SC.Symbol("scurve")); + var js_incremental_iter = function(start, end, step, type) { + var i = start, iter = { + next: function() { + var ret = i; + i += step; + if (i > end) { + iter.next = __stop__; + } + return type(ret); + } }; + return iter; + }; - spec.ramp = function() { - return this.composeUnaryOp($SC.Symbol("ramp")); + var js_decremental_iter = function(start, end, step, type) { + var i = start, iter = { + next: function() { + var ret = i; + i += step; + if (i < end) { + iter.next = __stop__; + } + return type(ret); + } }; + return iter; + }; - spec.isPositive = function() { - return this.composeUnaryOp($SC.Symbol("isPositive")); - }; + var js_numeric_iter = function(start, end, step, type) { + if (start === end) { + return one_shot_iter(type(start)); + } else if (start < end && step > 0) { + return js_incremental_iter(start, end, step, type); + } else if (start > end && step < 0) { + return js_decremental_iter(start, end, step, type); + } + return nop_iter; + }; - spec.isNegative = function() { - return this.composeUnaryOp($SC.Symbol("isNegative")); - }; + var js_numeric_iter$do = function($endval, type) { + var end = type($endval.__num__()).valueOf(); + return js_numeric_iter(0, end - 1, +1, type); + }; - spec.isStrictlyPositive = function() { - return this.composeUnaryOp($SC.Symbol("isStrictlyPositive")); - }; + var js_numeric_iter$reverseDo = function($startval, type) { + var start = type($startval.__num__()).valueOf(); + var end = (start|0) - start; + return js_numeric_iter(start - 1, end, -1, type); + }; - spec.rho = function() { - return this.composeUnaryOp($SC.Symbol("rho")); - }; + var js_numeric_iter$for = function($startval, $endval, type) { + var start = type($startval.__num__()).valueOf(); + var end = type($endval .__num__()).valueOf(); + var step = (start <= end) ? +1 : -1; - spec.theta = function() { - return this.composeUnaryOp($SC.Symbol("theta")); - }; + return js_numeric_iter(start, end, step, type); + }; - spec.rotate = function($function) { - return this.composeBinaryOp($SC.Symbol("rotate"), $function); - }; + var js_numeric_iter$forBy = function($startval, $endval, $stepval, type) { + var start = type($startval.__num__()).valueOf(); + var end = type($endval .__num__()).valueOf(); + var step = type($stepval .__num__()).valueOf(); - spec.dist = function($function) { - return this.composeBinaryOp($SC.Symbol("dist"), $function); - }; + return js_numeric_iter(start, end, step, type); + }; - spec["+"] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("+"), $function, $adverb); - }; + var js_numeric_iter$forSeries = function($startval, $second, $last, type) { + var start = type($startval.__num__()).valueOf(); + var second = type($second .__num__()).valueOf(); + var end = type($last .__num__()).valueOf(); + var step = second - start; - spec["-"] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("-"), $function, $adverb); - }; + return js_numeric_iter(start, end, step, type); + }; - spec["*"] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("*"), $function, $adverb); - }; + iterator.integer$do = function($endval) { + return js_numeric_iter$do($endval, $SC.Integer); + }; - spec["/"] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("/"), $function, $adverb); - }; + iterator.integer$reverseDo = function($startval) { + return js_numeric_iter$reverseDo($startval, $SC.Integer); + }; - spec.div = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("div"), $function, $adverb); - }; + iterator.integer$for = function($startval, $endval) { + return js_numeric_iter$for($startval, $endval, $SC.Integer); + }; - spec.mod = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("mod"), $function, $adverb); - }; + iterator.integer$forBy = function($startval, $endval, $stepval) { + return js_numeric_iter$forBy($startval, $endval, $stepval, $SC.Integer); + }; - spec.pow = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("pow"), $function, $adverb); - }; + iterator.integer$forSeries = function($startval, $second, $last) { + return js_numeric_iter$forSeries($startval, $second, $last, $SC.Integer); + }; - spec.min = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("min"), $function, $adverb); - }; + iterator.float$do = function($endval) { + return js_numeric_iter$do($endval, $SC.Float); + }; - spec.max = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("max"), $function, $adverb); - }; + iterator.float$reverseDo = function($startval) { + return js_numeric_iter$reverseDo($startval, $SC.Float); + }; - spec["<"] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("<"), $function, $adverb); - }; + iterator.float$for = function($startval, $endval) { + return js_numeric_iter$for($startval, $endval, $SC.Float); + }; - spec["<="] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("<="), $function, $adverb); - }; + iterator.float$forBy = function($startval, $endval, $stepval) { + return js_numeric_iter$forBy($startval, $endval, $stepval, $SC.Float); + }; - spec[">"] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol(">"), $function, $adverb); - }; + iterator.float$forSeries = function($startval, $second, $last) { + return js_numeric_iter$forSeries($startval, $second, $last, $SC.Float); + }; - spec[">="] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol(">="), $function, $adverb); + var list_iter = function(list) { + var i = 0, iter = { + next: function() { + var $ret = list[i++]; + if (i >= list.length) { + iter.next = __stop__; + } + return $ret; + } }; + return iter; + }; - spec.bitAnd = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("bitAnd"), $function, $adverb); - }; + var js_array_iter = function(list) { + if (list.length) { + return list_iter(list); + } + return nop_iter; + }; - spec.bitOr = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("bitOr"), $function, $adverb); - }; + iterator.array$do = function($array) { + return js_array_iter($array._.slice()); + }; - spec.bitXor = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("bitXor"), $function, $adverb); - }; + iterator.array$reverseDo = function($array) { + return js_array_iter($array._.slice().reverse()); + }; - spec.bitHammingDistance = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("bitHammingDistance"), $function, $adverb); - }; + sc.lang.iterator = iterator; - spec.lcm = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("lcm"), $function, $adverb); - }; +})(sc); - spec.gcd = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("gcd"), $function, $adverb); - }; +// src/sc/lang/classlib.js +(function(sc) { - spec.round = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("round"), $function, $adverb); - }; + sc.lang.classlib = {}; - spec.roundUp = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("roundUp"), $function, $adverb); - }; +})(sc); - spec.trunc = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("trunc"), $function, $adverb); - }; +// src/sc/lang/classlib/Core/Object.js +(function(sc) { - spec.atan2 = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("atan2"), $function, $adverb); - }; + var slice = [].slice; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; - spec.hypot = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("hypot"), $function, $adverb); - }; + sc.lang.klass.refine("Object", function(spec, utils) { + var BOOL = utils.BOOL; + var $nil = utils.$nil; + var $true = utils.$true; + var $false = utils.$false; + var $int_1 = utils.$int_1; + var SCArray = $SC("Array"); - spec.hypotApx = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("hypotApx"), $function, $adverb); + spec.__num__ = function() { + throw new Error("Wrong Type"); }; - spec.leftShift = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("leftShift"), $function, $adverb); + spec.__int__ = function() { + return this.__num__()|0; }; - spec.rightShift = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("rightShift"), $function, $adverb); + spec.__bool__ = function() { + throw new Error("Wrong Type"); }; - spec.unsignedRightShift = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("unsignedRightShift"), $function, $adverb); + spec.__sym__ = function() { + throw new Error("Wrong Type"); }; - spec.ring1 = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("ring1"), $function, $adverb); + spec.__str__ = function() { + return String(this); }; - spec.ring2 = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("ring2"), $function, $adverb); + // TODO: implements $new + // TODO: implements $newCopyArgs + + spec.$newFrom = function() { + return this._doesNotUnderstand("newFrom"); }; - spec.ring3 = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("ring3"), $function, $adverb); - }; + // TODO: implements dump + // TODO: implements post + // TODO: implements postln + // TODO: implements postc + // TODO: implements postcln + // TODO: implements postcs + // TODO: implements totalFree + // TODO: implements largestFreeBlock + // TODO: implements gcDumpGrey + // TODO: implements gcDumpSet + // TODO: implements gcInfo + // TODO: implements gcSanity + // TODO: implements canCallOS - spec.ring4 = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("ring4"), $function, $adverb); - }; + spec.size = utils.alwaysReturn$int_0; + spec.indexedSize = utils.alwaysReturn$int_0; + spec.flatSize = utils.alwaysReturn$int_1; - spec.difsqr = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("difsqr"), $function, $adverb); - }; + spec.do = function($function) { + sc.lang.iterator.execute( + sc.lang.iterator.object$do(this), + $function + ); - spec.sumsqr = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("sumsqr"), $function, $adverb); + return this; }; - spec.sqrsum = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("sqrsum"), $function, $adverb); - }; + spec.generate = fn(function($function, $state) { + this.do($function); - spec.sqrdif = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("sqrdif"), $function, $adverb); - }; + return $state; + }, "function; state"); - spec.absdif = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("absdif"), $function, $adverb); - }; + // already defined: class + // already defined: isKindOf + // already defined: isMemberOf - spec.thresh = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("thresh"), $function, $adverb); - }; + spec.respondsTo = fn(function($aSymbol) { + return $SC.Boolean(typeof this[$aSymbol.__sym__()] === "function"); + }, "aSymbol"); - spec.amclip = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("amclip"), $function, $adverb); - }; + // TODO: implements performMsg - spec.scaleneg = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("scaleneg"), $function, $adverb); - }; + spec.perform = function($selector) { + var selector, method; - spec.clip2 = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("clip2"), $function, $adverb); - }; + selector = $selector.__sym__(); + method = this[selector]; - spec.fold2 = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("fold2"), $function, $adverb); - }; + if (method) { + return method.apply(this, slice.call(arguments, 1)); + } - spec.wrap2 = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("wrap2"), $function, $adverb); + throw new Error("Message '" + selector + "' not understood."); }; - spec.excess = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("excess"), $function, $adverb); - }; + spec.performList = function($selector, $arglist) { + var selector, method; - spec.firstArg = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("firstArg"), $function, $adverb); - }; + selector = $selector.__sym__(); + method = this[selector]; - spec.rrand = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("rrand"), $function, $adverb); - }; + if (method) { + return method.apply(this, $arglist.asArray()._); + } - spec.exprand = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("exprand"), $function, $adverb); + throw new Error("Message '" + selector + "' not understood."); }; - spec["@"] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("@"), $function, $adverb); - }; + spec.functionPerformList = utils.nop; - spec.real = utils.nop; - spec.imag = function() { - return $SC.Float(0.0); - }; + // TODO: implements superPerform + // TODO: implements superPerformList + // TODO: implements tryPerform + // TODO: implements multiChannelPerform + // TODO: implements performWithEnvir + // TODO: implements performKeyValuePairs - spec["||"] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("||"), $function, $adverb); - }; + var copy = function(obj) { + var copied = obj; - spec["&&"] = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("&&"), $function, $adverb); - }; + if (Array.isArray(obj)) { + copied = obj.slice(); + } else if (obj && obj.constructor === Object) { + copied = {}; + Object.keys(obj).forEach(function(key) { + copied[key] = obj[key]; + }); + } - spec.xor = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("xor"), $function, $adverb); + return copied; }; - spec.nand = function($function, $adverb) { - return this.composeBinaryOp($SC.Symbol("nand"), $function, $adverb); + spec.copy = function() { + return this.shallowCopy(); }; - spec.not = function() { - return this.composeUnaryOp($SC.Symbol("not")); - }; + // TODO: implements contentsCopy - spec.ref = function() { - return this.composeUnaryOp($SC.Symbol("asRef")); - }; + spec.shallowCopy = function() { + var a = new this.__class._Spec(); - spec.clip = function($lo, $hi) { - return this.composeNAryOp($SC.Symbol("clip"), $SC.Array([ $lo, $hi ])); - }; + Object.keys(this).forEach(function(key) { + a[key] = copy(this[key]); + }, this); - spec.wrap = function($lo, $hi) { - return this.composeNAryOp($SC.Symbol("wrap"), $SC.Array([ $lo, $hi ])); - }; + if (this._ === this) { + a._ = a; + } - spec.fold = function($lo, $hi) { - return this.composeNAryOp($SC.Symbol("fold"), $SC.Array([ $lo, $hi ])); + return a; }; - spec.blend = fn(function($that, $blendFrac) { - return this.composeNAryOp( - $SC.Symbol("blend"), $SC.Array([ $that, $blendFrac ]) - ); - }, "that; blendFrac=0.5"); - - spec.linlin = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { - return this.composeNAryOp( - $SC.Symbol("linlin"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $clip ]) - ); - }, "inMin; inMax; outMin; outMax; clip=\\minmax"); - - spec.linexp = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { - return this.composeNAryOp( - $SC.Symbol("linexp"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $clip ]) - ); - }, "inMin; inMax; outMin; outMax; clip=\\minmax"); + // TODO: implements copyImmutable + // TODO: implements deepCopy - spec.explin = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { - return this.composeNAryOp( - $SC.Symbol("explin"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $clip ]) - ); - }, "inMin; inMax; outMin; outMax; clip=\\minmax"); + spec.dup = fn(function($n) { + var $this = this; + var $array, i, imax; - spec.expexp = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { - return this.composeNAryOp( - $SC.Symbol("expexp"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $clip ]) - ); - }, "inMin; inMax; outMin; outMax; clip=\\minmax"); + if (BOOL($n.isSequenceableCollection())) { + return SCArray.fillND($n, $SC.Function(function() { + return $this.copy(); + })); + } - spec.lincurve = fn(function($inMin, $inMax, $outMin, $outMax, $curve, $clip) { - return this.composeNAryOp( - $SC.Symbol("lincurve"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $curve, $clip ]) - ); - }, "inMin=0; inMax=1; outMin=1; outMax=1; curve=-4; clip=\\minmax"); + $array = SCArray.new($n); + for (i = 0, imax = $n.__int__(); i < imax; ++i) { + $array.add(this.copy()); + } - spec.curvelin = fn(function($inMin, $inMax, $outMin, $outMax, $curve, $clip) { - return this.composeNAryOp( - $SC.Symbol("curvelin"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $curve, $clip ]) - ); - }, "inMin=0; inMax=1; outMin=1; outMax=1; curve=-4; clip=\\minmax"); + return $array; + }, "n=2"); - spec.bilin = fn(function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) { - return this.composeNAryOp( - $SC.Symbol("bilin"), $SC.Array([ - $inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip - ]) - ); - }, "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"); + spec["!"] = function($n) { + return this.dup($n); + }; - spec.biexp = fn(function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) { - return this.composeNAryOp( - $SC.Symbol("biexp"), $SC.Array([ - $inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip - ]) - ); - }, "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"); + spec.poll = function() { + return this.value(); + }; - spec.moddif = fn(function($function, $mod) { - return this.composeNAryOp( - $SC.Symbol("moddif"), $SC.Array([ $function, $mod ]) - ); - }, "function; mod"); + spec.value = utils.nop; + spec.valueArray = utils.nop; + spec.valueEnvir = utils.nop; + spec.valueArrayEnvir = utils.nop; - spec.degreeToKey = fn(function($scale, $stepsPerOctave) { - return this.composeNAryOp( - $SC.Symbol("degreeToKey"), $SC.Array([ $scale, $stepsPerOctave ]) - ); - }, "scale; stepsPerOctave=12"); + spec["=="] = function($obj) { + return this ["==="] ($obj); + }; - spec.degrad = function() { - return this.composeUnaryOp($SC.Symbol("degrad")); + spec["!="] = function($obj) { + return (this ["=="] ($obj)).not(); }; - spec.raddeg = function() { - return this.composeUnaryOp($SC.Symbol("raddeg")); + spec["==="] = function($obj) { + return $SC.Boolean(this === $obj); }; - spec.applyTo = function() { - return this.value.apply(this, arguments); + spec["!=="] = function($obj) { + return $SC.Boolean(this !== $obj); }; - // TODO: implements <> - // TODO: implements sampled + // TODO: implements equals + // TODO: implements compareObject + // TODO: implements instVarHash + // TODO: implements basicHash + // TODO: implements hash + // TODO: implements identityHash - spec.asUGenInput = function($for) { - return this.value($for); + spec["->"] = function($obj) { + return $SC("Association").new(this, $obj); }; - spec.asAudioRateInput = function($for) { - var $result; - - $result = this.value($for); + spec.next = utils.nop; + spec.reset = utils.nop; - if ($result.rate().__sym__() !== "audio") { - return $SC("K2A").ar($result); - } + spec.first = fn(function($inval) { + this.reset(); + return this.next($inval); + }, "inval"); - return $result; + spec.iter = function() { + return $SC("OneShotStream").new(this); }; - spec.asControlInput = function() { - return this.value(); + spec.stop = utils.nop; + spec.free = utils.nop; + spec.clear = utils.nop; + spec.removedFromScheduler = utils.nop; + spec.isPlaying = utils.alwaysReturn$false; + + spec.embedInStream = function() { + return this.yield(); }; - spec.isValidUGenInput = utils.alwaysReturn$true; - }); + // TODO: implements cyc + // TODO: implements fin + // TODO: implements repeat + // TODO: implements loop - function SCUnaryOpFunction(args) { - this.__initializeWith__("AbstractFunction"); - this.$selector = args[0] || /* istanbul ignore next */ $nil; - this.$a = args[1] || /* istanbul ignore next */ $nil; - } + spec.asStream = utils.nop; - sc.lang.klass.define(SCUnaryOpFunction, "UnaryOpFunction : AbstractFunction", function(spec) { + // TODO: implements streamArg - spec.value = function() { - var $a = this.$a; - return $a.value.apply($a, arguments).perform(this.$selector); - }; + spec.eventAt = utils.alwaysReturn$nil; - spec.valueArray = function($args) { - return this.$a.valueArray($args).perform(this.$selector); - }; + spec.composeEvents = fn(function($event) { + return $event.copy(); + }, "event"); - // TODO: implements valueEnvir - // TODO: implements valueArrayEnvir + spec.finishEvent = utils.nop; + spec.atLimit = utils.alwaysReturn$false; + spec.isRest = utils.alwaysReturn$false; + spec.threadPlayer = utils.nop; + spec.threadPlayer_ = utils.nop; + spec["?"] = utils.nop; + spec["??"] = utils.nop; - spec.functionPerformList = function($selector, $arglist) { - return this.performList($selector, $arglist); + spec["!?"] = function($obj) { + return $obj.value(this); }; - // TODO: implements storeOn - }); + spec.isNil = utils.alwaysReturn$false; + spec.notNil = utils.alwaysReturn$true; + spec.isNumber = utils.alwaysReturn$false; + spec.isInteger = utils.alwaysReturn$false; + spec.isFloat = utils.alwaysReturn$false; + spec.isSequenceableCollection = utils.alwaysReturn$false; + spec.isCollection = utils.alwaysReturn$false; + spec.isArray = utils.alwaysReturn$false; + spec.isString = utils.alwaysReturn$false; + spec.containsSeqColl = utils.alwaysReturn$false; + spec.isValidUGenInput = utils.alwaysReturn$false; + spec.isException = utils.alwaysReturn$false; + spec.isFunction = utils.alwaysReturn$false; - function SCBinaryOpFunction(args) { - this.__initializeWith__("AbstractFunction"); - this.$selector = args[0] || /* istanbul ignore next */ $nil; - this.$a = args[1] || /* istanbul ignore next */ $nil; - this.$b = args[2] || /* istanbul ignore next */ $nil; - this.$adverb = args[3] || /* istanbul ignore next */ $nil; - } + spec.matchItem = fn(function($item) { + return this ["==="] ($item); + }, "item"); - sc.lang.klass.define(SCBinaryOpFunction, "BinaryOpFunction : AbstractFunction", function(spec) { + spec.trueAt = utils.alwaysReturn$false; - spec.value = function() { - return this.$a.value.apply(this.$a, arguments) - .perform(this.$selector, this.$b.value.apply(this.$b, arguments), this.$adverb); - }; + spec.falseAt = fn(function($key) { + return this.trueAt($key).not(); + }, "key"); - spec.valueArray = function($args) { - return this.$a.valueArray($args) - .perform(this.$selector, this.$b.valueArray($args, arguments), this.$adverb); + // TODO: implements pointsTo + // TODO: implements mutable + // TODO: implements frozen + // TODO: implements halt + // TODO: implements primitiveFailed + // TODO: implements reportError + // TODO: implements subclassResponsibility + spec._subclassResponsibility = function(methodName) { + throw new Error("RECEIVER " + String(this) + ": " + + "'" + methodName + "' should have been implemented by subclass"); }; - // TODO: implements valueEnvir - // TODO: implements valueArrayEnvir - - spec.functionPerformList = function($selector, $arglist) { - return this.performList($selector, $arglist); + // TODO: implements doesNotUnderstand + spec._doesNotUnderstand = function(methodName) { + throw new Error("RECEIVER " + this.__str__() + ": " + + "Message '" + methodName + "' not understood."); }; - // TODO: implements storeOn - }); - - function SCNAryOpFunction(args) { - this.__initializeWith__("AbstractFunction"); - this.$selector = args[0] || /* istanbul ignore next */ $nil; - this.$a = args[1] || /* istanbul ignore next */ $nil; - this.$arglist = args[2] || /* istanbul ignore next */ $nil; - } - - sc.lang.klass.define(SCNAryOpFunction, "NAryOpFunction : AbstractFunction", function(spec) { + // TODO: implements shouldNotImplement + // TODO: implements outOfContextReturn + // TODO: implements immutableError + // TODO: implements deprecated + // TODO: implements mustBeBoolean + // TODO: implements notYetImplemented + // TODO: implements dumpBackTrace + // TODO: implements getBackTrace + // TODO: implements throw - spec.value = function() { - var args = arguments; - return this.$a.value.apply(this.$a, args) - .performList(this.$selector, this.$arglist.collect($SC.Function(function($_) { - return $_.value.apply($_, args); - }))); + spec.species = function() { + return this.class(); }; - spec.valueArray = function($args) { - return this.$a.valueArray($args) - .performList(this.$selector, this.$arglist.collect($SC.Function(function($_) { - return $_.valueArray($args); - }))); + spec.asCollection = function() { + return $SC.Array([ this ]); }; - // TODO: implements valueEnvir - // TODO: implements valueArrayEnvir + spec.asSymbol = function() { + return this.asString().asSymbol(); + }; - spec.functionPerformList = function($selector, $arglist) { - return this.performList($selector, $arglist); + spec.asString = function() { + return $SC.String(String(this)); }; + // TODO: implements asCompileString + // TODO: implements cs + // TODO: implements printClassNameOn + // TODO: implements printOn // TODO: implements storeOn - }); + // TODO: implements storeParamsOn + // TODO: implements simplifyStoreArgs + // TODO: implements storeArgs + // TODO: implements storeModifiersOn - function SCFunctionList(args) { - this.__initializeWith__("AbstractFunction"); - this.$array = args[0] || /* istanbul ignore next */ $nil; - this._flopped = false; - } + spec.as = fn(function($aSimilarClass) { + return $aSimilarClass.newFrom(this); + }, "aSimilarClass"); - sc.lang.klass.define(SCFunctionList, "FunctionList : AbstractFunction", function(spec, utils) { - var $int_0 = utils.$int_0; + spec.dereference = utils.nop; - spec.array = function() { - return this.$array; + spec.reference = function() { + return $SC.Ref(this); }; - spec.array_ = fn(function($value) { - this.$array = $value; - return this; - }, "value"); + spec.asRef = function() { + return $SC.Ref(this); + }; - spec.flopped = function() { - return $SC.Boolean(this._flopped); + spec.asArray = function() { + return this.asCollection().asArray(); }; - spec.addFunc = fn(function($$functions) { - if (this._flopped) { - throw new Error("cannot add a function to a flopped FunctionList"); - } + spec.asSequenceableCollection = function() { + return this.asArray(); + }; - this.$array = this.$array.addAll($$functions); + spec.rank = utils.alwaysReturn$int_0; + + spec.deepCollect = fn(function($depth, $function, $index, $rank) { + return $function.value(this, $index, $rank); + }, "depth; function; index; rank"); + spec.deepDo = fn(function($depth, $function, $index, $rank) { + $function.value(this, $index, $rank); return this; - }, "*functions"); + }, "depth; function; index; rank"); - spec.removeFunc = function($function) { - this.$array.remove($function); + spec.slice = utils.nop; + spec.shape = utils.alwaysReturn$nil; + spec.unbubble = utils.nop; - if (this.$array.size() < 2) { - return this.$array.at($int_0); + spec.bubble = fn(function($depth, $levels) { + var levels, a; + + levels = $levels.__int__(); + if (levels <= 1) { + a = [ this ]; + } else { + a = [ + this.bubble($depth, $SC.Integer(levels - 1)) + ]; } - return this; - }; + return $SC.Array(a); + }, "depth; levels"); - spec.replaceFunc = function($find, $replace) { - this.$array = this.$array.replace($find, $replace); - return this; - }; - - spec.value = function() { - var $res, args = arguments; - - $res = this.$array.collect($SC.Function(function($_) { - return $_.value.apply($_, args); - })); - - return this._flopped ? $res.flop() : $res; - }; - - spec.valueArray = function($args) { - var $res; - - $res = this.$array.collect($SC.Function(function($_) { - return $_.valueArray($args); - })); + spec.obtain = fn(function($index, $default) { + if ($index.__num__() === 0) { + return this; + } else { + return $default; + } + }, "index; defaults"); - return this._flopped ? $res.flop() : $res; - }; + spec.instill = fn(function($index, $item, $default) { + if ($index.__num__() === 0) { + return $item; + } else { + return this.asArray().instill($index, $item, $default); + } + }, "index; item; default"); - // TODO: implements valueEnvir - // TODO: implements valueArrayEnvir + spec.addFunc = fn(function($$functions) { + return $SC("FunctionList").new(this ["++"] ($$functions)); + }, "*functions"); - spec.do = function($function) { - this.$array.do($function); + spec.removeFunc = function($function) { + if (this === $function) { + return $nil; + } return this; }; - spec.flop = function() { - if (!this._flopped) { - this.$array = this.$array.collect($SC.Function(function($_) { - return $_.flop(); - })); + spec.replaceFunc = fn(function($find, $replace) { + if (this === $find) { + return $replace; } - this._flopped = true; - return this; - }; - - // TODO: implements envirFlop - - spec.storeArgs = function() { - return $SC.Array([ this.$array ]); - }; - - }); + }, "find; replace"); -})(sc); + // TODO: implements addFuncTo + // TODO: implements removeFuncFrom -// src/sc/lang/classlib/Streams/Stream.js -(function(sc) { + spec.while = fn(function($body) { + var $this = this; - function SCStream() { - this.__initializeWith__("AbstractFunction"); - } + $SC.Function(function() { + return $this.value(); + }).while($SC.Function(function() { + return $body.value(); + })); - sc.lang.klass.define(SCStream, "Stream : AbstractFunction", function() { - // TODO: implements parent - // TODO: implements next - // TODO: implements iter - // TODO: implements value - // TODO: implements valueArray - // TODO: implements nextN - // TODO: implements all - // TODO: implements put - // TODO: implements putN - // TODO: implements putAll - // TODO: implements do - // TODO: implements subSample - // TODO: implements loop - // TODO: implements generate - // TODO: implements collect - // TODO: implements reject - // TODO: implements select - // TODO: implements dot - // TODO: implements interlace - // TODO: implements ++ - // TODO: implements appendStream - // TODO: implements collate - // TODO: implements <> - // TODO: implements composeUnaryOp - // TODO: implements composeBinaryOp - // TODO: implements reverseComposeBinaryOp - // TODO: implements composeNAryOp - // TODO: implements embedInStream - // TODO: implements while - // TODO: implements asEventStreamPlayer - // TODO: implements play - // TODO: implements trace - // TODO: implements constrain - // TODO: implements repeat - }); + return this; + }, "body"); - function SCPauseStream() { - this.__initializeWith__("Stream"); - } + spec.switch = function() { + var args, i, imax; - sc.lang.klass.define(SCPauseStream, "PauseStream : Stream", function() { - // TODO: implements stream - // TODO: implements originalStream - // TODO: implements clock - // TODO: implements nextBeat - // TODO: implements streamHasEnded - // TODO: implements streamHasEnded_ + args = slice.call(arguments); + for (i = 0, imax = args.length >> 1; i < imax; i++) { + if (BOOL(this ["=="] (args[i * 2]))) { + return args[i * 2 + 1].value(); + } + } - // TODO: implements isPlaying - // TODO: implements play - // TODO: implements reset - // TODO: implements stop - // TODO: implements prStop - // TODO: implements removedFromScheduler - // TODO: implements streamError - // TODO: implements wasStopped - // TODO: implements canPause - // TODO: implements pause - // TODO: implements resume - // TODO: implements refresh - // TODO: implements start - // TODO: implements stream_ - // TODO: implements next - // TODO: implements awake - // TODO: implements threadPlayer - }); + if (args.length % 2 === 1) { + return args[args.length - 1].value(); + } - function SCTask() { - this.__initializeWith__("PauseStream"); - } + return $nil; + }; - sc.lang.klass.define(SCTask, "Task : PauseStream", function() { - // TODO: implements storeArgs - }); + spec.yield = function() { + // TODO: implements yield + }; -})(sc); + // TODO: implements alwaysYield + // TODO: implements yieldAndReset + // TODO: implements idle + // TODO: implements $initClass + // TODO: implements dependants + // TODO: implements changed + // TODO: implements addDependant + // TODO: implements removeDependant + // TODO: implements release + // TODO: implements releaseDependants + // TODO: implements update + // TODO: implements addUniqueMethod + // TODO: implements removeUniqueMethods + // TODO: implements removeUniqueMethod + // TODO: implements inspect + // TODO: implements inspectorClass + // TODO: implements inspector + // TODO: implements crash + // TODO: implements stackDepth + // TODO: implements dumpStack + // TODO: implements dumpDetailedBackTrace + // TODO: implements freeze -// src/sc/lang/classlib/Math/Magnitude.js -(function(sc) { + spec["&"] = function($that) { + return this.bitAnd($that); + }; - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; + spec["|"] = function($that) { + return this.bitOr($that); + }; - sc.lang.klass.refine("Magnitude", function(spec) { - spec["=="] = function($aMagnitude) { - return $SC.Boolean(this.valueOf() === $aMagnitude.valueOf()); + spec["%"] = function($that) { + return this.mod($that); }; - spec["!="] = function($aMagnitude) { - return $SC.Boolean(this.valueOf() !== $aMagnitude.valueOf()); + spec["**"] = function($that) { + return this.pow($that); }; - spec.hash = function() { - return this._subclassResponsibility("hash"); + spec["<<"] = function($that) { + return this.leftShift($that); }; - spec["<"] = function($aMagnitude) { - return $SC.Boolean(this < $aMagnitude); + spec[">>"] = function($that) { + return this.rightShift($that); }; - spec[">"] = function($aMagnitude) { - return $SC.Boolean(this > $aMagnitude); + spec["+>>"] = function($that) { + return this.unsignedRightShift($that); }; - spec["<="] = function($aMagnitude) { - return $SC.Boolean(this <= $aMagnitude); + spec["="] = function($aMagnitude) { - return $SC.Boolean(this >= $aMagnitude); + spec.asInt = function() { + return this.asInteger(); }; - spec.exclusivelyBetween = fn(function($lo, $hi) { - return $SC.Boolean($lo < this && this < $hi); - }, "lo; hi"); + spec.blend = fn(function($that, $blendFrac) { + return this ["+"] ($blendFrac ["*"] ($that ["-"] (this))); + }, "that; blendFrac=0.5"); - spec.inclusivelyBetween = fn(function($lo, $hi) { - return $SC.Boolean($lo <= this && this <= $hi); - }, "lo; hi"); + spec.blendAt = fn(function($index, $method) { + var $iMin; - spec.min = fn(function($aMagnitude) { - return this <= $aMagnitude ? this : $aMagnitude; - }, "aMagnitude"); + $iMin = $index.roundUp($int_1).asInteger().__dec__(); + return this.perform($method, $iMin).blend( + this.perform($method, $iMin.__inc__()), + $index.absdif($iMin) + ); + }, "index; method=\\clipAt"); - spec.max = fn(function($aMagnitude) { - return this >= $aMagnitude ? this : $aMagnitude; - }, "aMagnitude"); + spec.blendPut = fn(function($index, $val, $method) { + var $iMin, $ratio; - spec.clip = fn(function($lo, $hi) { - return this <= $lo ? $lo : this >= $hi ? $hi : this; - }, "lo; hi"); - }); - -})(sc); + $iMin = $index.floor().asInteger(); + $ratio = $index.absdif($iMin); + this.perform($method, $iMin, $val ["*"] ($int_1 ["-"] ($ratio))); + this.perform($method, $iMin.__inc__(), $val ["*"] ($ratio)); -// src/sc/lang/classlib/Math/Number.js -(function(sc) { + return this; + }, "index; val; method=\\wrapPut"); - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; - var iterator = sc.lang.iterator; + spec.fuzzyEqual = fn(function($that, $precision) { + return $SC.Float(0.0).max( + $SC.Float(1.0) ["-"] ( + (this ["-"] ($that).abs()) ["/"] ($precision) + ) + ); + }, "that; precision=1.0"); - sc.lang.klass.refine("Number", function(spec, utils) { - spec.isNumber = utils.alwaysReturn$true; + spec.isUGen = utils.alwaysReturn$false; + spec.numChannels = utils.alwaysReturn$int_1; - spec["+"] = function() { - return this._subclassResponsibility("+"); - }; + spec.pair = fn(function($that) { + return $SC.Array([ this, $that ]); + }, "that"); - spec["-"] = function() { - return this._subclassResponsibility("-"); - }; + spec.pairs = fn(function($that) { + var $list; - spec["*"] = function() { - return this._subclassResponsibility("*"); - }; + $list = $SC.Array(); + this.asArray().do($SC.Function(function($a) { + $that.asArray().do($SC.Function(function($b) { + $list = $list.add($a.asArray() ["++"] ($b)); + })); + })); - spec["/"] = function() { - return this._subclassResponsibility("/"); - }; + return $list; + }, "that"); - spec.mod = function() { - return this._subclassResponsibility("mod"); - }; + spec.awake = fn(function($beats) { + return this.next($beats); + }, "beats"); - spec.div = function() { - return this._subclassResponsibility("div"); - }; + spec.beats_ = utils.nop; + spec.clock_ = utils.nop; - spec.pow = function() { - return this._subclassResponsibility("pow"); - }; + spec.performBinaryOpOnSomething = function($aSelector) { + var aSelector; - spec.performBinaryOpOnSeqColl = function($aSelector, $aSeqColl, $adverb) { - var $this = this; + aSelector = $aSelector.__sym__(); + if (aSelector === "==") { + return $false; + } + if (aSelector === "!=") { + return $true; + } - return $aSeqColl.collect($SC.Function(function($item) { - return $item.perform($aSelector, $this, $adverb); - })); + throw new Error("binary operator '" + aSelector + "' failed."); }; - // TODO: implements performBinaryOpOnPoint - - spec.rho = utils.nop; - - spec.theta = function() { - return $SC.Float(0.0); + spec.performBinaryOpOnSimpleNumber = function($aSelector, $thig, $adverb) { + return this.performBinaryOpOnSomething($aSelector, $thig, $adverb); }; - spec.real = utils.nop; - - spec.imag = function() { - return $SC.Float(0.0); - }; + spec.performBinaryOpOnSignal = spec.performBinaryOpOnSimpleNumber; + spec.performBinaryOpOnComplex = spec.performBinaryOpOnSimpleNumber; + spec.performBinaryOpOnSeqColl = spec.performBinaryOpOnSimpleNumber; + spec.performBinaryOpOnUGen = spec.performBinaryOpOnSimpleNumber; - // TODO: implements @ - // TODO: implements complex - // TODO: implements polar + // TODO: implements writeDefFile - spec.for = fn(function($endValue, $function) { - iterator.execute( - iterator.number$for(this, $endValue), - $function - ); - return this; - }, "endValue; function"); + spec.isInputUGen = utils.alwaysReturn$false; + spec.isOutputUGen = utils.alwaysReturn$false; + spec.isControlUGen = utils.alwaysReturn$false; + spec.source = utils.nop; + spec.asUGenInput = utils.nop; + spec.asControlInput = utils.nop; - spec.forBy = fn(function($endValue, $stepValue, $function) { - iterator.execute( - iterator.number$forBy(this, $endValue, $stepValue), - $function - ); + spec.asAudioRateInput = function() { + if (this.rate().__sym__() !== "audio") { + return $SC("K2A").ar(this); + } return this; - }, "endValue; stepValue; function"); + }; - spec.forSeries = fn(function($second, $last, $function) { - iterator.execute( - iterator.number$forSeries(this, $second, $last), - $function - ); - return this; - }, "second; last; function"); + // TODO: implements slotSize + // TODO: implements slotAt + // TODO: implements slotPut + // TODO: implements slotKey + // TODO: implements slotIndex + // TODO: implements slotsDo + // TODO: implements slotValuesDo + // TODO: implements getSlots + // TODO: implements setSlots + // TODO: implements instVarSize + // TODO: implements instVarAt + // TODO: implements instVarPut + // TODO: implements writeArchive + // TODO: implements $readArchive + // TODO: implements asArchive + // TODO: implements initFromArchive + // TODO: implements archiveAsCompileString + // TODO: implements archiveAsObject + // TODO: implements checkCanArchive + // TODO: implements writeTextArchive + // TODO: implements $readTextArchive + // TODO: implements asTextArchive + // TODO: implements getContainedObjects + // TODO: implements writeBinaryArchive + // TODO: implements $readBinaryArchive + // TODO: implements asBinaryArchive + // TODO: implements genNext + // TODO: implements genCurrent + // TODO: implements $classRedirect + // TODO: implements help }); })(sc); -// src/sc/lang/classlib/Math/SimpleNumber.js +// src/sc/lang/classlib/Core/AbstractFunction.js (function(sc) { - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; - var rand = sc.libs.random; - - function prOpSimpleNumber(selector, func) { - return function($aNumber, $adverb) { - var tag = $aNumber.__tag; - - switch (tag) { - case 770: - case 777: - return $SC.Boolean(func(this._, $aNumber._)); - } - - if ($aNumber.isSequenceableCollection().valueOf()) { - return $aNumber.performBinaryOpOnSimpleNumber( - $SC.Symbol(selector), this, $adverb - ); - } + var $SC = sc.lang.$SC; + var fn = sc.lang.fn; + var utils = sc.lang.klass.utils; + var $nil = utils.$nil; - return $SC.False(); + sc.lang.klass.refine("AbstractFunction", function(spec, utils) { + spec.composeUnaryOp = function($aSelector) { + return $SC("UnaryOpFunction").new($aSelector, this); }; - } - sc.lang.klass.refine("SimpleNumber", function(spec, utils) { - var $nil = utils.$nil; - var $int_0 = utils.$int_0; - var $int_1 = utils.$int_1; - var SCArray = $SC("Array"); - - spec.__newFrom__ = $SC.Float; + spec.composeBinaryOp = function($aSelector, $something, $adverb) { + return $SC("BinaryOpFunction").new($aSelector, this, $something, $adverb); + }; - spec.__bool__ = function() { - return this._ !== 0; + spec.reverseComposeBinaryOp = function($aSelector, $something, $adverb) { + return $SC("BinaryOpFunction").new($aSelector, $something, this, $adverb); }; - spec.__dec__ = function() { - return this.__newFrom__(this._ - 1); + spec.composeNAryOp = function($aSelector, $anArgList) { + return $SC("NAryOpFunction").new($aSelector, this, $anArgList); }; - spec.__inc__ = function() { - return this.__newFrom__(this._ + 1); + spec.performBinaryOpOnSimpleNumber = function($aSelector, $aNumber, $adverb) { + return this.reverseComposeBinaryOp($aSelector, $aNumber, $adverb); }; - spec.__int__ = function() { - if (!isFinite(this._)) { - return this._; - } - return this._|0; + spec.performBinaryOpOnSignal = function($aSelector, $aSignal, $adverb) { + return this.reverseComposeBinaryOp($aSelector, $aSignal, $adverb); }; - spec.__num__ = function() { - return this._; + spec.performBinaryOpOnComplex = function($aSelector, $aComplex, $adverb) { + return this.reverseComposeBinaryOp($aSelector, $aComplex, $adverb); }; - spec.isValidUGenInput = function() { - return $SC.Boolean(!isNaN(this._)); + spec.performBinaryOpOnSeqColl = function($aSelector, $aSeqColl, $adverb) { + return this.reverseComposeBinaryOp($aSelector, $aSeqColl, $adverb); }; - spec.numChannels = utils.alwaysReturn$int_1; + spec.neg = function() { + return this.composeUnaryOp($SC.Symbol("neg")); + }; - spec.magnitude = function() { - return this.abs(); + spec.reciprocal = function() { + return this.composeUnaryOp($SC.Symbol("reciprocal")); }; - spec.angle = function() { - return $SC.Float(this._ >= 0 ? 0 : Math.PI); + spec.bitNot = function() { + return this.composeUnaryOp($SC.Symbol("bitNot")); }; - spec.neg = function() { - return this.__newFrom__(-this._); + spec.abs = function() { + return this.composeUnaryOp($SC.Symbol("abs")); }; - // bitNot: implemented by subclass + spec.asFloat = function() { + return this.composeUnaryOp($SC.Symbol("asFloat")); + }; - spec.abs = function() { - return this.__newFrom__(Math.abs(this._)); + spec.asInteger = function() { + return this.composeUnaryOp($SC.Symbol("asInteger")); }; spec.ceil = function() { - return this.__newFrom__(Math.ceil(this._)); + return this.composeUnaryOp($SC.Symbol("ceil")); }; spec.floor = function() { - return this.__newFrom__(Math.floor(this._)); + return this.composeUnaryOp($SC.Symbol("floor")); }; spec.frac = function() { - var a = this._; - - if (a < 0) { - return this.__newFrom__(1 + (a - (a|0))); - } - return this.__newFrom__(a - (a|0)); + return this.composeUnaryOp($SC.Symbol("frac")); }; spec.sign = function() { - var a = this._; - return this.__newFrom__( - a > 0 ? 1 : a === 0 ? 0 : -1 - ); + return this.composeUnaryOp($SC.Symbol("sign")); }; spec.squared = function() { - return this.__newFrom__(this._ * this._); + return this.composeUnaryOp($SC.Symbol("squared")); }; spec.cubed = function() { - return this.__newFrom__(this._ * this._ * this._); + return this.composeUnaryOp($SC.Symbol("cubed")); }; spec.sqrt = function() { - return $SC.Float(Math.sqrt(this._)); + return this.composeUnaryOp($SC.Symbol("sqrt")); }; spec.exp = function() { - return $SC.Float(Math.exp(this._)); - }; - - spec.reciprocal = function() { - return $SC.Float(1 / this._); + return this.composeUnaryOp($SC.Symbol("exp")); }; spec.midicps = function() { - return $SC.Float( - 440 * Math.pow(2, (this._ - 69) * 1/12) - ); + return this.composeUnaryOp($SC.Symbol("midicps")); }; spec.cpsmidi = function() { - return $SC.Float( - Math.log(Math.abs(this._) * 1/440) * Math.LOG2E * 12 + 69 - ); + return this.composeUnaryOp($SC.Symbol("cpsmidi")); }; spec.midiratio = function() { - return $SC.Float( - Math.pow(2, this._ * 1/12) - ); + return this.composeUnaryOp($SC.Symbol("midiratio")); }; spec.ratiomidi = function() { - return $SC.Float( - Math.log(Math.abs(this._)) * Math.LOG2E * 12 - ); + return this.composeUnaryOp($SC.Symbol("ratiomidi")); }; spec.ampdb = function() { - return $SC.Float( - Math.log(this._) * Math.LOG10E * 20 - ); + return this.composeUnaryOp($SC.Symbol("ampdb")); }; spec.dbamp = function() { - return $SC.Float( - Math.pow(10, this._ * 0.05) - ); + return this.composeUnaryOp($SC.Symbol("dbamp")); }; spec.octcps = function() { - return $SC.Float( - 440 * Math.pow(2, this._ - 4.75) - ); + return this.composeUnaryOp($SC.Symbol("octcps")); }; spec.cpsoct = function() { - return $SC.Float( - Math.log(Math.abs(this._) * 1/440) * Math.LOG2E + 4.75 - ); + return this.composeUnaryOp($SC.Symbol("cpsoct")); }; spec.log = function() { - return $SC.Float(Math.log(this._)); + return this.composeUnaryOp($SC.Symbol("log")); }; spec.log2 = function() { - return $SC.Float(Math.log(Math.abs(this._)) * Math.LOG2E); + return this.composeUnaryOp($SC.Symbol("log2")); }; spec.log10 = function() { - return $SC.Float(Math.log(this._) * Math.LOG10E); + return this.composeUnaryOp($SC.Symbol("log10")); }; spec.sin = function() { - return $SC.Float(Math.sin(this._)); + return this.composeUnaryOp($SC.Symbol("sin")); }; spec.cos = function() { - return $SC.Float(Math.cos(this._)); + return this.composeUnaryOp($SC.Symbol("cos")); }; spec.tan = function() { - return $SC.Float(Math.tan(this._)); + return this.composeUnaryOp($SC.Symbol("tan")); }; spec.asin = function() { - return $SC.Float(Math.asin(this._)); + return this.composeUnaryOp($SC.Symbol("asin")); }; spec.acos = function() { - return $SC.Float(Math.acos(this._)); + return this.composeUnaryOp($SC.Symbol("acos")); }; spec.atan = function() { - return $SC.Float(Math.atan(this._)); + return this.composeUnaryOp($SC.Symbol("atan")); }; - function _sinh(a) { - return (Math.pow(Math.E, a) - Math.pow(Math.E, -a)) * 0.5; - } - spec.sinh = function() { - return $SC.Float(_sinh(this._)); + return this.composeUnaryOp($SC.Symbol("sinh")); }; - function _cosh(a) { - return (Math.pow(Math.E, a) + Math.pow(Math.E, -a)) * 0.5; - } - spec.cosh = function() { - return $SC.Float(_cosh(this._)); + return this.composeUnaryOp($SC.Symbol("cosh")); }; spec.tanh = function() { - return $SC.Float(_sinh(this._) / _cosh(this._)); + return this.composeUnaryOp($SC.Symbol("tanh")); }; spec.rand = function() { - return this.__newFrom__( - rand.next() * this._ - ); + return this.composeUnaryOp($SC.Symbol("rand")); }; spec.rand2 = function() { - return this.__newFrom__( - (rand.next() * 2 - 1) * this._ - ); + return this.composeUnaryOp($SC.Symbol("rand2")); }; spec.linrand = function() { - return this.__newFrom__( - Math.min(rand.next(), rand.next()) * this._ - ); + return this.composeUnaryOp($SC.Symbol("linrand")); }; spec.bilinrand = function() { - return this.__newFrom__( - (rand.next() - rand.next()) * this._ - ); + return this.composeUnaryOp($SC.Symbol("bilinrand")); }; spec.sum3rand = function() { - return this.__newFrom__( - (rand.next() + rand.next() + rand.next() - 1.5) * 2/3 * this._ - ); + return this.composeUnaryOp($SC.Symbol("sum3rand")); }; spec.distort = function() { - return $SC.Float( - this._ / (1 + Math.abs(this._)) - ); + return this.composeUnaryOp($SC.Symbol("distort")); }; spec.softclip = function() { - var a = this._, abs_a = Math.abs(a); - return $SC.Float(abs_a <= 0.5 ? a : (abs_a - 0.25) / a); + return this.composeUnaryOp($SC.Symbol("softclip")); }; spec.coin = function() { - return $SC.Boolean(rand.next() < this._); + return this.composeUnaryOp($SC.Symbol("coin")); + }; + + spec.even = function() { + return this.composeUnaryOp($SC.Symbol("even")); + }; + + spec.odd = function() { + return this.composeUnaryOp($SC.Symbol("odd")); + }; + + spec.rectWindow = function() { + return this.composeUnaryOp($SC.Symbol("rectWindow")); + }; + + spec.hanWindow = function() { + return this.composeUnaryOp($SC.Symbol("hanWindow")); + }; + + spec.welWindow = function() { + return this.composeUnaryOp($SC.Symbol("welWindow")); + }; + + spec.triWindow = function() { + return this.composeUnaryOp($SC.Symbol("triWindow")); + }; + + spec.scurve = function() { + return this.composeUnaryOp($SC.Symbol("scurve")); + }; + + spec.ramp = function() { + return this.composeUnaryOp($SC.Symbol("ramp")); }; spec.isPositive = function() { - return $SC.Boolean(this._ >= 0); + return this.composeUnaryOp($SC.Symbol("isPositive")); }; spec.isNegative = function() { - return $SC.Boolean(this._ < 0); + return this.composeUnaryOp($SC.Symbol("isNegative")); }; spec.isStrictlyPositive = function() { - return $SC.Boolean(this._ > 0); + return this.composeUnaryOp($SC.Symbol("isStrictlyPositive")); }; - spec.isNaN = function() { - return $SC.Boolean(isNaN(this._)); + spec.rho = function() { + return this.composeUnaryOp($SC.Symbol("rho")); }; - spec.asBoolean = function() { - return $SC.Boolean(this._ > 0); + spec.theta = function() { + return this.composeUnaryOp($SC.Symbol("theta")); }; - spec.booleanValue = function() { - return $SC.Boolean(this._ > 0); + spec.rotate = function($function) { + return this.composeBinaryOp($SC.Symbol("rotate"), $function); }; - spec.binaryValue = function() { - return this._ > 0 ? $int_1 : $int_0; + spec.dist = function($function) { + return this.composeBinaryOp($SC.Symbol("dist"), $function); }; - spec.rectWindow = function() { - var a = this._; - if (a < 0 || 1 < a) { - return $SC.Float(0); - } - return $SC.Float(1); + spec["+"] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("+"), $function, $adverb); }; - spec.hanWindow = function() { - var a = this._; - if (a < 0 || 1 < a) { - return $SC.Float(0); - } - return $SC.Float(0.5 - 0.5 * Math.cos(a * 2 * Math.PI)); + spec["-"] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("-"), $function, $adverb); }; - spec.welWindow = function() { - var a = this._; - if (a < 0 || 1 < a) { - return $SC.Float(0); - } - return $SC.Float(Math.sin(a * Math.PI)); + spec["*"] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("*"), $function, $adverb); }; - spec.triWindow = function() { - var a = this._; - if (a < 0 || 1 < a) { - return $SC.Float(0); - } - if (a < 0.5) { - return $SC.Float(2 * a); - } - return $SC.Float(-2 * a + 2); + spec["/"] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("/"), $function, $adverb); }; - spec.scurve = function() { - var a = this._; - if (a <= 0) { - return $SC.Float(0); - } - if (1 <= a) { - return $SC.Float(1); - } - return $SC.Float(a * a * (3 - 2 * a)); + spec.div = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("div"), $function, $adverb); }; - spec.ramp = function() { - var a = this._; - if (a <= 0) { - return $SC.Float(0); - } - if (1 <= a) { - return $SC.Float(1); - } - return $SC.Float(a); + spec.mod = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("mod"), $function, $adverb); }; - // +: implemented by subclass - // -: implemented by subclass - // *: implemented by subclass - // /: implemented by subclass - // mod: implemented by subclass - // div: implemented by subclass - // pow: implemented by subclass - // min: implemented by subclass - // max: implemented by subclass - // bitAnd: implemented by subclass - // bitOr : implemented by subclass - // bitXor: implemented by subclass + spec.pow = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("pow"), $function, $adverb); + }; - spec.bitTest = function($bit) { - return $SC.Boolean( - this.bitAnd($int_1.leftShift($bit)).valueOf() !== 0 - ); + spec.min = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("min"), $function, $adverb); }; - // lcm : implemented by subclass - // gcd : implemented by subclass - // round : implemented by subclass - // roundUp : implemented by subclass - // trunc : implemented by subclass - // atan2 : implemented by subclass - // hypot : implemented by subclass - // hypotApx: implemented by subclass - // leftShift : implemented by subclass - // rightShift : implemented by subclass - // unsignedRightShift: implemented by subclass - // ring1 : implemented by subclass - // ring2 : implemented by subclass - // ring3 : implemented by subclass - // ring4 : implemented by subclass - // difsqr: implemented by subclass - // sumsqr: implemented by subclass - // sqrsum: implemented by subclass - // sqrdif: implemented by subclass - // absdif: implemented by subclass - // thresh: implemented by subclass - // amclip: implemented by subclass - // clip2 : implemented by subclass - // fold2 : implemented by subclass - // wrap2 : implemented by subclass - // excess: implemented by subclass - // firstArg: implemented by subclass - // rrand : implemented by subclass - // exprand : implemented by subclass - - spec["=="] = function($aNumber) { - return $SC.Boolean(this._ === $aNumber._); + spec.max = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("max"), $function, $adverb); }; - spec["!="] = function($aNumber) { - return $SC.Boolean(this._ !== $aNumber._); + spec["<"] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("<"), $function, $adverb); }; - spec["<"] = prOpSimpleNumber("<", function(a, b) { - return a < b; - }); - spec[">"] = prOpSimpleNumber(">", function(a, b) { - return a > b; - }); - spec["<="] = prOpSimpleNumber("<=", function(a, b) { - return a <= b; - }); - spec[">="] = prOpSimpleNumber(">=", function(a, b) { - return a >= b; - }); + spec["<="] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("<="), $function, $adverb); + }; - spec.equalWithPrecision = fn(function($that, $precision) { - return this.absdif($that) ["<"] ($precision); - }, "that; precision=0.0001"); + spec[">"] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol(">"), $function, $adverb); + }; - // TODO: implements hash + spec[">="] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol(">="), $function, $adverb); + }; - spec.asInteger = function() { - return $SC.Integer(this._); + spec.bitAnd = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("bitAnd"), $function, $adverb); }; - spec.asFloat = function() { - return $SC.Float(this._); + spec.bitOr = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("bitOr"), $function, $adverb); }; - // TODO: implements asComplex - // TODO: implements asRect + spec.bitXor = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("bitXor"), $function, $adverb); + }; - spec.degrad = function() { - return $SC.Float(this._ * Math.PI / 180); + spec.bitHammingDistance = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("bitHammingDistance"), $function, $adverb); }; - spec.raddeg = function() { - return $SC.Float(this._ * 180 / Math.PI); + spec.lcm = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("lcm"), $function, $adverb); }; - // TODO: implements performBinaryOpOnSimpleNumber - // TODO: implements performBinaryOpOnComplex - // TODO: implements performBinaryOpOnSignal + spec.gcd = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("gcd"), $function, $adverb); + }; - spec.nextPowerOfTwo = function() { - return $SC.Float( - Math.pow(2, Math.ceil(Math.log(this._) / Math.log(2))) - ); + spec.round = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("round"), $function, $adverb); }; - spec.nextPowerOf = fn(function($base) { - return $base.pow( - (this.log() ["/"] ($base.log())).ceil() - ); - }, "base"); + spec.roundUp = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("roundUp"), $function, $adverb); + }; - spec.nextPowerOfThree = function() { - return $SC.Float( - Math.pow(3, Math.ceil(Math.log(this._) / Math.log(3))) - ); + spec.trunc = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("trunc"), $function, $adverb); }; - spec.previousPowerOf = fn(function($base) { - return $base.pow( - (this.log() ["/"] ($base.log())).ceil().__dec__() - ); - }, "base"); + spec.atan2 = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("atan2"), $function, $adverb); + }; - spec.quantize = fn(function($quantum, $tolerance, $strength) { - var $round, $diff; + spec.hypot = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("hypot"), $function, $adverb); + }; - $round = this.round($quantum); - $diff = $round ["-"] (this); + spec.hypotApx = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("hypotApx"), $function, $adverb); + }; - if ($diff.abs() < $tolerance) { - return this ["+"] ($strength ["*"] ($diff)); - } + spec.leftShift = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("leftShift"), $function, $adverb); + }; - return this; - }, "quantum=1.0; tolerance=0.05; strength=1.0"); + spec.rightShift = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("rightShift"), $function, $adverb); + }; - spec.linlin = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { - var $res = null; + spec.unsignedRightShift = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("unsignedRightShift"), $function, $adverb); + }; - $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); + spec.ring1 = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("ring1"), $function, $adverb); + }; - if ($res === null) { - // (this-inMin)/(inMax-inMin) * (outMax-outMin) + outMin; - $res = ((this ["-"] ($inMin)) ["/"] ($inMax ["-"] ($inMin)) - ["*"] ($outMax ["-"] ($outMin)) ["+"] ($outMin)); - } + spec.ring2 = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("ring2"), $function, $adverb); + }; - return $res; - }, "inMin; inMax; outMin; outMax; clip=\\minmax"); + spec.ring3 = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("ring3"), $function, $adverb); + }; - spec.linexp = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { - var $res = null; + spec.ring4 = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("ring4"), $function, $adverb); + }; - $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); + spec.difsqr = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("difsqr"), $function, $adverb); + }; - if ($res === null) { - // Math.pow(outMax/outMin, (this-inMin)/(inMax-inMin)) * outMin; - $res = $outMax ["/"] ($outMin).pow( - (this ["-"] ($inMin)) ["/"] ($inMax ["-"] ($inMin)) - ) ["*"] ($outMin); - } + spec.sumsqr = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("sumsqr"), $function, $adverb); + }; - return $res; - }, "inMin; inMax; outMin; outMax; clip=\\minmax"); + spec.sqrsum = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("sqrsum"), $function, $adverb); + }; - spec.explin = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { - var $res = null; + spec.sqrdif = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("sqrdif"), $function, $adverb); + }; - $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); + spec.absdif = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("absdif"), $function, $adverb); + }; - if ($res === null) { - // (((Math.log(this/inMin)) / (Math.log(inMax/inMin))) * (outMax-outMin)) + outMin; - $res = ((this ["/"] ($inMin).log() ["/"] ($inMax ["/"] ($inMin).log()) - ["*"] ($outMax ["-"] ($outMin))) ["+"] ($outMin)); - } + spec.thresh = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("thresh"), $function, $adverb); + }; - return $res; - }, "inMin; inMax; outMin; outMax; clip=\\minmax"); + spec.amclip = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("amclip"), $function, $adverb); + }; - spec.expexp = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { - var $res = null; + spec.scaleneg = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("scaleneg"), $function, $adverb); + }; - $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); + spec.clip2 = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("clip2"), $function, $adverb); + }; - if ($res === null) { - // Math.pow(outMax/outMin, Math.log(this/inMin) / Math.log(inMax/inMin)) * outMin; - $res = $outMax ["/"] ($outMin).pow( - this ["/"] ($inMin).log() ["/"] ($inMax ["/"] ($inMin).log()) - ) ["*"] ($outMin); - } + spec.fold2 = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("fold2"), $function, $adverb); + }; - return $res; - }, "inMin; inMax; outMin; outMax; clip=\\minmax"); + spec.wrap2 = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("wrap2"), $function, $adverb); + }; - spec.lincurve = fn(function($inMin, $inMax, $outMin, $outMax, $curve, $clip) { - var $res = null, $grow, $a, $b, $scaled; + spec.excess = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("excess"), $function, $adverb); + }; - $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); + spec.firstArg = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("firstArg"), $function, $adverb); + }; - if ($res === null) { - if (Math.abs($curve.valueOf()) < 0.001) { - $res = this.linlin($inMin, $inMax, $outMin, $outMax); - } else { - $grow = $curve.exp(); - $a = $outMax ["-"] ($outMin) ["/"] ($SC.Float(1.0) ["-"] ($grow)); - $b = $outMin ["+"] ($a); - $scaled = (this ["-"] ($inMin)) ["/"] ($inMax ["-"] ($inMin)); + spec.rrand = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("rrand"), $function, $adverb); + }; - $res = $b ["-"] ($a ["*"] ($grow.pow($scaled))); - } - } + spec.exprand = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("exprand"), $function, $adverb); + }; - return $res; - }, "inMin=0; inMax=1; outMin=0; outMax=1; curve=-4; clip=\\minmax"); + spec["@"] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("@"), $function, $adverb); + }; - spec.curvelin = fn(function($inMin, $inMax, $outMin, $outMax, $curve, $clip) { - var $res = null, $grow, $a, $b; - - $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); + spec.real = utils.nop; + spec.imag = function() { + return $SC.Float(0.0); + }; - if ($res === null) { - if (Math.abs($curve.valueOf()) < 0.001) { - $res = this.linlin($inMin, $inMax, $outMin, $outMax); - } else { - $grow = $curve.exp(); - $a = $inMax ["-"] ($inMin) ["/"] ($SC.Float(1.0) ["-"] ($grow)); - $b = $inMin ["+"] ($a); + spec["||"] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("||"), $function, $adverb); + }; - $res = ((($b ["-"] (this)) ["/"] ($a)).log() - ["*"] ($outMax ["-"] ($outMin)) ["/"] ($curve) ["+"] ($outMin)); - } - } + spec["&&"] = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("&&"), $function, $adverb); + }; - return $res; - }, "inMin=0; inMax=1; outMin=0; outMax=1; curve=-4; clip=\\minmax"); + spec.xor = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("xor"), $function, $adverb); + }; - spec.bilin = fn(function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) { - var $res = null; + spec.nand = function($function, $adverb) { + return this.composeBinaryOp($SC.Symbol("nand"), $function, $adverb); + }; - $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); + spec.not = function() { + return this.composeUnaryOp($SC.Symbol("not")); + }; - if ($res === null) { - if (this >= $inCenter) { - $res = this.linlin($inCenter, $inMax, $outCenter, $outMax, $SC.Symbol("none")); - } else { - $res = this.linlin($inMin, $inCenter, $outMin, $outCenter, $SC.Symbol("none")); - } - } + spec.ref = function() { + return this.composeUnaryOp($SC.Symbol("asRef")); + }; - return $res; - }, "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"); + spec.clip = function($lo, $hi) { + return this.composeNAryOp($SC.Symbol("clip"), $SC.Array([ $lo, $hi ])); + }; - spec.biexp = fn(function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) { - var $res = null; + spec.wrap = function($lo, $hi) { + return this.composeNAryOp($SC.Symbol("wrap"), $SC.Array([ $lo, $hi ])); + }; - $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); + spec.fold = function($lo, $hi) { + return this.composeNAryOp($SC.Symbol("fold"), $SC.Array([ $lo, $hi ])); + }; - if ($res === null) { - if (this >= $inCenter) { - $res = this.explin($inCenter, $inMax, $outCenter, $outMax, $SC.Symbol("none")); - } else { - $res = this.explin($inMin, $inCenter, $outMin, $outCenter, $SC.Symbol("none")); - } - } + spec.blend = fn(function($that, $blendFrac) { + return this.composeNAryOp( + $SC.Symbol("blend"), $SC.Array([ $that, $blendFrac ]) + ); + }, "that; blendFrac=0.5"); - return $res; - }, "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"); + spec.linlin = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { + return this.composeNAryOp( + $SC.Symbol("linlin"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $clip ]) + ); + }, "inMin; inMax; outMin; outMax; clip=\\minmax"); - spec.moddif = fn(function($aNumber, $mod) { - var $diff, $modhalf; + spec.linexp = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { + return this.composeNAryOp( + $SC.Symbol("linexp"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $clip ]) + ); + }, "inMin; inMax; outMin; outMax; clip=\\minmax"); - $diff = this.absdif($aNumber) ["%"] ($mod); - $modhalf = $mod ["*"] ($SC.Float(0.5)); + spec.explin = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { + return this.composeNAryOp( + $SC.Symbol("explin"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $clip ]) + ); + }, "inMin; inMax; outMin; outMax; clip=\\minmax"); - return $modhalf ["-"] ($diff.absdif($modhalf)); - }, "aNumber=0.0; mod=1.0"); + spec.expexp = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { + return this.composeNAryOp( + $SC.Symbol("expexp"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $clip ]) + ); + }, "inMin; inMax; outMin; outMax; clip=\\minmax"); - spec.lcurve = fn(function($a, $m, $n, $tau) { - var $rTau, $x; + spec.lincurve = fn(function($inMin, $inMax, $outMin, $outMax, $curve, $clip) { + return this.composeNAryOp( + $SC.Symbol("lincurve"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $curve, $clip ]) + ); + }, "inMin=0; inMax=1; outMin=1; outMax=1; curve=-4; clip=\\minmax"); - $x = this.neg(); + spec.curvelin = fn(function($inMin, $inMax, $outMin, $outMax, $curve, $clip) { + return this.composeNAryOp( + $SC.Symbol("curvelin"), $SC.Array([ $inMin, $inMax, $outMin, $outMax, $curve, $clip ]) + ); + }, "inMin=0; inMax=1; outMin=1; outMax=1; curve=-4; clip=\\minmax"); - if ($tau.__num__() === 1.0) { - // a * (m * exp(x) + 1) / (n * exp(x) + 1) - return $a ["*"] ( - $m ["*"] ($x.exp()).__inc__() - ) ["/"] ( - $n ["*"] ($x.exp()).__inc__() - ); - } else { - $rTau = $tau.reciprocal(); - return $a ["*"] ( - $m ["*"] ($x.exp()) ["*"] ($rTau).__inc__() - ) ["/"] ( - $n ["*"] ($x.exp()) ["*"] ($rTau).__inc__() - ); - } - }, "a=1.0; m=0.0; n=1.0; tau=1.0"); + spec.bilin = fn(function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) { + return this.composeNAryOp( + $SC.Symbol("bilin"), $SC.Array([ + $inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip + ]) + ); + }, "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"); - spec.gauss = fn(function($standardDeviation) { - // ^((((-2*log(1.0.rand)).sqrt * sin(2pi.rand)) * standardDeviation) + this) - return ($SC.Float(-2.0) ["*"] ($SC.Float(1.0).rand().log()).sqrt() ["*"] ( - $SC.Float(2 * Math.PI).rand().sin() - ) ["*"] ($standardDeviation)) ["+"] (this); - }, "standardDeviation"); + spec.biexp = fn(function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) { + return this.composeNAryOp( + $SC.Symbol("biexp"), $SC.Array([ + $inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip + ]) + ); + }, "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"); - spec.gaussCurve = fn(function($a, $b, $c) { - // ^a * (exp(squared(this - b) / (-2.0 * squared(c)))) - return $a ["*"] (( - (this ["-"] ($b).squared()) ["/"] ($SC.Float(-2.0) ["*"] ($c.squared())) - ).exp()); - }, "a=1.0; b=0.0; c=1.0"); + spec.moddif = fn(function($function, $mod) { + return this.composeNAryOp( + $SC.Symbol("moddif"), $SC.Array([ $function, $mod ]) + ); + }, "function; mod"); - // TODO: implements asPoint - // TODO: implements asWarp + spec.degreeToKey = fn(function($scale, $stepsPerOctave) { + return this.composeNAryOp( + $SC.Symbol("degreeToKey"), $SC.Array([ $scale, $stepsPerOctave ]) + ); + }, "scale; stepsPerOctave=12"); - spec.wait = function() { - return this.yield(); + spec.degrad = function() { + return this.composeUnaryOp($SC.Symbol("degrad")); }; - // TODO: implements waitUntil - // TODO: implements sleep - // TODO: implements printOn - // TODO: implements storeOn - - spec.rate = function() { - return $SC.Symbol("scalar"); + spec.raddeg = function() { + return this.composeUnaryOp($SC.Symbol("raddeg")); }; - spec.asAudioRateInput = function() { - if (this._ === 0) { - return $SC("Silent").ar(); - } - return $SC("DC").ar(this); + spec.applyTo = function() { + return this.value.apply(this, arguments); }; - spec.madd = fn(function($mul, $add) { - return (this ["*"] ($mul)) ["+"] ($add); - }, "mul; add"); + // TODO: implements <> + // TODO: implements sampled - spec.lag = utils.nop; - spec.lag2 = utils.nop; - spec.lag3 = utils.nop; - spec.lagud = utils.nop; - spec.lag2ud = utils.nop; - spec.lag3ud = utils.nop; - spec.varlag = utils.nop; - spec.slew = utils.nop; + spec.asUGenInput = function($for) { + return this.value($for); + }; - // TODO: implements writeInputSpec + spec.asAudioRateInput = function($for) { + var $result; - spec.series = fn(function($second, $last) { - var $step; - var last, step, size; + $result = this.value($for); - if ($second === $nil) { - if (this.valueOf() < $last.valueOf()) { - $second = this.__inc__(); - } else { - $second = this.__dec__(); - } + if ($result.rate().__sym__() !== "audio") { + return $SC("K2A").ar($result); } - $step = $second ["-"] (this); - - last = $last.__num__(); - step = $step.__num__(); - size = (Math.floor((last - this._) / step + 0.001)|0) + 1; - return SCArray.series($SC.Integer(size), this, $step); - }, "second; last"); + return $result; + }; - // TODO: implements seriesIter - // TODO: implements degreeToKey - // TODO: implements keyToDegree - // TODO: implements nearestInList - // TODO: implements nearestInScale - // TODO: implements partition - // TODO: implements nextTimeOnGrid - // TODO: implements playAndDelta - // TODO: implements asQuant - // TODO: implements asTimeString - // TODO: implements asFraction - // TODO: implements asBufWithValues - // TODO: implements schedBundleArrayOnClock + spec.asControlInput = function() { + return this.value(); + }; - spec.shallowCopy = utils.nop; + spec.isValidUGenInput = utils.alwaysReturn$true; }); - function clip_for_map($this, $inMin, $inMax, $outMin, $outMax, $clip) { + function SCUnaryOpFunction(args) { + this.__initializeWith__("AbstractFunction"); + this.$selector = args[0] || /* istanbul ignore next */ $nil; + this.$a = args[1] || /* istanbul ignore next */ $nil; + } - switch ($clip.__sym__()) { - case "minmax": - if ($this <= $inMin) { - return $outMin; - } - if ($this >= $inMax) { - return $outMax; - } - break; - case "min": - if ($this <= $inMin) { - return $outMin; - } - break; - case "max": - if ($this >= $inMax) { - return $outMax; - } - break; - } + sc.lang.klass.define(SCUnaryOpFunction, "UnaryOpFunction : AbstractFunction", function(spec) { - return null; - } + spec.value = function() { + var $a = this.$a; + return $a.value.apply($a, arguments).perform(this.$selector); + }; -})(sc); + spec.valueArray = function($args) { + return this.$a.valueArray($args).perform(this.$selector); + }; -// src/sc/lang/classlib/Math/bop.js -(function(sc) { + // TODO: implements valueEnvir + // TODO: implements valueArrayEnvir - var $SC = sc.lang.$SC; - var mathlib = sc.libs.mathlib; + spec.functionPerformList = function($selector, $arglist) { + return this.performList($selector, $arglist); + }; - sc.lang.classlib.bop = function(selector, type1, type2) { - var func = mathlib[selector]; + // TODO: implements storeOn + }); - return function($aNumber, $adverb) { - var tag = $aNumber.__tag; + function SCBinaryOpFunction(args) { + this.__initializeWith__("AbstractFunction"); + this.$selector = args[0] || /* istanbul ignore next */ $nil; + this.$a = args[1] || /* istanbul ignore next */ $nil; + this.$b = args[2] || /* istanbul ignore next */ $nil; + this.$adverb = args[3] || /* istanbul ignore next */ $nil; + } - switch (tag) { - case 770: - return type1(func(this._, $aNumber._)); - case 777: - return type2(func(this._, $aNumber._)); - } + sc.lang.klass.define(SCBinaryOpFunction, "BinaryOpFunction : AbstractFunction", function(spec) { - return $aNumber.performBinaryOpOnSimpleNumber( - $SC.Symbol(selector), this, $adverb - ); + spec.value = function() { + return this.$a.value.apply(this.$a, arguments) + .perform(this.$selector, this.$b.value.apply(this.$b, arguments), this.$adverb); }; - }; -})(sc); + spec.valueArray = function($args) { + return this.$a.valueArray($args) + .perform(this.$selector, this.$b.valueArray($args, arguments), this.$adverb); + }; -// src/sc/lang/classlib/Math/Integer.js -(function(sc) { + // TODO: implements valueEnvir + // TODO: implements valueArrayEnvir - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; - var iterator = sc.lang.iterator; - var mathlib = sc.libs.mathlib; + spec.functionPerformList = function($selector, $arglist) { + return this.performList($selector, $arglist); + }; - sc.lang.klass.refine("Integer", function(spec, utils) { - var $nil = utils.$nil; - var $int_1 = utils.$int_1; - var SCArray = $SC("Array"); + // TODO: implements storeOn + }); - spec.__newFrom__ = $SC.Integer; + function SCNAryOpFunction(args) { + this.__initializeWith__("AbstractFunction"); + this.$selector = args[0] || /* istanbul ignore next */ $nil; + this.$a = args[1] || /* istanbul ignore next */ $nil; + this.$arglist = args[2] || /* istanbul ignore next */ $nil; + } - spec.__int__ = function() { - return this._; - }; + sc.lang.klass.define(SCNAryOpFunction, "NAryOpFunction : AbstractFunction", function(spec) { - spec.toString = function() { - return String("" + this._); + spec.value = function() { + var args = arguments; + return this.$a.value.apply(this.$a, args) + .performList(this.$selector, this.$arglist.collect($SC.Function(function($_) { + return $_.value.apply($_, args); + }))); }; - spec.$new = function() { - throw new Error("Integer.new is illegal, should use literal."); + spec.valueArray = function($args) { + return this.$a.valueArray($args) + .performList(this.$selector, this.$arglist.collect($SC.Function(function($_) { + return $_.valueArray($args); + }))); }; - spec.isInteger = utils.alwaysReturn$true; + // TODO: implements valueEnvir + // TODO: implements valueArrayEnvir - // TODO: implements hash + spec.functionPerformList = function($selector, $arglist) { + return this.performList($selector, $arglist); + }; - [ - [ "+", $SC.Integer, $SC.Float ], - [ "-", $SC.Integer, $SC.Float ], - [ "*", $SC.Integer, $SC.Float ], - [ "/", $SC.Float , $SC.Float ], - [ "mod" , $SC.Integer, $SC.Float ], - [ "div" , $SC.Integer, $SC.Integer ], - [ "pow" , $SC.Float , $SC.Float ], - [ "min" , $SC.Integer, $SC.Float ], - [ "max" , $SC.Integer, $SC.Float ], - [ "bitAnd" , $SC.Integer, $SC.Float ], - [ "bitOr" , $SC.Integer, $SC.Float ], - [ "bitXor" , $SC.Integer, $SC.Float ], - [ "lcm" , $SC.Integer, $SC.Float ], - [ "gcd" , $SC.Integer, $SC.Float ], - [ "round" , $SC.Integer, $SC.Float ], - [ "roundUp" , $SC.Integer, $SC.Float ], - [ "trunc" , $SC.Integer, $SC.Float ], - [ "atan2" , $SC.Float , $SC.Float ], - [ "hypot" , $SC.Float , $SC.Float ], - [ "hypotApx", $SC.Float , $SC.Float ], - [ "leftShift" , $SC.Integer, $SC.Float ], - [ "rightShift" , $SC.Integer, $SC.Float ], - [ "unsignedRightShift", $SC.Integer, $SC.Float ], - [ "ring1" , $SC.Integer, $SC.Float ], - [ "ring2" , $SC.Integer, $SC.Float ], - [ "ring3" , $SC.Integer, $SC.Float ], - [ "ring4" , $SC.Integer, $SC.Float ], - [ "difsqr" , $SC.Integer, $SC.Float ], - [ "sumsqr" , $SC.Integer, $SC.Float ], - [ "sqrsum" , $SC.Integer, $SC.Float ], - [ "sqrdif" , $SC.Integer, $SC.Float ], - [ "absdif" , $SC.Integer, $SC.Float ], - [ "thresh" , $SC.Integer, $SC.Integer ], - [ "amclip" , $SC.Integer, $SC.Float ], - [ "scaleneg", $SC.Integer, $SC.Float ], - [ "clip2" , $SC.Integer, $SC.Float ], - [ "fold2" , $SC.Integer, $SC.Float ], - [ "excess" , $SC.Integer, $SC.Float ], - [ "firstArg", $SC.Integer, $SC.Integer ], - [ "rrand" , $SC.Integer, $SC.Float ], - [ "exprand" , $SC.Float , $SC.Float ], - ].forEach(function(items) { - spec[items[0]] = sc.lang.classlib.bop.apply(null, items); - }); + // TODO: implements storeOn + }); - spec.wrap2 = function($aNumber, $adverb) { - var tag = $aNumber.__tag; + function SCFunctionList(args) { + this.__initializeWith__("AbstractFunction"); + this.$array = args[0] || /* istanbul ignore next */ $nil; + this._flopped = false; + } - switch (tag) { - case 770: - return $SC.Integer(mathlib.iwrap(this._, -$aNumber._, $aNumber._)); - case 777: - return $SC.Float(mathlib.wrap2(this._, $aNumber._)); - } + sc.lang.klass.define(SCFunctionList, "FunctionList : AbstractFunction", function(spec, utils) { + var $int_0 = utils.$int_0; - return $aNumber.performBinaryOpOnSimpleNumber( - $SC.Symbol("wrap2"), this, $adverb - ); + spec.array = function() { + return this.$array; }; - spec.rrand = function($aNumber, $adverb) { - var tag = $aNumber.__tag; - - switch (tag) { - case 770: - return $SC.Integer(Math.round(mathlib.rrand(this._, $aNumber._))); - case 777: - return $SC.Float(mathlib.rrand(this._, $aNumber._)); - } + spec.array_ = fn(function($value) { + this.$array = $value; + return this; + }, "value"); - return $aNumber.performBinaryOpOnSimpleNumber( - $SC.Symbol("rrand"), this, $adverb - ); + spec.flopped = function() { + return $SC.Boolean(this._flopped); }; - spec.clip = fn(function($lo, $hi) { - // <-- _ClipInt --> - if ($lo.__tag === 1027) { - return $lo; - } - if ($hi.__tag === 1027) { - return $hi; - } - if ($lo.__tag === 770 && $hi.__tag === 770) { - return $SC.Integer( - mathlib.clip(this._, $lo.__int__(), $hi.__int__()) - ); + spec.addFunc = fn(function($$functions) { + if (this._flopped) { + throw new Error("cannot add a function to a flopped FunctionList"); } - return $SC.Float( - mathlib.clip(this._, $lo.__num__(), $hi.__num__()) - ); - }, "lo; hi"); + this.$array = this.$array.addAll($$functions); - spec.wrap = fn(function($lo, $hi) { - // <-- _WrapInt --> - if ($lo.__tag === 1027) { - return $lo; - } - if ($hi.__tag === 1027) { - return $hi; - } - if ($lo.__tag === 770 && $hi.__tag === 770) { - return $SC.Integer( - mathlib.iwrap(this._, $lo.__int__(), $hi.__int__()) - ); - } + return this; + }, "*functions"); - return $SC.Float( - mathlib.wrap(this._, $lo.__num__(), $hi.__num__()) - ); - }, "lo; hi"); + spec.removeFunc = function($function) { + this.$array.remove($function); - spec.fold = fn(function($lo, $hi) { - // <-- _FoldInt --> - if ($lo.__tag === 1027) { - return $lo; - } - if ($hi.__tag === 1027) { - return $hi; + if (this.$array.size() < 2) { + return this.$array.at($int_0); } - if ($lo.__tag === 770 && $hi.__tag === 770) { - return $SC.Integer( - mathlib.ifold(this._, $lo.__int__(), $hi.__int__()) - ); - } - - return $SC.Float( - mathlib.fold(this._, $lo.__num__(), $hi.__num__()) - ); - }, "lo; hi"); - spec.even = function() { - return $SC.Boolean(!(this._ & 1)); + return this; }; - spec.odd = function() { - return $SC.Boolean(!!(this._ & 1)); + spec.replaceFunc = function($find, $replace) { + this.$array = this.$array.replace($find, $replace); + return this; }; - spec.xrand = fn(function($exclude) { - return ($exclude ["+"] (this.__dec__().rand()) ["+"] ($int_1)) ["%"] (this); - }, "exclude=0"); + spec.value = function() { + var $res, args = arguments; - spec.xrand2 = fn(function($exclude) { - var raw, res; + $res = this.$array.collect($SC.Function(function($_) { + return $_.value.apply($_, args); + })); - raw = this._; - res = (mathlib.rand((2 * raw))|0) - raw; + return this._flopped ? $res.flop() : $res; + }; - if (res === $exclude._) { - return this; - } + spec.valueArray = function($args) { + var $res; - return $SC.Integer(res); - }, "exclude=0"); + $res = this.$array.collect($SC.Function(function($_) { + return $_.valueArray($args); + })); - spec.degreeToKey = fn(function($scale, $stepsPerOctave) { - return $scale.performDegreeToKey(this, $stepsPerOctave); - }, "scale; stepsPerOctave=12"); + return this._flopped ? $res.flop() : $res; + }; + + // TODO: implements valueEnvir + // TODO: implements valueArrayEnvir spec.do = function($function) { - iterator.execute( - iterator.integer$do(this), - $function - ); + this.$array.do($function); return this; }; - spec.generate = function($function) { - - $function.value(this); + spec.flop = function() { + if (!this._flopped) { + this.$array = this.$array.collect($SC.Function(function($_) { + return $_.flop(); + })); + } + this._flopped = true; return this; }; - spec.collectAs = fn(function($function, $class) { - var $res; - var i, imax; + // TODO: implements envirFlop - if ($class === $nil) { - $class = SCArray; - } + spec.storeArgs = function() { + return $SC.Array([ this.$array ]); + }; - $res = $class.new(this); - for (i = 0, imax = this._; i < imax; ++i) { - $res.add($function.value($SC.Integer(i))); - } + }); - return $res; - }, "function; class"); +})(sc); - spec.collect = function($function) { - return this.collectAs($function, SCArray); - }; +// src/sc/lang/classlib/Streams/Stream.js +(function(sc) { - spec.reverseDo = function($function) { - iterator.execute( - iterator.integer$reverseDo(this), - $function - ); - return this; - }; + function SCStream() { + this.__initializeWith__("AbstractFunction"); + } - spec.for = fn(function($endval, $function) { - iterator.execute( - iterator.integer$for(this, $endval), - $function - ); - return this; - }, "endval; function"); + sc.lang.klass.define(SCStream, "Stream : AbstractFunction", function() { + // TODO: implements parent + // TODO: implements next + // TODO: implements iter + // TODO: implements value + // TODO: implements valueArray + // TODO: implements nextN + // TODO: implements all + // TODO: implements put + // TODO: implements putN + // TODO: implements putAll + // TODO: implements do + // TODO: implements subSample + // TODO: implements loop + // TODO: implements generate + // TODO: implements collect + // TODO: implements reject + // TODO: implements select + // TODO: implements dot + // TODO: implements interlace + // TODO: implements ++ + // TODO: implements appendStream + // TODO: implements collate + // TODO: implements <> + // TODO: implements composeUnaryOp + // TODO: implements composeBinaryOp + // TODO: implements reverseComposeBinaryOp + // TODO: implements composeNAryOp + // TODO: implements embedInStream + // TODO: implements while + // TODO: implements asEventStreamPlayer + // TODO: implements play + // TODO: implements trace + // TODO: implements constrain + // TODO: implements repeat + }); - spec.forBy = fn(function($endval, $stepval, $function) { - iterator.execute( - iterator.integer$forBy(this, $endval, $stepval), - $function - ); - return this; - }, "endval; stepval; function"); + function SCPauseStream() { + this.__initializeWith__("Stream"); + } - spec.to = fn(function($hi, $step) { - return $SC("Interval").new(this, $hi, $step); - }, "hi; step=1"); + sc.lang.klass.define(SCPauseStream, "PauseStream : Stream", function() { + // TODO: implements stream + // TODO: implements originalStream + // TODO: implements clock + // TODO: implements nextBeat + // TODO: implements streamHasEnded + // TODO: implements streamHasEnded_ - spec.asAscii = function() { - // <-- _AsAscii --> - return $SC.Char(String.fromCharCode(this._|0)); - }; + // TODO: implements isPlaying + // TODO: implements play + // TODO: implements reset + // TODO: implements stop + // TODO: implements prStop + // TODO: implements removedFromScheduler + // TODO: implements streamError + // TODO: implements wasStopped + // TODO: implements canPause + // TODO: implements pause + // TODO: implements resume + // TODO: implements refresh + // TODO: implements start + // TODO: implements stream_ + // TODO: implements next + // TODO: implements awake + // TODO: implements threadPlayer + }); - spec.asUnicode = utils.nop; + function SCTask() { + this.__initializeWith__("PauseStream"); + } - spec.asDigit = function() { - var c; + sc.lang.klass.define(SCTask, "Task : PauseStream", function() { + // TODO: implements storeArgs + }); - // - c = this._; - if (0 <= c && c <= 9) { - return $SC.Char(String(c)); - } - if (10 <= c && c <= 35) { - return $SC.Char(String.fromCharCode(c + 55)); - } +})(sc); - throw new Error("Integer: asDigit must be 0 <= this <= 35"); - }; +// src/sc/lang/classlib/Math/Magnitude.js +(function(sc) { - spec.asBinaryDigits = fn(function($numDigits) { - var raw, array, numDigits, i; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; - raw = this._; - numDigits = $numDigits.__int__(); - array = new Array(numDigits); - for (i = 0; i < numDigits; ++i) { - array.unshift($SC.Integer((raw >> i) & 1)); - } + sc.lang.klass.refine("Magnitude", function(spec) { + spec["=="] = function($aMagnitude) { + return $SC.Boolean(this.valueOf() === $aMagnitude.valueOf()); + }; - return $SC.Array(array); - }, "numDigits=8"); + spec["!="] = function($aMagnitude) { + return $SC.Boolean(this.valueOf() !== $aMagnitude.valueOf()); + }; - spec.asDigits = fn(function($base, $numDigits) { - var $num; - var array, numDigits, i; + spec.hash = function() { + return this._subclassResponsibility("hash"); + }; - $num = this; - if ($numDigits === $nil) { - $numDigits = ( - this.log() ["/"] ($base.log() ["+"] ($SC.Float(1e-10))) - ).asInteger().__inc__(); - } + spec["<"] = function($aMagnitude) { + return $SC.Boolean(this < $aMagnitude); + }; - array = []; - numDigits = $numDigits.__int__(); - array = new Array(numDigits); - for (i = 0; i < numDigits; ++i) { - array.unshift($num ["%"] ($base)); - $num = $num.div($base); - } + spec[">"] = function($aMagnitude) { + return $SC.Boolean(this > $aMagnitude); + }; - return $SC.Array(array); - }, "base=10; numDigits"); + spec["<="] = function($aMagnitude) { + return $SC.Boolean(this <= $aMagnitude); + }; - // TODO: implements nextPowerOfTwo - // TODO: implements isPowerOfTwo - // TODO: implements leadingZeroes - // TODO: implements trailingZeroes - // TODO: implements numBits - // TODO: implements log2Ceil - // TODO: implements grayCode - // TODO: implements setBit - // TODO: implements nthPrime - // TODO: implements prevPrime - // TODO: implements nextPrime - // TODO: implements indexOfPrime - // TODO: implements isPrime - // TODO: implements exit - // TODO: implements asStringToBase - // TODO: implements asBinaryString - // TODO: implements asHexString - // TODO: implements asIPString - // TODO: implements archiveAsCompileString + spec[">="] = function($aMagnitude) { + return $SC.Boolean(this >= $aMagnitude); + }; - spec.geom = fn(function($start, $grow) { - return SCArray.geom(this, $start, $grow); - }, "start; grow"); + spec.exclusivelyBetween = fn(function($lo, $hi) { + return $SC.Boolean($lo < this && this < $hi); + }, "lo; hi"); - spec.fib = fn(function($a, $b) { - return SCArray.fib(this, $a, $b); - }, "a=0.0; b=1.0"); + spec.inclusivelyBetween = fn(function($lo, $hi) { + return $SC.Boolean($lo <= this && this <= $hi); + }, "lo; hi"); - // TODO: implements factors - // TODO: implements pidRunning - // TODO: implements factorial - // TODO: implements isCaps - // TODO: implements isShift - // TODO: implements isCtrl - // TODO: implements isAlt - // TODO: implements isCmd - // TODO: implements isNumPad - // TODO: implements isHelp - // TODO: implements isFun + spec.min = fn(function($aMagnitude) { + return this <= $aMagnitude ? this : $aMagnitude; + }, "aMagnitude"); - spec.bitNot = function() { - return $SC.Integer(~this._); - }; + spec.max = fn(function($aMagnitude) { + return this >= $aMagnitude ? this : $aMagnitude; + }, "aMagnitude"); + + spec.clip = fn(function($lo, $hi) { + return this <= $lo ? $lo : this >= $hi ? $hi : this; + }, "lo; hi"); }); })(sc); -// src/sc/lang/classlib/Math/Float.js +// src/sc/lang/classlib/Math/Number.js (function(sc) { var fn = sc.lang.fn; var $SC = sc.lang.$SC; var iterator = sc.lang.iterator; - var mathlib = sc.libs.mathlib; - sc.lang.klass.refine("Float", function(spec, utils) { - spec.toString = function() { - var raw = this._; - - if (raw === Infinity) { - return "inf"; - } - if (raw === -Infinity) { - return "-inf"; - } - if (isNaN(raw)) { - return "nan"; - } + sc.lang.klass.refine("Number", function(spec, utils) { + spec.isNumber = utils.alwaysReturn$true; - return String(this._); + spec["+"] = function() { + return this._subclassResponsibility("+"); }; - spec.$new = function() { - throw new Error("Float.new is illegal, should use literal."); + spec["-"] = function() { + return this._subclassResponsibility("-"); }; - spec.isFloat = utils.alwaysReturn$true; - spec.asFloat = utils.nop; + spec["*"] = function() { + return this._subclassResponsibility("*"); + }; - [ - [ "+" , $SC.Float, $SC.Float ], - [ "-" , $SC.Float, $SC.Float ], - [ "*" , $SC.Float, $SC.Float ], - [ "/" , $SC.Float, $SC.Float ], - [ "mod" , $SC.Float , $SC.Float ], - [ "div" , $SC.Integer, $SC.Integer ], - [ "pow" , $SC.Float , $SC.Float ], - [ "min" , $SC.Float , $SC.Float ], - [ "max" , $SC.Float , $SC.Float ], - [ "bitAnd" , $SC.Float , $SC.Float ], - [ "bitOr" , $SC.Float , $SC.Float ], - [ "bitXor" , $SC.Float , $SC.Float ], - [ "lcm" , $SC.Float , $SC.Float ], - [ "gcd" , $SC.Float , $SC.Float ], - [ "round" , $SC.Float , $SC.Float ], - [ "roundUp" , $SC.Float , $SC.Float ], - [ "trunc" , $SC.Float , $SC.Float ], - [ "atan2" , $SC.Float , $SC.Float ], - [ "hypot" , $SC.Float , $SC.Float ], - [ "hypotApx", $SC.Float , $SC.Float ], - [ "leftShift" , $SC.Float, $SC.Float ], - [ "rightShift" , $SC.Float, $SC.Float ], - [ "unsignedRightShift", $SC.Float, $SC.Float ], - [ "ring1" , $SC.Float, $SC.Float ], - [ "ring2" , $SC.Float, $SC.Float ], - [ "ring3" , $SC.Float, $SC.Float ], - [ "ring4" , $SC.Float, $SC.Float ], - [ "difsqr" , $SC.Float, $SC.Float ], - [ "sumsqr" , $SC.Float, $SC.Float ], - [ "sqrsum" , $SC.Float, $SC.Float ], - [ "sqrdif" , $SC.Float, $SC.Float ], - [ "absdif" , $SC.Float, $SC.Float ], - [ "thresh" , $SC.Float, $SC.Float ], - [ "amclip" , $SC.Float, $SC.Float ], - [ "scaleneg", $SC.Float, $SC.Float ], - [ "clip2" , $SC.Float, $SC.Float ], - [ "fold2" , $SC.Float, $SC.Float ], - [ "wrap2" , $SC.Float, $SC.Float ], - [ "excess" , $SC.Float, $SC.Float ], - [ "firstArg", $SC.Float, $SC.Float ], - [ "rrand" , $SC.Float, $SC.Float ], - [ "exprand" , $SC.Float, $SC.Float ], - ].forEach(function(items) { - spec[items[0]] = sc.lang.classlib.bop.apply(null, items); - }); + spec["/"] = function() { + return this._subclassResponsibility("/"); + }; - spec.clip = fn(function($lo, $hi) { - // <-- _ClipFloat --> - if ($lo.__tag === 1027) { - return $lo; - } - if ($hi.__tag === 1027) { - return $hi; - } + spec.mod = function() { + return this._subclassResponsibility("mod"); + }; - return $SC.Float( - mathlib.clip(this._, $lo.__num__(), $hi.__num__()) - ); - }, "lo; hi"); + spec.div = function() { + return this._subclassResponsibility("div"); + }; - spec.wrap = fn(function($lo, $hi) { - // <-- _WrapInt --> - if ($lo.__tag === 1027) { - return $lo; - } - if ($hi.__tag === 1027) { - return $hi; - } + spec.pow = function() { + return this._subclassResponsibility("pow"); + }; - return $SC.Float( - mathlib.wrap(this._, $lo.__num__(), $hi.__num__()) - ); - }, "lo; hi"); + spec.performBinaryOpOnSeqColl = function($aSelector, $aSeqColl, $adverb) { + var $this = this; - spec.fold = fn(function($lo, $hi) { - // <-- _FoldFloat --> - if ($lo.__tag === 1027) { - return $lo; - } - if ($hi.__tag === 1027) { - return $hi; - } + return $aSeqColl.collect($SC.Function(function($item) { + return $item.perform($aSelector, $this, $adverb); + })); + }; - return $SC.Float( - mathlib.fold(this._, $lo.__num__(), $hi.__num__()) - ); - }, "lo; hi"); + // TODO: implements performBinaryOpOnPoint - // TODO: implements coin - // TODO: implements xrand2 + spec.rho = utils.nop; - spec.as32Bits = function() { - // <-- _As32Bits --> - return $SC.Integer( - new Int32Array( - new Float32Array([ this._ ]).buffer - )[0] - ); + spec.theta = function() { + return $SC.Float(0.0); }; - spec.high32Bits = function() { - // <-- _High32Bits --> - return $SC.Integer( - new Int32Array( - new Float64Array([ this._ ]).buffer - )[1] - ); - }; + spec.real = utils.nop; - spec.low32Bits = function() { - // <-- _Low32Bits --> - return $SC.Integer( - new Int32Array( - new Float64Array([ this._ ]).buffer - )[0] - ); + spec.imag = function() { + return $SC.Float(0.0); }; - spec.$from32Bits = fn(function($word) { - // <-- _From32Bits --> - return $SC.Float( - new Float32Array( - new Int32Array([ $word.__num__() ]).buffer - )[0] - ); - }, "word"); + // TODO: implements @ + // TODO: implements complex + // TODO: implements polar - spec.$from64Bits = fn(function($hiWord, $loWord) { - // <-- _From64Bits --> - return $SC.Float( - new Float64Array( - new Int32Array([ $loWord.__num__(), $hiWord.__num__() ]).buffer - )[0] + spec.for = fn(function($endValue, $function) { + iterator.execute( + iterator.number$for(this, $endValue), + $function ); - }, "hiWord; loWord"); + return this; + }, "endValue; function"); - spec.do = function($function) { + spec.forBy = fn(function($endValue, $stepValue, $function) { iterator.execute( - iterator.float$do(this), + iterator.number$forBy(this, $endValue, $stepValue), $function ); return this; - }; + }, "endValue; stepValue; function"); - spec.reverseDo = function($function) { + spec.forSeries = fn(function($second, $last, $function) { iterator.execute( - iterator.float$reverseDo(this), + iterator.number$forSeries(this, $second, $last), $function ); return this; - }; + }, "second; last; function"); + }); - // TODO: implements asStringPrec - // TODO: implements archiveAsCompileString - // TODO: implements storeOn - // TODO: implements switch +})(sc); - spec.bitNot = function() { - var f64 = new Float64Array([ this._ ]); - var i32 = new Int32Array(f64.buffer); - i32[0] = ~i32[0]; - return $SC.Float(f64[0]); - }; - }); - -})(sc); - -// src/sc/lang/classlib/Core/Thread.js +// src/sc/lang/classlib/Math/SimpleNumber.js (function(sc) { - function SCThread() { - this.__initializeWith__("Stream"); - } + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; + var rand = sc.libs.random; - sc.lang.klass.define(SCThread, "Thread : Stream", function() { - // TODO: implements state - // TODO: implements parent - // TODO: implements primitiveError - // TODO: implements primitiveIndex - // TODO: implements beats - // TODO: implements seconds - // TODO: implements clock - // TODO: implements nextBeat - // TODO: implements endBeat - // TODO: implements endBeat_ - // TODO: implements endValue - // TODO: implements endValue_ - // TODO: implements exceptionHandler - // TODO: implements exceptionHandler_ - // TODO: implements threadPlayer_ - // TODO: implements executingPath - // TODO: implements oldExecutingPath + function prOpSimpleNumber(selector, func) { + return function($aNumber, $adverb) { + var tag = $aNumber.__tag; - // TODO: implements init - // TODO: implements copy - // TODO: implements clock_ - // TODO: implements seconds_ - // TODO: implements beats_ - // TODO: implements isPlaying - // TODO: implements threadPlayer - // TODO: implements findThreadPlayer - // TODO: implements randSeed_ - // TODO: implements randData_ - // TODO: implements randData - // TODO: implements failedPrimitiveName - // TODO: implements handleError - // TODO: implements next - // TODO: implements value - // TODO: implements valueArray - // TODO: implements $primitiveError - // TODO: implements $primitiveErrorString - // TODO: implements storeOn - // TODO: implements archiveAsCompileString - // TODO: implements checkCanArchive - }); + switch (tag) { + case 770: + case 777: + return $SC.Boolean(func(this._, $aNumber._)); + } - function SCRoutine() { - this.__initializeWith__("Thread"); + if ($aNumber.isSequenceableCollection().valueOf()) { + return $aNumber.performBinaryOpOnSimpleNumber( + $SC.Symbol(selector), this, $adverb + ); + } + + return $SC.False(); + }; } - sc.lang.klass.define(SCRoutine, "Routine : Thread", function() { - // TODO: implements $run - // TODO: implements next - // TODO: implements value - // TODO: implements resume - // TODO: implements run - // TODO: implements valueArray - // TODO: implements reset - // TODO: implements stop - // TODO: implements p - // TODO: implements storeArgs - // TODO: implements storeOn - // TODO: implements awake - }); + sc.lang.klass.refine("SimpleNumber", function(spec, utils) { + var $nil = utils.$nil; + var $int_0 = utils.$int_0; + var $int_1 = utils.$int_1; + var SCArray = $SC("Array"); -})(sc); + spec.__newFrom__ = $SC.Float; -// src/sc/lang/classlib/Core/Symbol.js -(function(sc) { + spec.__bool__ = function() { + return this._ !== 0; + }; - var $SC = sc.lang.$SC; + spec.__dec__ = function() { + return this.__newFrom__(this._ - 1); + }; - sc.lang.klass.refine("Symbol", function(spec, utils) { - var $nil = utils.$nil; + spec.__inc__ = function() { + return this.__newFrom__(this._ + 1); + }; - spec.__sym__ = function() { - return this._; + spec.__int__ = function() { + if (!isFinite(this._)) { + return this._; + } + return this._|0; }; - spec.__str__ = function() { + spec.__num__ = function() { return this._; }; - spec.$new = function() { - throw new Error("Symbol.new is illegal, should use literal."); + spec.isValidUGenInput = function() { + return $SC.Boolean(!isNaN(this._)); }; - spec.asSymbol = utils.nop; + spec.numChannels = utils.alwaysReturn$int_1; - spec.asInteger = function() { - var m = /^[-+]?\d+/.exec(this._); - return $SC.Integer(m ? m[0]|0 : 0); + spec.magnitude = function() { + return this.abs(); }; - spec.asFloat = function() { - var m = /^[-+]?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?/.exec(this._); - return $SC.Float(m ? +m[0] : 0); + spec.angle = function() { + return $SC.Float(this._ >= 0 ? 0 : Math.PI); }; - spec.ascii = function() { - return this.asString().ascii(); + spec.neg = function() { + return this.__newFrom__(-this._); }; - // TODO: implements asCompileString + // bitNot: implemented by subclass - spec.asClass = function() { - if (sc.lang.klass.exists(this._)) { - return sc.lang.klass.get(this._); - } - return $nil; + spec.abs = function() { + return this.__newFrom__(Math.abs(this._)); }; - // TODO: implements asSetter - // TODO: implements asGetter - // TODO: implements asSpec - // TODO: implements asWarp - // TODO: implements asTuning - // TODO: implements asScale - // TODO: implements isSetter - // TODO: implements isClassName - // TODO: implements isMetaClassName - // TODO: implements isPrefix - // TODO: implements isPrimitiveName - // TODO: implements isPrimitive - // TODO: implements isMap - // TODO: implements isRest - // TODO: implements envirGet - // TODO: implements envirPut - // TODO: implements blend - // TODO: implements ++ - // TODO: implements asBinOpString - // TODO: implements applyTo - // TODO: implements performBinaryOpOnSomething + spec.ceil = function() { + return this.__newFrom__(Math.ceil(this._)); + }; - spec.neg = utils.nop; - spec.bitNot = utils.nop; - spec.abs = utils.nop; - spec.ceil = utils.nop; - spec.floor = utils.nop; - spec.frac = utils.nop; - spec.sign = utils.nop; - spec.sqrt = utils.nop; - spec.exp = utils.nop; - spec.midicps = utils.nop; - spec.cpsmidi = utils.nop; - spec.midiratio = utils.nop; - spec.ratiomidi = utils.nop; - spec.ampdb = utils.nop; - spec.dbamp = utils.nop; - spec.octcps = utils.nop; - spec.cpsoct = utils.nop; - spec.log = utils.nop; - spec.log2 = utils.nop; - spec.log10 = utils.nop; - spec.sin = utils.nop; - spec.cos = utils.nop; - spec.tan = utils.nop; - spec.asin = utils.nop; - spec.acos = utils.nop; - spec.atan = utils.nop; - spec.sinh = utils.nop; - spec.cosh = utils.nop; - spec.tanh = utils.nop; - spec.rand = utils.nop; - spec.rand2 = utils.nop; - spec.linrand = utils.nop; - spec.bilinrand = utils.nop; - spec.sum3rand = utils.nop; - spec.distort = utils.nop; - spec.softclip = utils.nop; - spec.coin = utils.nop; - spec.even = utils.nop; - spec.odd = utils.nop; - spec.rectWindow = utils.nop; - spec.hanWindow = utils.nop; - spec.welWindow = utils.nop; - spec.triWindow = utils.nop; - spec.scurve = utils.nop; - spec.ramp = utils.nop; - spec["+"] = utils.nop; - spec["-"] = utils.nop; - spec["*"] = utils.nop; - spec["/"] = utils.nop; - spec.mod = utils.nop; - spec.min = utils.nop; - spec.max = utils.nop; - spec.bitAnd = utils.nop; - spec.bitOr = utils.nop; - spec.bitXor = utils.nop; - spec.bitHammingDistance = utils.nop; - // TODO: Implements hammingDistance - spec.lcm = utils.nop; - spec.gcd = utils.nop; - spec.round = utils.nop; - spec.roundUp = utils.nop; - spec.trunc = utils.nop; - spec.atan2 = utils.nop; - spec.hypot = utils.nop; - spec.hypotApx = utils.nop; - spec.pow = utils.nop; - spec.leftShift = utils.nop; - spec.rightShift = utils.nop; - spec.unsignedRightShift = utils.nop; - spec.rrand = utils.nop; - spec.exprand = utils.nop; + spec.floor = function() { + return this.__newFrom__(Math.floor(this._)); + }; - // TODO: Implements < - // TODO: Implements > - // TODO: Implements <= - // TODO: Implements >= + spec.frac = function() { + var a = this._; - spec.degreeToKey = utils.nop; - spec.degrad = utils.nop; - spec.raddeg = utils.nop; - spec.doNumberOp = utils.nop; - spec.doComplexOp = utils.nop; - spec.doSignalOp = utils.nop; - - // TODO: Implements doListOp - // TODO: Implements primitiveIndex - // TODO: Implements specialIndex - // TODO: Implements printOn - // TODO: Implements storeOn - // TODO: Implements codegen_UGenCtorArg - - spec.archiveAsCompileString = utils.alwaysReturn$true; - - // TODO: Implements kr - // TODO: Implements ir - // TODO: Implements tr - // TODO: Implements ar - // TODO: Implements matchOSCAddressPattern - // TODO: Implements help + if (a < 0) { + return this.__newFrom__(1 + (a - (a|0))); + } + return this.__newFrom__(a - (a|0)); + }; - spec.asString = function() { - return $SC.String(this._); + spec.sign = function() { + var a = this._; + return this.__newFrom__( + a > 0 ? 1 : a === 0 ? 0 : -1 + ); }; - spec.shallowCopy = utils.nop; + spec.squared = function() { + return this.__newFrom__(this._ * this._); + }; - spec.performBinaryOpOnSimpleNumber = utils.nop; - }); + spec.cubed = function() { + return this.__newFrom__(this._ * this._ * this._); + }; -})(sc); + spec.sqrt = function() { + return $SC.Float(Math.sqrt(this._)); + }; -// src/sc/lang/classlib/Core/Ref.js -(function(sc) { + spec.exp = function() { + return $SC.Float(Math.exp(this._)); + }; - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; + spec.reciprocal = function() { + return $SC.Float(1 / this._); + }; - function SCRef(args) { - this.__initializeWith__("Object"); - this._value = args[0] || $SC.Nil(); - } + spec.midicps = function() { + return $SC.Float( + 440 * Math.pow(2, (this._ - 69) * 1/12) + ); + }; - sc.lang.klass.define(SCRef, "Ref : AbstractFunction", function(spec, utils) { - spec.valueOf = function() { - return this._value.valueOf(); + spec.cpsmidi = function() { + return $SC.Float( + Math.log(Math.abs(this._) * 1/440) * Math.LOG2E * 12 + 69 + ); }; - spec.value = function() { - return this._value; + spec.midiratio = function() { + return $SC.Float( + Math.pow(2, this._ * 1/12) + ); }; - spec.value_ = fn(function($value) { - this._value = $value; - return this; - }, "value"); + spec.ratiomidi = function() { + return $SC.Float( + Math.log(Math.abs(this._)) * Math.LOG2E * 12 + ); + }; - // $new + spec.ampdb = function() { + return $SC.Float( + Math.log(this._) * Math.LOG10E * 20 + ); + }; - spec.set = fn(function($thing) { - this._value = $thing; - return this; - }, "thing"); + spec.dbamp = function() { + return $SC.Float( + Math.pow(10, this._ * 0.05) + ); + }; - spec.get = function() { - return this._value; + spec.octcps = function() { + return $SC.Float( + 440 * Math.pow(2, this._ - 4.75) + ); }; - spec.dereference = spec.value; + spec.cpsoct = function() { + return $SC.Float( + Math.log(Math.abs(this._) * 1/440) * Math.LOG2E + 4.75 + ); + }; - spec.asRef = utils.nop; + spec.log = function() { + return $SC.Float(Math.log(this._)); + }; - spec.valueArray = spec.value; + spec.log2 = function() { + return $SC.Float(Math.log(Math.abs(this._)) * Math.LOG2E); + }; - spec.valueEnvir = spec.value; + spec.log10 = function() { + return $SC.Float(Math.log(this._) * Math.LOG10E); + }; - spec.valueArrayEnvir = spec.value; + spec.sin = function() { + return $SC.Float(Math.sin(this._)); + }; - spec.next = spec.value; + spec.cos = function() { + return $SC.Float(Math.cos(this._)); + }; - spec.asUGenInput = utils.nop; + spec.tan = function() { + return $SC.Float(Math.tan(this._)); + }; - // TODO: implements printOn - // TODO: implements storeOn + spec.asin = function() { + return $SC.Float(Math.asin(this._)); + }; - spec.at = function($key) { - return this._value.at($key); + spec.acos = function() { + return $SC.Float(Math.acos(this._)); }; - spec.put = function($key, $val) { - return this._value.put($key, $val); + spec.atan = function() { + return $SC.Float(Math.atan(this._)); }; - // TODO: implements seq - // TODO: implements asControlInput - // TODO: implements asBufWithValues - // TODO: implements multichannelExpandRef - }); + function _sinh(a) { + return (Math.pow(Math.E, a) - Math.pow(Math.E, -a)) * 0.5; + } -})(sc); + spec.sinh = function() { + return $SC.Float(_sinh(this._)); + }; -// src/sc/lang/classlib/Core/Nil.js -(function(sc) { + function _cosh(a) { + return (Math.pow(Math.E, a) + Math.pow(Math.E, -a)) * 0.5; + } - var slice = [].slice; - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; + spec.cosh = function() { + return $SC.Float(_cosh(this._)); + }; - sc.lang.klass.refine("Nil", function(spec, utils) { - var $nil = utils.$nil; + spec.tanh = function() { + return $SC.Float(_sinh(this._) / _cosh(this._)); + }; - spec.__num__ = function() { - return 0; + spec.rand = function() { + return this.__newFrom__( + rand.next() * this._ + ); }; - spec.__bool__ = function() { - return false; + spec.rand2 = function() { + return this.__newFrom__( + (rand.next() * 2 - 1) * this._ + ); }; - spec.__sym__ = function() { - return "nil"; + spec.linrand = function() { + return this.__newFrom__( + Math.min(rand.next(), rand.next()) * this._ + ); }; - spec.toString = function() { - return "nil"; + spec.bilinrand = function() { + return this.__newFrom__( + (rand.next() - rand.next()) * this._ + ); }; - spec.$new = function() { - throw new Error("Nil.new is illegal, should use literal."); + spec.sum3rand = function() { + return this.__newFrom__( + (rand.next() + rand.next() + rand.next() - 1.5) * 2/3 * this._ + ); }; - spec.isNil = utils.alwaysReturn$true; - spec.notNil = utils.alwaysReturn$false; + spec.distort = function() { + return $SC.Float( + this._ / (1 + Math.abs(this._)) + ); + }; - spec["?"] = function($obj) { - return $obj; + spec.softclip = function() { + var a = this._, abs_a = Math.abs(a); + return $SC.Float(abs_a <= 0.5 ? a : (abs_a - 0.25) / a); }; - spec["??"] = function($obj) { - return $obj.value(); + spec.coin = function() { + return $SC.Boolean(rand.next() < this._); }; - spec["!?"] = utils.nop; + spec.isPositive = function() { + return $SC.Boolean(this._ >= 0); + }; - spec.asBoolean = utils.alwaysReturn$false; - spec.booleanValue = utils.alwaysReturn$false; + spec.isNegative = function() { + return $SC.Boolean(this._ < 0); + }; - spec.push = fn(function($function) { - return $function.value(); - }, "function"); + spec.isStrictlyPositive = function() { + return $SC.Boolean(this._ > 0); + }; - spec.appendStream = fn(function($stream) { - return $stream; - }, "stream"); + spec.isNaN = function() { + return $SC.Boolean(isNaN(this._)); + }; - spec.pop = utils.nop; - spec.source = utils.nop; - spec.source_ = utils.nop; + spec.asBoolean = function() { + return $SC.Boolean(this._ > 0); + }; - spec.rate = utils.nop; - spec.numChannels = utils.nop; - spec.isPlaying = utils.alwaysReturn$false; + spec.booleanValue = function() { + return $SC.Boolean(this._ > 0); + }; - spec.do = utils.nop; - spec.reverseDo = utils.nop; - spec.pairsDo = utils.nop; - spec.collect = utils.nop; - spec.select = utils.nop; - spec.reject = utils.nop; - spec.detect = utils.nop; - spec.collectAs = utils.nop; - spec.selectAs = utils.nop; - spec.rejectAs = utils.nop; + spec.binaryValue = function() { + return this._ > 0 ? $int_1 : $int_0; + }; - spec.dependants = function() { - return $SC("IdentitySet").new(); + spec.rectWindow = function() { + var a = this._; + if (a < 0 || 1 < a) { + return $SC.Float(0); + } + return $SC.Float(1); }; - spec.changed = utils.nop; - spec.addDependant = utils.nop; - spec.removeDependant = utils.nop; - spec.release = utils.nop; - spec.update = utils.nop; + spec.hanWindow = function() { + var a = this._; + if (a < 0 || 1 < a) { + return $SC.Float(0); + } + return $SC.Float(0.5 - 0.5 * Math.cos(a * 2 * Math.PI)); + }; - spec.transformEvent = fn(function($event) { - return $event; - }, "event"); + spec.welWindow = function() { + var a = this._; + if (a < 0 || 1 < a) { + return $SC.Float(0); + } + return $SC.Float(Math.sin(a * Math.PI)); + }; - spec.awake = utils.alwaysReturn$nil; + spec.triWindow = function() { + var a = this._; + if (a < 0 || 1 < a) { + return $SC.Float(0); + } + if (a < 0.5) { + return $SC.Float(2 * a); + } + return $SC.Float(-2 * a + 2); + }; - spec.play = utils.nop; + spec.scurve = function() { + var a = this._; + if (a <= 0) { + return $SC.Float(0); + } + if (1 <= a) { + return $SC.Float(1); + } + return $SC.Float(a * a * (3 - 2 * a)); + }; - spec.nextTimeOnGrid = fn(function($clock) { - if ($clock === $nil) { - return $clock; + spec.ramp = function() { + var a = this._; + if (a <= 0) { + return $SC.Float(0); } - return $SC.Function(function() { - return $clock.nextTimeOnGrid(); - }); - }, "clock"); + if (1 <= a) { + return $SC.Float(1); + } + return $SC.Float(a); + }; - spec.asQuant = function() { - return $SC("Quant").default(); + // +: implemented by subclass + // -: implemented by subclass + // *: implemented by subclass + // /: implemented by subclass + // mod: implemented by subclass + // div: implemented by subclass + // pow: implemented by subclass + // min: implemented by subclass + // max: implemented by subclass + // bitAnd: implemented by subclass + // bitOr : implemented by subclass + // bitXor: implemented by subclass + + spec.bitTest = function($bit) { + return $SC.Boolean( + this.bitAnd($int_1.leftShift($bit)).valueOf() !== 0 + ); }; - spec.swapThisGroup = utils.nop; - spec.performMsg = utils.nop; + // lcm : implemented by subclass + // gcd : implemented by subclass + // round : implemented by subclass + // roundUp : implemented by subclass + // trunc : implemented by subclass + // atan2 : implemented by subclass + // hypot : implemented by subclass + // hypotApx: implemented by subclass + // leftShift : implemented by subclass + // rightShift : implemented by subclass + // unsignedRightShift: implemented by subclass + // ring1 : implemented by subclass + // ring2 : implemented by subclass + // ring3 : implemented by subclass + // ring4 : implemented by subclass + // difsqr: implemented by subclass + // sumsqr: implemented by subclass + // sqrsum: implemented by subclass + // sqrdif: implemented by subclass + // absdif: implemented by subclass + // thresh: implemented by subclass + // amclip: implemented by subclass + // clip2 : implemented by subclass + // fold2 : implemented by subclass + // wrap2 : implemented by subclass + // excess: implemented by subclass + // firstArg: implemented by subclass + // rrand : implemented by subclass + // exprand : implemented by subclass - spec.printOn = fn(function($stream) { - $stream.putAll($SC.String("nil")); - return this; - }, "stream"); + spec["=="] = function($aNumber) { + return $SC.Boolean(this._ === $aNumber._); + }; - spec.storeOn = fn(function($stream) { - $stream.putAll($SC.String("nil")); - return this; - }, "stream"); + spec["!="] = function($aNumber) { + return $SC.Boolean(this._ !== $aNumber._); + }; - spec.matchItem = utils.alwaysReturn$true; + spec["<"] = prOpSimpleNumber("<", function(a, b) { + return a < b; + }); + spec[">"] = prOpSimpleNumber(">", function(a, b) { + return a > b; + }); + spec["<="] = prOpSimpleNumber("<=", function(a, b) { + return a <= b; + }); + spec[">="] = prOpSimpleNumber(">=", function(a, b) { + return a >= b; + }); - spec.add = fn(function($value) { - return $SC.Array([ $value ]); - }, "value"); + spec.equalWithPrecision = fn(function($that, $precision) { + return this.absdif($that) ["<"] ($precision); + }, "that; precision=0.0001"); - spec.addAll = fn(function($array) { - return $array.asArray(); - }, "array"); + // TODO: implements hash - spec["++"] = function($array) { - return $array.asArray(); + spec.asInteger = function() { + return $SC.Integer(this._); }; - spec.asCollection = function() { - return $SC.Array(); + spec.asFloat = function() { + return $SC.Float(this._); }; - spec.remove = utils.nop; + // TODO: implements asComplex + // TODO: implements asRect - spec.set = utils.nop; + spec.degrad = function() { + return $SC.Float(this._ * Math.PI / 180); + }; - spec.get = fn(function($prevVal) { - return $prevVal; - }, "prevVal"); + spec.raddeg = function() { + return $SC.Float(this._ * 180 / Math.PI); + }; - spec.addFunc = function() { - var functions = slice.call(arguments); - if (functions.length <= 1) { - return functions[0]; - } - return $SC("FunctionList").new($SC.Array(functions)); + // TODO: implements performBinaryOpOnSimpleNumber + // TODO: implements performBinaryOpOnComplex + // TODO: implements performBinaryOpOnSignal + + spec.nextPowerOfTwo = function() { + return $SC.Float( + Math.pow(2, Math.ceil(Math.log(this._) / Math.log(2))) + ); }; - spec.removeFunc = utils.nop; + spec.nextPowerOf = fn(function($base) { + return $base.pow( + (this.log() ["/"] ($base.log())).ceil() + ); + }, "base"); - spec.replaceFunc = utils.nop; - spec.seconds_ = utils.nop; - spec.throw = utils.nop; + spec.nextPowerOfThree = function() { + return $SC.Float( + Math.pow(3, Math.ceil(Math.log(this._) / Math.log(3))) + ); + }; - // TODO: implements handleError + spec.previousPowerOf = fn(function($base) { + return $base.pow( + (this.log() ["/"] ($base.log())).ceil().__dec__() + ); + }, "base"); - spec.archiveAsCompileString = utils.alwaysReturn$true; + spec.quantize = fn(function($quantum, $tolerance, $strength) { + var $round, $diff; - spec.asSpec = function() { - return $SC("ControlSpec").new(); - }; + $round = this.round($quantum); + $diff = $round ["-"] (this); - spec.superclassesDo = utils.nop; + if ($diff.abs() < $tolerance) { + return this ["+"] ($strength ["*"] ($diff)); + } - spec.shallowCopy = utils.nop; - }); + return this; + }, "quantum=1.0; tolerance=0.05; strength=1.0"); -})(sc); + spec.linlin = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { + var $res = null; -// src/sc/lang/classlib/Core/Kernel.js -(function(sc) { + $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); - sc.lang.klass.refine("Class", { - // TODO: implements superclass - // TODO: implements asClass - // TODO: implements initClass - // TODO: implements $initClassTree - // TODO: implements $allClasses - // TODO: implements findMethod - // TODO: implements findRespondingMethodFor - // TODO: implements findOverriddenMethod - // TODO: implements superclassesDo - // TODO: implements while - // TODO: implements dumpByteCodes - // TODO: implements dumpClassSubtree - // TODO: implements dumpInterface - // TODO: implements asString - // TODO: implements printOn - // TODO: implements storeOn - // TODO: implements archiveAsCompileString - // TODO: implements hasHelpFile - // TODO: implements helpFilePath - // TODO: implements help - // TODO: implements openHelpFile - // TODO: implements shallowCopy - // TODO: implements openCodeFile - // TODO: implements classVars - // TODO: implements inspectorClass - // TODO: implements findReferences - // TODO: implements $findAllReferences - // TODO: implements allSubclasses - // TODO: implements superclasses - }); + if ($res === null) { + // (this-inMin)/(inMax-inMin) * (outMax-outMin) + outMin; + $res = ((this ["-"] ($inMin)) ["/"] ($inMax ["-"] ($inMin)) + ["*"] ($outMax ["-"] ($outMin)) ["+"] ($outMin)); + } -})(sc); + return $res; + }, "inMin; inMax; outMin; outMax; clip=\\minmax"); -// src/sc/lang/classlib/Core/Function.js -(function(sc) { + spec.linexp = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { + var $res = null; - var slice = [].slice; - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; + $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); - sc.lang.klass.refine("Function", function(spec, utils) { - var BOOL = utils.BOOL; - var $nil = utils.$nil; - var SCArray = $SC("Array"); + if ($res === null) { + // Math.pow(outMax/outMin, (this-inMin)/(inMax-inMin)) * outMin; + $res = $outMax ["/"] ($outMin).pow( + (this ["-"] ($inMin)) ["/"] ($inMax ["-"] ($inMin)) + ) ["*"] ($outMin); + } - // TODO: implements def + return $res; + }, "inMin; inMax; outMin; outMax; clip=\\minmax"); - spec.$new = function() { - throw new Error("Function.new is illegal, should use literal."); - }; + spec.explin = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { + var $res = null; - spec.isFunction = utils.alwaysReturn$true; + $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); - // TODO: implements isClosed + if ($res === null) { + // (((Math.log(this/inMin)) / (Math.log(inMax/inMin))) * (outMax-outMin)) + outMin; + $res = ((this ["/"] ($inMin).log() ["/"] ($inMax ["/"] ($inMin).log()) + ["*"] ($outMax ["-"] ($outMin))) ["+"] ($outMin)); + } - spec.archiveAsCompileString = utils.alwaysReturn$true; - spec.archiveAsObject = utils.alwaysReturn$true; + return $res; + }, "inMin; inMax; outMin; outMax; clip=\\minmax"); - // TODO: implements checkCanArchive + spec.expexp = fn(function($inMin, $inMax, $outMin, $outMax, $clip) { + var $res = null; - spec.shallowCopy = utils.nop; + $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); - spec.choose = function() { - return this.value(); - }; + if ($res === null) { + // Math.pow(outMax/outMin, Math.log(this/inMin) / Math.log(inMax/inMin)) * outMin; + $res = $outMax ["/"] ($outMin).pow( + this ["/"] ($inMin).log() ["/"] ($inMax ["/"] ($inMin).log()) + ) ["*"] ($outMin); + } - spec.update = function() { - return this._.apply(this, arguments); - }; + return $res; + }, "inMin; inMax; outMin; outMax; clip=\\minmax"); - spec.value = function() { - return this._.apply(this, arguments); - }; + spec.lincurve = fn(function($inMin, $inMax, $outMin, $outMax, $curve, $clip) { + var $res = null, $grow, $a, $b, $scaled; - spec.valueArray = function($args) { - return this._.apply(this, $args.asArray()._); - }; + $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); - // TODO: implements valueEnvir - // TODO: implements valueArrayEnvir - // TODO: implements functionPerformList - // TODO: implements valueWithEnvir - // TODO: implements performWithEnvir - // TODO: implements performKeyValuePairs - // TODO: implements numArgs - // TODO: implements numVars - // TODO: implements varArgs - // TODO: implements loop - // TODO: implements block + if ($res === null) { + if (Math.abs($curve.valueOf()) < 0.001) { + $res = this.linlin($inMin, $inMax, $outMin, $outMax); + } else { + $grow = $curve.exp(); + $a = $outMax ["-"] ($outMin) ["/"] ($SC.Float(1.0) ["-"] ($grow)); + $b = $outMin ["+"] ($a); + $scaled = (this ["-"] ($inMin)) ["/"] ($inMax ["-"] ($inMin)); - spec.asRoutine = function() { - return $SC("Routine").new(this); - }; + $res = $b ["-"] ($a ["*"] ($grow.pow($scaled))); + } + } - spec.dup = fn(function($n) { - return SCArray.fill($n, this); - }, "n=2"); + return $res; + }, "inMin=0; inMax=1; outMin=0; outMax=1; curve=-4; clip=\\minmax"); - // TODO: implements sum - // TODO: implements defer - // TODO: implements thunk - // TODO: implements transformEvent - // TODO: implements set - // TODO: implements get - // TODO: implements fork - // TODO: implements forkIfNeeded - // TODO: implements awake - // TODO: implements cmdPeriod - // TODO: implements bench - // TODO: implements protect - // TODO: implements try - // TODO: implements prTry - // TODO: implements handleError + spec.curvelin = fn(function($inMin, $inMax, $outMin, $outMax, $curve, $clip) { + var $res = null, $grow, $a, $b; - spec.case = function() { - var args, i, imax; + $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); - args = slice.call(arguments); - args.unshift(this); + if ($res === null) { + if (Math.abs($curve.valueOf()) < 0.001) { + $res = this.linlin($inMin, $inMax, $outMin, $outMax); + } else { + $grow = $curve.exp(); + $a = $inMax ["-"] ($inMin) ["/"] ($SC.Float(1.0) ["-"] ($grow)); + $b = $inMin ["+"] ($a); - for (i = 0, imax = args.length >> 1; i < imax; ++i) { - if (BOOL(args[i * 2].value())) { - return args[i * 2 + 1].value(); + $res = ((($b ["-"] (this)) ["/"] ($a)).log() + ["*"] ($outMax ["-"] ($outMin)) ["/"] ($curve) ["+"] ($outMin)); } } - if (args.length % 2 === 1) { - return args[args.length - 1].value(); - } - - return $nil; - }; - - spec.r = function() { - return $SC("Routine").new(this); - }; - - spec.p = function() { - return $SC("Prout").new(this); - }; + return $res; + }, "inMin=0; inMax=1; outMin=0; outMax=1; curve=-4; clip=\\minmax"); - // TODO: implements matchItem - // TODO: implements performDegreeToKey + spec.bilin = fn(function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) { + var $res = null; - spec.flop = function() { - var $this = this; - // if(def.argNames.isNil) { ^this }; - return $SC.Function(function() { - var $$args = $SC.Array(slice.call(arguments)); - return $$args.flop().collect($SC.Function(function($_) { - return $this.valueArray($_); - })); - }); - }; + $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); - // TODO: implements envirFlop - // TODO: implements makeFlopFunc - // TODO: implements inEnvir + if ($res === null) { + if (this >= $inCenter) { + $res = this.linlin($inCenter, $inMax, $outCenter, $outMax, $SC.Symbol("none")); + } else { + $res = this.linlin($inMin, $inCenter, $outMin, $outCenter, $SC.Symbol("none")); + } + } - spec.while = fn(function($body) { - sc.lang.iterator.execute( - sc.lang.iterator.function$while(this), - $body - ); - return this; - }, "body"); - }); + return $res; + }, "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"); -})(sc); + spec.biexp = fn(function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) { + var $res = null; -// src/sc/lang/classlib/Core/Char.js -(function(sc) { + $res = clip_for_map(this, $inMin, $inMax, $outMin, $outMax, $clip); - var $SC = sc.lang.$SC; + if ($res === null) { + if (this >= $inCenter) { + $res = this.explin($inCenter, $inMax, $outCenter, $outMax, $SC.Symbol("none")); + } else { + $res = this.explin($inMin, $inCenter, $outMin, $outCenter, $SC.Symbol("none")); + } + } - sc.lang.klass.refine("Char", function(spec, utils) { - spec.__str__ = function() { - return this._; - }; + return $res; + }, "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"); - spec.$nl = function() { - return $SC.Char("\n"); - }; + spec.moddif = fn(function($aNumber, $mod) { + var $diff, $modhalf; - spec.$ff = function() { - return $SC.Char("\f"); - }; + $diff = this.absdif($aNumber) ["%"] ($mod); + $modhalf = $mod ["*"] ($SC.Float(0.5)); - spec.$tab = function() { - return $SC.Char("\t"); - }; + return $modhalf ["-"] ($diff.absdif($modhalf)); + }, "aNumber=0.0; mod=1.0"); - spec.$space = function() { - return $SC.Char(" "); - }; + spec.lcurve = fn(function($a, $m, $n, $tau) { + var $rTau, $x; - spec.$comma = function() { - return $SC.Char(","); - }; + $x = this.neg(); - spec.$new = function() { - throw new Error("Char.new is illegal, should use literal."); - }; + if ($tau.__num__() === 1.0) { + // a * (m * exp(x) + 1) / (n * exp(x) + 1) + return $a ["*"] ( + $m ["*"] ($x.exp()).__inc__() + ) ["/"] ( + $n ["*"] ($x.exp()).__inc__() + ); + } else { + $rTau = $tau.reciprocal(); + return $a ["*"] ( + $m ["*"] ($x.exp()) ["*"] ($rTau).__inc__() + ) ["/"] ( + $n ["*"] ($x.exp()) ["*"] ($rTau).__inc__() + ); + } + }, "a=1.0; m=0.0; n=1.0; tau=1.0"); - // TODO: implements hash + spec.gauss = fn(function($standardDeviation) { + // ^((((-2*log(1.0.rand)).sqrt * sin(2pi.rand)) * standardDeviation) + this) + return ($SC.Float(-2.0) ["*"] ($SC.Float(1.0).rand().log()).sqrt() ["*"] ( + $SC.Float(2 * Math.PI).rand().sin() + ) ["*"] ($standardDeviation)) ["+"] (this); + }, "standardDeviation"); - spec.ascii = function() { - return $SC.Integer(this._.charCodeAt(0)); - }; + spec.gaussCurve = fn(function($a, $b, $c) { + // ^a * (exp(squared(this - b) / (-2.0 * squared(c)))) + return $a ["*"] (( + (this ["-"] ($b).squared()) ["/"] ($SC.Float(-2.0) ["*"] ($c.squared())) + ).exp()); + }, "a=1.0; b=0.0; c=1.0"); - spec.digit = function() { - var ascii = this._.charCodeAt(0); - if (0x30 <= ascii && ascii <= 0x39) { - return $SC.Integer(ascii - 0x30); - } - if (0x41 <= ascii && ascii <= 0x5a) { - return $SC.Integer(ascii - 0x37); - } - if (0x61 <= ascii && ascii <= 0x7a) { - return $SC.Integer(ascii - 0x57); - } - throw new Error("digitValue failed"); + // TODO: implements asPoint + // TODO: implements asWarp + + spec.wait = function() { + return this.yield(); }; - spec.asAscii = utils.nop; + // TODO: implements waitUntil + // TODO: implements sleep + // TODO: implements printOn + // TODO: implements storeOn - spec.asUnicode = function() { - return this.ascii(); + spec.rate = function() { + return $SC.Symbol("scalar"); }; - spec.toUpper = function() { - return $SC.Char(this._.toUpperCase()); + spec.asAudioRateInput = function() { + if (this._ === 0) { + return $SC("Silent").ar(); + } + return $SC("DC").ar(this); }; - spec.toLower = function() { - return $SC.Char(this._.toLowerCase()); - }; + spec.madd = fn(function($mul, $add) { + return (this ["*"] ($mul)) ["+"] ($add); + }, "mul; add"); - spec.isAlpha = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x41 <= ascii && ascii <= 0x5a) || - (0x61 <= ascii && ascii <= 0x7a)); - }; + spec.lag = utils.nop; + spec.lag2 = utils.nop; + spec.lag3 = utils.nop; + spec.lagud = utils.nop; + spec.lag2ud = utils.nop; + spec.lag3ud = utils.nop; + spec.varlag = utils.nop; + spec.slew = utils.nop; - spec.isAlphaNum = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x30 <= ascii && ascii <= 0x39) || - (0x41 <= ascii && ascii <= 0x5a) || - (0x61 <= ascii && ascii <= 0x7a)); - }; + // TODO: implements writeInputSpec - spec.isPrint = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x20 <= ascii && ascii <= 0x7e)); - }; + spec.series = fn(function($second, $last) { + var $step; + var last, step, size; - spec.isPunct = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x21 <= ascii && ascii <= 0x2f) || - (0x3a <= ascii && ascii <= 0x40) || - (0x5b <= ascii && ascii <= 0x60) || - (0x7b <= ascii && ascii <= 0x7e)); - }; + if ($second === $nil) { + if (this.valueOf() < $last.valueOf()) { + $second = this.__inc__(); + } else { + $second = this.__dec__(); + } + } + $step = $second ["-"] (this); - spec.isControl = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x00 <= ascii && ascii <= 0x1f) || ascii === 0x7f); - }; + last = $last.__num__(); + step = $step.__num__(); + size = (Math.floor((last - this._) / step + 0.001)|0) + 1; - spec.isSpace = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x09 <= ascii && ascii <= 0x0d) || ascii === 0x20); - }; + return SCArray.series($SC.Integer(size), this, $step); + }, "second; last"); - spec.isVowl = function() { - var ch = this._.charAt(0).toUpperCase(); - return $SC.Boolean("AEIOU".indexOf(ch) !== -1); - }; + // TODO: implements seriesIter + // TODO: implements degreeToKey + // TODO: implements keyToDegree + // TODO: implements nearestInList + // TODO: implements nearestInScale + // TODO: implements partition + // TODO: implements nextTimeOnGrid + // TODO: implements playAndDelta + // TODO: implements asQuant + // TODO: implements asTimeString + // TODO: implements asFraction + // TODO: implements asBufWithValues + // TODO: implements schedBundleArrayOnClock - spec.isDecDigit = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x30 <= ascii && ascii <= 0x39)); - }; + spec.shallowCopy = utils.nop; + }); - spec.isUpper = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x41 <= ascii && ascii <= 0x5a)); - }; + function clip_for_map($this, $inMin, $inMax, $outMin, $outMax, $clip) { - spec.isLower = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x61 <= ascii && ascii <= 0x7a)); - }; + switch ($clip.__sym__()) { + case "minmax": + if ($this <= $inMin) { + return $outMin; + } + if ($this >= $inMax) { + return $outMax; + } + break; + case "min": + if ($this <= $inMin) { + return $outMin; + } + break; + case "max": + if ($this >= $inMax) { + return $outMax; + } + break; + } - spec.isFileSafe = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean((0x20 <= ascii && ascii <= 0x7e) && - ascii !== 0x2f && // 0x2f is '/' - ascii !== 0x3a && // 0x3a is ':' - ascii !== 0x22); // 0x22 is '"' - }; + return null; + } - spec.isPathSeparator = function() { - var ascii = this._.charCodeAt(0); - return $SC.Boolean(ascii === 0x2f); - }; +})(sc); - spec["<"] = function($aChar) { - return $SC.Boolean(this.ascii() < $aChar.ascii()); - }; +// src/sc/lang/classlib/Math/bop.js +(function(sc) { - spec["++"] = function($that) { - return $SC.String(this._ + $that.__str__()); - }; + var $SC = sc.lang.$SC; + var mathlib = sc.libs.mathlib; - // TODO: implements $bullet - // TODO: implements printOn - // TODO: implements storeOn + sc.lang.classlib.bop = function(selector, type1, type2) { + var func = mathlib[selector]; - spec.archiveAsCompileString = function() { - return $SC.True(); - }; + return function($aNumber, $adverb) { + var tag = $aNumber.__tag; - spec.asString = function() { - return $SC.String(this._); - }; + switch (tag) { + case 770: + return type1(func(this._, $aNumber._)); + case 777: + return type2(func(this._, $aNumber._)); + } - spec.shallowCopy = utils.nop; - }); + return $aNumber.performBinaryOpOnSimpleNumber( + $SC.Symbol(selector), this, $adverb + ); + }; + }; })(sc); -// src/sc/lang/classlib/Core/Boolean.js +// src/sc/lang/classlib/Math/Integer.js (function(sc) { var fn = sc.lang.fn; var $SC = sc.lang.$SC; + var iterator = sc.lang.iterator; + var mathlib = sc.libs.mathlib; - sc.lang.klass.refine("Boolean", function(spec, utils) { - spec.__bool__ = function() { + sc.lang.klass.refine("Integer", function(spec, utils) { + var $nil = utils.$nil; + var $int_1 = utils.$int_1; + var SCArray = $SC("Array"); + + spec.__newFrom__ = $SC.Integer; + + spec.__int__ = function() { return this._; }; spec.toString = function() { - return String(this._); + return String("" + this._); }; spec.$new = function() { - throw new Error("Boolean.new is illegal, should use literal."); + throw new Error("Integer.new is illegal, should use literal."); }; - spec.xor = function($bool) { - return $SC.Boolean(this === $bool).not(); - }; + spec.isInteger = utils.alwaysReturn$true; - // TODO: implements if - // TODO: implements nop - // TODO: implements && - // TODO: implements || - // TODO: implements and - // TODO: implements or - // TODO: implements nand - // TODO: implements asInteger - // TODO: implements binaryValue - - spec.asBoolean = utils.nop; - spec.booleanValue = utils.nop; - - // TODO: implements keywordWarnings - // TODO: implements trace - // TODO: implements printOn - // TODO: implements storeOn + // TODO: implements hash - spec.archiveAsCompileString = utils.alwaysReturn$true; + [ + [ "+", $SC.Integer, $SC.Float ], + [ "-", $SC.Integer, $SC.Float ], + [ "*", $SC.Integer, $SC.Float ], + [ "/", $SC.Float , $SC.Float ], + [ "mod" , $SC.Integer, $SC.Float ], + [ "div" , $SC.Integer, $SC.Integer ], + [ "pow" , $SC.Float , $SC.Float ], + [ "min" , $SC.Integer, $SC.Float ], + [ "max" , $SC.Integer, $SC.Float ], + [ "bitAnd" , $SC.Integer, $SC.Float ], + [ "bitOr" , $SC.Integer, $SC.Float ], + [ "bitXor" , $SC.Integer, $SC.Float ], + [ "lcm" , $SC.Integer, $SC.Float ], + [ "gcd" , $SC.Integer, $SC.Float ], + [ "round" , $SC.Integer, $SC.Float ], + [ "roundUp" , $SC.Integer, $SC.Float ], + [ "trunc" , $SC.Integer, $SC.Float ], + [ "atan2" , $SC.Float , $SC.Float ], + [ "hypot" , $SC.Float , $SC.Float ], + [ "hypotApx", $SC.Float , $SC.Float ], + [ "leftShift" , $SC.Integer, $SC.Float ], + [ "rightShift" , $SC.Integer, $SC.Float ], + [ "unsignedRightShift", $SC.Integer, $SC.Float ], + [ "ring1" , $SC.Integer, $SC.Float ], + [ "ring2" , $SC.Integer, $SC.Float ], + [ "ring3" , $SC.Integer, $SC.Float ], + [ "ring4" , $SC.Integer, $SC.Float ], + [ "difsqr" , $SC.Integer, $SC.Float ], + [ "sumsqr" , $SC.Integer, $SC.Float ], + [ "sqrsum" , $SC.Integer, $SC.Float ], + [ "sqrdif" , $SC.Integer, $SC.Float ], + [ "absdif" , $SC.Integer, $SC.Float ], + [ "thresh" , $SC.Integer, $SC.Integer ], + [ "amclip" , $SC.Integer, $SC.Float ], + [ "scaleneg", $SC.Integer, $SC.Float ], + [ "clip2" , $SC.Integer, $SC.Float ], + [ "fold2" , $SC.Integer, $SC.Float ], + [ "excess" , $SC.Integer, $SC.Float ], + [ "firstArg", $SC.Integer, $SC.Integer ], + [ "rrand" , $SC.Integer, $SC.Float ], + [ "exprand" , $SC.Float , $SC.Float ], + ].forEach(function(items) { + spec[items[0]] = sc.lang.classlib.bop.apply(null, items); + }); - spec.while = function() { - var msg = "While was called with a fixed (unchanging) Boolean as the condition. "; - msg += "Please supply a Function instead."; - throw new Error(msg); - }; + spec.wrap2 = function($aNumber, $adverb) { + var tag = $aNumber.__tag; - spec.shallowCopy = utils.nop; - }); + switch (tag) { + case 770: + return $SC.Integer(mathlib.iwrap(this._, -$aNumber._, $aNumber._)); + case 777: + return $SC.Float(mathlib.wrap2(this._, $aNumber._)); + } - sc.lang.klass.refine("True", function(spec, utils) { - spec.$new = function() { - throw new Error("True.new is illegal, should use literal."); + return $aNumber.performBinaryOpOnSimpleNumber( + $SC.Symbol("wrap2"), this, $adverb + ); }; - spec.if = fn(function($trueFunc) { - return $trueFunc.value(); - }, "trueFunc"); + spec.rrand = function($aNumber, $adverb) { + var tag = $aNumber.__tag; - spec.not = utils.alwaysReturn$false; + switch (tag) { + case 770: + return $SC.Integer(Math.round(mathlib.rrand(this._, $aNumber._))); + case 777: + return $SC.Float(mathlib.rrand(this._, $aNumber._)); + } - spec["&&"] = function($that) { - return $that.value(); + return $aNumber.performBinaryOpOnSimpleNumber( + $SC.Symbol("rrand"), this, $adverb + ); }; - spec["||"] = utils.nop; - - spec.and = fn(function($that) { - return $that.value(); - }, "that"); - - spec.or = spec["||"]; - - spec.nand = fn(function($that) { - return $that.value().not(); - }, "that"); + spec.clip = fn(function($lo, $hi) { + // <-- _ClipInt --> + if ($lo.__tag === 1027) { + return $lo; + } + if ($hi.__tag === 1027) { + return $hi; + } + if ($lo.__tag === 770 && $hi.__tag === 770) { + return $SC.Integer( + mathlib.clip(this._, $lo.__int__(), $hi.__int__()) + ); + } - spec.asInteger = utils.alwaysReturn$int_1; - spec.binaryValue = utils.alwaysReturn$int_1; - }); + return $SC.Float( + mathlib.clip(this._, $lo.__num__(), $hi.__num__()) + ); + }, "lo; hi"); - sc.lang.klass.refine("False", function(spec, utils) { - spec.$new = function() { - throw new Error("False.new is illegal, should use literal."); - }; + spec.wrap = fn(function($lo, $hi) { + // <-- _WrapInt --> + if ($lo.__tag === 1027) { + return $lo; + } + if ($hi.__tag === 1027) { + return $hi; + } + if ($lo.__tag === 770 && $hi.__tag === 770) { + return $SC.Integer( + mathlib.iwrap(this._, $lo.__int__(), $hi.__int__()) + ); + } - spec.if = fn(function($trueFunc, $falseFunc) { - return $falseFunc.value(); - }, "trueFunc; falseFunc"); + return $SC.Float( + mathlib.wrap(this._, $lo.__num__(), $hi.__num__()) + ); + }, "lo; hi"); - spec.not = utils.alwaysReturn$true; + spec.fold = fn(function($lo, $hi) { + // <-- _FoldInt --> + if ($lo.__tag === 1027) { + return $lo; + } + if ($hi.__tag === 1027) { + return $hi; + } + if ($lo.__tag === 770 && $hi.__tag === 770) { + return $SC.Integer( + mathlib.ifold(this._, $lo.__int__(), $hi.__int__()) + ); + } - spec["&&"] = utils.nop; + return $SC.Float( + mathlib.fold(this._, $lo.__num__(), $hi.__num__()) + ); + }, "lo; hi"); - spec["||"] = function($that) { - return $that.value(); + spec.even = function() { + return $SC.Boolean(!(this._ & 1)); }; - spec.and = utils.nop; - - spec.or = fn(function($that) { - return $that.value(); - }, "that"); + spec.odd = function() { + return $SC.Boolean(!!(this._ & 1)); + }; - spec.nand = utils.alwaysReturn$true; - spec.asInteger = utils.alwaysReturn$int_0; - spec.binaryValue = utils.alwaysReturn$int_0; - }); + spec.xrand = fn(function($exclude) { + return ($exclude ["+"] (this.__dec__().rand()) ["+"] ($int_1)) ["%"] (this); + }, "exclude=0"); -})(sc); + spec.xrand2 = fn(function($exclude) { + var raw, res; -// src/sc/lang/classlib/Collections/Collection.js -(function(sc) { + raw = this._; + res = (mathlib.rand((2 * raw))|0) - raw; - var $SC = sc.lang.$SC; - var fn = sc.lang.fn; + if (res === $exclude._) { + return this; + } - sc.lang.klass.refine("Collection", function(spec, utils) { - var BOOL = utils.BOOL; - var $nil = utils.$nil; - var $true = utils.$true; - var $false = utils.$false; - var $int_0 = utils.$int_0; - var $int_1 = utils.$int_1; - var SCArray = $SC("Array"); + return $SC.Integer(res); + }, "exclude=0"); - spec.$newFrom = fn(function($aCollection) { - var $newCollection; + spec.degreeToKey = fn(function($scale, $stepsPerOctave) { + return $scale.performDegreeToKey(this, $stepsPerOctave); + }, "scale; stepsPerOctave=12"); - $newCollection = this.new($aCollection.size()); - $aCollection.do($SC.Function(function($item) { - $newCollection.add($item); - })); + spec.do = function($function) { + iterator.execute( + iterator.integer$do(this), + $function + ); + return this; + }; - return $newCollection; - }, "aCollection"); + spec.generate = function($function) { - spec.$with = fn(function($$args) { - var $newColl; + $function.value(this); - $newColl = this.new($$args.size()); - $newColl.addAll($$args); + return this; + }; - return $newColl; - }, "*args"); + spec.collectAs = fn(function($function, $class) { + var $res; + var i, imax; - spec.$fill = fn(function($size, $function) { - var $obj; - var size, i; + if ($class === $nil) { + $class = SCArray; + } - if (BOOL($size.isSequenceableCollection())) { - return this.fillND($size, $function); + $res = $class.new(this); + for (i = 0, imax = this._; i < imax; ++i) { + $res.add($function.value($SC.Integer(i))); } - $obj = this.new($size); + return $res; + }, "function; class"); - size = $size.__int__(); - for (i = 0; i < size; ++i) { - $obj.add($function.value($SC.Integer(i))); - } - - return $obj; - }, "size; function"); - - spec.$fill2D = fn(function($rows, $cols, $function) { - var $this = this, $obj, $obj2, $row, $col; - var rows, cols, i, j; + spec.collect = function($function) { + return this.collectAs($function, SCArray); + }; - $obj = this.new($rows); + spec.reverseDo = function($function) { + iterator.execute( + iterator.integer$reverseDo(this), + $function + ); + return this; + }; - rows = $rows.__int__(); - cols = $cols.__int__(); + spec.for = fn(function($endval, $function) { + iterator.execute( + iterator.integer$for(this, $endval), + $function + ); + return this; + }, "endval; function"); - for (i = 0; i < rows; ++i) { - $row = $SC.Integer(i); - $obj2 = $this.new($cols); - for (j = 0; j < cols; ++j) { - $col = $SC.Integer(j); - $obj2 = $obj2.add($function.value($row, $col)); - } - $obj = $obj.add($obj2); - } + spec.forBy = fn(function($endval, $stepval, $function) { + iterator.execute( + iterator.integer$forBy(this, $endval, $stepval), + $function + ); + return this; + }, "endval; stepval; function"); - return $obj; - }, "rows; cols; function"); + spec.to = fn(function($hi, $step) { + return $SC("Interval").new(this, $hi, $step); + }, "hi; step=1"); - spec.$fill3D = fn(function($planes, $rows, $cols, $function) { - var $this = this, $obj, $obj2, $obj3, $plane, $row, $col; - var planes, rows, cols, i, j, k; + spec.asAscii = function() { + // <-- _AsAscii --> + return $SC.Char(String.fromCharCode(this._|0)); + }; - $obj = this.new($planes); + spec.asUnicode = utils.nop; - planes = $planes.__int__(); - rows = $rows .__int__(); - cols = $cols .__int__(); + spec.asDigit = function() { + var c; - for (i = 0; i < planes; ++i) { - $plane = $SC.Integer(i); - $obj2 = $this.new($rows); - for (j = 0; j < rows; ++j) { - $row = $SC.Integer(j); - $obj3 = $this.new($cols); - for (k = 0; k < cols; ++k) { - $col = $SC.Integer(k); - $obj3 = $obj3.add($function.value($plane, $row, $col)); - } - $obj2 = $obj2.add($obj3); - } - $obj = $obj.add($obj2); + // + c = this._; + if (0 <= c && c <= 9) { + return $SC.Char(String(c)); } - - return $obj; - }, "planes; rows; cols; function"); - - var fillND = function($this, $dimensions, $function, $args) { - var $n, $obj, $argIndex; - - $n = $dimensions.first(); - $obj = $this.new($n); - $argIndex = $args.size(); - $args = $args ["++"] ($int_0); - - if ($dimensions.size().__int__() <= 1) { - $n.do($SC.Function(function($i) { - $obj.add($function.valueArray($args.put($argIndex, $i))); - })); - } else { - $dimensions = $dimensions.drop($int_1); - $n.do($SC.Function(function($i) { - $obj = $obj.add(fillND($this, $dimensions, $function, $args.put($argIndex, $i))); - })); + if (10 <= c && c <= 35) { + return $SC.Char(String.fromCharCode(c + 55)); } - return $obj; + throw new Error("Integer: asDigit must be 0 <= this <= 35"); }; - spec.$fillND = fn(function($dimensions, $function) { - return fillND(this, $dimensions, $function, $SC.Array([])); - }, "dimensions; function"); + spec.asBinaryDigits = fn(function($numDigits) { + var raw, array, numDigits, i; - spec["@"] = function($index) { - return this.at($index); - }; + raw = this._; + numDigits = $numDigits.__int__(); + array = new Array(numDigits); + for (i = 0; i < numDigits; ++i) { + array.unshift($SC.Integer((raw >> i) & 1)); + } - spec["=="] = function($aCollection) { - var $res = null; + return $SC.Array(array); + }, "numDigits=8"); - if ($aCollection.class() !== this.class()) { - return $false; + spec.asDigits = fn(function($base, $numDigits) { + var $num; + var array, numDigits, i; + + $num = this; + if ($numDigits === $nil) { + $numDigits = ( + this.log() ["/"] ($base.log() ["+"] ($SC.Float(1e-10))) + ).asInteger().__inc__(); } - if (this.size() !== $aCollection.size()) { - return $false; + + array = []; + numDigits = $numDigits.__int__(); + array = new Array(numDigits); + for (i = 0; i < numDigits; ++i) { + array.unshift($num ["%"] ($base)); + $num = $num.div($base); } - this.do($SC.Function(function($item) { - if (!BOOL($aCollection.includes($item))) { - $res = $false; - return 65535; - } - })); - return $res || $true; - }; + return $SC.Array(array); + }, "base=10; numDigits"); - // TODO: implements hash + // TODO: implements nextPowerOfTwo + // TODO: implements isPowerOfTwo + // TODO: implements leadingZeroes + // TODO: implements trailingZeroes + // TODO: implements numBits + // TODO: implements log2Ceil + // TODO: implements grayCode + // TODO: implements setBit + // TODO: implements nthPrime + // TODO: implements prevPrime + // TODO: implements nextPrime + // TODO: implements indexOfPrime + // TODO: implements isPrime + // TODO: implements exit + // TODO: implements asStringToBase + // TODO: implements asBinaryString + // TODO: implements asHexString + // TODO: implements asIPString + // TODO: implements archiveAsCompileString - spec.species = function() { - return SCArray; - }; + spec.geom = fn(function($start, $grow) { + return SCArray.geom(this, $start, $grow); + }, "start; grow"); - spec.do = function() { - return this._subclassResponsibility("do"); - }; + spec.fib = fn(function($a, $b) { + return SCArray.fib(this, $a, $b); + }, "a=0.0; b=1.0"); - // TODO: implements iter + // TODO: implements factors + // TODO: implements pidRunning + // TODO: implements factorial + // TODO: implements isCaps + // TODO: implements isShift + // TODO: implements isCtrl + // TODO: implements isAlt + // TODO: implements isCmd + // TODO: implements isNumPad + // TODO: implements isHelp + // TODO: implements isFun - spec.size = function() { - var tally = 0; + spec.bitNot = function() { + return $SC.Integer(~this._); + }; + }); - this.do($SC.Function(function() { - tally++; - })); +})(sc); - return $SC.Integer(tally); - }; +// src/sc/lang/classlib/Math/Float.js +(function(sc) { - spec.flatSize = function() { - return this.sum($SC.Function(function($_) { - return $_.flatSize(); - })); - }; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; + var iterator = sc.lang.iterator; + var mathlib = sc.libs.mathlib; - spec.isEmpty = function() { - return $SC.Boolean(this.size().__int__() === 0); - }; + sc.lang.klass.refine("Float", function(spec, utils) { + spec.toString = function() { + var raw = this._; - spec.notEmpty = function() { - return $SC.Boolean(this.size().__int__() !== 0); - }; + if (raw === Infinity) { + return "inf"; + } + if (raw === -Infinity) { + return "-inf"; + } + if (isNaN(raw)) { + return "nan"; + } - spec.asCollection = utils.nop; - spec.isCollection = utils.alwaysReturn$true; + return String(this._); + }; - spec.add = function() { - return this._subclassResponsibility("add"); + spec.$new = function() { + throw new Error("Float.new is illegal, should use literal."); }; - spec.addAll = fn(function($aCollection) { - var $this = this; + spec.isFloat = utils.alwaysReturn$true; + spec.asFloat = utils.nop; - $aCollection.asCollection().do($SC.Function(function($item) { - return $this.add($item); - })); - - return this; - }, "aCollection"); + [ + [ "+" , $SC.Float, $SC.Float ], + [ "-" , $SC.Float, $SC.Float ], + [ "*" , $SC.Float, $SC.Float ], + [ "/" , $SC.Float, $SC.Float ], + [ "mod" , $SC.Float , $SC.Float ], + [ "div" , $SC.Integer, $SC.Integer ], + [ "pow" , $SC.Float , $SC.Float ], + [ "min" , $SC.Float , $SC.Float ], + [ "max" , $SC.Float , $SC.Float ], + [ "bitAnd" , $SC.Float , $SC.Float ], + [ "bitOr" , $SC.Float , $SC.Float ], + [ "bitXor" , $SC.Float , $SC.Float ], + [ "lcm" , $SC.Float , $SC.Float ], + [ "gcd" , $SC.Float , $SC.Float ], + [ "round" , $SC.Float , $SC.Float ], + [ "roundUp" , $SC.Float , $SC.Float ], + [ "trunc" , $SC.Float , $SC.Float ], + [ "atan2" , $SC.Float , $SC.Float ], + [ "hypot" , $SC.Float , $SC.Float ], + [ "hypotApx", $SC.Float , $SC.Float ], + [ "leftShift" , $SC.Float, $SC.Float ], + [ "rightShift" , $SC.Float, $SC.Float ], + [ "unsignedRightShift", $SC.Float, $SC.Float ], + [ "ring1" , $SC.Float, $SC.Float ], + [ "ring2" , $SC.Float, $SC.Float ], + [ "ring3" , $SC.Float, $SC.Float ], + [ "ring4" , $SC.Float, $SC.Float ], + [ "difsqr" , $SC.Float, $SC.Float ], + [ "sumsqr" , $SC.Float, $SC.Float ], + [ "sqrsum" , $SC.Float, $SC.Float ], + [ "sqrdif" , $SC.Float, $SC.Float ], + [ "absdif" , $SC.Float, $SC.Float ], + [ "thresh" , $SC.Float, $SC.Float ], + [ "amclip" , $SC.Float, $SC.Float ], + [ "scaleneg", $SC.Float, $SC.Float ], + [ "clip2" , $SC.Float, $SC.Float ], + [ "fold2" , $SC.Float, $SC.Float ], + [ "wrap2" , $SC.Float, $SC.Float ], + [ "excess" , $SC.Float, $SC.Float ], + [ "firstArg", $SC.Float, $SC.Float ], + [ "rrand" , $SC.Float, $SC.Float ], + [ "exprand" , $SC.Float, $SC.Float ], + ].forEach(function(items) { + spec[items[0]] = sc.lang.classlib.bop.apply(null, items); + }); - spec.remove = function() { - return this._subclassResponsibility("remove"); - }; + spec.clip = fn(function($lo, $hi) { + // <-- _ClipFloat --> + if ($lo.__tag === 1027) { + return $lo; + } + if ($hi.__tag === 1027) { + return $hi; + } - spec.removeAll = fn(function($list) { - var $this = this; + return $SC.Float( + mathlib.clip(this._, $lo.__num__(), $hi.__num__()) + ); + }, "lo; hi"); - $list.do($SC.Function(function($item) { - $this.remove($item); - })); + spec.wrap = fn(function($lo, $hi) { + // <-- _WrapInt --> + if ($lo.__tag === 1027) { + return $lo; + } + if ($hi.__tag === 1027) { + return $hi; + } - return this; - }, "list"); + return $SC.Float( + mathlib.wrap(this._, $lo.__num__(), $hi.__num__()) + ); + }, "lo; hi"); - spec.removeEvery = fn(function($list) { - this.removeAllSuchThat($SC.Function(function($_) { - return $list.includes($_); - })); - return this; - }, "list"); + spec.fold = fn(function($lo, $hi) { + // <-- _FoldFloat --> + if ($lo.__tag === 1027) { + return $lo; + } + if ($hi.__tag === 1027) { + return $hi; + } - spec.removeAllSuchThat = function($function) { - var $this = this, $removedItems, $copy; + return $SC.Float( + mathlib.fold(this._, $lo.__num__(), $hi.__num__()) + ); + }, "lo; hi"); - $removedItems = this.class().new(); - $copy = this.copy(); - $copy.do($SC.Function(function($item) { - if (BOOL($function.value($item))) { - $this.remove($item); - $removedItems = $removedItems.add($item); - } - })); + // TODO: implements coin + // TODO: implements xrand2 - return $removedItems; + spec.as32Bits = function() { + // <-- _As32Bits --> + return $SC.Integer( + new Int32Array( + new Float32Array([ this._ ]).buffer + )[0] + ); }; - spec.atAll = fn(function($keys) { - var $this = this; - - return $keys.collect($SC.Function(function($index) { - return $this.at($index); - })); - }, "keys"); + spec.high32Bits = function() { + // <-- _High32Bits --> + return $SC.Integer( + new Int32Array( + new Float64Array([ this._ ]).buffer + )[1] + ); + }; - spec.putEach = fn(function($keys, $values) { - var keys, values, i, imax; + spec.low32Bits = function() { + // <-- _Low32Bits --> + return $SC.Integer( + new Int32Array( + new Float64Array([ this._ ]).buffer + )[0] + ); + }; - $keys = $keys.asArray(); - $values = $values.asArray(); + spec.$from32Bits = fn(function($word) { + // <-- _From32Bits --> + return $SC.Float( + new Float32Array( + new Int32Array([ $word.__num__() ]).buffer + )[0] + ); + }, "word"); - keys = $keys._; - values = $values._; - for (i = 0, imax = keys.length; i < imax; ++i) { - this.put(keys[i], values[i % values.length]); - } + spec.$from64Bits = fn(function($hiWord, $loWord) { + // <-- _From64Bits --> + return $SC.Float( + new Float64Array( + new Int32Array([ $loWord.__num__(), $hiWord.__num__() ]).buffer + )[0] + ); + }, "hiWord; loWord"); + spec.do = function($function) { + iterator.execute( + iterator.float$do(this), + $function + ); return this; - }, "keys; values"); - - spec.includes = fn(function($item1) { - var $res = null; - - this.do($SC.Function(function($item2) { - if ($item1 === $item2) { - $res = $true; - return 65535; - } - })); - - return $res || $false; - }, "item1"); + }; - spec.includesEqual = fn(function($item1) { - var $res = null; + spec.reverseDo = function($function) { + iterator.execute( + iterator.float$reverseDo(this), + $function + ); + return this; + }; - this.do($SC.Function(function($item2) { - if (BOOL( $item1 ["=="] ($item2) )) { - $res = $true; - return 65535; - } - })); + // TODO: implements asStringPrec + // TODO: implements archiveAsCompileString + // TODO: implements storeOn + // TODO: implements switch - return $res || $false; - }, "item1"); + spec.bitNot = function() { + var f64 = new Float64Array([ this._ ]); + var i32 = new Int32Array(f64.buffer); + i32[0] = ~i32[0]; + return $SC.Float(f64[0]); + }; + }); - spec.includesAny = fn(function($aCollection) { - var $this = this, $res = null; +})(sc); - $aCollection.do($SC.Function(function($item) { - if (BOOL($this.includes($item))) { - $res = $true; - return 65535; - } - })); +// src/sc/lang/classlib/Core/Thread.js +(function(sc) { - return $res || $false; - }, "aCollection"); + function SCThread() { + this.__initializeWith__("Stream"); + } - spec.includesAll = fn(function($aCollection) { - var $this = this, $res = null; + sc.lang.klass.define(SCThread, "Thread : Stream", function() { + // TODO: implements state + // TODO: implements parent + // TODO: implements primitiveError + // TODO: implements primitiveIndex + // TODO: implements beats + // TODO: implements seconds + // TODO: implements clock + // TODO: implements nextBeat + // TODO: implements endBeat + // TODO: implements endBeat_ + // TODO: implements endValue + // TODO: implements endValue_ + // TODO: implements exceptionHandler + // TODO: implements exceptionHandler_ + // TODO: implements threadPlayer_ + // TODO: implements executingPath + // TODO: implements oldExecutingPath - $aCollection.do($SC.Function(function($item) { - if (!BOOL($this.includes($item))) { - $res = $false; - return 65535; - } - })); - - return $res || $true; - }, "aCollection"); - - spec.matchItem = fn(function($item) { - return this.includes($item); - }, "item"); - - spec.collect = function($function) { - return this.collectAs($function, this.species()); - }; - - spec.select = function($function) { - return this.selectAs($function, this.species()); - }; - - spec.reject = function($function) { - return this.rejectAs($function, this.species()); - }; - - spec.collectAs = fn(function($function, $class) { - var $res; - - $res = $class.new(this.size()); - this.do($SC.Function(function($elem, $i) { - return $res.add($function.value($elem, $i)); - })); - - return $res; - }, "function; class"); - - spec.selectAs = fn(function($function, $class) { - var $res; - - $res = $class.new(this.size()); - this.do($SC.Function(function($elem, $i) { - if (BOOL($function.value($elem, $i))) { - $res = $res.add($elem); - } - })); - - return $res; - }, "function; class"); - - spec.rejectAs = fn(function($function, $class) { - var $res; + // TODO: implements init + // TODO: implements copy + // TODO: implements clock_ + // TODO: implements seconds_ + // TODO: implements beats_ + // TODO: implements isPlaying + // TODO: implements threadPlayer + // TODO: implements findThreadPlayer + // TODO: implements randSeed_ + // TODO: implements randData_ + // TODO: implements randData + // TODO: implements failedPrimitiveName + // TODO: implements handleError + // TODO: implements next + // TODO: implements value + // TODO: implements valueArray + // TODO: implements $primitiveError + // TODO: implements $primitiveErrorString + // TODO: implements storeOn + // TODO: implements archiveAsCompileString + // TODO: implements checkCanArchive + }); - $res = $class.new(this.size()); - this.do($SC.Function(function($elem, $i) { - if (!BOOL($function.value($elem, $i))) { - $res = $res.add($elem); - } - })); + function SCRoutine() { + this.__initializeWith__("Thread"); + } - return $res; - }, "function; class"); + sc.lang.klass.define(SCRoutine, "Routine : Thread", function() { + // TODO: implements $run + // TODO: implements next + // TODO: implements value + // TODO: implements resume + // TODO: implements run + // TODO: implements valueArray + // TODO: implements reset + // TODO: implements stop + // TODO: implements p + // TODO: implements storeArgs + // TODO: implements storeOn + // TODO: implements awake + }); - spec.detect = function($function) { - var $res = null; +})(sc); - this.do($SC.Function(function($elem, $i) { - if (BOOL($function.value($elem, $i))) { - $res = $elem; - return 65535; - } - })); +// src/sc/lang/classlib/Core/Symbol.js +(function(sc) { - return $res || $nil; - }; + var $SC = sc.lang.$SC; - spec.detectIndex = function($function) { - var $res = null; + sc.lang.klass.refine("Symbol", function(spec, utils) { + var $nil = utils.$nil; - this.do($SC.Function(function($elem, $i) { - if (BOOL($function.value($elem, $i))) { - $res = $i; - return 65535; - } - })); - return $res || $nil; + spec.__sym__ = function() { + return this._; }; - spec.doMsg = function() { - var args = arguments; - this.do($SC.Function(function($item) { - $item.perform.apply($item, args); - })); - return this; + spec.__str__ = function() { + return this._; }; - spec.collectMsg = function() { - var args = arguments; - return this.collect($SC.Function(function($item) { - return $item.perform.apply($item, args); - })); + spec.$new = function() { + throw new Error("Symbol.new is illegal, should use literal."); }; - spec.selectMsg = function() { - var args = arguments; - return this.select($SC.Function(function($item) { - return $item.perform.apply($item, args); - })); - }; + spec.asSymbol = utils.nop; - spec.rejectMsg = function() { - var args = arguments; - return this.reject($SC.Function(function($item) { - return $item.perform.apply($item, args); - })); + spec.asInteger = function() { + var m = /^[-+]?\d+/.exec(this._); + return $SC.Integer(m ? m[0]|0 : 0); }; - spec.detectMsg = fn(function($selector, $$args) { - return this.detect($SC.Function(function($item) { - return $item.performList($selector, $$args); - })); - }, "selector; *args"); - - spec.detectIndexMsg = fn(function($selector, $$args) { - return this.detectIndex($SC.Function(function($item) { - return $item.performList($selector, $$args); - })); - }, "selector; *args"); - - spec.lastForWhich = function($function) { - var $res = null; - this.do($SC.Function(function($elem, $i) { - if (BOOL($function.value($elem, $i))) { - $res = $elem; - } else { - return 65535; - } - })); - - return $res || $nil; + spec.asFloat = function() { + var m = /^[-+]?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?/.exec(this._); + return $SC.Float(m ? +m[0] : 0); }; - spec.lastIndexForWhich = function($function) { - var $res = null; - this.do($SC.Function(function($elem, $i) { - if (BOOL($function.value($elem, $i))) { - $res = $i; - } else { - return 65535; - } - })); - - return $res || $nil; + spec.ascii = function() { + return this.asString().ascii(); }; - spec.inject = fn(function($thisValue, $function) { - var $nextValue; - - $nextValue = $thisValue; - this.do($SC.Function(function($item, $i) { - $nextValue = $function.value($nextValue, $item, $i); - })); - - return $nextValue; - }, "thisValue; function"); - - spec.injectr = fn(function($thisValue, $function) { - var $this = this, size, $nextValue; - - size = this.size().__int__(); - $nextValue = $thisValue; - this.do($SC.Function(function($item, $i) { - $item = $this.at($SC.Integer(--size)); - $nextValue = $function.value($nextValue, $item, $i); - })); - - return $nextValue; - }, "thisValue; function"); - - spec.count = function($function) { - var sum = 0; - this.do($SC.Function(function($elem, $i) { - if (BOOL($function.value($elem, $i))) { - sum++; - } - })); + // TODO: implements asCompileString - return $SC.Integer(sum); + spec.asClass = function() { + if (sc.lang.klass.exists(this._)) { + return sc.lang.klass.get(this._); + } + return $nil; }; - spec.occurrencesOf = fn(function($obj) { - var sum = 0; + // TODO: implements asSetter + // TODO: implements asGetter + // TODO: implements asSpec + // TODO: implements asWarp + // TODO: implements asTuning + // TODO: implements asScale + // TODO: implements isSetter + // TODO: implements isClassName + // TODO: implements isMetaClassName + // TODO: implements isPrefix + // TODO: implements isPrimitiveName + // TODO: implements isPrimitive + // TODO: implements isMap + // TODO: implements isRest + // TODO: implements envirGet + // TODO: implements envirPut + // TODO: implements blend + // TODO: implements ++ + // TODO: implements asBinOpString + // TODO: implements applyTo + // TODO: implements performBinaryOpOnSomething - this.do($SC.Function(function($elem) { - if (BOOL($elem ["=="] ($obj))) { - sum++; - } - })); + spec.neg = utils.nop; + spec.bitNot = utils.nop; + spec.abs = utils.nop; + spec.ceil = utils.nop; + spec.floor = utils.nop; + spec.frac = utils.nop; + spec.sign = utils.nop; + spec.sqrt = utils.nop; + spec.exp = utils.nop; + spec.midicps = utils.nop; + spec.cpsmidi = utils.nop; + spec.midiratio = utils.nop; + spec.ratiomidi = utils.nop; + spec.ampdb = utils.nop; + spec.dbamp = utils.nop; + spec.octcps = utils.nop; + spec.cpsoct = utils.nop; + spec.log = utils.nop; + spec.log2 = utils.nop; + spec.log10 = utils.nop; + spec.sin = utils.nop; + spec.cos = utils.nop; + spec.tan = utils.nop; + spec.asin = utils.nop; + spec.acos = utils.nop; + spec.atan = utils.nop; + spec.sinh = utils.nop; + spec.cosh = utils.nop; + spec.tanh = utils.nop; + spec.rand = utils.nop; + spec.rand2 = utils.nop; + spec.linrand = utils.nop; + spec.bilinrand = utils.nop; + spec.sum3rand = utils.nop; + spec.distort = utils.nop; + spec.softclip = utils.nop; + spec.coin = utils.nop; + spec.even = utils.nop; + spec.odd = utils.nop; + spec.rectWindow = utils.nop; + spec.hanWindow = utils.nop; + spec.welWindow = utils.nop; + spec.triWindow = utils.nop; + spec.scurve = utils.nop; + spec.ramp = utils.nop; + spec["+"] = utils.nop; + spec["-"] = utils.nop; + spec["*"] = utils.nop; + spec["/"] = utils.nop; + spec.mod = utils.nop; + spec.min = utils.nop; + spec.max = utils.nop; + spec.bitAnd = utils.nop; + spec.bitOr = utils.nop; + spec.bitXor = utils.nop; + spec.bitHammingDistance = utils.nop; + // TODO: Implements hammingDistance + spec.lcm = utils.nop; + spec.gcd = utils.nop; + spec.round = utils.nop; + spec.roundUp = utils.nop; + spec.trunc = utils.nop; + spec.atan2 = utils.nop; + spec.hypot = utils.nop; + spec.hypotApx = utils.nop; + spec.pow = utils.nop; + spec.leftShift = utils.nop; + spec.rightShift = utils.nop; + spec.unsignedRightShift = utils.nop; + spec.rrand = utils.nop; + spec.exprand = utils.nop; - return $SC.Integer(sum); - }, "obj"); + // TODO: Implements < + // TODO: Implements > + // TODO: Implements <= + // TODO: Implements >= - spec.any = function($function) { - var $res = null; + spec.degreeToKey = utils.nop; + spec.degrad = utils.nop; + spec.raddeg = utils.nop; + spec.doNumberOp = utils.nop; + spec.doComplexOp = utils.nop; + spec.doSignalOp = utils.nop; - this.do($SC.Function(function($elem, $i) { - if (BOOL($function.value($elem, $i))) { - $res = $true; - return 65535; - } - })); + // TODO: Implements doListOp + // TODO: Implements primitiveIndex + // TODO: Implements specialIndex + // TODO: Implements printOn + // TODO: Implements storeOn + // TODO: Implements codegen_UGenCtorArg - return $res || $false; + spec.archiveAsCompileString = utils.alwaysReturn$true; + + // TODO: Implements kr + // TODO: Implements ir + // TODO: Implements tr + // TODO: Implements ar + // TODO: Implements matchOSCAddressPattern + // TODO: Implements help + + spec.asString = function() { + return $SC.String(this._); }; - spec.every = function($function) { - var $res = null; + spec.shallowCopy = utils.nop; - this.do($SC.Function(function($elem, $i) { - if (!BOOL($function.value($elem, $i))) { - $res = $false; - return 65535; - } - })); + spec.performBinaryOpOnSimpleNumber = utils.nop; + }); - return $res || $true; - }; +})(sc); - spec.sum = fn(function($function) { - var $sum; +// src/sc/lang/classlib/Core/Ref.js +(function(sc) { - $sum = $int_0; - if ($function === $nil) { - this.do($SC.Function(function($elem) { - $sum = $sum ["+"] ($elem); - })); - } else { - this.do($SC.Function(function($elem, $i) { - $sum = $sum ["+"] ($function.value($elem, $i)); - })); - } + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; - return $sum; - }, "function"); + function SCRef(args) { + this.__initializeWith__("Object"); + this._value = args[0] || $SC.Nil(); + } - spec.mean = function($function) { - return this.sum($function) ["/"] (this.size()); + sc.lang.klass.define(SCRef, "Ref : AbstractFunction", function(spec, utils) { + spec.valueOf = function() { + return this._value.valueOf(); }; - spec.product = fn(function($function) { - var $product; - - $product = $int_1; - if ($function === $nil) { - this.do($SC.Function(function($elem) { - $product = $product ["*"] ($elem); - })); - } else { - this.do($SC.Function(function($elem, $i) { - $product = $product ["*"] ($function.value($elem, $i)); - })); - } + spec.value = function() { + return this._value; + }; - return $product; - }, "function"); + spec.value_ = fn(function($value) { + this._value = $value; + return this; + }, "value"); - spec.sumabs = function() { - var $sum; + // $new - $sum = $int_0; - this.do($SC.Function(function($elem) { - if (BOOL($elem.isSequenceableCollection())) { - $elem = $elem.at($int_0); - } - $sum = $sum ["+"] ($elem.abs()); - })); + spec.set = fn(function($thing) { + this._value = $thing; + return this; + }, "thing"); - return $sum; + spec.get = function() { + return this._value; }; - spec.maxItem = fn(function($function) { - var $maxValue, $maxElement; + spec.dereference = spec.value; - $maxValue = $nil; - $maxElement = $nil; - if ($function === $nil) { - this.do($SC.Function(function($elem) { - if ($maxElement === $nil) { - $maxElement = $elem; - } else if ($elem > $maxElement) { - $maxElement = $elem; - } - })); - } else { - this.do($SC.Function(function($elem, $i) { - var $val; - if ($maxValue === $nil) { - $maxValue = $function.value($elem, $i); - $maxElement = $elem; - } else { - $val = $function.value($elem, $i); - if ($val > $maxValue) { - $maxValue = $val; - $maxElement = $elem; - } - } - })); - } + spec.asRef = utils.nop; - return $maxElement; - }, "function"); + spec.valueArray = spec.value; - spec.minItem = fn(function($function) { - var $minValue, $minElement; + spec.valueEnvir = spec.value; - $minValue = $nil; - $minElement = $nil; - if ($function === $nil) { - this.do($SC.Function(function($elem) { - if ($minElement === $nil) { - $minElement = $elem; - } else if ($elem < $minElement) { - $minElement = $elem; - } - })); - } else { - this.do($SC.Function(function($elem, $i) { - var $val; - if ($minValue === $nil) { - $minValue = $function.value($elem, $i); - $minElement = $elem; - } else { - $val = $function.value($elem, $i); - if ($val < $minValue) { - $minValue = $val; - $minElement = $elem; - } - } - })); - } + spec.valueArrayEnvir = spec.value; - return $minElement; - }, "function"); + spec.next = spec.value; - spec.maxIndex = fn(function($function) { - var $maxValue, $maxIndex; + spec.asUGenInput = utils.nop; - $maxValue = $nil; - $maxIndex = $nil; - if ($function === $nil) { - this.do($SC.Function(function($elem, $index) { - if ($maxValue === $nil) { - $maxValue = $elem; - $maxIndex = $index; - } else if ($elem > $maxValue) { - $maxValue = $elem; - $maxIndex = $index; - } - })); - } else { - this.do($SC.Function(function($elem, $i) { - var $val; - if ($maxValue === $nil) { - $maxValue = $function.value($elem, $i); - $maxIndex = $i; - } else { - $val = $function.value($elem, $i); - if ($val > $maxValue) { - $maxValue = $val; - $maxIndex = $i; - } - } - })); - } + // TODO: implements printOn + // TODO: implements storeOn - return $maxIndex; - }, "function"); + spec.at = function($key) { + return this._value.at($key); + }; - spec.minIndex = fn(function($function) { - var $maxValue, $minIndex; + spec.put = function($key, $val) { + return this._value.put($key, $val); + }; - $maxValue = $nil; - $minIndex = $nil; - if ($function === $nil) { - this.do($SC.Function(function($elem, $index) { - if ($maxValue === $nil) { - $maxValue = $elem; - $minIndex = $index; - } else if ($elem < $maxValue) { - $maxValue = $elem; - $minIndex = $index; - } - })); - } else { - this.do($SC.Function(function($elem, $i) { - var $val; - if ($maxValue === $nil) { - $maxValue = $function.value($elem, $i); - $minIndex = $i; - } else { - $val = $function.value($elem, $i); - if ($val < $maxValue) { - $maxValue = $val; - $minIndex = $i; - } - } - })); - } + // TODO: implements seq + // TODO: implements asControlInput + // TODO: implements asBufWithValues + // TODO: implements multichannelExpandRef + }); - return $minIndex; - }, "function"); +})(sc); - spec.maxValue = fn(function($function) { - var $maxValue, $maxElement; +// src/sc/lang/classlib/Core/Nil.js +(function(sc) { - $maxValue = $nil; - $maxElement = $nil; - this.do($SC.Function(function($elem, $i) { - var $val; - if ($maxValue === $nil) { - $maxValue = $function.value($elem, $i); - $maxElement = $elem; - } else { - $val = $function.value($elem, $i); - if ($val > $maxValue) { - $maxValue = $val; - $maxElement = $elem; - } - } - })); + var slice = [].slice; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; - return $maxValue; - }, "function"); + sc.lang.klass.refine("Nil", function(spec, utils) { + var $nil = utils.$nil; - spec.minValue = fn(function($function) { - var $minValue, $minElement; + spec.__num__ = function() { + return 0; + }; - $minValue = $nil; - $minElement = $nil; - this.do($SC.Function(function($elem, $i) { - var $val; - if ($minValue === $nil) { - $minValue = $function.value($elem, $i); - $minElement = $elem; - } else { - $val = $function.value($elem, $i); - if ($val < $minValue) { - $minValue = $val; - $minElement = $elem; - } - } - })); + spec.__bool__ = function() { + return false; + }; - return $minValue; - }, "function"); + spec.__sym__ = function() { + return "nil"; + }; - spec.maxSizeAtDepth = fn(function($rank) { - var rank, maxsize = 0; + spec.toString = function() { + return "nil"; + }; - rank = $rank.__num__(); - if (rank === 0) { - return this.size(); - } + spec.$new = function() { + throw new Error("Nil.new is illegal, should use literal."); + }; - this.do($SC.Function(function($sublist) { - var sz; - if (BOOL($sublist.isCollection())) { - sz = $sublist.maxSizeAtDepth($SC.Integer(rank - 1)); - } else { - sz = 1; - } - if (sz > maxsize) { - maxsize = sz; - } - })); + spec.isNil = utils.alwaysReturn$true; + spec.notNil = utils.alwaysReturn$false; - return $SC.Integer(maxsize); - }, "rank"); + spec["?"] = function($obj) { + return $obj; + }; - spec.maxDepth = fn(function($max) { - var $res; + spec["??"] = function($obj) { + return $obj.value(); + }; - $res = $max; - this.do($SC.Function(function($elem) { - if (BOOL($elem.isCollection())) { - $res = $res.max($elem.maxDepth($max.__inc__())); - } - })); + spec["!?"] = utils.nop; - return $res; - }, "max=1"); + spec.asBoolean = utils.alwaysReturn$false; + spec.booleanValue = utils.alwaysReturn$false; - spec.deepCollect = fn(function($depth, $function, $index, $rank) { - if ($depth === $nil) { - $rank = $rank.__inc__(); - return this.collect($SC.Function(function($item, $i) { - return $item.deepCollect($depth, $function, $i, $rank); - })); - } - if ($depth.__num__() <= 0) { - return $function.value(this, $index, $rank); - } - $depth = $depth.__dec__(); - $rank = $rank.__inc__(); + spec.push = fn(function($function) { + return $function.value(); + }, "function"); - return this.collect($SC.Function(function($item, $i) { - return $item.deepCollect($depth, $function, $i, $rank); - })); - }, "depth=1; function; index=0; rank=0"); + spec.appendStream = fn(function($stream) { + return $stream; + }, "stream"); - spec.deepDo = fn(function($depth, $function, $index, $rank) { - if ($depth === $nil) { - $rank = $rank.__inc__(); - return this.do($SC.Function(function($item, $i) { - $item.deepDo($depth, $function, $i, $rank); - })); - } - if ($depth.__num__() <= 0) { - $function.value(this, $index, $rank); - return this; - } - $depth = $depth.__dec__(); - $rank = $rank.__inc__(); + spec.pop = utils.nop; + spec.source = utils.nop; + spec.source_ = utils.nop; - return this.do($SC.Function(function($item, $i) { - $item.deepDo($depth, $function, $i, $rank); - })); - }, "depth=1; function; index=0; rank=0"); + spec.rate = utils.nop; + spec.numChannels = utils.nop; + spec.isPlaying = utils.alwaysReturn$false; - spec.invert = fn(function($axis) { - var $index; + spec.do = utils.nop; + spec.reverseDo = utils.nop; + spec.pairsDo = utils.nop; + spec.collect = utils.nop; + spec.select = utils.nop; + spec.reject = utils.nop; + spec.detect = utils.nop; + spec.collectAs = utils.nop; + spec.selectAs = utils.nop; + spec.rejectAs = utils.nop; - if (BOOL(this.isEmpty())) { - return this.species().new(); - } - if ($axis !== $nil) { - $index = $axis ["*"] ($SC.Integer(2)); - } else { - $index = this.minItem() ["+"] (this.maxItem()); - } + spec.dependants = function() { + return $SC("IdentitySet").new(); + }; - return $index ["-"] (this); - }, "axis"); + spec.changed = utils.nop; + spec.addDependant = utils.nop; + spec.removeDependant = utils.nop; + spec.release = utils.nop; + spec.update = utils.nop; - spec.sect = fn(function($that) { - var $result; + spec.transformEvent = fn(function($event) { + return $event; + }, "event"); - $result = this.species().new(); - this.do($SC.Function(function($item) { - if (BOOL($that.includes($item))) { - $result = $result.add($item); - } - })); + spec.awake = utils.alwaysReturn$nil; - return $result; - }, "that"); + spec.play = utils.nop; - spec.union = fn(function($that) { - var $result; + spec.nextTimeOnGrid = fn(function($clock) { + if ($clock === $nil) { + return $clock; + } + return $SC.Function(function() { + return $clock.nextTimeOnGrid(); + }); + }, "clock"); - $result = this.copy(); - $that.do($SC.Function(function($item) { - if (!BOOL($result.includes($item))) { - $result = $result.add($item); - } - })); + spec.asQuant = function() { + return $SC("Quant").default(); + }; - return $result; - }, "that"); + spec.swapThisGroup = utils.nop; + spec.performMsg = utils.nop; - spec.difference = fn(function($that) { - return this.copy().removeAll($that); - }, "that"); + spec.printOn = fn(function($stream) { + $stream.putAll($SC.String("nil")); + return this; + }, "stream"); - spec.symmetricDifference = fn(function($that) { - var $this = this, $result; + spec.storeOn = fn(function($stream) { + $stream.putAll($SC.String("nil")); + return this; + }, "stream"); - $result = this.species().new(); - $this.do($SC.Function(function($item) { - if (!BOOL($that.includes($item))) { - $result = $result.add($item); - } - })); - $that.do($SC.Function(function($item) { - if (!BOOL($this.includes($item))) { - $result = $result.add($item); - } - })); + spec.matchItem = utils.alwaysReturn$true; - return $result; - }, "that"); + spec.add = fn(function($value) { + return $SC.Array([ $value ]); + }, "value"); - spec.isSubsetOf = fn(function($that) { - return $that.includesAll(this); - }, "that"); + spec.addAll = fn(function($array) { + return $array.asArray(); + }, "array"); - spec.asArray = function() { - return SCArray.new(this.size()).addAll(this); + spec["++"] = function($array) { + return $array.asArray(); }; - spec.asBag = function() { - return $SC("Bag").new(this.size()).addAll(this); + spec.asCollection = function() { + return $SC.Array(); }; - spec.asList = function() { - return $SC("List").new(this.size()).addAll(this); - }; + spec.remove = utils.nop; - spec.asSet = function() { - return $SC("Set").new(this.size()).addAll(this); + spec.set = utils.nop; + + spec.get = fn(function($prevVal) { + return $prevVal; + }, "prevVal"); + + spec.addFunc = function() { + var functions = slice.call(arguments); + if (functions.length <= 1) { + return functions[0]; + } + return $SC("FunctionList").new($SC.Array(functions)); }; - spec.asSortedList = function($function) { - return $SC("SortedList").new(this.size(), $function).addAll(this); + spec.removeFunc = utils.nop; + + spec.replaceFunc = utils.nop; + spec.seconds_ = utils.nop; + spec.throw = utils.nop; + + // TODO: implements handleError + + spec.archiveAsCompileString = utils.alwaysReturn$true; + + spec.asSpec = function() { + return $SC("ControlSpec").new(); }; - // TODO: implements powerset - // TODO: implements flopDict - // TODO: implements histo - // TODO: implements printAll - // TODO: implements printcsAll - // TODO: implements dumpAll + spec.superclassesDo = utils.nop; + + spec.shallowCopy = utils.nop; + }); + +})(sc); + +// src/sc/lang/classlib/Core/Kernel.js +(function(sc) { + + sc.lang.klass.refine("Class", { + // TODO: implements superclass + // TODO: implements asClass + // TODO: implements initClass + // TODO: implements $initClassTree + // TODO: implements $allClasses + // TODO: implements findMethod + // TODO: implements findRespondingMethodFor + // TODO: implements findOverriddenMethod + // TODO: implements superclassesDo + // TODO: implements while + // TODO: implements dumpByteCodes + // TODO: implements dumpClassSubtree + // TODO: implements dumpInterface + // TODO: implements asString // TODO: implements printOn // TODO: implements storeOn - // TODO: implements storeItemsOn - // TODO: implements printItemsOn - // TODO: implements writeDef - // TODO: implements writeInputSpec - // TODO: implements case - // TODO: implements makeEnvirValPairs + // TODO: implements archiveAsCompileString + // TODO: implements hasHelpFile + // TODO: implements helpFilePath + // TODO: implements help + // TODO: implements openHelpFile + // TODO: implements shallowCopy + // TODO: implements openCodeFile + // TODO: implements classVars + // TODO: implements inspectorClass + // TODO: implements findReferences + // TODO: implements $findAllReferences + // TODO: implements allSubclasses + // TODO: implements superclasses }); })(sc); -// src/sc/lang/classlib/Collections/SequenceableCollection.js +// src/sc/lang/classlib/Core/Function.js (function(sc) { var slice = [].slice; var fn = sc.lang.fn; var $SC = sc.lang.$SC; - sc.lang.klass.refine("SequenceableCollection", function(spec, utils) { - var BOOL = utils.BOOL; - var $nil = utils.$nil; - var $true = utils.$true; - var $false = utils.$false; - var $int_0 = utils.$int_0; - var $int_1 = utils.$int_1; - - spec["|@|"] = function($index) { - return this.clipAt($index); - }; + sc.lang.klass.refine("Function", function(spec, utils) { + var BOOL = utils.BOOL; + var $nil = utils.$nil; + var SCArray = $SC("Array"); - spec["@@"] = function($index) { - return this.wrapAt($index); - }; + // TODO: implements def - spec["@|@"] = function($index) { - return this.foldAt($index); + spec.$new = function() { + throw new Error("Function.new is illegal, should use literal."); }; - spec.$series = fn(function($size, $start, $step) { - var $obj, i, imax; + spec.isFunction = utils.alwaysReturn$true; - $obj = this.new($size); - for (i = 0, imax = $size.__int__(); i < imax; ++i) { - $obj.add($start ["+"] ($step ["*"] ($SC.Integer(i)))); - } + // TODO: implements isClosed - return $obj; - }, "size; start=0; step=1"); + spec.archiveAsCompileString = utils.alwaysReturn$true; + spec.archiveAsObject = utils.alwaysReturn$true; - spec.$geom = fn(function($size, $start, $grow) { - var $obj, i, imax; + // TODO: implements checkCanArchive - $obj = this.new($size); - for (i = 0, imax = $size.__int__(); i < imax; ++i) { - $obj.add($start); - $start = $start ["*"] ($grow); - } + spec.shallowCopy = utils.nop; - return $obj; - }, "size; start; grow"); + spec.choose = function() { + return this.value(); + }; - spec.$fib = fn(function($size, $a, $b) { - var $obj, $temp, i, imax; + spec.update = function() { + return this._.apply(this, arguments); + }; - $obj = this.new($size); - for (i = 0, imax = $size.__int__(); i < imax; ++i) { - $obj.add($b); - $temp = $b; - $b = $a ["+"] ($b); - $a = $temp; - } + spec.value = function() { + return this._.apply(this, arguments); + }; - return $obj; - }, "size; a=0.0; b=1.0"); + spec.valueArray = function($args) { + return this._.apply(this, $args.asArray()._); + }; - // TODO: implements $rand - // TODO: implements $rand2 - // TODO: implements $linrand + // TODO: implements valueEnvir + // TODO: implements valueArrayEnvir + // TODO: implements functionPerformList + // TODO: implements valueWithEnvir + // TODO: implements performWithEnvir + // TODO: implements performKeyValuePairs + // TODO: implements numArgs + // TODO: implements numVars + // TODO: implements varArgs + // TODO: implements loop + // TODO: implements block - spec.$interpolation = fn(function($size, $start, $end) { - var $obj, $step, i, imax; + spec.asRoutine = function() { + return $SC("Routine").new(this); + }; - $obj = this.new($size); - if ($size.__int__() === 1) { - return $obj.add($start); - } + spec.dup = fn(function($n) { + return SCArray.fill($n, this); + }, "n=2"); - $step = ($end ["-"] ($start)) ["/"] ($size.__dec__()); - for (i = 0, imax = $size.__int__(); i < imax; ++i) { - $obj.add($start ["+"] ($SC.Integer(i) ["*"] ($step))); - } + // TODO: implements sum + // TODO: implements defer + // TODO: implements thunk + // TODO: implements transformEvent + // TODO: implements set + // TODO: implements get + // TODO: implements fork + // TODO: implements forkIfNeeded + // TODO: implements awake + // TODO: implements cmdPeriod + // TODO: implements bench + // TODO: implements protect + // TODO: implements try + // TODO: implements prTry + // TODO: implements handleError - return $obj; - }, "size; start=0.0; end=1.0"); + spec.case = function() { + var args, i, imax; - spec["++"] = function($aSequenceableCollection) { - var $newlist; + args = slice.call(arguments); + args.unshift(this); - $newlist = this.species().new(this.size() ["+"] ($aSequenceableCollection.size())); - $newlist = $newlist.addAll(this).addAll($aSequenceableCollection); + for (i = 0, imax = args.length >> 1; i < imax; ++i) { + if (BOOL(args[i * 2].value())) { + return args[i * 2 + 1].value(); + } + } - return $newlist; + if (args.length % 2 === 1) { + return args[args.length - 1].value(); + } + + return $nil; }; - // TODO: implements +++ + spec.r = function() { + return $SC("Routine").new(this); + }; - spec.asSequenceableCollection = utils.nop; + spec.p = function() { + return $SC("Prout").new(this); + }; - spec.choose = function() { - return this.at(this.size().rand()); + // TODO: implements matchItem + // TODO: implements performDegreeToKey + + spec.flop = function() { + var $this = this; + // if(def.argNames.isNil) { ^this }; + return $SC.Function(function() { + var $$args = $SC.Array(slice.call(arguments)); + return $$args.flop().collect($SC.Function(function($_) { + return $this.valueArray($_); + })); + }); }; - spec.wchoose = fn(function($weights) { - return this.at($weights.windex()); - }, "weights"); + // TODO: implements envirFlop + // TODO: implements makeFlopFunc + // TODO: implements inEnvir - spec["=="] = function($aCollection) { - var $res = null; + spec.while = fn(function($body) { + sc.lang.iterator.execute( + sc.lang.iterator.function$while(this), + $body + ); + return this; + }, "body"); + }); - if ($aCollection.class() !== this.class()) { - return $false; - } - if (this.size() !== $aCollection.size()) { - return $false; - } - this.do($SC.Function(function($item, $i) { - if (BOOL($item ["!="] ($aCollection.at($i)))) { - $res = $false; - return 65535; - } - })); +})(sc); - return $res || $true; +// src/sc/lang/classlib/Core/Char.js +(function(sc) { + + var $SC = sc.lang.$SC; + + sc.lang.klass.refine("Char", function(spec, utils) { + spec.__str__ = function() { + return this._; }; - // TODO: implements hash + spec.$nl = function() { + return $SC.Char("\n"); + }; - spec.copyRange = fn(function($start, $end) { - var $newColl, i, end; + spec.$ff = function() { + return $SC.Char("\f"); + }; - i = $start.__int__(); - end = $end.__int__(); - $newColl = this.species().new($SC.Integer(end - i)); - while (i <= end) { - $newColl.add(this.at($SC.Integer(i++))); - } + spec.$tab = function() { + return $SC.Char("\t"); + }; - return $newColl; - }, "start; end"); + spec.$space = function() { + return $SC.Char(" "); + }; - spec.keep = fn(function($n) { - var n, size; + spec.$comma = function() { + return $SC.Char(","); + }; - n = $n.__int__(); - if (n >= 0) { - return this.copyRange($int_0, $SC.Integer(n - 1)); - } - size = this.size().__int__(); + spec.$new = function() { + throw new Error("Char.new is illegal, should use literal."); + }; - return this.copyRange($SC.Integer(size + n), $SC.Integer(size - 1)); - }, "n"); + // TODO: implements hash - spec.drop = fn(function($n) { - var n, size; + spec.ascii = function() { + return $SC.Integer(this._.charCodeAt(0)); + }; - n = $n.__int__(); - size = this.size().__int__(); - if (n >= 0) { - return this.copyRange($n, $SC.Integer(size - 1)); + spec.digit = function() { + var ascii = this._.charCodeAt(0); + if (0x30 <= ascii && ascii <= 0x39) { + return $SC.Integer(ascii - 0x30); } + if (0x41 <= ascii && ascii <= 0x5a) { + return $SC.Integer(ascii - 0x37); + } + if (0x61 <= ascii && ascii <= 0x7a) { + return $SC.Integer(ascii - 0x57); + } + throw new Error("digitValue failed"); + }; - return this.copyRange($int_0, $SC.Integer(size + n - 1)); - }, "n"); + spec.asAscii = utils.nop; - spec.copyToEnd = fn(function($start) { - return this.copyRange($start, $SC.Integer(this.size().__int__() - 1)); - }, "start"); + spec.asUnicode = function() { + return this.ascii(); + }; - spec.copyFromStart = fn(function($end) { - return this.copyRange($int_0, $end); - }, "end"); + spec.toUpper = function() { + return $SC.Char(this._.toUpperCase()); + }; - spec.indexOf = fn(function($item) { - var $ret = null; + spec.toLower = function() { + return $SC.Char(this._.toLowerCase()); + }; - this.do($SC.Function(function($elem, $i) { - if ($item === $elem) { - $ret = $i; - return 65535; - } - })); + spec.isAlpha = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x41 <= ascii && ascii <= 0x5a) || + (0x61 <= ascii && ascii <= 0x7a)); + }; - return $ret || $nil; - }, "item"); + spec.isAlphaNum = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x30 <= ascii && ascii <= 0x39) || + (0x41 <= ascii && ascii <= 0x5a) || + (0x61 <= ascii && ascii <= 0x7a)); + }; - spec.indicesOfEqual = fn(function($item) { - var indices = []; + spec.isPrint = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x20 <= ascii && ascii <= 0x7e)); + }; - this.do($SC.Function(function($elem, $i) { - if ($item === $elem) { - indices.push($i); - } - })); + spec.isPunct = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x21 <= ascii && ascii <= 0x2f) || + (0x3a <= ascii && ascii <= 0x40) || + (0x5b <= ascii && ascii <= 0x60) || + (0x7b <= ascii && ascii <= 0x7e)); + }; - return indices.length ? $SC.Array(indices) : $nil; - }, "item"); + spec.isControl = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x00 <= ascii && ascii <= 0x1f) || ascii === 0x7f); + }; - spec.find = fn(function($sublist, $offset) { - var $subSize_1, $first, $index; - var size, offset, i, imax; + spec.isSpace = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x09 <= ascii && ascii <= 0x0d) || ascii === 0x20); + }; - $subSize_1 = $sublist.size().__dec__(); - $first = $sublist.first(); + spec.isVowl = function() { + var ch = this._.charAt(0).toUpperCase(); + return $SC.Boolean("AEIOU".indexOf(ch) !== -1); + }; - size = this.size().__int__(); - offset = $offset.__int__(); - for (i = 0, imax = size - offset; i < imax; ++i) { - $index = $SC.Integer(i + offset); - if (BOOL(this.at($index) ["=="] ($first))) { - if (BOOL(this.copyRange($index, $index ["+"] ($subSize_1)) ["=="] ($sublist))) { - return $index; - } - } - } + spec.isDecDigit = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x30 <= ascii && ascii <= 0x39)); + }; - return $nil; - }, "sublist; offset=0"); + spec.isUpper = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x41 <= ascii && ascii <= 0x5a)); + }; - spec.findAll = fn(function($arr, $offset) { - var $this = this, $indices, $i; - - $indices = $nil; - $i = $int_0; + spec.isLower = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x61 <= ascii && ascii <= 0x7a)); + }; - while (($i = $this.find($arr, $offset)) !== $nil) { - $indices = $indices.add($i); - $offset = $i.__inc__(); - } + spec.isFileSafe = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean((0x20 <= ascii && ascii <= 0x7e) && + ascii !== 0x2f && // 0x2f is '/' + ascii !== 0x3a && // 0x3a is ':' + ascii !== 0x22); // 0x22 is '"' + }; - return $indices; - }, "arr; offset=0"); + spec.isPathSeparator = function() { + var ascii = this._.charCodeAt(0); + return $SC.Boolean(ascii === 0x2f); + }; - spec.indexOfGreaterThan = fn(function($val) { - return this.detectIndex($SC.Function(function($item) { - return $SC.Boolean($item > $val); - })); - }, "val"); + spec["<"] = function($aChar) { + return $SC.Boolean(this.ascii() < $aChar.ascii()); + }; - spec.indexIn = fn(function($val) { - var $i, $j; + spec["++"] = function($that) { + return $SC.String(this._ + $that.__str__()); + }; - $j = this.indexOfGreaterThan($val); - if ($j === $nil) { - return this.size().__dec__(); - } - if ($j === $int_0) { - return $j; - } + // TODO: implements $bullet + // TODO: implements printOn + // TODO: implements storeOn - $i = $j.__dec__(); + spec.archiveAsCompileString = function() { + return $SC.True(); + }; - if ($val ["-"] (this.at($i)) < this.at($j) ["-"] ($val)) { - return $i; - } + spec.asString = function() { + return $SC.String(this._); + }; - return $j; - }, "val"); + spec.shallowCopy = utils.nop; + }); - spec.indexInBetween = fn(function($val) { - var $a, $b, $div, $i; +})(sc); - if (BOOL(this.isEmpty())) { - return $nil; - } - $i = this.indexOfGreaterThan($val); +// src/sc/lang/classlib/Core/Boolean.js +(function(sc) { - if ($i === $nil) { - return this.size().__dec__(); - } - if ($i === $int_0) { - return $i; - } + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; - $a = this.at($i.__dec__()); - $b = this.at($i); - $div = $b ["-"] ($a); + sc.lang.klass.refine("Boolean", function(spec, utils) { + spec.__bool__ = function() { + return this._; + }; - // if (BOOL($div ["=="] ($int_0))) { - // return $i; - // } + spec.toString = function() { + return String(this._); + }; - return (($val ["-"] ($a)) ["/"] ($div)) ["+"] ($i.__dec__()); - }, "val"); + spec.$new = function() { + throw new Error("Boolean.new is illegal, should use literal."); + }; - spec.isSeries = fn(function($step) { - var $res = null; + spec.xor = function($bool) { + return $SC.Boolean(this === $bool).not(); + }; - if (this.size() <= 1) { - return $true; - } - this.doAdjacentPairs($SC.Function(function($a, $b) { - var $diff = $b ["-"] ($a); - if ($step === $nil) { - $step = $diff; - } else if (BOOL($step ["!="] ($diff))) { - $res = $false; - return 65535; - } - })); + // TODO: implements if + // TODO: implements nop + // TODO: implements && + // TODO: implements || + // TODO: implements and + // TODO: implements or + // TODO: implements nand + // TODO: implements asInteger + // TODO: implements binaryValue - return $res || $true; - }, "step"); + spec.asBoolean = utils.nop; + spec.booleanValue = utils.nop; - spec.resamp0 = fn(function($newSize) { - var $this = this, $factor; + // TODO: implements keywordWarnings + // TODO: implements trace + // TODO: implements printOn + // TODO: implements storeOn - $factor = ( - this.size().__dec__() - ) ["/"] ( - ($newSize.__dec__()).max($int_1) - ); + spec.archiveAsCompileString = utils.alwaysReturn$true; - return this.species().fill($newSize, $SC.Function(function($i) { - return $this.at($i ["*"] ($factor).round($SC.Float(1.0)).asInteger()); - })); - }, "newSize"); + spec.while = function() { + var msg = "While was called with a fixed (unchanging) Boolean as the condition. "; + msg += "Please supply a Function instead."; + throw new Error(msg); + }; - spec.resamp1 = fn(function($newSize) { - var $this = this, $factor; + spec.shallowCopy = utils.nop; + }); - $factor = ( - this.size().__dec__() - ) ["/"] ( - ($newSize.__dec__()).max($int_1) - ); + sc.lang.klass.refine("True", function(spec, utils) { + spec.$new = function() { + throw new Error("True.new is illegal, should use literal."); + }; - return this.species().fill($newSize, $SC.Function(function($i) { - return $this.blendAt($i ["*"] ($factor)); - })); - }, "newSize"); + spec.if = fn(function($trueFunc) { + return $trueFunc.value(); + }, "trueFunc"); - spec.remove = fn(function($item) { - var $index; + spec.not = utils.alwaysReturn$false; - $index = this.indexOf($item); - if ($index !== $nil) { - return this.removeAt($index); - } + spec["&&"] = function($that) { + return $that.value(); + }; - return $nil; - }, "item"); + spec["||"] = utils.nop; - spec.removing = fn(function($item) { - var $coll; + spec.and = fn(function($that) { + return $that.value(); + }, "that"); - $coll = this.copy(); - $coll.remove($item); + spec.or = spec["||"]; - return $coll; - }, "item"); + spec.nand = fn(function($that) { + return $that.value().not(); + }, "that"); - spec.take = fn(function($item) { - var $index; + spec.asInteger = utils.alwaysReturn$int_1; + spec.binaryValue = utils.alwaysReturn$int_1; + }); - $index = this.indexOf($item); - if ($index !== $nil) { - return this.takeAt($index); - } + sc.lang.klass.refine("False", function(spec, utils) { + spec.$new = function() { + throw new Error("False.new is illegal, should use literal."); + }; - return $nil; - }, "item"); + spec.if = fn(function($trueFunc, $falseFunc) { + return $falseFunc.value(); + }, "trueFunc; falseFunc"); - spec.lastIndex = function() { - var size = this.size().__int__(); + spec.not = utils.alwaysReturn$true; - if (size > 0) { - return $SC.Integer(size - 1); - } + spec["&&"] = utils.nop; - return $nil; + spec["||"] = function($that) { + return $that.value(); }; - spec.middleIndex = function() { - var size = this.size().__int__(); - - if (size > 0) { - return $SC.Integer((size - 1) >> 1); - } + spec.and = utils.nop; - return $nil; - }; + spec.or = fn(function($that) { + return $that.value(); + }, "that"); - spec.first = function() { - var size = this.size().__int__(); + spec.nand = utils.alwaysReturn$true; + spec.asInteger = utils.alwaysReturn$int_0; + spec.binaryValue = utils.alwaysReturn$int_0; + }); - if (size > 0) { - return this.at($int_0); - } +})(sc); - return $nil; - }; +// src/sc/lang/classlib/Collections/Collection.js +(function(sc) { - spec.last = function() { - var size = this.size().__int__(); + var $SC = sc.lang.$SC; + var fn = sc.lang.fn; - if (size > 0) { - return this.at($SC.Integer(size - 1)); - } + sc.lang.klass.refine("Collection", function(spec, utils) { + var BOOL = utils.BOOL; + var $nil = utils.$nil; + var $true = utils.$true; + var $false = utils.$false; + var $int_0 = utils.$int_0; + var $int_1 = utils.$int_1; + var SCArray = $SC("Array"); - return $nil; - }; + spec.$newFrom = fn(function($aCollection) { + var $newCollection; - spec.middle = function() { - var size = this.size().__int__(); + $newCollection = this.new($aCollection.size()); + $aCollection.do($SC.Function(function($item) { + $newCollection.add($item); + })); - if (size > 0) { - return this.at($SC.Integer((size - 1) >> 1)); - } + return $newCollection; + }, "aCollection"); - return $nil; - }; + spec.$with = fn(function($$args) { + var $newColl; - spec.top = function() { - return this.last(); - }; + $newColl = this.new($$args.size()); + $newColl.addAll($$args); - spec.putFirst = fn(function($obj) { - var size = this.size().__int__(); + return $newColl; + }, "*args"); - if (size > 0) { - return this.put($int_0, $obj); - } + spec.$fill = fn(function($size, $function) { + var $obj; + var size, i; - return this; - }, "obj"); + if (BOOL($size.isSequenceableCollection())) { + return this.fillND($size, $function); + } - spec.putLast = fn(function($obj) { - var size = this.size().__int__(); + $obj = this.new($size); - if (size > 0) { - return this.put($SC.Integer(size - 1), $obj); + size = $size.__int__(); + for (i = 0; i < size; ++i) { + $obj.add($function.value($SC.Integer(i))); } - return this; - }, "obj"); - - spec.obtain = fn(function($index, $default) { - var $res; + return $obj; + }, "size; function"); - $res = this.at($index); - if ($res === $nil) { - $res = $default; - } + spec.$fill2D = fn(function($rows, $cols, $function) { + var $this = this, $obj, $obj2, $row, $col; + var rows, cols, i, j; - return $res; - }, "index; default"); + $obj = this.new($rows); - spec.instill = fn(function($index, $item, $default) { - var $res; + rows = $rows.__int__(); + cols = $cols.__int__(); - if ($index.__num__() >= this.size()) { - $res = this.extend($index.__inc__(), $default); - } else { - $res = this.copy(); + for (i = 0; i < rows; ++i) { + $row = $SC.Integer(i); + $obj2 = $this.new($cols); + for (j = 0; j < cols; ++j) { + $col = $SC.Integer(j); + $obj2 = $obj2.add($function.value($row, $col)); + } + $obj = $obj.add($obj2); } - return $res.put($index, $item); - }, "index; item; default"); + return $obj; + }, "rows; cols; function"); - spec.pairsDo = function($function) { - var $this = this, $int2 = $SC.Integer(2); + spec.$fill3D = fn(function($planes, $rows, $cols, $function) { + var $this = this, $obj, $obj2, $obj3, $plane, $row, $col; + var planes, rows, cols, i, j, k; - $int_0.forBy(this.size() ["-"] ($int2), $int2, $SC.Function(function($i) { - return $function.value($this.at($i), $this.at($i.__inc__()), $i); - })); + $obj = this.new($planes); - return this; - }; + planes = $planes.__int__(); + rows = $rows .__int__(); + cols = $cols .__int__(); - spec.keysValuesDo = function($function) { - return this.pairsDo($function); - }; + for (i = 0; i < planes; ++i) { + $plane = $SC.Integer(i); + $obj2 = $this.new($rows); + for (j = 0; j < rows; ++j) { + $row = $SC.Integer(j); + $obj3 = $this.new($cols); + for (k = 0; k < cols; ++k) { + $col = $SC.Integer(k); + $obj3 = $obj3.add($function.value($plane, $row, $col)); + } + $obj2 = $obj2.add($obj3); + } + $obj = $obj.add($obj2); + } - spec.doAdjacentPairs = function($function) { - var $i; - var size, i, imax; + return $obj; + }, "planes; rows; cols; function"); - size = this.size().__int__(); - for (i = 0, imax = size - 1; i < imax; ++i) { - $i = $SC.Integer(i); - $function.value(this.at($i), this.at($i.__inc__()), $i); + var fillND = function($this, $dimensions, $function, $args) { + var $n, $obj, $argIndex; + + $n = $dimensions.first(); + $obj = $this.new($n); + $argIndex = $args.size(); + $args = $args ["++"] ($int_0); + + if ($dimensions.size().__int__() <= 1) { + $n.do($SC.Function(function($i) { + $obj.add($function.valueArray($args.put($argIndex, $i))); + })); + } else { + $dimensions = $dimensions.drop($int_1); + $n.do($SC.Function(function($i) { + $obj = $obj.add(fillND($this, $dimensions, $function, $args.put($argIndex, $i))); + })); } - return this; + return $obj; }; - spec.separate = fn(function($function) { - var $this = this, $list, $sublist; - - $list = $SC.Array(); - $sublist = this.species().new(); - this.doAdjacentPairs($SC.Function(function($a, $b, $i) { - $sublist = $sublist.add($a); - if (BOOL($function.value($a, $b, $i))) { - $list = $list.add($sublist); - $sublist = $this.species().new(); - } - })); - if (BOOL(this.notEmpty())) { - $sublist = $sublist.add(this.last()); - } - $list = $list.add($sublist); + spec.$fillND = fn(function($dimensions, $function) { + return fillND(this, $dimensions, $function, $SC.Array([])); + }, "dimensions; function"); - return $list; - }, "function=true"); + spec["@"] = function($index) { + return this.at($index); + }; - spec.delimit = function($function) { - var $this = this, $list, $sublist; + spec["=="] = function($aCollection) { + var $res = null; - $list = $SC.Array(); - $sublist = this.species().new(); - this.do($SC.Function(function($item, $i) { - if (BOOL($function.value($item, $i))) { - $list = $list.add($sublist); - $sublist = $this.species().new(); - } else { - $sublist = $sublist.add($item); + if ($aCollection.class() !== this.class()) { + return $false; + } + if (this.size() !== $aCollection.size()) { + return $false; + } + this.do($SC.Function(function($item) { + if (!BOOL($aCollection.includes($item))) { + $res = $false; + return 65535; } })); - $list = $list.add($sublist); - return $list; + return $res || $true; }; - spec.clump = fn(function($groupSize) { - var $this = this, $list, $sublist; + // TODO: implements hash - $list = $SC.Array(); - $sublist = this.species().new($groupSize); - this.do($SC.Function(function($item) { - $sublist.add($item); - if ($sublist.size() >= $groupSize) { - $list.add($sublist); - $sublist = $this.species().new($groupSize); - } - })); - if ($sublist.size() > 0) { - $list = $list.add($sublist); - } + spec.species = function() { + return SCArray; + }; - return $list; - }, "groupSize"); + spec.do = function() { + return this._subclassResponsibility("do"); + }; - spec.clumps = fn(function($groupSizeList) { - var $this = this, $list, $subSize, $sublist, i = 0; + // TODO: implements iter - $list = $SC.Array(); - $subSize = $groupSizeList.at($int_0); - $sublist = this.species().new($subSize); - this.do($SC.Function(function($item) { - $sublist = $sublist.add($item); - if ($sublist.size() >= $subSize) { - $list = $list.add($sublist); - $subSize = $groupSizeList.wrapAt($SC.Integer(++i)); - $sublist = $this.species().new($subSize); - } + spec.size = function() { + var tally = 0; + + this.do($SC.Function(function() { + tally++; })); - if ($sublist.size() > 0) { - $list = $list.add($sublist); - } - return $list; - }, "groupSizeList"); + return $SC.Integer(tally); + }; - spec.curdle = fn(function($probability) { - return this.separate($SC.Function(function() { - return $probability.coin(); + spec.flatSize = function() { + return this.sum($SC.Function(function($_) { + return $_.flatSize(); })); - }, "probability"); + }; - spec.flatten = fn(function($numLevels) { - return this._flatten($numLevels.__num__()); - }, "numLevels=1"); + spec.isEmpty = function() { + return $SC.Boolean(this.size().__int__() === 0); + }; - spec._flatten = fn(function(numLevels) { - var $list; + spec.notEmpty = function() { + return $SC.Boolean(this.size().__int__() !== 0); + }; - if (numLevels <= 0) { - return this; - } - numLevels = numLevels - 1; + spec.asCollection = utils.nop; + spec.isCollection = utils.alwaysReturn$true; - $list = this.species().new(); - this.do($SC.Function(function($item) { - if ($item._flatten) { - $list = $list.addAll($item._flatten(numLevels)); - } else { - $list = $list.add($item); - } + spec.add = function() { + return this._subclassResponsibility("add"); + }; + + spec.addAll = fn(function($aCollection) { + var $this = this; + + $aCollection.asCollection().do($SC.Function(function($item) { + return $this.add($item); })); - return $list; - }, "numLevels"); + return this; + }, "aCollection"); - spec.flat = function() { - return this._flat(this.species().new(this.flatSize())); + spec.remove = function() { + return this._subclassResponsibility("remove"); }; - spec._flat = fn(function($list) { - this.do($SC.Function(function($item) { - if ($item._flat) { - $list = $item._flat($list); - } else { - $list = $list.add($item); - } + spec.removeAll = fn(function($list) { + var $this = this; + + $list.do($SC.Function(function($item) { + $this.remove($item); })); - return $list; + + return this; }, "list"); - spec.flatIf = fn(function($func) { - return this._flatIf($func); - }, "func"); + spec.removeEvery = fn(function($list) { + this.removeAllSuchThat($SC.Function(function($_) { + return $list.includes($_); + })); + return this; + }, "list"); - spec._flatIf = function($func) { - var $list; + spec.removeAllSuchThat = function($function) { + var $this = this, $removedItems, $copy; - $list = this.species().new(this.size()); - this.do($SC.Function(function($item, $i) { - if ($item._flatIf && BOOL($func.value($item, $i))) { - $list = $list.addAll($item._flatIf($func)); - } else { - $list = $list.add($item); + $removedItems = this.class().new(); + $copy = this.copy(); + $copy.do($SC.Function(function($item) { + if (BOOL($function.value($item))) { + $this.remove($item); + $removedItems = $removedItems.add($item); } })); - return $list; + return $removedItems; }; - spec.flop = function() { - var $this = this, $list, $size, $maxsize; + spec.atAll = fn(function($keys) { + var $this = this; - $size = this.size(); - $maxsize = $int_0; - this.do($SC.Function(function($sublist) { - var $sz; - if (BOOL($sublist.isSequenceableCollection())) { - $sz = $sublist.size(); - } else { - $sz = $int_1; - } - if ($sz > $maxsize) { - $maxsize = $sz; - } + return $keys.collect($SC.Function(function($index) { + return $this.at($index); })); + }, "keys"); - $list = this.species().fill($maxsize, $SC.Function(function() { - return $this.species().new($size); - })); + spec.putEach = fn(function($keys, $values) { + var keys, values, i, imax; - this.do($SC.Function(function($isublist) { - if (BOOL($isublist.isSequenceableCollection())) { - $list.do($SC.Function(function($jsublist, $j) { - $jsublist.add($isublist.wrapAt($j)); - })); - } else { - $list.do($SC.Function(function($jsublist) { - $jsublist.add($isublist); - })); - } - })); + $keys = $keys.asArray(); + $values = $values.asArray(); - return $list; - }; + keys = $keys._; + values = $values._; + for (i = 0, imax = keys.length; i < imax; ++i) { + this.put(keys[i], values[i % values.length]); + } - spec.flopWith = fn(function($func) { - var $this = this, $maxsize; + return this; + }, "keys; values"); - $maxsize = this.maxValue($SC.Function(function($sublist) { - if (BOOL($sublist.isSequenceableCollection())) { - return $sublist.size(); + spec.includes = fn(function($item1) { + var $res = null; + + this.do($SC.Function(function($item2) { + if ($item1 === $item2) { + $res = $true; + return 65535; } - return $int_1; })); - return this.species().fill($maxsize, $SC.Function(function($i) { - return $func.valueArray($this.collect($SC.Function(function($sublist) { - if (BOOL($sublist.isSequenceableCollection())) { - return $sublist.wrapAt($i); - } else { - return $sublist; - } - }))); + return $res || $false; + }, "item1"); + + spec.includesEqual = fn(function($item1) { + var $res = null; + + this.do($SC.Function(function($item2) { + if (BOOL( $item1 ["=="] ($item2) )) { + $res = $true; + return 65535; + } })); - }, "func"); - // TODO: implements flopTogether - // TODO: implements flopDeep - // TODO: implements wrapAtDepth - // TODO: implements unlace - // TODO: implements integrate - // TODO: implements differentiate - // TODO: implements convertDigits - // TODO: implements hammingDistance - // TODO: implements degreeToKey - // TODO: implements keyToDegree - // TODO: implements nearestInScale - // TODO: implements nearestInList - // TODO: implements transposeKey - // TODO: implements mode - // TODO: implements performDegreeToKey - // TODO: implements performNearestInList - // TODO: implements performNearestInScale - // TODO: implements convertRhythm - // TODO: implements sumRhythmDivisions - // TODO: implements convertOneRhythm + return $res || $false; + }, "item1"); - spec.isSequenceableCollection = utils.alwaysReturn$true; + spec.includesAny = fn(function($aCollection) { + var $this = this, $res = null; - spec.containsSeqColl = function() { - return this.any($SC.Function(function($_) { - return $_.isSequenceableCollection(); + $aCollection.do($SC.Function(function($item) { + if (BOOL($this.includes($item))) { + $res = $true; + return 65535; + } })); - }; - spec.neg = function() { - return this.performUnaryOp($SC.Symbol("neg")); - }; + return $res || $false; + }, "aCollection"); - spec.bitNot = function() { - return this.performUnaryOp($SC.Symbol("bitNot")); - }; + spec.includesAll = fn(function($aCollection) { + var $this = this, $res = null; - spec.abs = function() { - return this.performUnaryOp($SC.Symbol("abs")); - }; + $aCollection.do($SC.Function(function($item) { + if (!BOOL($this.includes($item))) { + $res = $false; + return 65535; + } + })); - spec.ceil = function() { - return this.performUnaryOp($SC.Symbol("ceil")); - }; + return $res || $true; + }, "aCollection"); - spec.floor = function() { - return this.performUnaryOp($SC.Symbol("floor")); - }; + spec.matchItem = fn(function($item) { + return this.includes($item); + }, "item"); - spec.frac = function() { - return this.performUnaryOp($SC.Symbol("frac")); + spec.collect = function($function) { + return this.collectAs($function, this.species()); }; - spec.sign = function() { - return this.performUnaryOp($SC.Symbol("sign")); + spec.select = function($function) { + return this.selectAs($function, this.species()); }; - spec.squared = function() { - return this.performUnaryOp($SC.Symbol("squared")); + spec.reject = function($function) { + return this.rejectAs($function, this.species()); }; - spec.cubed = function() { - return this.performUnaryOp($SC.Symbol("cubed")); - }; + spec.collectAs = fn(function($function, $class) { + var $res; - spec.sqrt = function() { - return this.performUnaryOp($SC.Symbol("sqrt")); - }; + $res = $class.new(this.size()); + this.do($SC.Function(function($elem, $i) { + return $res.add($function.value($elem, $i)); + })); - spec.exp = function() { - return this.performUnaryOp($SC.Symbol("exp")); - }; + return $res; + }, "function; class"); - spec.reciprocal = function() { - return this.performUnaryOp($SC.Symbol("reciprocal")); - }; - - spec.midicps = function() { - return this.performUnaryOp($SC.Symbol("midicps")); - }; + spec.selectAs = fn(function($function, $class) { + var $res; - spec.cpsmidi = function() { - return this.performUnaryOp($SC.Symbol("cpsmidi")); - }; + $res = $class.new(this.size()); + this.do($SC.Function(function($elem, $i) { + if (BOOL($function.value($elem, $i))) { + $res = $res.add($elem); + } + })); - spec.midiratio = function() { - return this.performUnaryOp($SC.Symbol("midiratio")); - }; + return $res; + }, "function; class"); - spec.ratiomidi = function() { - return this.performUnaryOp($SC.Symbol("ratiomidi")); - }; + spec.rejectAs = fn(function($function, $class) { + var $res; - spec.ampdb = function() { - return this.performUnaryOp($SC.Symbol("ampdb")); - }; + $res = $class.new(this.size()); + this.do($SC.Function(function($elem, $i) { + if (!BOOL($function.value($elem, $i))) { + $res = $res.add($elem); + } + })); - spec.dbamp = function() { - return this.performUnaryOp($SC.Symbol("dbamp")); - }; + return $res; + }, "function; class"); - spec.octcps = function() { - return this.performUnaryOp($SC.Symbol("octcps")); - }; + spec.detect = function($function) { + var $res = null; - spec.cpsoct = function() { - return this.performUnaryOp($SC.Symbol("cpsoct")); - }; + this.do($SC.Function(function($elem, $i) { + if (BOOL($function.value($elem, $i))) { + $res = $elem; + return 65535; + } + })); - spec.log = function() { - return this.performUnaryOp($SC.Symbol("log")); + return $res || $nil; }; - spec.log2 = function() { - return this.performUnaryOp($SC.Symbol("log2")); - }; + spec.detectIndex = function($function) { + var $res = null; - spec.log10 = function() { - return this.performUnaryOp($SC.Symbol("log10")); + this.do($SC.Function(function($elem, $i) { + if (BOOL($function.value($elem, $i))) { + $res = $i; + return 65535; + } + })); + return $res || $nil; }; - spec.sin = function() { - return this.performUnaryOp($SC.Symbol("sin")); + spec.doMsg = function() { + var args = arguments; + this.do($SC.Function(function($item) { + $item.perform.apply($item, args); + })); + return this; }; - spec.cos = function() { - return this.performUnaryOp($SC.Symbol("cos")); + spec.collectMsg = function() { + var args = arguments; + return this.collect($SC.Function(function($item) { + return $item.perform.apply($item, args); + })); }; - spec.tan = function() { - return this.performUnaryOp($SC.Symbol("tan")); + spec.selectMsg = function() { + var args = arguments; + return this.select($SC.Function(function($item) { + return $item.perform.apply($item, args); + })); }; - spec.asin = function() { - return this.performUnaryOp($SC.Symbol("asin")); + spec.rejectMsg = function() { + var args = arguments; + return this.reject($SC.Function(function($item) { + return $item.perform.apply($item, args); + })); }; - spec.acos = function() { - return this.performUnaryOp($SC.Symbol("acos")); - }; + spec.detectMsg = fn(function($selector, $$args) { + return this.detect($SC.Function(function($item) { + return $item.performList($selector, $$args); + })); + }, "selector; *args"); - spec.atan = function() { - return this.performUnaryOp($SC.Symbol("atan")); - }; + spec.detectIndexMsg = fn(function($selector, $$args) { + return this.detectIndex($SC.Function(function($item) { + return $item.performList($selector, $$args); + })); + }, "selector; *args"); - spec.sinh = function() { - return this.performUnaryOp($SC.Symbol("sinh")); - }; + spec.lastForWhich = function($function) { + var $res = null; + this.do($SC.Function(function($elem, $i) { + if (BOOL($function.value($elem, $i))) { + $res = $elem; + } else { + return 65535; + } + })); - spec.cosh = function() { - return this.performUnaryOp($SC.Symbol("cosh")); + return $res || $nil; }; - spec.tanh = function() { - return this.performUnaryOp($SC.Symbol("tanh")); - }; + spec.lastIndexForWhich = function($function) { + var $res = null; + this.do($SC.Function(function($elem, $i) { + if (BOOL($function.value($elem, $i))) { + $res = $i; + } else { + return 65535; + } + })); - spec.rand = function() { - return this.performUnaryOp($SC.Symbol("rand")); + return $res || $nil; }; - spec.rand2 = function() { - return this.performUnaryOp($SC.Symbol("rand2")); - }; + spec.inject = fn(function($thisValue, $function) { + var $nextValue; - spec.linrand = function() { - return this.performUnaryOp($SC.Symbol("linrand")); - }; + $nextValue = $thisValue; + this.do($SC.Function(function($item, $i) { + $nextValue = $function.value($nextValue, $item, $i); + })); - spec.bilinrand = function() { - return this.performUnaryOp($SC.Symbol("bilinrand")); - }; + return $nextValue; + }, "thisValue; function"); - spec.sum3rand = function() { - return this.performUnaryOp($SC.Symbol("sum3rand")); - }; + spec.injectr = fn(function($thisValue, $function) { + var $this = this, size, $nextValue; - spec.distort = function() { - return this.performUnaryOp($SC.Symbol("distort")); - }; + size = this.size().__int__(); + $nextValue = $thisValue; + this.do($SC.Function(function($item, $i) { + $item = $this.at($SC.Integer(--size)); + $nextValue = $function.value($nextValue, $item, $i); + })); - spec.softclip = function() { - return this.performUnaryOp($SC.Symbol("softclip")); - }; + return $nextValue; + }, "thisValue; function"); - spec.coin = function() { - return this.performUnaryOp($SC.Symbol("coin")); - }; + spec.count = function($function) { + var sum = 0; + this.do($SC.Function(function($elem, $i) { + if (BOOL($function.value($elem, $i))) { + sum++; + } + })); - spec.even = function() { - return this.performUnaryOp($SC.Symbol("even")); + return $SC.Integer(sum); }; - spec.odd = function() { - return this.performUnaryOp($SC.Symbol("odd")); - }; + spec.occurrencesOf = fn(function($obj) { + var sum = 0; - spec.isPositive = function() { - return this.performUnaryOp($SC.Symbol("isPositive")); - }; + this.do($SC.Function(function($elem) { + if (BOOL($elem ["=="] ($obj))) { + sum++; + } + })); - spec.isNegative = function() { - return this.performUnaryOp($SC.Symbol("isNegative")); - }; + return $SC.Integer(sum); + }, "obj"); - spec.isStrictlyPositive = function() { - return this.performUnaryOp($SC.Symbol("isStrictlyPositive")); - }; + spec.any = function($function) { + var $res = null; - spec.rectWindow = function() { - return this.performUnaryOp($SC.Symbol("rectWindow")); - }; + this.do($SC.Function(function($elem, $i) { + if (BOOL($function.value($elem, $i))) { + $res = $true; + return 65535; + } + })); - spec.hanWindow = function() { - return this.performUnaryOp($SC.Symbol("hanWindow")); + return $res || $false; }; - spec.welWindow = function() { - return this.performUnaryOp($SC.Symbol("welWindow")); - }; + spec.every = function($function) { + var $res = null; - spec.triWindow = function() { - return this.performUnaryOp($SC.Symbol("triWindow")); - }; + this.do($SC.Function(function($elem, $i) { + if (!BOOL($function.value($elem, $i))) { + $res = $false; + return 65535; + } + })); - spec.scurve = function() { - return this.performUnaryOp($SC.Symbol("scurve")); + return $res || $true; }; - spec.ramp = function() { - return this.performUnaryOp($SC.Symbol("ramp")); - }; + spec.sum = fn(function($function) { + var $sum; - spec.asFloat = function() { - return this.performUnaryOp($SC.Symbol("asFloat")); - }; - - spec.asInteger = function() { - return this.performUnaryOp($SC.Symbol("asInteger")); - }; + $sum = $int_0; + if ($function === $nil) { + this.do($SC.Function(function($elem) { + $sum = $sum ["+"] ($elem); + })); + } else { + this.do($SC.Function(function($elem, $i) { + $sum = $sum ["+"] ($function.value($elem, $i)); + })); + } - spec.nthPrime = function() { - return this.performUnaryOp($SC.Symbol("nthPrime")); - }; + return $sum; + }, "function"); - spec.prevPrime = function() { - return this.performUnaryOp($SC.Symbol("prevPrime")); + spec.mean = function($function) { + return this.sum($function) ["/"] (this.size()); }; - spec.nextPrime = function() { - return this.performUnaryOp($SC.Symbol("nextPrime")); - }; + spec.product = fn(function($function) { + var $product; - spec.indexOfPrime = function() { - return this.performUnaryOp($SC.Symbol("indexOfPrime")); - }; + $product = $int_1; + if ($function === $nil) { + this.do($SC.Function(function($elem) { + $product = $product ["*"] ($elem); + })); + } else { + this.do($SC.Function(function($elem, $i) { + $product = $product ["*"] ($function.value($elem, $i)); + })); + } - spec.real = function() { - return this.performUnaryOp($SC.Symbol("real")); - }; + return $product; + }, "function"); - spec.imag = function() { - return this.performUnaryOp($SC.Symbol("imag")); - }; + spec.sumabs = function() { + var $sum; - spec.magnitude = function() { - return this.performUnaryOp($SC.Symbol("magnitude")); - }; + $sum = $int_0; + this.do($SC.Function(function($elem) { + if (BOOL($elem.isSequenceableCollection())) { + $elem = $elem.at($int_0); + } + $sum = $sum ["+"] ($elem.abs()); + })); - spec.magnitudeApx = function() { - return this.performUnaryOp($SC.Symbol("magnitudeApx")); + return $sum; }; - spec.phase = function() { - return this.performUnaryOp($SC.Symbol("phase")); - }; + spec.maxItem = fn(function($function) { + var $maxValue, $maxElement; - spec.angle = function() { - return this.performUnaryOp($SC.Symbol("angle")); - }; + $maxValue = $nil; + $maxElement = $nil; + if ($function === $nil) { + this.do($SC.Function(function($elem) { + if ($maxElement === $nil) { + $maxElement = $elem; + } else if ($elem > $maxElement) { + $maxElement = $elem; + } + })); + } else { + this.do($SC.Function(function($elem, $i) { + var $val; + if ($maxValue === $nil) { + $maxValue = $function.value($elem, $i); + $maxElement = $elem; + } else { + $val = $function.value($elem, $i); + if ($val > $maxValue) { + $maxValue = $val; + $maxElement = $elem; + } + } + })); + } - spec.rho = function() { - return this.performUnaryOp($SC.Symbol("rho")); - }; + return $maxElement; + }, "function"); - spec.theta = function() { - return this.performUnaryOp($SC.Symbol("theta")); - }; + spec.minItem = fn(function($function) { + var $minValue, $minElement; - spec.degrad = function() { - return this.performUnaryOp($SC.Symbol("degrad")); + $minValue = $nil; + $minElement = $nil; + if ($function === $nil) { + this.do($SC.Function(function($elem) { + if ($minElement === $nil) { + $minElement = $elem; + } else if ($elem < $minElement) { + $minElement = $elem; + } + })); + } else { + this.do($SC.Function(function($elem, $i) { + var $val; + if ($minValue === $nil) { + $minValue = $function.value($elem, $i); + $minElement = $elem; + } else { + $val = $function.value($elem, $i); + if ($val < $minValue) { + $minValue = $val; + $minElement = $elem; + } + } + })); + } - }; - spec.raddeg = function() { - return this.performUnaryOp($SC.Symbol("raddeg")); - }; + return $minElement; + }, "function"); - spec["+"] = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("+"), $aNumber, $adverb); - }; + spec.maxIndex = fn(function($function) { + var $maxValue, $maxIndex; - spec["-"] = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("-"), $aNumber, $adverb); - }; + $maxValue = $nil; + $maxIndex = $nil; + if ($function === $nil) { + this.do($SC.Function(function($elem, $index) { + if ($maxValue === $nil) { + $maxValue = $elem; + $maxIndex = $index; + } else if ($elem > $maxValue) { + $maxValue = $elem; + $maxIndex = $index; + } + })); + } else { + this.do($SC.Function(function($elem, $i) { + var $val; + if ($maxValue === $nil) { + $maxValue = $function.value($elem, $i); + $maxIndex = $i; + } else { + $val = $function.value($elem, $i); + if ($val > $maxValue) { + $maxValue = $val; + $maxIndex = $i; + } + } + })); + } - spec["*"] = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("*"), $aNumber, $adverb); - }; + return $maxIndex; + }, "function"); - spec["/"] = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("/"), $aNumber, $adverb); - }; + spec.minIndex = fn(function($function) { + var $maxValue, $minIndex; - spec.div = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("div"), $aNumber, $adverb); - }; + $maxValue = $nil; + $minIndex = $nil; + if ($function === $nil) { + this.do($SC.Function(function($elem, $index) { + if ($maxValue === $nil) { + $maxValue = $elem; + $minIndex = $index; + } else if ($elem < $maxValue) { + $maxValue = $elem; + $minIndex = $index; + } + })); + } else { + this.do($SC.Function(function($elem, $i) { + var $val; + if ($maxValue === $nil) { + $maxValue = $function.value($elem, $i); + $minIndex = $i; + } else { + $val = $function.value($elem, $i); + if ($val < $maxValue) { + $maxValue = $val; + $minIndex = $i; + } + } + })); + } - spec.mod = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("mod"), $aNumber, $adverb); - }; + return $minIndex; + }, "function"); - spec.pow = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("pow"), $aNumber, $adverb); - }; + spec.maxValue = fn(function($function) { + var $maxValue, $maxElement; - spec.min = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("min"), $aNumber, $adverb); - }; + $maxValue = $nil; + $maxElement = $nil; + this.do($SC.Function(function($elem, $i) { + var $val; + if ($maxValue === $nil) { + $maxValue = $function.value($elem, $i); + $maxElement = $elem; + } else { + $val = $function.value($elem, $i); + if ($val > $maxValue) { + $maxValue = $val; + $maxElement = $elem; + } + } + })); - spec.max = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("max"), $aNumber, $adverb); - }; + return $maxValue; + }, "function"); - spec["<"] = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("<"), $aNumber, $adverb); - }; + spec.minValue = fn(function($function) { + var $minValue, $minElement; - spec["<="] = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("<="), $aNumber, $adverb); - }; - - spec[">"] = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol(">"), $aNumber, $adverb); - }; + $minValue = $nil; + $minElement = $nil; + this.do($SC.Function(function($elem, $i) { + var $val; + if ($minValue === $nil) { + $minValue = $function.value($elem, $i); + $minElement = $elem; + } else { + $val = $function.value($elem, $i); + if ($val < $minValue) { + $minValue = $val; + $minElement = $elem; + } + } + })); - spec[">="] = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol(">="), $aNumber, $adverb); - }; + return $minValue; + }, "function"); - spec.bitAnd = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("bitAnd"), $aNumber, $adverb); - }; + spec.maxSizeAtDepth = fn(function($rank) { + var rank, maxsize = 0; - spec.bitOr = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("bitOr"), $aNumber, $adverb); - }; + rank = $rank.__num__(); + if (rank === 0) { + return this.size(); + } - spec.bitXor = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("bitXor"), $aNumber, $adverb); - }; + this.do($SC.Function(function($sublist) { + var sz; + if (BOOL($sublist.isCollection())) { + sz = $sublist.maxSizeAtDepth($SC.Integer(rank - 1)); + } else { + sz = 1; + } + if (sz > maxsize) { + maxsize = sz; + } + })); - spec.bitHammingDistance = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("bitHammingDistance"), $aNumber, $adverb); - }; + return $SC.Integer(maxsize); + }, "rank"); - spec.lcm = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("lcm"), $aNumber, $adverb); - }; + spec.maxDepth = fn(function($max) { + var $res; - spec.gcd = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("gcd"), $aNumber, $adverb); - }; + $res = $max; + this.do($SC.Function(function($elem) { + if (BOOL($elem.isCollection())) { + $res = $res.max($elem.maxDepth($max.__inc__())); + } + })); - spec.round = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("round"), $aNumber, $adverb); - }; + return $res; + }, "max=1"); - spec.roundUp = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("roundUp"), $aNumber, $adverb); - }; + spec.deepCollect = fn(function($depth, $function, $index, $rank) { + if ($depth === $nil) { + $rank = $rank.__inc__(); + return this.collect($SC.Function(function($item, $i) { + return $item.deepCollect($depth, $function, $i, $rank); + })); + } + if ($depth.__num__() <= 0) { + return $function.value(this, $index, $rank); + } + $depth = $depth.__dec__(); + $rank = $rank.__inc__(); - spec.trunc = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("trunc"), $aNumber, $adverb); - }; + return this.collect($SC.Function(function($item, $i) { + return $item.deepCollect($depth, $function, $i, $rank); + })); + }, "depth=1; function; index=0; rank=0"); - spec.atan2 = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("atan2"), $aNumber, $adverb); - }; + spec.deepDo = fn(function($depth, $function, $index, $rank) { + if ($depth === $nil) { + $rank = $rank.__inc__(); + return this.do($SC.Function(function($item, $i) { + $item.deepDo($depth, $function, $i, $rank); + })); + } + if ($depth.__num__() <= 0) { + $function.value(this, $index, $rank); + return this; + } + $depth = $depth.__dec__(); + $rank = $rank.__inc__(); - spec.hypot = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("hypot"), $aNumber, $adverb); - }; + return this.do($SC.Function(function($item, $i) { + $item.deepDo($depth, $function, $i, $rank); + })); + }, "depth=1; function; index=0; rank=0"); - spec.hypotApx = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("hypotApx"), $aNumber, $adverb); - }; + spec.invert = fn(function($axis) { + var $index; - spec.leftShift = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("leftShift"), $aNumber, $adverb); - }; + if (BOOL(this.isEmpty())) { + return this.species().new(); + } + if ($axis !== $nil) { + $index = $axis ["*"] ($SC.Integer(2)); + } else { + $index = this.minItem() ["+"] (this.maxItem()); + } - spec.rightShift = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("rightShift"), $aNumber, $adverb); - }; + return $index ["-"] (this); + }, "axis"); - spec.unsignedRightShift = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("unsignedRightShift"), $aNumber, $adverb); - }; + spec.sect = fn(function($that) { + var $result; - spec.ring1 = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("ring1"), $aNumber, $adverb); - }; + $result = this.species().new(); + this.do($SC.Function(function($item) { + if (BOOL($that.includes($item))) { + $result = $result.add($item); + } + })); - spec.ring2 = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("ring2"), $aNumber, $adverb); - }; + return $result; + }, "that"); - spec.ring3 = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("ring3"), $aNumber, $adverb); - }; + spec.union = fn(function($that) { + var $result; - spec.ring4 = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("ring4"), $aNumber, $adverb); - }; + $result = this.copy(); + $that.do($SC.Function(function($item) { + if (!BOOL($result.includes($item))) { + $result = $result.add($item); + } + })); - spec.difsqr = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("difsqr"), $aNumber, $adverb); - }; + return $result; + }, "that"); - spec.sumsqr = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("sumsqr"), $aNumber, $adverb); - }; + spec.difference = fn(function($that) { + return this.copy().removeAll($that); + }, "that"); - spec.sqrsum = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("sqrsum"), $aNumber, $adverb); - }; + spec.symmetricDifference = fn(function($that) { + var $this = this, $result; - spec.sqrdif = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("sqrdif"), $aNumber, $adverb); - }; + $result = this.species().new(); + $this.do($SC.Function(function($item) { + if (!BOOL($that.includes($item))) { + $result = $result.add($item); + } + })); + $that.do($SC.Function(function($item) { + if (!BOOL($this.includes($item))) { + $result = $result.add($item); + } + })); - spec.absdif = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("absdif"), $aNumber, $adverb); - }; + return $result; + }, "that"); - spec.thresh = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("thresh"), $aNumber, $adverb); - }; + spec.isSubsetOf = fn(function($that) { + return $that.includesAll(this); + }, "that"); - spec.amclip = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("amclip"), $aNumber, $adverb); + spec.asArray = function() { + return SCArray.new(this.size()).addAll(this); }; - spec.scaleneg = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("scaleneg"), $aNumber, $adverb); + spec.asBag = function() { + return $SC("Bag").new(this.size()).addAll(this); }; - spec.clip2 = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("clip2"), $aNumber, $adverb); + spec.asList = function() { + return $SC("List").new(this.size()).addAll(this); }; - spec.fold2 = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("fold2"), $aNumber, $adverb); + spec.asSet = function() { + return $SC("Set").new(this.size()).addAll(this); }; - spec.wrap2 = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("wrap2"), $aNumber, $adverb); + spec.asSortedList = function($function) { + return $SC("SortedList").new(this.size(), $function).addAll(this); }; - spec.excess = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("excess"), $aNumber, $adverb); - }; + // TODO: implements powerset + // TODO: implements flopDict + // TODO: implements histo + // TODO: implements printAll + // TODO: implements printcsAll + // TODO: implements dumpAll + // TODO: implements printOn + // TODO: implements storeOn + // TODO: implements storeItemsOn + // TODO: implements printItemsOn + // TODO: implements writeDef + // TODO: implements writeInputSpec + // TODO: implements case + // TODO: implements makeEnvirValPairs + }); - spec.firstArg = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("firstArg"), $aNumber, $adverb); - }; +})(sc); - spec.rrand = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("rrand"), $aNumber, $adverb); - }; +// src/sc/lang/classlib/Collections/SequenceableCollection.js +(function(sc) { - spec.exprand = function($aNumber, $adverb) { - return this.performBinaryOp($SC.Symbol("exprand"), $aNumber, $adverb); + var slice = [].slice; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; + + sc.lang.klass.refine("SequenceableCollection", function(spec, utils) { + var BOOL = utils.BOOL; + var $nil = utils.$nil; + var $true = utils.$true; + var $false = utils.$false; + var $int_0 = utils.$int_0; + var $int_1 = utils.$int_1; + + spec["|@|"] = function($index) { + return this.clipAt($index); }; - spec.performUnaryOp = function($aSelector) { - return this.collect($SC.Function(function($item) { - return $item.perform($aSelector); - })); + spec["@@"] = function($index) { + return this.wrapAt($index); }; - spec.performBinaryOp = function($aSelector, $theOperand, $adverb) { - return $theOperand.performBinaryOpOnSeqColl($aSelector, this, $adverb); + spec["@|@"] = function($index) { + return this.foldAt($index); }; - spec.performBinaryOpOnSeqColl = function($aSelector, $theOperand, $adverb) { - var adverb; + spec.$series = fn(function($size, $start, $step) { + var $obj, i, imax; - if ($adverb === $nil || !$adverb) { - return _performBinaryOpOnSeqColl_adverb_nil( - this, $aSelector, $theOperand - ); - } - if (BOOL($adverb.isInteger())) { - return _performBinaryOpOnSeqColl_adverb_int( - this, $aSelector, $theOperand, $adverb.valueOf() - ); + $obj = this.new($size); + for (i = 0, imax = $size.__int__(); i < imax; ++i) { + $obj.add($start ["+"] ($step ["*"] ($SC.Integer(i)))); } - adverb = $adverb.__sym__(); - if (adverb === "t") { - return _performBinaryOpOnSeqColl_adverb_t( - this, $aSelector, $theOperand - ); - } - if (adverb === "x") { - return _performBinaryOpOnSeqColl_adverb_x( - this, $aSelector, $theOperand - ); - } - if (adverb === "s") { - return _performBinaryOpOnSeqColl_adverb_s( - this, $aSelector, $theOperand - ); - } - if (adverb === "f") { - return _performBinaryOpOnSeqColl_adverb_f( - this, $aSelector, $theOperand - ); - } + return $obj; + }, "size; start=0; step=1"); - throw new Error( - "unrecognized adverb: '" + adverb + "' for operator '" + String($aSelector) + "'" - ); - }; + spec.$geom = fn(function($size, $start, $grow) { + var $obj, i, imax; - function _performBinaryOpOnSeqColl_adverb_nil($this, $aSelector, $theOperand) { - var $size, $newList, $i; - var size, i; + $obj = this.new($size); + for (i = 0, imax = $size.__int__(); i < imax; ++i) { + $obj.add($start); + $start = $start ["*"] ($grow); + } - $size = $this.size().max($theOperand.size()); - $newList = $this.species().new($size); + return $obj; + }, "size; start; grow"); - size = $size.__int__(); - for (i = 0; i < size; ++i) { - $i = $SC.Integer(i); - $newList.add( - $theOperand.wrapAt($i).perform($aSelector, $this.wrapAt($i)) - ); - } + spec.$fib = fn(function($size, $a, $b) { + var $obj, $temp, i, imax; - return $newList; - } + $obj = this.new($size); + for (i = 0, imax = $size.__int__(); i < imax; ++i) { + $obj.add($b); + $temp = $b; + $b = $a ["+"] ($b); + $a = $temp; + } - function _performBinaryOpOnSeqColl_adverb_int($this, $aSelector, $theOperand, adverb) { - var $size, $newList, $i; - var size, i; + return $obj; + }, "size; a=0.0; b=1.0"); - if (adverb === 0) { - $size = $this.size().max($theOperand.size()); - $newList = $this.species().new($size); + // TODO: implements $rand + // TODO: implements $rand2 + // TODO: implements $linrand - size = $size.__int__(); - for (i = 0; i < size; ++i) { - $i = $SC.Integer(i); - $newList.add($theOperand.wrapAt($i).perform($aSelector, $this.wrapAt($i))); - } + spec.$interpolation = fn(function($size, $start, $end) { + var $obj, $step, i, imax; - } else if (adverb > 0) { + $obj = this.new($size); + if ($size.__int__() === 1) { + return $obj.add($start); + } - $newList = $theOperand.collect($SC.Function(function($item) { - return $item.perform($aSelector, $this, $SC.Integer(adverb - 1)); - })); + $step = ($end ["-"] ($start)) ["/"] ($size.__dec__()); + for (i = 0, imax = $size.__int__(); i < imax; ++i) { + $obj.add($start ["+"] ($SC.Integer(i) ["*"] ($step))); + } - } else { + return $obj; + }, "size; start=0.0; end=1.0"); - $newList = $this.collect($SC.Function(function($item) { - return $theOperand.perform($aSelector, $item, $SC.Integer(adverb + 1)); - })); + spec["++"] = function($aSequenceableCollection) { + var $newlist; - } + $newlist = this.species().new(this.size() ["+"] ($aSequenceableCollection.size())); + $newlist = $newlist.addAll(this).addAll($aSequenceableCollection); - return $newList; - } + return $newlist; + }; - function _performBinaryOpOnSeqColl_adverb_t($this, $aSelector, $theOperand) { - var $size, $newList, $i; - var size, i; + // TODO: implements +++ - $size = $theOperand.size(); - $newList = $this.species().new($size); + spec.asSequenceableCollection = utils.nop; - size = $size.__int__(); - for (i = 0; i < size; ++i) { - $i = $SC.Integer(i); - $newList.add($theOperand.at($i).perform($aSelector, $this)); - } + spec.choose = function() { + return this.at(this.size().rand()); + }; - return $newList; - } + spec.wchoose = fn(function($weights) { + return this.at($weights.windex()); + }, "weights"); - function _performBinaryOpOnSeqColl_adverb_x($this, $aSelector, $theOperand) { - var $size, $newList; + spec["=="] = function($aCollection) { + var $res = null; - $size = $theOperand.size() ["*"] ($this.size()); - $newList = $this.species().new($size); - $theOperand.do($SC.Function(function($a) { - $this.do($SC.Function(function($b) { - $newList.add($a.perform($aSelector, $b)); - })); + if ($aCollection.class() !== this.class()) { + return $false; + } + if (this.size() !== $aCollection.size()) { + return $false; + } + this.do($SC.Function(function($item, $i) { + if (BOOL($item ["!="] ($aCollection.at($i)))) { + $res = $false; + return 65535; + } })); - return $newList; - } + return $res || $true; + }; - function _performBinaryOpOnSeqColl_adverb_s($this, $aSelector, $theOperand) { - var $size, $newList, $i; - var size, i; + // TODO: implements hash - $size = $this.size().min($theOperand.size()); - $newList = $this.species().new($size); + spec.copyRange = fn(function($start, $end) { + var $newColl, i, end; - size = $size.__int__(); - for (i = 0; i < size; ++i) { - $i = $SC.Integer(i); - $newList.add($theOperand.wrapAt($i).perform($aSelector, $this.wrapAt($i))); + i = $start.__int__(); + end = $end.__int__(); + $newColl = this.species().new($SC.Integer(end - i)); + while (i <= end) { + $newColl.add(this.at($SC.Integer(i++))); } - return $newList; - } - - function _performBinaryOpOnSeqColl_adverb_f($this, $aSelector, $theOperand) { - var $size, $newList, $i; - var size, i; + return $newColl; + }, "start; end"); - $size = $this.size().max($theOperand.size()); - $newList = $this.species().new($size); + spec.keep = fn(function($n) { + var n, size; - size = $size.__int__(); - for (i = 0; i < size; ++i) { - $i = $SC.Integer(i); - $newList.add($theOperand.foldAt($i).perform($aSelector, $this.foldAt($i))); + n = $n.__int__(); + if (n >= 0) { + return this.copyRange($int_0, $SC.Integer(n - 1)); } + size = this.size().__int__(); - return $newList; - } + return this.copyRange($SC.Integer(size + n), $SC.Integer(size - 1)); + }, "n"); - spec.performBinaryOpOnSimpleNumber = function($aSelector, $aNumber, $adverb) { - return this.collect($SC.Function(function($item) { - return $aNumber.perform($aSelector, $item, $adverb); - })); - }; + spec.drop = fn(function($n) { + var n, size; - spec.performBinaryOpOnComplex = function($aSelector, $aComplex, $adverb) { - return this.collect($SC.Function(function($item) { - return $aComplex.perform($aSelector, $item, $adverb); - })); - }; + n = $n.__int__(); + size = this.size().__int__(); + if (n >= 0) { + return this.copyRange($n, $SC.Integer(size - 1)); + } - spec.asFraction = function($denominator, $fasterBetter) { - return this.collect($SC.Function(function($item) { - return $item.asFraction($denominator, $fasterBetter); + return this.copyRange($int_0, $SC.Integer(size + n - 1)); + }, "n"); + + spec.copyToEnd = fn(function($start) { + return this.copyRange($start, $SC.Integer(this.size().__int__() - 1)); + }, "start"); + + spec.copyFromStart = fn(function($end) { + return this.copyRange($int_0, $end); + }, "end"); + + spec.indexOf = fn(function($item) { + var $ret = null; + + this.do($SC.Function(function($elem, $i) { + if ($item === $elem) { + $ret = $i; + return 65535; + } })); - }; - // TODO: implements asPoint - // TODO: implements asRect + return $ret || $nil; + }, "item"); - spec.ascii = function() { - return this.collect($SC.Function(function($item) { - return $item.ascii(); + spec.indicesOfEqual = fn(function($item) { + var indices = []; + + this.do($SC.Function(function($elem, $i) { + if ($item === $elem) { + indices.push($i); + } })); - }; - spec.rate = function() { - if (this.size().__int__() === 1) { - return this.first().rate(); - } - return this.collect($SC.Function(function($item) { - return $item.rate(); - })).minItem(); - }; + return indices.length ? $SC.Array(indices) : $nil; + }, "item"); - spec.multiChannelPerform = function() { - var method; + spec.find = fn(function($sublist, $offset) { + var $subSize_1, $first, $index; + var size, offset, i, imax; - if (this.size() > 0) { - method = utils.getMethod("Object", "multiChannelPerform"); - return method.apply(this, arguments); + $subSize_1 = $sublist.size().__dec__(); + $first = $sublist.first(); + + size = this.size().__int__(); + offset = $offset.__int__(); + for (i = 0, imax = size - offset; i < imax; ++i) { + $index = $SC.Integer(i + offset); + if (BOOL(this.at($index) ["=="] ($first))) { + if (BOOL(this.copyRange($index, $index ["+"] ($subSize_1)) ["=="] ($sublist))) { + return $index; + } + } } - return this.class().new(); - }; + return $nil; + }, "sublist; offset=0"); - spec.multichannelExpandRef = utils.nop; + spec.findAll = fn(function($arr, $offset) { + var $this = this, $indices, $i; - spec.clip = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("clip") ].concat(slice.call(arguments)) - ); - }; + $indices = $nil; + $i = $int_0; - spec.wrap = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("wrap") ].concat(slice.call(arguments)) - ); - }; + while (($i = $this.find($arr, $offset)) !== $nil) { + $indices = $indices.add($i); + $offset = $i.__inc__(); + } - spec.fold = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("fold") ].concat(slice.call(arguments)) - ); - }; + return $indices; + }, "arr; offset=0"); - spec.linlin = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("linlin") ].concat(slice.call(arguments)) - ); - }; + spec.indexOfGreaterThan = fn(function($val) { + return this.detectIndex($SC.Function(function($item) { + return $SC.Boolean($item > $val); + })); + }, "val"); - spec.linexp = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("linexp") ].concat(slice.call(arguments)) - ); - }; + spec.indexIn = fn(function($val) { + var $i, $j; - spec.explin = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("explin") ].concat(slice.call(arguments)) - ); - }; + $j = this.indexOfGreaterThan($val); + if ($j === $nil) { + return this.size().__dec__(); + } + if ($j === $int_0) { + return $j; + } - spec.expexp = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("expexp") ].concat(slice.call(arguments)) - ); - }; + $i = $j.__dec__(); - spec.lincurve = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("lincurve") ].concat(slice.call(arguments)) - ); - }; + if ($val ["-"] (this.at($i)) < this.at($j) ["-"] ($val)) { + return $i; + } - spec.curvelin = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("curvelin") ].concat(slice.call(arguments)) - ); - }; + return $j; + }, "val"); - spec.bilin = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("bilin") ].concat(slice.call(arguments)) - ); - }; + spec.indexInBetween = fn(function($val) { + var $a, $b, $div, $i; - spec.biexp = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("biexp") ].concat(slice.call(arguments)) - ); - }; + if (BOOL(this.isEmpty())) { + return $nil; + } + $i = this.indexOfGreaterThan($val); - spec.moddif = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("moddif") ].concat(slice.call(arguments)) - ); - }; + if ($i === $nil) { + return this.size().__dec__(); + } + if ($i === $int_0) { + return $i; + } - spec.range = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("range") ].concat(slice.call(arguments)) - ); - }; + $a = this.at($i.__dec__()); + $b = this.at($i); + $div = $b ["-"] ($a); - spec.exprange = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("exprange") ].concat(slice.call(arguments)) - ); - }; + // if (BOOL($div ["=="] ($int_0))) { + // return $i; + // } - spec.curverange = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("curverange") ].concat(slice.call(arguments)) - ); - }; + return (($val ["-"] ($a)) ["/"] ($div)) ["+"] ($i.__dec__()); + }, "val"); - spec.unipolar = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("unipolar") ].concat(slice.call(arguments)) - ); - }; + spec.isSeries = fn(function($step) { + var $res = null; - spec.bipolar = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("bipolar") ].concat(slice.call(arguments)) - ); - }; + if (this.size() <= 1) { + return $true; + } + this.doAdjacentPairs($SC.Function(function($a, $b) { + var $diff = $b ["-"] ($a); + if ($step === $nil) { + $step = $diff; + } else if (BOOL($step ["!="] ($diff))) { + $res = $false; + return 65535; + } + })); - spec.lag = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("lag") ].concat(slice.call(arguments)) - ); - }; + return $res || $true; + }, "step"); - spec.lag2 = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("lag2") ].concat(slice.call(arguments)) - ); - }; + spec.resamp0 = fn(function($newSize) { + var $this = this, $factor; - spec.lag3 = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("lag3") ].concat(slice.call(arguments)) + $factor = ( + this.size().__dec__() + ) ["/"] ( + ($newSize.__dec__()).max($int_1) ); - }; - spec.lagud = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("lagud") ].concat(slice.call(arguments)) - ); - }; + return this.species().fill($newSize, $SC.Function(function($i) { + return $this.at($i ["*"] ($factor).round($SC.Float(1.0)).asInteger()); + })); + }, "newSize"); - spec.lag2ud = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("lag2ud") ].concat(slice.call(arguments)) - ); - }; - - spec.lag3ud = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("lag3ud") ].concat(slice.call(arguments)) - ); - }; - - spec.varlag = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("varlag") ].concat(slice.call(arguments)) - ); - }; + spec.resamp1 = fn(function($newSize) { + var $this = this, $factor; - spec.slew = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("slew") ].concat(slice.call(arguments)) + $factor = ( + this.size().__dec__() + ) ["/"] ( + ($newSize.__dec__()).max($int_1) ); - }; - spec.blend = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("blend") ].concat(slice.call(arguments)) - ); - }; + return this.species().fill($newSize, $SC.Function(function($i) { + return $this.blendAt($i ["*"] ($factor)); + })); + }, "newSize"); - spec.checkBadValues = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("checkBadValues") ].concat(slice.call(arguments)) - ); - }; + spec.remove = fn(function($item) { + var $index; - spec.prune = function() { - return this.multiChannelPerform.apply( - this, [ $SC.Symbol("prune") ].concat(slice.call(arguments)) - ); - }; + $index = this.indexOf($item); + if ($index !== $nil) { + return this.removeAt($index); + } - // TODO: implements minNyquist - // TODO: implements sort - // TODO: implements sortBy - // TODO: implements sortMap - // TODO: implements sortedMedian - // TODO: implements median - // TODO: implements quickSort - // TODO: implements order + return $nil; + }, "item"); - spec.swap = fn(function($i, $j) { - var $temp; + spec.removing = fn(function($item) { + var $coll; - $temp = this.at($i); - this.put($i, this.at($j)); - this.put($j, $temp); + $coll = this.copy(); + $coll.remove($item); - return this; - }, "i; j"); + return $coll; + }, "item"); - // TODO: implements quickSortRange - // TODO: implements mergeSort - // TODO: implements mergeSortTemp - // TODO: implements mergeTemp - // TODO: implements insertionSort - // TODO: implements insertionSortRange - // TODO: implements hoareMedian - // TODO: implements hoareFind - // TODO: implements hoarePartition - // TODO: implements $streamContensts - // TODO: implements $streamContenstsLimit + spec.take = fn(function($item) { + var $index; - spec.wrapAt = fn(function($index) { - $index = $index ["%"] (this.size()); - return this.at($index); - }, "index"); + $index = this.indexOf($item); + if ($index !== $nil) { + return this.takeAt($index); + } - spec.wrapPut = fn(function($index, $value) { - $index = $index ["%"] (this.size()); - return this.put($index, $value); - }, "index; value"); + return $nil; + }, "item"); - // TODO: implements reduce - // TODO: implements join - // TODO: implements nextTimeOnGrid - // TODO: implements asQuant - // TODO: implements schedBundleArrayOnClock - }); + spec.lastIndex = function() { + var size = this.size().__int__(); -})(sc); + if (size > 0) { + return $SC.Integer(size - 1); + } -// src/sc/lang/classlib/Collections/ArrayedCollection.js -(function(sc) { + return $nil; + }; - var slice = [].slice; - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; - var iterator = sc.lang.iterator; - var rand = sc.libs.random; - var mathlib = sc.libs.mathlib; + spec.middleIndex = function() { + var size = this.size().__int__(); - sc.lang.klass.refine("ArrayedCollection", function(spec, utils) { - var BOOL = utils.BOOL; - var $nil = utils.$nil; - var $int_0 = utils.$int_0; - var $int_1 = utils.$int_1; + if (size > 0) { + return $SC.Integer((size - 1) >> 1); + } - spec.valueOf = function() { - return this._.map(function(elem) { - return elem.valueOf(); - }); + return $nil; }; - spec.__elem__ = function(item) { - return item; - }; + spec.first = function() { + var size = this.size().__int__(); - spec._ThrowIfImmutable = function() { - if (this._immutable) { - throw new Error("Attempted write to immutable object."); + if (size > 0) { + return this.at($int_0); } - }; - - // TODO: implements $newClear - // TODO: implements indexedSize - spec.size = function() { - return $SC.Integer(this._.length); + return $nil; }; - // TODO: implements maxSize + spec.last = function() { + var size = this.size().__int__(); - spec.swap = fn(function($a, $b) { - var raw = this._; - var a, b, len, tmp; + if (size > 0) { + return this.at($SC.Integer(size - 1)); + } - this._ThrowIfImmutable(); + return $nil; + }; - a = $a.__int__(); - b = $b.__int__(); - len = raw.length; + spec.middle = function() { + var size = this.size().__int__(); - if (a < 0 || len <= a || b < 0 || len <= b) { - throw new Error("out of index"); + if (size > 0) { + return this.at($SC.Integer((size - 1) >> 1)); } - tmp = raw[b]; - raw[b] = raw[a]; - raw[a] = tmp; + return $nil; + }; - return this; - }, "a; b"); + spec.top = function() { + return this.last(); + }; - spec.at = fn(function($index) { - var i; + spec.putFirst = fn(function($obj) { + var size = this.size().__int__(); - if (Array.isArray($index._)) { - return $SC.Array($index._.map(function($index) { - i = $index.__int__(); - if (i < 0 || this._.length <= i) { - return $nil; - } - return this._[i]; - }, this)); + if (size > 0) { + return this.put($int_0, $obj); } - i = $index.__int__(); - - return this._[i] || $nil; - }, "index"); + return this; + }, "obj"); - spec.clipAt = fn(function($index) { - var i; + spec.putLast = fn(function($obj) { + var size = this.size().__int__(); - if (Array.isArray($index._)) { - return $SC.Array($index._.map(function($index) { - i = mathlib.clip_idx($index.__int__(), this._.length); - return this._[i]; - }, this)); + if (size > 0) { + return this.put($SC.Integer(size - 1), $obj); } - i = mathlib.clip_idx($index.__int__(), this._.length); - - return this._[i]; - }, "index"); + return this; + }, "obj"); - spec.wrapAt = fn(function($index) { - var i; + spec.obtain = fn(function($index, $default) { + var $res; - if (Array.isArray($index._)) { - return $SC.Array($index._.map(function($index) { - var i = mathlib.wrap_idx($index.__int__(), this._.length); - return this._[i]; - }, this)); + $res = this.at($index); + if ($res === $nil) { + $res = $default; } - i = mathlib.wrap_idx($index.__int__(), this._.length); - - return this._[i]; - }, "index"); + return $res; + }, "index; default"); - spec.foldAt = fn(function($index) { - var i; + spec.instill = fn(function($index, $item, $default) { + var $res; - if (Array.isArray($index._)) { - return $SC.Array($index._.map(function($index) { - var i = mathlib.fold_idx($index.__int__(), this._.length); - return this._[i]; - }, this)); + if ($index.__num__() >= this.size()) { + $res = this.extend($index.__inc__(), $default); + } else { + $res = this.copy(); } - i = mathlib.fold_idx($index.__int__(), this._.length); - - return this._[i]; - }, "index"); + return $res.put($index, $item); + }, "index; item; default"); - spec.put = fn(function($index, $item) { - var i; - - this._ThrowIfImmutable(); + spec.pairsDo = function($function) { + var $this = this, $int2 = $SC.Integer(2); - if (Array.isArray($index._)) { - $index._.forEach(function($index) { - var i = $index.__int__(); - if (i < 0 || this._.length <= i) { - throw new Error("out of index"); - } - this._[i] = this.__elem__($item); - }, this); - } else { - i = $index.__int__(); - if (i < 0 || this._.length <= i) { - throw new Error("out of index"); - } - this._[i] = this.__elem__($item); - } + $int_0.forBy(this.size() ["-"] ($int2), $int2, $SC.Function(function($i) { + return $function.value($this.at($i), $this.at($i.__inc__()), $i); + })); return this; - }, "index; item"); + }; - spec.clipPut = fn(function($index, $item) { - this._ThrowIfImmutable(); + spec.keysValuesDo = function($function) { + return this.pairsDo($function); + }; - if (Array.isArray($index._)) { - $index._.forEach(function($index) { - this._[mathlib.clip_idx($index.__int__(), this._.length)] = this.__elem__($item); - }, this); - } else { - this._[mathlib.clip_idx($index.__int__(), this._.length)] = this.__elem__($item); + spec.doAdjacentPairs = function($function) { + var $i; + var size, i, imax; + + size = this.size().__int__(); + for (i = 0, imax = size - 1; i < imax; ++i) { + $i = $SC.Integer(i); + $function.value(this.at($i), this.at($i.__inc__()), $i); } return this; - }, "index; item"); + }; - spec.wrapPut = fn(function($index, $item) { - this._ThrowIfImmutable(); + spec.separate = fn(function($function) { + var $this = this, $list, $sublist; - if (Array.isArray($index._)) { - $index._.forEach(function($index) { - this._[mathlib.wrap_idx($index.__int__(), this._.length)] = this.__elem__($item); - }, this); - } else { - this._[mathlib.wrap_idx($index.__int__(), this._.length)] = this.__elem__($item); + $list = $SC.Array(); + $sublist = this.species().new(); + this.doAdjacentPairs($SC.Function(function($a, $b, $i) { + $sublist = $sublist.add($a); + if (BOOL($function.value($a, $b, $i))) { + $list = $list.add($sublist); + $sublist = $this.species().new(); + } + })); + if (BOOL(this.notEmpty())) { + $sublist = $sublist.add(this.last()); } + $list = $list.add($sublist); - return this; - }, "index; item"); - - spec.foldPut = fn(function($index, $item) { - this._ThrowIfImmutable(); + return $list; + }, "function=true"); - if (Array.isArray($index._)) { - $index._.forEach(function($index) { - this._[mathlib.fold_idx($index.__int__(), this._.length)] = this.__elem__($item); - }, this); - } else { - this._[mathlib.fold_idx($index.__int__(), this._.length)] = this.__elem__($item); - } + spec.delimit = function($function) { + var $this = this, $list, $sublist; - return this; - }, "index; item"); + $list = $SC.Array(); + $sublist = this.species().new(); + this.do($SC.Function(function($item, $i) { + if (BOOL($function.value($item, $i))) { + $list = $list.add($sublist); + $sublist = $this.species().new(); + } else { + $sublist = $sublist.add($item); + } + })); + $list = $list.add($sublist); - spec.removeAt = fn(function($index) { - var raw = this._; - var index; + return $list; + }; - this._ThrowIfImmutable(); + spec.clump = fn(function($groupSize) { + var $this = this, $list, $sublist; - index = $index.__int__(); - if (index < 0 || raw.length <= index) { - throw new Error("out of index"); + $list = $SC.Array(); + $sublist = this.species().new($groupSize); + this.do($SC.Function(function($item) { + $sublist.add($item); + if ($sublist.size() >= $groupSize) { + $list.add($sublist); + $sublist = $this.species().new($groupSize); + } + })); + if ($sublist.size() > 0) { + $list = $list.add($sublist); } - return raw.splice(index, 1)[0]; - }, "index"); - - spec.takeAt = fn(function($index) { - var raw = this._; - var index, ret, instead; + return $list; + }, "groupSize"); - this._ThrowIfImmutable(); + spec.clumps = fn(function($groupSizeList) { + var $this = this, $list, $subSize, $sublist, i = 0; - index = $index.__int__(); - if (index < 0 || raw.length <= index) { - throw new Error("out of index"); + $list = $SC.Array(); + $subSize = $groupSizeList.at($int_0); + $sublist = this.species().new($subSize); + this.do($SC.Function(function($item) { + $sublist = $sublist.add($item); + if ($sublist.size() >= $subSize) { + $list = $list.add($sublist); + $subSize = $groupSizeList.wrapAt($SC.Integer(++i)); + $sublist = $this.species().new($subSize); + } + })); + if ($sublist.size() > 0) { + $list = $list.add($sublist); } - ret = raw[index]; - instead = raw.pop(); - if (index !== raw.length) { - raw[index] = instead; - } + return $list; + }, "groupSizeList"); - return ret; - }, "index"); + spec.curdle = fn(function($probability) { + return this.separate($SC.Function(function() { + return $probability.coin(); + })); + }, "probability"); - spec.indexOf = fn(function($item) { - var index; + spec.flatten = fn(function($numLevels) { + return this._flatten($numLevels.__num__()); + }, "numLevels=1"); - index = this._.indexOf($item); - return index === -1 ? $nil : $SC.Integer(index); - }, "item"); + spec._flatten = fn(function(numLevels) { + var $list; - spec.indexOfGreaterThan = fn(function($val) { - var raw = this._; - var val, i, imax = raw.length; + if (numLevels <= 0) { + return this; + } + numLevels = numLevels - 1; - val = $val.__num__(); - for (i = 0; i < imax; ++i) { - if (raw[i].__num__() > val) { - return $SC.Integer(i); + $list = this.species().new(); + this.do($SC.Function(function($item) { + if ($item._flatten) { + $list = $list.addAll($item._flatten(numLevels)); + } else { + $list = $list.add($item); } - } + })); - return $nil; - }, "val"); + return $list; + }, "numLevels"); - spec.takeThese = fn(function($func) { - var raw = this._; - var i = 0, $i; + spec.flat = function() { + return this._flat(this.species().new(this.flatSize())); + }; - $i = $SC.Integer(i); - while (i < raw.length) { - if (BOOL($func.value(raw[i], $i))) { - this.takeAt($i); + spec._flat = fn(function($list) { + this.do($SC.Function(function($item) { + if ($item._flat) { + $list = $item._flat($list); } else { - $i = $SC.Integer(++i); + $list = $list.add($item); } - } + })); + return $list; + }, "list"); - return this; + spec.flatIf = fn(function($func) { + return this._flatIf($func); }, "func"); - spec.replace = fn(function($find, $replace) { - var $index, $out, $array; - - this._ThrowIfImmutable(); + spec._flatIf = function($func) { + var $list; - $out = $SC.Array(); - $array = this; - $find = $find.asArray(); - $replace = $replace.asArray(); - $SC.Function(function() { - return ($index = $array.find($find)).notNil(); - }).while($SC.Function(function() { - $out = $out ["++"] ($array.keep($index)) ["++"] ($replace); - $array = $array.drop($index ["+"] ($find.size())); + $list = this.species().new(this.size()); + this.do($SC.Function(function($item, $i) { + if ($item._flatIf && BOOL($func.value($item, $i))) { + $list = $list.addAll($item._flatIf($func)); + } else { + $list = $list.add($item); + } })); - return $out ["++"] ($array); - }, "find; replace"); - - spec.slotSize = function() { - return this.size(); - }; - - spec.slotAt = function($index) { - return this.at($index); + return $list; }; - spec.slotPut = function($index, $value) { - return this.put($index, $value); - }; + spec.flop = function() { + var $this = this, $list, $size, $maxsize; - spec.slotKey = function($index) { - return $index; - }; + $size = this.size(); + $maxsize = $int_0; + this.do($SC.Function(function($sublist) { + var $sz; + if (BOOL($sublist.isSequenceableCollection())) { + $sz = $sublist.size(); + } else { + $sz = $int_1; + } + if ($sz > $maxsize) { + $maxsize = $sz; + } + })); - spec.slotIndex = utils.alwaysReturn$nil; + $list = this.species().fill($maxsize, $SC.Function(function() { + return $this.species().new($size); + })); - spec.getSlots = function() { - return this.copy(); - }; + this.do($SC.Function(function($isublist) { + if (BOOL($isublist.isSequenceableCollection())) { + $list.do($SC.Function(function($jsublist, $j) { + $jsublist.add($isublist.wrapAt($j)); + })); + } else { + $list.do($SC.Function(function($jsublist) { + $jsublist.add($isublist); + })); + } + })); - spec.setSlots = function($array) { - return this.overWrite($array); + return $list; }; - spec.atModify = fn(function($index, $function) { - this.put($index, $function.value(this.at($index), $index)); - return this; - }, "index; function"); - - spec.atInc = fn(function($index, $inc) { - this.put($index, this.at($index) ["+"] ($inc)); - return this; - }, "index; inc=1"); + spec.flopWith = fn(function($func) { + var $this = this, $maxsize; - spec.atDec = fn(function($index, $dec) { - this.put($index, this.at($index) ["-"] ($dec)); - return this; - }, "index; dec=1"); + $maxsize = this.maxValue($SC.Function(function($sublist) { + if (BOOL($sublist.isSequenceableCollection())) { + return $sublist.size(); + } + return $int_1; + })); - spec.isArray = utils.alwaysReturn$true; - spec.asArray = utils.nop; + return this.species().fill($maxsize, $SC.Function(function($i) { + return $func.valueArray($this.collect($SC.Function(function($sublist) { + if (BOOL($sublist.isSequenceableCollection())) { + return $sublist.wrapAt($i); + } else { + return $sublist; + } + }))); + })); + }, "func"); - spec.copyRange = fn(function($start, $end) { - var start, end, instance, raw; + // TODO: implements flopTogether + // TODO: implements flopDeep + // TODO: implements wrapAtDepth + // TODO: implements unlace + // TODO: implements integrate + // TODO: implements differentiate + // TODO: implements convertDigits + // TODO: implements hammingDistance + // TODO: implements degreeToKey + // TODO: implements keyToDegree + // TODO: implements nearestInScale + // TODO: implements nearestInList + // TODO: implements transposeKey + // TODO: implements mode + // TODO: implements performDegreeToKey + // TODO: implements performNearestInList + // TODO: implements performNearestInScale + // TODO: implements convertRhythm + // TODO: implements sumRhythmDivisions + // TODO: implements convertOneRhythm - if ($start === $nil) { - start = 0; - } else { - start = $start.__int__(); - } - if ($end === $nil) { - end = this._.length; - } else { - end = $end.__int__(); - } - raw = this._.slice(start, end + 1); + spec.isSequenceableCollection = utils.alwaysReturn$true; - instance = new this.__Spec(); - instance._ = raw; - return instance; - }, "start; end"); + spec.containsSeqColl = function() { + return this.any($SC.Function(function($_) { + return $_.isSequenceableCollection(); + })); + }; - spec.copySeries = fn(function($first, $second, $last) { - var i, first, second, last, step, instance, raw; + spec.neg = function() { + return this.performUnaryOp($SC.Symbol("neg")); + }; - raw = []; - if ($first === $nil) { - first = 0; - } else { - first = $first.__int__(); - } - if ($second === $nil) { - second = first + 1; - } else { - second = $second.__int__(); - } - if ($last === $nil) { - last = Infinity; - } else { - last = $last.__int__(); - } - last = Math.max(0, Math.min(last, this._.length - 1)); - step = second - first; + spec.bitNot = function() { + return this.performUnaryOp($SC.Symbol("bitNot")); + }; - if (step > 0) { - for (i = first; i <= last; i += step) { - raw.push(this._[i]); - } - } else if (step < 0) { - for (i = first; i >= last; i += step) { - raw.push(this._[i]); - } - } + spec.abs = function() { + return this.performUnaryOp($SC.Symbol("abs")); + }; - instance = new this.__Spec(); - instance._ = raw; - return instance; - }, "first; second; last"); + spec.ceil = function() { + return this.performUnaryOp($SC.Symbol("ceil")); + }; - spec.putSeries = fn(function($first, $second, $last, $value) { - var i, first, second, last, step; + spec.floor = function() { + return this.performUnaryOp($SC.Symbol("floor")); + }; - this._ThrowIfImmutable(); + spec.frac = function() { + return this.performUnaryOp($SC.Symbol("frac")); + }; - if ($first === $nil) { - first = 0; - } else { - first = $first.__int__(); - } - if ($second === $nil) { - second = first + 1; - } else { - second = $second.__int__(); - } - if ($last === $nil) { - last = Infinity; - } else { - last = $last.__int__(); - } - last = Math.max(0, Math.min(last, this._.length - 1)); - step = second - first; + spec.sign = function() { + return this.performUnaryOp($SC.Symbol("sign")); + }; - $value = this.__elem__($value); + spec.squared = function() { + return this.performUnaryOp($SC.Symbol("squared")); + }; - if (step > 0) { - for (i = first; i <= last; i += step) { - this._[i] = $value; - } - } else if (step < 0) { - for (i = first; i >= last; i += step) { - this._[i] = $value; - } - } + spec.cubed = function() { + return this.performUnaryOp($SC.Symbol("cubed")); + }; - return this; - }, "first; second; last; value"); + spec.sqrt = function() { + return this.performUnaryOp($SC.Symbol("sqrt")); + }; - spec.add = fn(function($item) { - this._ThrowIfImmutable(); - this._.push(this.__elem__($item)); + spec.exp = function() { + return this.performUnaryOp($SC.Symbol("exp")); + }; - return this; - }, "item"); + spec.reciprocal = function() { + return this.performUnaryOp($SC.Symbol("reciprocal")); + }; - spec.addAll = fn(function($aCollection) { - var $this = this; + spec.midicps = function() { + return this.performUnaryOp($SC.Symbol("midicps")); + }; - this._ThrowIfImmutable(); + spec.cpsmidi = function() { + return this.performUnaryOp($SC.Symbol("cpsmidi")); + }; - if ($aCollection.isSequenceableCollection().valueOf()) { - $aCollection.do($SC.Function(function($item) { - $this._.push($this.__elem__($item)); - })); - } else { - this.add($aCollection); - } + spec.midiratio = function() { + return this.performUnaryOp($SC.Symbol("midiratio")); + }; - return this; - }, "aCollection"); + spec.ratiomidi = function() { + return this.performUnaryOp($SC.Symbol("ratiomidi")); + }; - spec.putEach = fn(function($keys, $values) { - var keys, values, i, imax; + spec.ampdb = function() { + return this.performUnaryOp($SC.Symbol("ampdb")); + }; - this._ThrowIfImmutable(); + spec.dbamp = function() { + return this.performUnaryOp($SC.Symbol("dbamp")); + }; - $keys = $keys.asArray(); - $values = $values.asArray(); + spec.octcps = function() { + return this.performUnaryOp($SC.Symbol("octcps")); + }; - keys = $keys._; - values = $values._; - for (i = 0, imax = keys.length; i < imax; ++i) { - this.put(keys[i], this.__elem__(values[i % values.length])); - } + spec.cpsoct = function() { + return this.performUnaryOp($SC.Symbol("cpsoct")); + }; - return this; - }, "keys; values"); + spec.log = function() { + return this.performUnaryOp($SC.Symbol("log")); + }; - spec.extend = fn(function($size, $item) { - var instance, raw, size, i; + spec.log2 = function() { + return this.performUnaryOp($SC.Symbol("log2")); + }; - raw = this._.slice(); - size = $size.__int__(); - if (raw.length > size) { - raw.splice(size); - } else if (raw.length < size) { - for (i = size - raw.length; i--; ) { - raw.push(this.__elem__($item)); - } - } + spec.log10 = function() { + return this.performUnaryOp($SC.Symbol("log10")); + }; - instance = new this.__Spec(); - instance._ = raw; - return instance; - }, "size; item"); + spec.sin = function() { + return this.performUnaryOp($SC.Symbol("sin")); + }; - spec.insert = fn(function($index, $item) { - var index; + spec.cos = function() { + return this.performUnaryOp($SC.Symbol("cos")); + }; - this._ThrowIfImmutable(); + spec.tan = function() { + return this.performUnaryOp($SC.Symbol("tan")); + }; - index = Math.max(0, $index.__int__()); - this._.splice(index, 0, this.__elem__($item)); + spec.asin = function() { + return this.performUnaryOp($SC.Symbol("asin")); + }; - return this; - }, "index; item"); + spec.acos = function() { + return this.performUnaryOp($SC.Symbol("acos")); + }; - spec.move = function($fromIndex, $toIndex) { - return this.insert($toIndex, this.removeAt($fromIndex)); + spec.atan = function() { + return this.performUnaryOp($SC.Symbol("atan")); }; - spec.addFirst = fn(function($item) { - var instance, raw; + spec.sinh = function() { + return this.performUnaryOp($SC.Symbol("sinh")); + }; - raw = this._.slice(); - raw.unshift(this.__elem__($item)); + spec.cosh = function() { + return this.performUnaryOp($SC.Symbol("cosh")); + }; - instance = new this.__Spec(); - instance._ = raw; - return instance; - }, "item"); + spec.tanh = function() { + return this.performUnaryOp($SC.Symbol("tanh")); + }; - spec.addIfNotNil = fn(function($item) { - if ($item === $nil) { - return this; - } + spec.rand = function() { + return this.performUnaryOp($SC.Symbol("rand")); + }; - return this.addFirst(this.__elem__($item)); - }, "item"); + spec.rand2 = function() { + return this.performUnaryOp($SC.Symbol("rand2")); + }; - spec.pop = function() { - if (this._.length === 0) { - return $nil; - } - this._ThrowIfImmutable(); - return this._.pop(); + spec.linrand = function() { + return this.performUnaryOp($SC.Symbol("linrand")); }; - spec["++"] = function($anArray) { - var instance, raw; + spec.bilinrand = function() { + return this.performUnaryOp($SC.Symbol("bilinrand")); + }; - raw = this._.slice(); + spec.sum3rand = function() { + return this.performUnaryOp($SC.Symbol("sum3rand")); + }; - instance = new this.__Spec(); - instance._ = raw; - if ($anArray !== $nil) { - instance.addAll($anArray); - } - return instance; + spec.distort = function() { + return this.performUnaryOp($SC.Symbol("distort")); }; - // TODO: implements overWrite - // TODO: implements grow - // TODO: implements growClear + spec.softclip = function() { + return this.performUnaryOp($SC.Symbol("softclip")); + }; - spec.seriesFill = fn(function($start, $step) { - var i, imax; + spec.coin = function() { + return this.performUnaryOp($SC.Symbol("coin")); + }; - for (i = 0, imax = this._.length; i < imax; ++i) { - this.put($SC.Integer(i), $start); - $start = $start ["+"] ($step); - } + spec.even = function() { + return this.performUnaryOp($SC.Symbol("even")); + }; - return this; - }, "start; step"); + spec.odd = function() { + return this.performUnaryOp($SC.Symbol("odd")); + }; - spec.fill = fn(function($value) { - var raw, i, imax; + spec.isPositive = function() { + return this.performUnaryOp($SC.Symbol("isPositive")); + }; - this._ThrowIfImmutable(); + spec.isNegative = function() { + return this.performUnaryOp($SC.Symbol("isNegative")); + }; - $value = this.__elem__($value); + spec.isStrictlyPositive = function() { + return this.performUnaryOp($SC.Symbol("isStrictlyPositive")); + }; - raw = this._; - for (i = 0, imax = raw.length; i < imax; ++i) { - raw[i] = $value; - } + spec.rectWindow = function() { + return this.performUnaryOp($SC.Symbol("rectWindow")); + }; - return this; - }, "value"); + spec.hanWindow = function() { + return this.performUnaryOp($SC.Symbol("hanWindow")); + }; - spec.do = function($function) { - iterator.execute( - iterator.array$do(this), - $function - ); - return this; + spec.welWindow = function() { + return this.performUnaryOp($SC.Symbol("welWindow")); }; - spec.reverseDo = function($function) { - iterator.execute( - iterator.array$reverseDo(this), - $function - ); - return this; + spec.triWindow = function() { + return this.performUnaryOp($SC.Symbol("triWindow")); }; - spec.reverse = function() { - var $res = this.copy(); + spec.scurve = function() { + return this.performUnaryOp($SC.Symbol("scurve")); + }; - $res._.reverse(); + spec.ramp = function() { + return this.performUnaryOp($SC.Symbol("ramp")); + }; - return $res; + spec.asFloat = function() { + return this.performUnaryOp($SC.Symbol("asFloat")); }; - spec.windex = function() { - var raw = this._; - var x, r, i, imax; + spec.asInteger = function() { + return this.performUnaryOp($SC.Symbol("asInteger")); + }; - // <-- _ArrayWindex --> - x = 0; - r = rand.next(); - for (i = 0, imax = raw.length; i < imax; ++i) { - x += raw[i].__num__(); - if (x >= r) { - return $SC.Integer(i); - } - } + spec.nthPrime = function() { + return this.performUnaryOp($SC.Symbol("nthPrime")); + }; - return $int_0; + spec.prevPrime = function() { + return this.performUnaryOp($SC.Symbol("prevPrime")); }; - spec.normalizeSum = function() { - return this ["*"] (this.sum().reciprocal()); + spec.nextPrime = function() { + return this.performUnaryOp($SC.Symbol("nextPrime")); }; - spec.normalize = fn(function($min, $max) { - var $minItem, $maxItem; + spec.indexOfPrime = function() { + return this.performUnaryOp($SC.Symbol("indexOfPrime")); + }; - $minItem = this.minItem(); - $maxItem = this.maxItem(); - return this.collect($SC.Function(function($el) { - return $el.linlin($minItem, $maxItem, $min, $max); - })); - }, "min=0.0; max=1.0"); + spec.real = function() { + return this.performUnaryOp($SC.Symbol("real")); + }; - // TODO: implements asciiPlot - // TODO: implements perfectShuffle - // TODO: implements performInPlace + spec.imag = function() { + return this.performUnaryOp($SC.Symbol("imag")); + }; - spec.clipExtend = fn(function($length) { - var last = this._[this._.length - 1] || $nil; - return this.extend($length, last); - }, "length"); + spec.magnitude = function() { + return this.performUnaryOp($SC.Symbol("magnitude")); + }; - spec.rank = function() { - return $int_1 ["+"] (this.first().rank()); + spec.magnitudeApx = function() { + return this.performUnaryOp($SC.Symbol("magnitudeApx")); }; - spec.shape = function() { - return $SC.Array([ this.size() ]) ["++"] (this.at($int_0).shape()); + spec.phase = function() { + return this.performUnaryOp($SC.Symbol("phase")); }; - spec.reshape = function() { - var $result; - var shape, size, i, imax; + spec.angle = function() { + return this.performUnaryOp($SC.Symbol("angle")); + }; - shape = slice.call(arguments); + spec.rho = function() { + return this.performUnaryOp($SC.Symbol("rho")); + }; - size = 1; - for (i = 0, imax = shape.length; i < imax; ++i) { - size *= shape[i].__int__(); - } + spec.theta = function() { + return this.performUnaryOp($SC.Symbol("theta")); + }; - $result = this.flat().wrapExtend($SC.Integer(size)); - for (i = imax - 1; i >= 1; --i) { - $result = $result.clump(shape[i]); - } + spec.degrad = function() { + return this.performUnaryOp($SC.Symbol("degrad")); - return $result; + }; + spec.raddeg = function() { + return this.performUnaryOp($SC.Symbol("raddeg")); }; - spec.reshapeLike = fn(function($another, $indexing) { - var $index, $flat; - - $index = $int_0; - $flat = this.flat(); + spec["+"] = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("+"), $aNumber, $adverb); + }; - return $another.deepCollect($SC.Integer(0x7FFFFFFF), $SC.Function(function() { - var $item = $flat.perform($indexing, $index); - $index = $index.__inc__(); - return $item; - })); - }, "another; indexing=\\wrapAt"); + spec["-"] = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("-"), $aNumber, $adverb); + }; - // TODO: implements deepCollect - // TODO: implements deepDo + spec["*"] = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("*"), $aNumber, $adverb); + }; - spec.unbubble = fn(function($depth, $levels) { - if ($depth.__num__() <= 0) { - if (this.size().__int__() > 1) { - return this; - } - if ($levels.__int__() <= 1) { - return this.at($int_0); - } - return this.at($int_0).unbubble($depth, $levels.__dec__()); - } + spec["/"] = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("/"), $aNumber, $adverb); + }; - return this.collect($SC.Function(function($item) { - return $item.unbubble($depth.__dec__()); - })); - }, "depth=0; levels=1"); + spec.div = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("div"), $aNumber, $adverb); + }; - spec.bubble = fn(function($depth, $levels) { - if ($depth.__int__() <= 0) { - if ($levels.__int__() <= 1) { - return $SC.Array([ this ]); - } - return $SC.Array([ this.bubble($depth, $levels.__dec__()) ]); - } + spec.mod = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("mod"), $aNumber, $adverb); + }; - return this.collect($SC.Function(function($item) { - return $item.bubble($depth.__dec__(), $levels); - })); - }, "depth=0; levels=1"); + spec.pow = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("pow"), $aNumber, $adverb); + }; - spec.slice = fn(function($$cuts) { - var $firstCut, $list; - var cuts_size, cuts; + spec.min = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("min"), $aNumber, $adverb); + }; - cuts_size = $$cuts.size().__int__(); - if (cuts_size === 0) { - return this.copy(); - } + spec.max = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("max"), $aNumber, $adverb); + }; - $firstCut = $$cuts.at($int_0); - if ($firstCut === $nil) { - $list = this.copy(); - } else { - $list = this.at($firstCut.asArray()); - } + spec["<"] = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("<"), $aNumber, $adverb); + }; - if (cuts_size === 1) { - return $list.unbubble(); - } + spec["<="] = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("<="), $aNumber, $adverb); + }; - cuts = $$cuts._.slice(1); - return $list.collect($SC.Function(function($item) { - return $item.slice.apply($item, cuts); - })).unbubble(); - }, "*cuts"); + spec[">"] = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol(">"), $aNumber, $adverb); + }; - spec.$iota = function() { - var $a; - var args, product, i, imax, a; + spec[">="] = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol(">="), $aNumber, $adverb); + }; - args = arguments; + spec.bitAnd = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("bitAnd"), $aNumber, $adverb); + }; - product = 1; - for (i = 0, imax = args.length; i < imax; ++i) { - product *= args[i].__int__(); - } + spec.bitOr = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("bitOr"), $aNumber, $adverb); + }; - a = new Array(product); - for (i = 0; i < product; ++i) { - a[i] = $SC.Integer(i); - } + spec.bitXor = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("bitXor"), $aNumber, $adverb); + }; - $a = $SC.Array(a); - return $a.reshape.apply($a, args); + spec.bitHammingDistance = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("bitHammingDistance"), $aNumber, $adverb); }; - // TODO: implements asRandomTable - // TODO: implements tableRand - // TODO: implements msgSize - // TODO: implements bundleSize - // TODO: implements clumpBundles + spec.lcm = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("lcm"), $aNumber, $adverb); + }; - spec.includes = function($item) { - return $SC.Boolean(this._.indexOf($item) !== -1); + spec.gcd = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("gcd"), $aNumber, $adverb); }; - }); - sc.lang.klass.refine("RawArray", function(spec, utils) { - spec.archiveAsCompileString = utils.alwaysReturn$true; - spec.archiveAsObject = utils.alwaysReturn$true; - spec.rate = function() { - return $SC.Symbol("scalar"); + spec.round = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("round"), $aNumber, $adverb); }; - // TODO: implements readFromStream - // TODO: implements powerset - }); + spec.roundUp = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("roundUp"), $aNumber, $adverb); + }; -})(sc); + spec.trunc = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("trunc"), $aNumber, $adverb); + }; -// src/sc/lang/classlib/Collections/String.js -(function(sc) { + spec.atan2 = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("atan2"), $aNumber, $adverb); + }; - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; + spec.hypot = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("hypot"), $aNumber, $adverb); + }; - sc.lang.klass.refine("String", function(spec, utils) { - var $nil = utils.$nil; - var $false = utils.$false; + spec.hypotApx = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("hypotApx"), $aNumber, $adverb); + }; - spec.__str__ = function() { - return this.valueOf(); + spec.leftShift = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("leftShift"), $aNumber, $adverb); }; - spec.__elem__ = function($item) { - if ($item.__tag !== 1028) { - throw new TypeError("Wrong type."); - } - return $item; + spec.rightShift = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("rightShift"), $aNumber, $adverb); }; - spec.valueOf = function() { - return this._.map(function(elem) { - return elem.__str__(); - }).join(""); + spec.unsignedRightShift = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("unsignedRightShift"), $aNumber, $adverb); }; - spec.toString = function() { - return this.valueOf(); + spec.ring1 = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("ring1"), $aNumber, $adverb); }; - // TODO: implements unixCmdActions - // TODO: implements unixCmdActions_ + spec.ring2 = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("ring2"), $aNumber, $adverb); + }; - spec.$new = function() { - throw new Error("String.new is illegal, should use literal."); + spec.ring3 = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("ring3"), $aNumber, $adverb); }; - // TODO: implements $initClass - // TODO: implements $doUnixCmdAction - // TODO: implements unixCmd - // TODO: implements unixCmdGetStdOut + spec.ring4 = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("ring4"), $aNumber, $adverb); + }; - spec.asSymbol = function() { - return $SC.Symbol(this.__str__()); + spec.difsqr = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("difsqr"), $aNumber, $adverb); }; - spec.asInteger = function() { - var m = /^[-+]?\d+/.exec(this.__str__()); - return $SC.Integer(m ? m[0]|0 : 0); + spec.sumsqr = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("sumsqr"), $aNumber, $adverb); }; - spec.asFloat = function() { - var m = /^[-+]?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?/.exec(this.__str__()); - return $SC.Float(m ? +m[0] : 0); + spec.sqrsum = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("sqrsum"), $aNumber, $adverb); }; - spec.ascii = function() { - var raw = this.__str__(); - var a, i, imax; + spec.sqrdif = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("sqrdif"), $aNumber, $adverb); + }; - a = new Array(raw.length); - for (i = 0, imax = a.length; i < imax; ++i) { - a[i] = $SC.Integer(raw.charCodeAt(i)); - } + spec.absdif = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("absdif"), $aNumber, $adverb); + }; - return $SC.Array(a); + spec.thresh = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("thresh"), $aNumber, $adverb); }; - // TODO: implements stripRTF - // TODO: implements stripHTML - // TODO: implements $scDir - - spec.compare = fn(function($aString, $ignoreCase) { - var araw, braw, length, i, a, b, cmp, func; - - if ($aString.__tag !== 1034) { - return $nil; - } - - araw = this._; - braw = $aString._; - length = Math.min(araw.length, braw.length); - - if ($ignoreCase.__bool__()) { - func = function(ch) { - return ch.toLowerCase(); - }; - } else { - func = function(ch) { - return ch; - }; - } - for (i = 0; i < length; i++) { - a = func(araw[i]._).charCodeAt(0); - b = func(braw[i]._).charCodeAt(0); - cmp = a - b; - if (cmp !== 0) { - return $SC.Integer(cmp < 0 ? -1 : +1); - } - } - - if (araw.length < braw.length) { - cmp = -1; - } else if (araw.length > braw.length) { - cmp = 1; - } - - return $SC.Integer(cmp); - }, "aString; ignoreCase=false"); - - spec["<"] = function($aString) { - return $SC.Boolean( - this.compare($aString, $false).valueOf() < 0 - ); + spec.amclip = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("amclip"), $aNumber, $adverb); }; - spec[">"] = function($aString) { - return $SC.Boolean( - this.compare($aString, $false).valueOf() > 0 - ); + spec.scaleneg = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("scaleneg"), $aNumber, $adverb); }; - spec["<="] = function($aString) { - return $SC.Boolean( - this.compare($aString, $false).valueOf() <= 0 - ); + spec.clip2 = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("clip2"), $aNumber, $adverb); }; - spec[">="] = function($aString) { - return $SC.Boolean( - this.compare($aString, $false).valueOf() >= 0 - ); + spec.fold2 = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("fold2"), $aNumber, $adverb); }; - spec["=="] = function($aString) { - return $SC.Boolean( - this.compare($aString, $false).valueOf() === 0 - ); + spec.wrap2 = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("wrap2"), $aNumber, $adverb); }; - spec["!="] = function($aString) { - return $SC.Boolean( - this.compare($aString, $false).valueOf() !== 0 - ); + spec.excess = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("excess"), $aNumber, $adverb); }; - // TODO: implements hash - - spec.performBinaryOpOnSimpleNumber = function($aSelector, $aNumber) { - return $aNumber.asString().perform($aSelector, this); + spec.firstArg = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("firstArg"), $aNumber, $adverb); }; - spec.performBinaryOpOnComplex = function($aSelector, $aComplex) { - return $aComplex.asString().perform($aSelector, this); + spec.rrand = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("rrand"), $aNumber, $adverb); }; - spec.multiChannelPerform = function() { - throw new Error("String:multiChannelPerform. Cannot expand strings."); + spec.exprand = function($aNumber, $adverb) { + return this.performBinaryOp($SC.Symbol("exprand"), $aNumber, $adverb); }; - spec.isString = utils.alwaysReturn$true; - - spec.asString = utils.nop; - - spec.asCompileString = function() { - return $SC.String("\"" + this.__str__() + "\""); + spec.performUnaryOp = function($aSelector) { + return this.collect($SC.Function(function($item) { + return $item.perform($aSelector); + })); }; - spec.species = function() { - return $SC("String"); + spec.performBinaryOp = function($aSelector, $theOperand, $adverb) { + return $theOperand.performBinaryOpOnSeqColl($aSelector, this, $adverb); }; - // TODO: implements postln - // TODO: implements post - // TODO: implements postcln - // TODO: implements postc - // TODO: implements postf - // TODO: implements format - // TODO: implements matchRegexp - // TODO: implements fformat - // TODO: implements die - // TODO: implements error - // TODO: implements warn - // TODO: implements inform + spec.performBinaryOpOnSeqColl = function($aSelector, $theOperand, $adverb) { + var adverb; - spec["++"] = function($anObject) { - return $SC.String( - this.toString() + $anObject.asString().toString() - ); - }; + if ($adverb === $nil || !$adverb) { + return _performBinaryOpOnSeqColl_adverb_nil( + this, $aSelector, $theOperand + ); + } + if (BOOL($adverb.isInteger())) { + return _performBinaryOpOnSeqColl_adverb_int( + this, $aSelector, $theOperand, $adverb.valueOf() + ); + } - spec["+"] = function($anObject) { - return $SC.String( - this.toString() + " " + $anObject.asString().toString() + adverb = $adverb.__sym__(); + if (adverb === "t") { + return _performBinaryOpOnSeqColl_adverb_t( + this, $aSelector, $theOperand + ); + } + if (adverb === "x") { + return _performBinaryOpOnSeqColl_adverb_x( + this, $aSelector, $theOperand + ); + } + if (adverb === "s") { + return _performBinaryOpOnSeqColl_adverb_s( + this, $aSelector, $theOperand + ); + } + if (adverb === "f") { + return _performBinaryOpOnSeqColl_adverb_f( + this, $aSelector, $theOperand + ); + } + + throw new Error( + "unrecognized adverb: '" + adverb + "' for operator '" + String($aSelector) + "'" ); }; - // TODO: implements catArgs - // TODO: implements scatArgs - // TODO: implements ccatArgs - // TODO: implements catList - // TODO: implements scatList - // TODO: implements ccatList - // TODO: implements split - // TODO: implements containsStringAt - // TODO: implements icontainsStringAt - // TODO: implements contains - // TODO: implements containsi - // TODO: implements findRegexp - // TODO: implements findAllRegexp - // TODO: implements find - // TODO: implements findBackwards - // TODO: implements endsWith - // TODO: implements beginsWith - // TODO: implements findAll - // TODO: implements replace - // TODO: implements escapeChar - // TODO: implements shellQuote - // TODO: implements quote - // TODO: implements tr - // TODO: implements insert - // TODO: implements wrapExtend - // TODO: implements zeroPad - // TODO: implements padLeft - // TODO: implements padRight - // TODO: implements underlined - // TODO: implements scramble - // TODO: implements rotate - // TODO: implements compile - // TODO: implements interpret - // TODO: implements interpretPrint - // TODO: implements $readNew - // TODO: implements printOn - // TODO: implements storeOn - // TODO: implements inspectorClass - // TODO: implements standardizePath - // TODO: implements realPath - // TODO: implements withTrailingSlash - // TODO: implements withoutTrailingSlash - // TODO: implements absolutePath - // TODO: implements pathMatch - // TODO: implements load - // TODO: implements loadPaths - // TODO: implements loadRelative - // TODO: implements resolveRelative - // TODO: implements include - // TODO: implements exclude - // TODO: implements basename - // TODO: implements dirname - // TODO: implements splittext - // TODO: implements +/+ - // TODO: implements asRelativePath - // TODO: implements asAbsolutePath - // TODO: implements systemCmd - // TODO: implements gethostbyname - // TODO: implements getenv - // TODO: implements setenv - // TODO: implements unsetenv - // TODO: implements codegen_UGenCtorArg - // TODO: implements ugenCodeString - // TODO: implements asSecs - // TODO: implements speak - // TODO: implements toLower - // TODO: implements toUpper - // TODO: implements mkdir - // TODO: implements parseYAML - // TODO: implements parseYAMLFile - }); + function _performBinaryOpOnSeqColl_adverb_nil($this, $aSelector, $theOperand) { + var $size, $newList, $i; + var size, i; -})(sc); + $size = $this.size().max($theOperand.size()); + $newList = $this.species().new($size); -// src/sc/lang/classlib/Collections/Set.js -(function(sc) { + size = $size.__int__(); + for (i = 0; i < size; ++i) { + $i = $SC.Integer(i); + $newList.add( + $theOperand.wrapAt($i).perform($aSelector, $this.wrapAt($i)) + ); + } - function SCSet() { - this.__initializeWith__("Collection"); - } + return $newList; + } - sc.lang.klass.define(SCSet, "Set : Collection", function() { - // TODO: implements species - // TODO: implements copy - // TODO: implements do - // TODO: implements clear - // TODO: implements makeEmpty - // TODO: implements includes - // TODO: implements findMatch - // TODO: implements add - // TODO: implements remove - // TODO: implements choose - // TODO: implements pop - // TODO: implements powerset - // TODO: implements unify - // TODO: implements sect - // TODO: implements union - // TODO: implements difference - // TODO: implements symmetricDifference - // TODO: implements isSubsetOf - // TODO: implements initSet - // TODO: implements putCheck - // TODO: implements fullCheck - // TODO: implements grow - // TODO: implements noCheckAdd - // TODO: implements scanFor - // TODO: implements fixCollisionsFrom - // TODO: implements keyAt - // TODO: implements asSet - }); + function _performBinaryOpOnSeqColl_adverb_int($this, $aSelector, $theOperand, adverb) { + var $size, $newList, $i; + var size, i; -})(sc); + if (adverb === 0) { + $size = $this.size().max($theOperand.size()); + $newList = $this.species().new($size); -// src/sc/lang/classlib/Collections/Dictionary.js -(function(sc) { + size = $size.__int__(); + for (i = 0; i < size; ++i) { + $i = $SC.Integer(i); + $newList.add($theOperand.wrapAt($i).perform($aSelector, $this.wrapAt($i))); + } - function SCDictionary() { - this.__initializeWith__("Set"); - this._ = {}; - } + } else if (adverb > 0) { - sc.lang.klass.define(SCDictionary, "Dictionary : Set", function() { - // TODO: implements $newFrom - // TODO: implements at - // TODO: implements atFail - // TODO: implements matchAt - // TODO: implements trueAt - // TODO: implements add - // TODO: implements put - // TODO: implements putAll - // TODO: implements putPairs - // TODO: implements getPairs - // TODO: implements associationAt - // TODO: implements associationAtFail - // TODO: implements keys - // TODO: implements values - // TODO: implements includes - // TODO: implements includesKey - // TODO: implements removeAt - // TODO: implements removeAtFail - // TODO: implements remove - // TODO: implements removeFail - // TODO: implements keysValuesDo - // TODO: implements keysValuesChange - // TODO: implements do - // TODO: implements keysDo - // TODO: implements associationsDo - // TODO: implements pairsDo - // TODO: implements collect - // TODO: implements select - // TODO: implements reject - // TODO: implements invert - // TODO: implements merge - // TODO: implements blend - // TODO: implements findKeyForValue - // TODO: implements sortedKeysValuesDo - // TODO: implements choose - // TODO: implements order - // TODO: implements powerset - // TODO: implements transformEvent - // TODO: implements embedInStream - // TODO: implements asSortedArray - // TODO: implements asKeyValuePairs - // TODO: implements keysValuesArrayDo - // TODO: implements grow - // TODO: implements fixCollisionsFrom - // TODO: implements scanFor - // TODO: implements storeItemsOn - // TODO: implements printItemsOn - }); + $newList = $theOperand.collect($SC.Function(function($item) { + return $item.perform($aSelector, $this, $SC.Integer(adverb - 1)); + })); - function SCIdentityDictionary() { - this.__initializeWith__("Dictionary"); - } + } else { - sc.lang.klass.define(SCIdentityDictionary, "IdentityDictionary : Dictionary", function() { - // TODO: implements at - // TODO: implements put - // TODO: implements putGet - // TODO: implements includesKey - // TODO: implements findKeyForValue - // TODO: implements scanFor - // TODO: implements freezeAsParent - // TODO: implements insertParent - // TODO: implements storeItemsOn - // TODO: implements doesNotUnderstand - // TODO: implements nextTimeOnGrid - // TODO: implements asQuant - // TODO: implements timingOffset - }); + $newList = $this.collect($SC.Function(function($item) { + return $theOperand.perform($aSelector, $item, $SC.Integer(adverb + 1)); + })); -})(sc); + } -// src/sc/lang/classlib/Collections/Environment.js -(function(sc) { + return $newList; + } - function SCEnvironment() { - this.__initializeWith__("IdentityDictionary"); - } + function _performBinaryOpOnSeqColl_adverb_t($this, $aSelector, $theOperand) { + var $size, $newList, $i; + var size, i; - sc.lang.klass.define(SCEnvironment, "Environment : IdentityDictionary", function() { - // TODO: implements $make - // TODO: implements $use - // TODO: implements make - // TODO: implements use - // TODO: implements eventAt - // TODO: implements composeEvents - // TODO: implements $pop - // TODO: implements $push - // TODO: implements pop - // TODO: implements push - // TODO: implements linkDoc - // TODO: implements unlinkDoc - }); + $size = $theOperand.size(); + $newList = $this.species().new($size); -})(sc); + size = $size.__int__(); + for (i = 0; i < size; ++i) { + $i = $SC.Integer(i); + $newList.add($theOperand.at($i).perform($aSelector, $this)); + } -// src/sc/lang/classlib/Collections/Event.js -(function(sc) { + return $newList; + } - function SCEvent() { - this.__initializeWith__("Environment"); - } + function _performBinaryOpOnSeqColl_adverb_x($this, $aSelector, $theOperand) { + var $size, $newList; - sc.lang.klass.define(SCEvent, "Event : Environment", function() { - // TODO: implements $default - // TODO: implements $silent - // TODO: implements $addEventType - // TODO: implements next - // TODO: implements delta - // TODO: implements play - // TODO: implements isRest - // TODO: implements isPlaying_ - // TODO: implements isRunning_ - // TODO: implements playAndDelta - // TODO: implements synchWithQuant - // TODO: implements asControlInput - // TODO: implements asUGenInput - // TODO: implements printOn - // TODO: implements storeOn - // TODO: implements $initClass - // TODO: implements $makeDefaultSynthDef - // TODO: implements $makeParentEvents - }); + $size = $theOperand.size() ["*"] ($this.size()); + $newList = $this.species().new($size); + $theOperand.do($SC.Function(function($a) { + $this.do($SC.Function(function($b) { + $newList.add($a.perform($aSelector, $b)); + })); + })); -})(sc); + return $newList; + } -// src/sc/lang/classlib/Collections/Array.js -(function(sc) { + function _performBinaryOpOnSeqColl_adverb_s($this, $aSelector, $theOperand) { + var $size, $newList, $i; + var size, i; - var slice = [].slice; - var fn = sc.lang.fn; - var $SC = sc.lang.$SC; - var rand = sc.libs.random; - var mathlib = sc.libs.mathlib; + $size = $this.size().min($theOperand.size()); + $newList = $this.species().new($size); - sc.lang.klass.refine("Array", function(spec, utils) { - var BOOL = utils.BOOL; - var $nil = utils.$nil; - var SCArray = $SC("Array"); + size = $size.__int__(); + for (i = 0; i < size; ++i) { + $i = $SC.Integer(i); + $newList.add($theOperand.wrapAt($i).perform($aSelector, $this.wrapAt($i))); + } - spec.$with = function() { - return $SC.Array(slice.call(arguments)); - }; + return $newList; + } - spec.reverse = function() { - // <-- _ArrayReverse --> - return $SC.Array(this._.slice().reverse()); - }; + function _performBinaryOpOnSeqColl_adverb_f($this, $aSelector, $theOperand) { + var $size, $newList, $i; + var size, i; - spec.scramble = function() { - var a, tmp, i, j, m; + $size = $this.size().max($theOperand.size()); + $newList = $this.species().new($size); - // <-- _ArrayScramble --> - a = this._.slice(); - m = a.length; - if (m > 1) { - for (i = 0; m > 0; ++i, --m) { - j = i + (rand.next() * m)|0; - tmp = a[i]; - a[i] = a[j]; - a[j] = tmp; - } + size = $size.__int__(); + for (i = 0; i < size; ++i) { + $i = $SC.Integer(i); + $newList.add($theOperand.foldAt($i).perform($aSelector, $this.foldAt($i))); } - return $SC.Array(a); - }; + return $newList; + } - spec.mirror = function() { - var raw = this._; - var size, i, j, imax, a; + spec.performBinaryOpOnSimpleNumber = function($aSelector, $aNumber, $adverb) { + return this.collect($SC.Function(function($item) { + return $aNumber.perform($aSelector, $item, $adverb); + })); + }; - // <-- _ArrayMirror --> - size = raw.length * 2 - 1; - if (size < 2) { - return $SC.Array(raw.slice(0)); - } + spec.performBinaryOpOnComplex = function($aSelector, $aComplex, $adverb) { + return this.collect($SC.Function(function($item) { + return $aComplex.perform($aSelector, $item, $adverb); + })); + }; - a = new Array(size); - for (i = 0, imax = raw.length; i < imax; ++i) { - a[i] = raw[i]; + spec.asFraction = function($denominator, $fasterBetter) { + return this.collect($SC.Function(function($item) { + return $item.asFraction($denominator, $fasterBetter); + })); + }; + + // TODO: implements asPoint + // TODO: implements asRect + + spec.ascii = function() { + return this.collect($SC.Function(function($item) { + return $item.ascii(); + })); + }; + + spec.rate = function() { + if (this.size().__int__() === 1) { + return this.first().rate(); } - for (j = imax - 2, imax = size; i < imax; ++i, --j) { - a[i] = raw[j]; + return this.collect($SC.Function(function($item) { + return $item.rate(); + })).minItem(); + }; + + spec.multiChannelPerform = function() { + var method; + + if (this.size() > 0) { + method = utils.getMethod("Object", "multiChannelPerform"); + return method.apply(this, arguments); } - return $SC.Array(a); + return this.class().new(); }; - spec.mirror1 = function() { - var raw = this._; - var size, i, j, imax, a; + spec.multichannelExpandRef = utils.nop; - // <-- _ArrayMirror1 --> - size = raw.length * 2 - 2; - if (size < 2) { - return $SC.Array(raw.slice(0)); - } + spec.clip = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("clip") ].concat(slice.call(arguments)) + ); + }; - a = new Array(size); - for (i = 0, imax = raw.length; i < imax; ++i) { - a[i] = raw[i]; - } - for (j = imax - 2, imax = size; i < imax; ++i, --j) { - a[i] = raw[j]; - } + spec.wrap = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("wrap") ].concat(slice.call(arguments)) + ); + }; - return $SC.Array(a); + spec.fold = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("fold") ].concat(slice.call(arguments)) + ); }; - spec.mirror2 = function() { - var raw = this._; - var size, i, j, imax, a; + spec.linlin = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("linlin") ].concat(slice.call(arguments)) + ); + }; - // <-- _ArrayMirror2 --> - size = raw.length * 2; - if (size < 2) { - return $SC.Array(raw.slice(0)); - } + spec.linexp = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("linexp") ].concat(slice.call(arguments)) + ); + }; - a = new Array(size); - for (i = 0, imax = raw.length; i < imax; ++i) { - a[i] = raw[i]; - } - for (j = imax - 1, imax = size; i < imax; ++i, --j) { - a[i] = raw[j]; - } + spec.explin = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("explin") ].concat(slice.call(arguments)) + ); + }; - return $SC.Array(a); + spec.expexp = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("expexp") ].concat(slice.call(arguments)) + ); }; - spec.stutter = fn(function($n) { - var raw = this._; - var n, a, i, j, imax, k; + spec.lincurve = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("lincurve") ].concat(slice.call(arguments)) + ); + }; - // <-- _ArrayStutter --> - n = Math.max(0, $n.__int__()); - a = new Array(raw.length * n); - for (i = 0, j = 0, imax = raw.length; i < imax; ++i) { - for (k = 0; k < n; ++k, ++j) { - a[j] = raw[i]; - } - } + spec.curvelin = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("curvelin") ].concat(slice.call(arguments)) + ); + }; - return $SC.Array(a); - }, "n=2"); + spec.bilin = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("bilin") ].concat(slice.call(arguments)) + ); + }; - spec.rotate = fn(function($n) { - var raw = this._; - var n, a, size, i, j; + spec.biexp = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("biexp") ].concat(slice.call(arguments)) + ); + }; - // <-- _ArrayRotate --> - n = $n.__int__(); - a = new Array(raw.length); - size = a.length; - n %= size; - if (n < 0) { - n += size; - } - for (i = 0, j = n; i < size; ++i) { - a[j] = raw[i]; - if (++j >= size) { - j = 0; - } - } + spec.moddif = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("moddif") ].concat(slice.call(arguments)) + ); + }; - return $SC.Array(a); - }, "n=1"); + spec.range = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("range") ].concat(slice.call(arguments)) + ); + }; - spec.pyramid = fn(function($patternType) { - var patternType; - var obj1, obj2, i, j, k, n, numslots, x; + spec.exprange = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("exprange") ].concat(slice.call(arguments)) + ); + }; - obj1 = this._; - obj2 = []; + spec.curverange = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("curverange") ].concat(slice.call(arguments)) + ); + }; - patternType = Math.max(1, Math.min($patternType.__int__(), 10)); - x = numslots = obj1.length; + spec.unipolar = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("unipolar") ].concat(slice.call(arguments)) + ); + }; - switch (patternType) { - case 1: - n = (x * x + x) >> 1; - for (i = 0, k = 0; i < numslots; ++i) { - for (j = 0; j <= i; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - case 2: - n = (x * x + x) >> 1; - for (i = 0, k = 0; i < numslots; ++i) { - for (j = numslots - 1 - i; j <= numslots - 1; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - case 3: - n = (x * x + x) >> 1; - for (i = 0, k = 0; i < numslots; ++i) { - for (j = 0; j <= numslots - 1 - i; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - case 4: - n = (x * x + x) >> 1; - for (i = 0, k = 0; i < numslots; ++i) { - for (j = i; j <= numslots - 1; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - case 5: - n = x * x; - for (i = k = 0; i < numslots; ++i) { - for (j = 0; j <= i; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - for (i = 0; i < numslots - 1; ++i) { - for (j = 0; j <= numslots - 2 - i; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - case 6: - n = x * x; - for (i = 0, k = 0; i < numslots; ++i) { - for (j = numslots - 1 - i; j <= numslots - 1; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - for (i = 0; i < numslots - 1; ++i) { - for (j = i + 1; j <= numslots - 1; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - case 7: - n = x * x + x - 1; - for (i = 0, k = 0; i < numslots; ++i) { - for (j = 0; j <= numslots - 1 - i; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - for (i = 1; i < numslots; ++i) { - for (j = 0; j <= i; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - case 8: - n = x * x + x - 1; - for (i = 0, k = 0; i < numslots; ++i) { - for (j = i; j <= numslots - 1; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - for (i = 1; i < numslots; ++i) { - for (j = numslots - 1 - i; j <= numslots - 1; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - case 9: - n = x * x; - for (i = 0, k = 0; i < numslots; ++i) { - for (j = 0; j <= i; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - for (i = 0; i < numslots - 1; ++i) { - for (j = i + 1; j <= numslots - 1; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - case 10: - n = x * x; - for (i = 0, k = 0; i < numslots; ++i) { - for (j = numslots - 1 - i; j <= numslots - 1; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - for (i = 0; i < numslots - 1; ++i) { - for (j = 0; j <= numslots - 2 - i; ++j, ++k) { - obj2[k] = obj1[j]; - } - } - break; - } - - return $SC.Array(obj2); - }, "n=1"); + spec.bipolar = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("bipolar") ].concat(slice.call(arguments)) + ); + }; - spec.pyramidg = fn(function($patternType) { - var raw = this._; - var patternType; - var list = [], lastIndex, i; + spec.lag = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("lag") ].concat(slice.call(arguments)) + ); + }; - patternType = Math.max(1, Math.min($patternType.__int__(), 10)); - lastIndex = raw.length - 1; + spec.lag2 = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("lag2") ].concat(slice.call(arguments)) + ); + }; - switch (patternType) { - case 1: - for (i = 0; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(0, i + 1))); - } - break; - case 2: - for (i = 0; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(lastIndex - i, lastIndex + 1))); - } - break; - case 3: - for (i = lastIndex; i >= 0; --i) { - list.push($SC.Array(raw.slice(0, i + 1))); - } - break; - case 4: - for (i = 0; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(i, lastIndex + 1))); - } - break; - case 5: - for (i = 0; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(0, i + 1))); - } - for (i = lastIndex - 1; i >= 0; --i) { - list.push($SC.Array(raw.slice(0, i + 1))); - } - break; - case 6: - for (i = 0; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(lastIndex - i, lastIndex + 1))); - } - for (i = lastIndex - 1; i >= 0; --i) { - list.push($SC.Array(raw.slice(lastIndex - i, lastIndex + 1))); - } - break; - case 7: - for (i = lastIndex; i >= 0; --i) { - list.push($SC.Array(raw.slice(0, i + 1))); - } - for (i = 1; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(0, i + 1))); - } - break; - case 8: - for (i = 0; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(i, lastIndex + 1))); - } - for (i = lastIndex - 1; i >= 0; --i) { - list.push($SC.Array(raw.slice(i, lastIndex + 1))); - } - break; - case 9: - for (i = 0; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(0, i + 1))); - } - for (i = 1; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(i, lastIndex + 1))); - } - break; - case 10: - for (i = 0; i <= lastIndex; ++i) { - list.push($SC.Array(raw.slice(lastIndex - i, lastIndex + 1))); - } - for (i = lastIndex - 1; i >= 0; --i) { - list.push($SC.Array(raw.slice(0, i + 1))); - } - break; - } + spec.lag3 = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("lag3") ].concat(slice.call(arguments)) + ); + }; - return $SC.Array(list); - }, "n=1"); + spec.lagud = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("lagud") ].concat(slice.call(arguments)) + ); + }; - spec.sputter = fn(function($probability, $maxlen) { - var list, prob, maxlen, i, length; + spec.lag2ud = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("lag2ud") ].concat(slice.call(arguments)) + ); + }; - list = []; - prob = 1.0 - $probability.__num__(); - maxlen = $maxlen.__int__(); - i = 0; - length = this._.length; - while (i < length && list.length < maxlen) { - list.push(this._[i]); - if (rand.next() < prob) { - i += 1; - } - } + spec.lag3ud = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("lag3ud") ].concat(slice.call(arguments)) + ); + }; - return $SC.Array(list); - }, "probability=0.25; maxlen=100"); + spec.varlag = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("varlag") ].concat(slice.call(arguments)) + ); + }; - spec.lace = fn(function($length) { - var raw = this._; - var length, wrap = raw.length; - var a, i, $item; + spec.slew = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("slew") ].concat(slice.call(arguments)) + ); + }; - if ($length === $nil) { - $length = $SC.Integer(wrap); - } + spec.blend = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("blend") ].concat(slice.call(arguments)) + ); + }; - length = $length.__int__(); - a = new Array(length); + spec.checkBadValues = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("checkBadValues") ].concat(slice.call(arguments)) + ); + }; - for (i = 0; i < length; ++i) { - $item = raw[i % wrap]; - if (Array.isArray($item._)) { - a[i] = $item._[ ((i / wrap)|0) % $item._.length ]; - } else { - a[i] = $item; - } - } + spec.prune = function() { + return this.multiChannelPerform.apply( + this, [ $SC.Symbol("prune") ].concat(slice.call(arguments)) + ); + }; - return $SC.Array(a); - }, "length"); + // TODO: implements minNyquist + // TODO: implements sort + // TODO: implements sortBy + // TODO: implements sortMap + // TODO: implements sortedMedian + // TODO: implements median + // TODO: implements quickSort + // TODO: implements order - spec.permute = fn(function($nthPermutation) { - var raw = this._; - var obj1, obj2, size, $item; - var nthPermutation, i, imax, j; + spec.swap = fn(function($i, $j) { + var $temp; - obj1 = raw; - obj2 = raw.slice(); - size = raw.length; - nthPermutation = $nthPermutation.__int__(); + $temp = this.at($i); + this.put($i, this.at($j)); + this.put($j, $temp); - for (i = 0, imax = size - 1; i < imax; ++i) { - j = i + nthPermutation % (size - i); - nthPermutation = (nthPermutation / (size - i))|0; + return this; + }, "i; j"); - $item = obj2[i]; - obj2[i] = obj2[j]; - obj2[j] = $item; - } + // TODO: implements quickSortRange + // TODO: implements mergeSort + // TODO: implements mergeSortTemp + // TODO: implements mergeTemp + // TODO: implements insertionSort + // TODO: implements insertionSortRange + // TODO: implements hoareMedian + // TODO: implements hoareFind + // TODO: implements hoarePartition + // TODO: implements $streamContensts + // TODO: implements $streamContenstsLimit - return $SC.Array(obj2); - }, "nthPermutation=0"); + spec.wrapAt = fn(function($index) { + $index = $index ["%"] (this.size()); + return this.at($index); + }, "index"); - spec.allTuples = fn(function($maxTuples) { - var maxSize; - var obj1, obj2, obj3, obj4, newSize, tupSize; - var i, j, k; + spec.wrapPut = fn(function($index, $value) { + $index = $index ["%"] (this.size()); + return this.put($index, $value); + }, "index; value"); - maxSize = $maxTuples.__int__(); + // TODO: implements reduce + // TODO: implements join + // TODO: implements nextTimeOnGrid + // TODO: implements asQuant + // TODO: implements schedBundleArrayOnClock + }); - obj1 = this._; - newSize = 1; - tupSize = obj1.length; - for (i = 0; i < tupSize; ++i) { - if (Array.isArray(obj1[i]._)) { - newSize *= obj1[i]._.length; - } - } - newSize = Math.min(newSize, maxSize); +})(sc); - obj2 = new Array(newSize); +// src/sc/lang/classlib/Collections/ArrayedCollection.js +(function(sc) { - for (i = 0; i < newSize; ++i) { - k = i; - obj3 = new Array(tupSize); - for (j = tupSize - 1; j >= 0; --j) { - if (Array.isArray(obj1[j]._)) { - obj4 = obj1[j]._; - obj3[j] = obj4[k % obj4.length]; - k = (k / obj4.length)|0; - } else { - obj3[j] = obj1[j]; - } - } - obj2[i] = $SC.Array(obj3); - } + var slice = [].slice; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; + var iterator = sc.lang.iterator; + var rand = sc.libs.random; + var mathlib = sc.libs.mathlib; - return $SC.Array(obj2); - }, "maxTuples=16384"); + sc.lang.klass.refine("ArrayedCollection", function(spec, utils) { + var BOOL = utils.BOOL; + var $nil = utils.$nil; + var $int_0 = utils.$int_0; + var $int_1 = utils.$int_1; - spec.wrapExtend = fn(function($size) { - var raw = this._; - var size, a, i; + spec.valueOf = function() { + return this._.map(function(elem) { + return elem.valueOf(); + }); + }; - size = Math.max(0, $size.__int__()); - if (raw.length < size) { - a = new Array(size); - for (i = 0; i < size; ++i) { - a[i] = raw[i % raw.length]; - } - } else { - a = raw.slice(0, size); + spec.__elem__ = function(item) { + return item; + }; + + spec._ThrowIfImmutable = function() { + if (this._immutable) { + throw new Error("Attempted write to immutable object."); } + }; - return $SC.Array(a); - }, "size"); + // TODO: implements $newClear + // TODO: implements indexedSize - spec.foldExtend = fn(function($size) { + spec.size = function() { + return $SC.Integer(this._.length); + }; + + // TODO: implements maxSize + + spec.swap = fn(function($a, $b) { var raw = this._; - var size, a, i; + var a, b, len, tmp; - size = Math.max(0, $size.__int__()); + this._ThrowIfImmutable(); - if (raw.length < size) { - a = new Array(size); - for (i = 0; i < size; ++i) { - a[i] = raw[mathlib.fold_idx(i, raw.length)]; - } - } else { - a = raw.slice(0, size); + a = $a.__int__(); + b = $b.__int__(); + len = raw.length; + + if (a < 0 || len <= a || b < 0 || len <= b) { + throw new Error("out of index"); } - return $SC.Array(a); - }, "size"); + tmp = raw[b]; + raw[b] = raw[a]; + raw[a] = tmp; - spec.clipExtend = fn(function($size) { - var raw = this._; - var size, a, i, imax, b; + return this; + }, "a; b"); - size = Math.max(0, $size.__int__()); + spec.at = fn(function($index) { + var i; - if (raw.length < size) { - a = new Array(size); - for (i = 0, imax = raw.length; i < imax; ++i) { - a[i] = raw[i]; - } - for (b = a[i - 1]; i < size; ++i) { - a[i] = b; - } - } else { - a = raw.slice(0, size); + if (Array.isArray($index._)) { + return $SC.Array($index._.map(function($index) { + i = $index.__int__(); + if (i < 0 || this._.length <= i) { + return $nil; + } + return this._[i]; + }, this)); } - return $SC.Array(a); - }, "size"); + i = $index.__int__(); - spec.slide = fn(function($windowLength, $stepSize) { - var raw = this._; - var windowLength, stepSize; - var obj1, obj2, m, n, numwin, numslots; - var i, j, h, k; + return this._[i] || $nil; + }, "index"); - windowLength = $windowLength.__int__(); - stepSize = $stepSize.__int__(); - obj1 = raw; - obj2 = []; - m = windowLength; - n = stepSize; - numwin = ((raw.length + n - m) / n)|0; - numslots = numwin * m; + spec.clipAt = fn(function($index) { + var i; - for (i = h = k = 0; i < numwin; ++i,h += n) { - for (j = h; j < m + h; ++j) { - obj2[k++] = obj1[j]; - } + if (Array.isArray($index._)) { + return $SC.Array($index._.map(function($index) { + i = mathlib.clip_idx($index.__int__(), this._.length); + return this._[i]; + }, this)); } - return $SC.Array(obj2); - }, "windowLength=3; stepSize=1"); + i = mathlib.clip_idx($index.__int__(), this._.length); - spec.containsSeqColl = function() { - var raw = this._; - var i, imax; + return this._[i]; + }, "index"); - for (i = 0, imax = raw.length; i < imax; ++i) { - if (BOOL(raw[i].isSequenceableCollection())) { - return $SC.True(); - } + spec.wrapAt = fn(function($index) { + var i; + + if (Array.isArray($index._)) { + return $SC.Array($index._.map(function($index) { + var i = mathlib.wrap_idx($index.__int__(), this._.length); + return this._[i]; + }, this)); } - return $SC.False(); - }; + i = mathlib.wrap_idx($index.__int__(), this._.length); - spec.unlace = fn(function($clumpSize, $numChan) { - var raw = this._; - var clumpSize, numChan; - var a, b, size, i, j, k; + return this._[i]; + }, "index"); - clumpSize = $clumpSize.__int__(); - numChan = $numChan .__int__(); - size = (raw.length / clumpSize)|0; - size = size - (size % numChan); - if (size) { - a = new Array(clumpSize); - for (i = 0; i < clumpSize; ++i) { - b = new Array(size); - for (j = 0; j < size; j += numChan) { - for (k = 0; k < numChan; ++k) { - b[j + k] = raw[i * numChan + k + j * clumpSize]; - } - } - a[i] = $SC.Array(b); - } - } else { - a = []; + spec.foldAt = fn(function($index) { + var i; + + if (Array.isArray($index._)) { + return $SC.Array($index._.map(function($index) { + var i = mathlib.fold_idx($index.__int__(), this._.length); + return this._[i]; + }, this)); } - return $SC.Array(a); - }, "clumpSize=2; numChan=1"); + i = mathlib.fold_idx($index.__int__(), this._.length); - // TODO: implements interlace - // TODO: implements deinterlace + return this._[i]; + }, "index"); - spec.flop = function() { - return this.multiChannelExpand(); - }; + spec.put = fn(function($index, $item) { + var i; - spec.multiChannelExpand = function() { - var raw = this._; - var maxSize, size, obj1, obj2, obj3; - var i, j; + this._ThrowIfImmutable(); - obj1 = raw; - maxSize = obj1.reduce(function(len, $elem) { - return Math.max(len, Array.isArray($elem._) ? $elem._.length : 1); - }, 0); + if (Array.isArray($index._)) { + $index._.forEach(function($index) { + var i = $index.__int__(); + if (i < 0 || this._.length <= i) { + throw new Error("out of index"); + } + this._[i] = this.__elem__($item); + }, this); + } else { + i = $index.__int__(); + if (i < 0 || this._.length <= i) { + throw new Error("out of index"); + } + this._[i] = this.__elem__($item); + } - obj2 = new Array(maxSize); - size = obj1.length; + return this; + }, "index; item"); - if (size === 0) { - obj2[0] = $SC.Array([]); + spec.clipPut = fn(function($index, $item) { + this._ThrowIfImmutable(); + + if (Array.isArray($index._)) { + $index._.forEach(function($index) { + this._[mathlib.clip_idx($index.__int__(), this._.length)] = this.__elem__($item); + }, this); } else { - for (i = 0; i < maxSize; ++i) { - obj3 = new Array(size); - for (j = 0; j < size; ++j) { - if (Array.isArray(obj1[j]._)) { - obj3[j] = obj1[j]._[i % obj1[j]._.length]; - } else { - obj3[j] = obj1[j]; - } - } - obj2[i] = $SC.Array(obj3); - } + this._[mathlib.clip_idx($index.__int__(), this._.length)] = this.__elem__($item); } - return $SC.Array(obj2); - }; + return this; + }, "index; item"); - // TODO: implements envirPairs + spec.wrapPut = fn(function($index, $item) { + this._ThrowIfImmutable(); - spec.shift = fn(function($n, $filler) { - var $fill, $remain; + if (Array.isArray($index._)) { + $index._.forEach(function($index) { + this._[mathlib.wrap_idx($index.__int__(), this._.length)] = this.__elem__($item); + }, this); + } else { + this._[mathlib.wrap_idx($index.__int__(), this._.length)] = this.__elem__($item); + } - $fill = SCArray.fill($n.abs(), $filler); - $remain = this.drop($n.neg()); + return this; + }, "index; item"); - if ($n < 0) { - return $remain ["++"] ($fill); + spec.foldPut = fn(function($index, $item) { + this._ThrowIfImmutable(); + + if (Array.isArray($index._)) { + $index._.forEach(function($index) { + this._[mathlib.fold_idx($index.__int__(), this._.length)] = this.__elem__($item); + }, this); + } else { + this._[mathlib.fold_idx($index.__int__(), this._.length)] = this.__elem__($item); } - return $fill ["++"] ($remain); - }, "n; fillter=0.0"); + return this; + }, "index; item"); - spec.powerset = function() { + spec.removeAt = fn(function($index) { var raw = this._; - var arrSize, powersize; - var result, elemArr, mod, i, j; + var index; - arrSize = this.size().__int__(); - powersize = Math.pow(2, arrSize); + this._ThrowIfImmutable(); - result = []; - for (i = 0; i < powersize; ++i) { - elemArr = []; - for (j = 0; j < arrSize; ++j) { - mod = Math.pow(2, j); - if (((i / mod)|0) % 2) { - elemArr.push(raw[j]); - } + index = $index.__int__(); + if (index < 0 || raw.length <= index) { + throw new Error("out of index"); + } + + return raw.splice(index, 1)[0]; + }, "index"); + + spec.takeAt = fn(function($index) { + var raw = this._; + var index, ret, instead; + + this._ThrowIfImmutable(); + + index = $index.__int__(); + if (index < 0 || raw.length <= index) { + throw new Error("out of index"); + } + + ret = raw[index]; + instead = raw.pop(); + if (index !== raw.length) { + raw[index] = instead; + } + + return ret; + }, "index"); + + spec.indexOf = fn(function($item) { + var index; + + index = this._.indexOf($item); + return index === -1 ? $nil : $SC.Integer(index); + }, "item"); + + spec.indexOfGreaterThan = fn(function($val) { + var raw = this._; + var val, i, imax = raw.length; + + val = $val.__num__(); + for (i = 0; i < imax; ++i) { + if (raw[i].__num__() > val) { + return $SC.Integer(i); } - result[i] = $SC.Array(elemArr); } - return $SC.Array(result); - }; + return $nil; + }, "val"); - // TODO: implements source + spec.takeThese = fn(function($func) { + var raw = this._; + var i = 0, $i; - spec.asUGenInput = function($for) { - return this.collect($SC.Function(function($_) { - return $_.asUGenInput($for); + $i = $SC.Integer(i); + while (i < raw.length) { + if (BOOL($func.value(raw[i], $i))) { + this.takeAt($i); + } else { + $i = $SC.Integer(++i); + } + } + + return this; + }, "func"); + + spec.replace = fn(function($find, $replace) { + var $index, $out, $array; + + this._ThrowIfImmutable(); + + $out = $SC.Array(); + $array = this; + $find = $find.asArray(); + $replace = $replace.asArray(); + $SC.Function(function() { + return ($index = $array.find($find)).notNil(); + }).while($SC.Function(function() { + $out = $out ["++"] ($array.keep($index)) ["++"] ($replace); + $array = $array.drop($index ["+"] ($find.size())); })); + + return $out ["++"] ($array); + }, "find; replace"); + + spec.slotSize = function() { + return this.size(); }; - spec.asAudioRateInput = function($for) { - return this.collect($SC.Function(function($_) { - return $_.asAudioRateInput($for); - })); + spec.slotAt = function($index) { + return this.at($index); }; - spec.asControlInput = function() { - return this.collect($SC.Function(function($_) { - return $_.asControlInput(); - })); + spec.slotPut = function($index, $value) { + return this.put($index, $value); }; - spec.isValidUGenInput = utils.alwaysReturn$true; + spec.slotKey = function($index) { + return $index; + }; - spec.numChannels = function() { - return this.size(); + spec.slotIndex = utils.alwaysReturn$nil; + + spec.getSlots = function() { + return this.copy(); }; - // TODO: implements poll - // TODO: implements dpoll - // TODO: implements evnAt - // TODO: implements atIdentityHash - // TODO: implements atIdentityHashInPairs - // TODO: implements asSpec - // TODO: implements fork + spec.setSlots = function($array) { + return this.overWrite($array); + }; - spec.madd = fn(function($mul, $add) { - return $SC("MulAdd").new(this, $mul, $add); - }, "mul=1.0; add=0.0"); + spec.atModify = fn(function($index, $function) { + this.put($index, $function.value(this.at($index), $index)); + return this; + }, "index; function"); - // TODO: implements asRawOSC - // TODO: implements printOn - // TODO: implements storeOn - }); + spec.atInc = fn(function($index, $inc) { + this.put($index, this.at($index) ["+"] ($inc)); + return this; + }, "index; inc=1"); -})(sc); + spec.atDec = fn(function($index, $dec) { + this.put($index, this.at($index) ["-"] ($dec)); + return this; + }, "index; dec=1"); -// src/sc/lang/codegen.js -(function(sc) { + spec.isArray = utils.alwaysReturn$true; + spec.asArray = utils.nop; - var codegen = {}; - var Syntax = sc.lang.compiler.Syntax; - var Token = sc.lang.compiler.Token; - var Message = sc.lang.compiler.Message; - var SegmentedMethod = { - idle : true, - sleep: true, - wait : true, - yield: true - }; + spec.copyRange = fn(function($start, $end) { + var start, end, instance, raw; - var Scope = sc.lang.compiler.Scope({ - add_delegate: function(stmt, id, indent, peek, scope) { - if (stmt.vars.length === 0) { - this._addNewVariableStatement(stmt, id, indent); + if ($start === $nil) { + start = 0; } else { - this._appendVariable(stmt, id); + start = $start.__int__(); } - if (scope) { - peek.declared[id] = true; + if ($end === $nil) { + end = this._.length; + } else { + end = $end.__int__(); } - }, - _addNewVariableStatement: function(stmt, id, indent) { - stmt.head.push(indent, "var "); - stmt.vars.push($id(id)); - if (id.charAt(0) !== "_") { - stmt.vars.push(" = $SC.Nil()"); + raw = this._.slice(start, end + 1); + + instance = new this.__Spec(); + instance._ = raw; + return instance; + }, "start; end"); + + spec.copySeries = fn(function($first, $second, $last) { + var i, first, second, last, step, instance, raw; + + raw = []; + if ($first === $nil) { + first = 0; + } else { + first = $first.__int__(); } - stmt.tail.push(";", "\n"); - }, - _appendVariable: function(stmt, id) { - stmt.vars.push( - ", ", $id(id) - ); - if (id.charAt(0) !== "_") { - stmt.vars.push(" = $SC.Nil()"); + if ($second === $nil) { + second = first + 1; + } else { + second = $second.__int__(); } - }, - begin: function(stream, args) { - var declared = this.getDeclaredVariable(); - var stmt = { head: [], vars: [], tail: [] }; - var i, imax; - - this.stack.push({ - vars : {}, - args : {}, - declared: declared, - indent : this.parent.base, - stmt : stmt - }); + if ($last === $nil) { + last = Infinity; + } else { + last = $last.__int__(); + } + last = Math.max(0, Math.min(last, this._.length - 1)); + step = second - first; - for (i = 0, imax = args.length; i < imax; i++) { - this.add("arg", args[i]); + if (step > 0) { + for (i = first; i <= last; i += step) { + raw.push(this._[i]); + } + } else if (step < 0) { + for (i = first; i >= last; i += step) { + raw.push(this._[i]); + } } - stream.push(stmt.head, stmt.vars, stmt.tail); - } - }); + instance = new this.__Spec(); + instance._ = raw; + return instance; + }, "first; second; last"); - function CodeGen(opts) { - this.opts = opts || {}; - this.base = ""; - this.state = { - calledSegmentedMethod: false, - syncBlockScope: null - }; - this.scope = new Scope(this); - if (typeof this.opts.bare === "undefined") { - this.opts.bare = false; - } - } + spec.putSeries = fn(function($first, $second, $last, $value) { + var i, first, second, last, step; - CodeGen.prototype.toSourceNodeWhenNeeded = function(generated) { - if (Array.isArray(generated)) { - return this.flattenToString(generated); - } - return generated; - }; + this._ThrowIfImmutable(); - CodeGen.prototype.flattenToString = function(list) { - var i, imax, e, result = ""; - for (i = 0, imax = list.length; i < imax; ++i) { - e = list[i]; - result += Array.isArray(e) ? this.flattenToString(e) : e; - } - return result; - }; + if ($first === $nil) { + first = 0; + } else { + first = $first.__int__(); + } + if ($second === $nil) { + second = first + 1; + } else { + second = $second.__int__(); + } + if ($last === $nil) { + last = Infinity; + } else { + last = $last.__int__(); + } + last = Math.max(0, Math.min(last, this._.length - 1)); + step = second - first; - CodeGen.prototype.addIndent = function(stmt) { - return [ this.base, stmt ]; - }; + $value = this.__elem__($value); - CodeGen.prototype.generate = function(node, opts) { - var result; + if (step > 0) { + for (i = first; i <= last; i += step) { + this._[i] = $value; + } + } else if (step < 0) { + for (i = first; i >= last; i += step) { + this._[i] = $value; + } + } - if (Array.isArray(node)) { - result = [ - "(", this.stitchWith(node, ", ", function(item) { - return this.generate(item, opts); - }), ")" - ]; - } else if (node && node.type) { - result = this[node.type](node, opts); - result = this.toSourceNodeWhenNeeded(result, node); - } else if (typeof node === "string") { - result = $id(node); - } else { - result = node; - } + return this; + }, "first; second; last; value"); - return result; - }; + spec.add = fn(function($item) { + this._ThrowIfImmutable(); + this._.push(this.__elem__($item)); - CodeGen.prototype.withFunction = function(args, fn) { - var result; - var argItems, base, body; + return this; + }, "item"); - argItems = this.stitchWith(args, ", ", function(item) { - return this.generate(item); - }); + spec.addAll = fn(function($aCollection) { + var $this = this; - result = [ "function(", argItems, ") {\n" ]; + this._ThrowIfImmutable(); - base = this.base; - this.base += " "; + if ($aCollection.isSequenceableCollection().valueOf()) { + $aCollection.do($SC.Function(function($item) { + $this._.push($this.__elem__($item)); + })); + } else { + this.add($aCollection); + } - this.scope.begin(result, args); + return this; + }, "aCollection"); - body = fn.call(this); + spec.putEach = fn(function($keys, $values) { + var keys, values, i, imax; - if (body.length) { - result.push(body); - } else { - result.push(this.base, "return $SC.Nil();"); - } + this._ThrowIfImmutable(); - this.scope.end(); + $keys = $keys.asArray(); + $values = $values.asArray(); - this.base = base; + keys = $keys._; + values = $values._; + for (i = 0, imax = keys.length; i < imax; ++i) { + this.put(keys[i], this.__elem__(values[i % values.length])); + } - result.push("\n", this.base, "}"); + return this; + }, "keys; values"); - return result; - }; + spec.extend = fn(function($size, $item) { + var instance, raw, size, i; - CodeGen.prototype.withIndent = function(fn) { - var base, result; + raw = this._.slice(); + size = $size.__int__(); + if (raw.length > size) { + raw.splice(size); + } else if (raw.length < size) { + for (i = size - raw.length; i--; ) { + raw.push(this.__elem__($item)); + } + } - base = this.base; - this.base += " "; - result = fn.call(this); - this.base = base; + instance = new this.__Spec(); + instance._ = raw; + return instance; + }, "size; item"); - return result; - }; + spec.insert = fn(function($index, $item) { + var index; - CodeGen.prototype.insertArrayElement = function(elements) { - var result, items; + this._ThrowIfImmutable(); - result = [ "[", "]" ]; + index = Math.max(0, $index.__int__()); + this._.splice(index, 0, this.__elem__($item)); - if (elements.length) { - items = this.withIndent(function() { - return this.stitchWith(elements, "\n", function(item) { - return [ this.base, this.generate(item), "," ]; - }); - }); - result.splice(1, 0, "\n", items, "\n", this.base); - } + return this; + }, "index; item"); - return result; - }; + spec.move = function($fromIndex, $toIndex) { + return this.insert($toIndex, this.removeAt($fromIndex)); + }; - CodeGen.prototype.insertKeyValueElement = function(keyValues, with_comma) { - var result = []; + spec.addFirst = fn(function($item) { + var instance, raw; - if (keyValues) { - if (with_comma) { - result.push(", "); - } - result.push( - "{ ", this.stitchWith(Object.keys(keyValues), ", ", function(key) { - return [ key, ": ", this.generate(keyValues[key]) ]; - }), " }" - ); - } - - return result; - }; + raw = this._.slice(); + raw.unshift(this.__elem__($item)); - CodeGen.prototype.stitchWith = function(elements, bond, fn) { - var result, item; - var count, i, imax; + instance = new this.__Spec(); + instance._ = raw; + return instance; + }, "item"); - result = []; - for (i = count = 0, imax = elements.length; i < imax; ++i) { - if (count) { - result.push(bond); + spec.addIfNotNil = fn(function($item) { + if ($item === $nil) { + return this; } - item = fn.call(this, elements[i], i); + return this.addFirst(this.__elem__($item)); + }, "item"); - if (typeof item !== "undefined") { - result.push(item); - count += 1; + spec.pop = function() { + if (this._.length === 0) { + return $nil; } - } + this._ThrowIfImmutable(); + return this._.pop(); + }; - return result; - }; + spec["++"] = function($anArray) { + var instance, raw; - CodeGen.prototype.throwError = function(obj, messageFormat) { - var args, message; + raw = this._.slice(); - args = Array.prototype.slice.call(arguments, 1); - message = messageFormat.replace(/%(\d)/g, function(whole, index) { - return args[index]; - }); + instance = new this.__Spec(); + instance._ = raw; + if ($anArray !== $nil) { + instance.addAll($anArray); + } + return instance; + }; - throw new Error(message); - }; + // TODO: implements overWrite + // TODO: implements grow + // TODO: implements growClear - CodeGen.prototype.AssignmentExpression = function(node) { - if (Array.isArray(node.left)) { - return this._DestructuringAssignment(node); - } + spec.seriesFill = fn(function($start, $step) { + var i, imax; - return this._SimpleAssignment(node); - }; + for (i = 0, imax = this._.length; i < imax; ++i) { + this.put($SC.Integer(i), $start); + $start = $start ["+"] ($step); + } - CodeGen.prototype._SimpleAssignment = function(node) { - var result = []; - var opts; + return this; + }, "start; step"); - opts = { right: node.right, used: false }; + spec.fill = fn(function($value) { + var raw, i, imax; - result.push(this.generate(node.left, opts)); + this._ThrowIfImmutable(); - if (!opts.used) { - result.push(" " + node.operator + " ", this.generate(opts.right)); - } + $value = this.__elem__($value); - return result; - }; + raw = this._; + for (i = 0, imax = raw.length; i < imax; ++i) { + raw[i] = $value; + } - CodeGen.prototype._DestructuringAssignment = function(node) { - var elements = node.left; - var operator = node.operator; - var assignments; + return this; + }, "value"); - this.scope.add("var", "_ref"); + spec.do = function($function) { + iterator.execute( + iterator.array$do(this), + $function + ); + return this; + }; - assignments = this.withIndent(function() { - var result, lastUsedIndex; + spec.reverseDo = function($function) { + iterator.execute( + iterator.array$reverseDo(this), + $function + ); + return this; + }; - lastUsedIndex = elements.length; + spec.reverse = function() { + var $res = this.copy(); - result = [ - this.stitchWith(elements, ",\n", function(item, i) { - return this.addIndent(this._Assign( - item, operator, "_ref.at($SC.Integer(" + i + "))" - )); - }) - ]; + $res._.reverse(); - if (node.remain) { - result.push(",\n", this.addIndent(this._Assign( - node.remain, operator, "_ref.copyToEnd($SC.Integer(" + lastUsedIndex + "))" - ))); - } + return $res; + }; - return result; - }); + spec.windex = function() { + var raw = this._; + var x, r, i, imax; - return [ - "(_ref = ", this.generate(node.right), ",\n", - assignments , ",\n", - this.addIndent("_ref)") - ]; - }; + // <-- _ArrayWindex --> + x = 0; + r = rand.next(); + for (i = 0, imax = raw.length; i < imax; ++i) { + x += raw[i].__num__(); + if (x >= r) { + return $SC.Integer(i); + } + } - CodeGen.prototype._Assign = function(left, operator, right) { - var result = []; - var opts; + return $int_0; + }; - opts = { right: right, used: false }; + spec.normalizeSum = function() { + return this ["*"] (this.sum().reciprocal()); + }; - result.push(this.generate(left, opts)); + spec.normalize = fn(function($min, $max) { + var $minItem, $maxItem; - if (!opts.used) { - result.push(" " + operator + " ", right); - } + $minItem = this.minItem(); + $maxItem = this.maxItem(); + return this.collect($SC.Function(function($el) { + return $el.linlin($minItem, $maxItem, $min, $max); + })); + }, "min=0.0; max=1.0"); - return result; - }; + // TODO: implements asciiPlot + // TODO: implements perfectShuffle + // TODO: implements performInPlace - CodeGen.prototype.BinaryExpression = function(node) { - var operator = node.operator; + spec.clipExtend = fn(function($length) { + var last = this._[this._.length - 1] || $nil; + return this.extend($length, last); + }, "length"); - if (operator === "===" || operator === "!==") { - return this._EqualityOperator(node); - } + spec.rank = function() { + return $int_1 ["+"] (this.first().rank()); + }; - return this._BinaryExpression(node); - }; + spec.shape = function() { + return $SC.Array([ this.size() ]) ["++"] (this.at($int_0).shape()); + }; - CodeGen.prototype._EqualityOperator = function(node) { - return [ - "$SC.Boolean(", - this.generate(node.left), " " + node.operator + " ", this.generate(node.right), - ")" - ]; - }; + spec.reshape = function() { + var $result; + var shape, size, i, imax; - CodeGen.prototype._BinaryExpression = function(node) { - var result, operator, ch; + shape = slice.call(arguments); - result = [ this.generate(node.left) ]; - operator = node.operator; + size = 1; + for (i = 0, imax = shape.length; i < imax; ++i) { + size *= shape[i].__int__(); + } - ch = operator.charCodeAt(0); + $result = this.flat().wrapExtend($SC.Integer(size)); + for (i = imax - 1; i >= 1; --i) { + $result = $result.clump(shape[i]); + } - if (0x61 <= ch && ch <= 0x7a) { - result.push(".", operator); - } else { - result.push(" ['", operator, "'] "); - } + return $result; + }; - result.push("(", this.generate(node.right)); - if (node.adverb) { - result.push(", ", this.generate(node.adverb)); - } - result.push(")"); + spec.reshapeLike = fn(function($another, $indexing) { + var $index, $flat; - return result; - }; + $index = $int_0; + $flat = this.flat(); - CodeGen.prototype.BlockExpression = function(node) { - var body = this.withFunction([], function() { - return this._Statements(node.body); - }); + return $another.deepCollect($SC.Integer(0x7FFFFFFF), $SC.Function(function() { + var $item = $flat.perform($indexing, $index); + $index = $index.__inc__(); + return $item; + })); + }, "another; indexing=\\wrapAt"); - return [ "(", body, ")()" ]; - }; + // TODO: implements deepCollect + // TODO: implements deepDo - CodeGen.prototype.CallExpression = function(node) { - if (isSegmentedMethod(node)) { - this.state.calledSegmentedMethod = true; - } + spec.unbubble = fn(function($depth, $levels) { + if ($depth.__num__() <= 0) { + if (this.size().__int__() > 1) { + return this; + } + if ($levels.__int__() <= 1) { + return this.at($int_0); + } + return this.at($int_0).unbubble($depth, $levels.__dec__()); + } - if (node.args.expand) { - return this._ExpandCall(node); - } + return this.collect($SC.Function(function($item) { + return $item.unbubble($depth.__dec__()); + })); + }, "depth=0; levels=1"); - return this._SimpleCall(node); - }; + spec.bubble = fn(function($depth, $levels) { + if ($depth.__int__() <= 0) { + if ($levels.__int__() <= 1) { + return $SC.Array([ this ]); + } + return $SC.Array([ this.bubble($depth, $levels.__dec__()) ]); + } - CodeGen.prototype._SimpleCall = function(node) { - var args; - var list; - var hasActualArgument; + return this.collect($SC.Function(function($item) { + return $item.bubble($depth.__dec__(), $levels); + })); + }, "depth=0; levels=1"); - list = node.args.list; - hasActualArgument = !!list.length; + spec.slice = fn(function($$cuts) { + var $firstCut, $list; + var cuts_size, cuts; - args = [ - this.stitchWith(list, ", ", function(item) { - return this.generate(item); - }), - this.insertKeyValueElement(node.args.keywords, hasActualArgument) - ]; + cuts_size = $$cuts.size().__int__(); + if (cuts_size === 0) { + return this.copy(); + } - return [ - this.generate(node.callee), ".", node.method.name, "(", args, ")" - ]; - }; + $firstCut = $$cuts.at($int_0); + if ($firstCut === $nil) { + $list = this.copy(); + } else { + $list = this.at($firstCut.asArray()); + } - CodeGen.prototype._ExpandCall = function(node) { - var result; + if (cuts_size === 1) { + return $list.unbubble(); + } - this.scope.add("var", "_ref"); + cuts = $$cuts._.slice(1); + return $list.collect($SC.Function(function($item) { + return $item.slice.apply($item, cuts); + })).unbubble(); + }, "*cuts"); - result = [ - "(_ref = ", - this.generate(node.callee), - ", _ref." + node.method.name + ".apply(_ref, ", - this.insertArrayElement(node.args.list), ".concat(", - this.generate(node.args.expand), ".asArray()._", - this.insertKeyValueElement(node.args.keywords, true), - ")))" - ]; + spec.$iota = function() { + var $a; + var args, product, i, imax, a; - return result; - }; + args = arguments; - CodeGen.prototype.GlobalExpression = function(node) { - return "$SC.Global." + node.id.name; - }; + product = 1; + for (i = 0, imax = args.length; i < imax; ++i) { + product *= args[i].__int__(); + } - CodeGen.prototype.FunctionExpression = function(node) { - var fn, info; + a = new Array(product); + for (i = 0; i < product; ++i) { + a[i] = $SC.Integer(i); + } - info = getInformationOfFunction(node); + $a = $SC.Array(a); + return $a.reshape.apply($a, args); + }; - if (!isSegmentedBlock(node)) { - fn = CodeGen.prototype._SimpleFunction; - } else { - fn = CodeGen.prototype._SegmentedFunction; - } + // TODO: implements asRandomTable + // TODO: implements tableRand + // TODO: implements msgSize + // TODO: implements bundleSize + // TODO: implements clumpBundles - return [ - fn.call(this, node, info.args), - this._FunctionMetadata(info), ")" - ]; - }; + spec.includes = function($item) { + return $SC.Boolean(this._.indexOf($item) !== -1); + }; + }); - var format_argument = function(node) { - switch (node.valueType) { - case Token.NilLiteral : return "nil"; - case Token.TrueLiteral : return "true"; - case Token.FalseLiteral : return "false"; - case Token.CharLiteral : return "$" + node.value; - case Token.SymbolLiteral: return "\\" + node.value; - } - switch (node.value) { - case "Infinity" : return "inf"; - case "-Infinity": return "-inf"; - } - return node.value; - }; + sc.lang.klass.refine("RawArray", function(spec, utils) { + spec.archiveAsCompileString = utils.alwaysReturn$true; + spec.archiveAsObject = utils.alwaysReturn$true; + spec.rate = function() { + return $SC.Symbol("scalar"); + }; - CodeGen.prototype._FunctionMetadata = function(info) { - var keys, vals; - var args, result; + // TODO: implements readFromStream + // TODO: implements powerset + }); - keys = info.keys; - vals = info.vals; +})(sc); - if (keys.length === 0 && !info.remain && !info.closed) { - return []; - } +// src/sc/lang/classlib/Collections/String.js +(function(sc) { - args = this.stitchWith(keys, "; ", function(item, i) { - var result = [ keys[i] ]; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; - if (vals[i]) { - if (vals[i].type === Syntax.ListExpression) { - result.push("=[ ", this.stitchWith(vals[i].elements, ", ", function(item) { - return format_argument(item); - }), " ]"); - } else { - result.push("=", format_argument(vals[i])); - } + sc.lang.klass.refine("String", function(spec, utils) { + var $nil = utils.$nil; + var $false = utils.$false; + + spec.__str__ = function() { + return this.valueOf(); + }; + + spec.__elem__ = function($item) { + if ($item.__tag !== 1028) { + throw new TypeError("Wrong type."); } + return $item; + }; - return result; - }); + spec.valueOf = function() { + return this._.map(function(elem) { + return elem.__str__(); + }).join(""); + }; - result = [ ", '", args ]; + spec.toString = function() { + return this.valueOf(); + }; - if (info.remain) { - if (keys.length) { - result.push("; "); - } - result.push("*" + info.remain); - } - result.push("'"); + // TODO: implements unixCmdActions + // TODO: implements unixCmdActions_ - if (info.closed) { - result.push(", true"); - } + spec.$new = function() { + throw new Error("String.new is illegal, should use literal."); + }; - return result; - }; + // TODO: implements $initClass + // TODO: implements $doUnixCmdAction + // TODO: implements unixCmd + // TODO: implements unixCmdGetStdOut - CodeGen.prototype._SimpleFunction = function(node, args) { - var body; + spec.asSymbol = function() { + return $SC.Symbol(this.__str__()); + }; - body = this.withFunction(args, function() { - return this._Statements(node.body); - }); + spec.asInteger = function() { + var m = /^[-+]?\d+/.exec(this.__str__()); + return $SC.Integer(m ? m[0]|0 : 0); + }; - return [ "$SC.Function(", body ]; - }; + spec.asFloat = function() { + var m = /^[-+]?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?/.exec(this.__str__()); + return $SC.Float(m ? +m[0] : 0); + }; - CodeGen.prototype._SegmentedFunction = function(node, args) { - var fargs, body, assignArguments; + spec.ascii = function() { + var raw = this.__str__(); + var a, i, imax; - fargs = args.map(function(_, i) { - return "_arg" + i; - }); + a = new Array(raw.length); + for (i = 0, imax = a.length; i < imax; ++i) { + a[i] = $SC.Integer(raw.charCodeAt(i)); + } - assignArguments = function(item, i) { - return "$" + args[i] + " = " + fargs[i]; + return $SC.Array(a); }; - body = this.withFunction([], function() { - var result = []; - var fragments = [], syncBlockScope; - var elements = node.body; - var i, imax; - var functionBodies; + // TODO: implements stripRTF + // TODO: implements stripHTML + // TODO: implements $scDir + + spec.compare = fn(function($aString, $ignoreCase) { + var araw, braw, length, i, a, b, cmp, func; + + if ($aString.__tag !== 1034) { + return $nil; + } + + araw = this._; + braw = $aString._; + length = Math.min(araw.length, braw.length); + + if ($ignoreCase.__bool__()) { + func = function(ch) { + return ch.toLowerCase(); + }; + } else { + func = function(ch) { + return ch; + }; + } + for (i = 0; i < length; i++) { + a = func(araw[i]._).charCodeAt(0); + b = func(braw[i]._).charCodeAt(0); + cmp = a - b; + if (cmp !== 0) { + return $SC.Integer(cmp < 0 ? -1 : +1); + } + } + + if (araw.length < braw.length) { + cmp = -1; + } else if (araw.length > braw.length) { + cmp = 1; + } + + return $SC.Integer(cmp); + }, "aString; ignoreCase=false"); + + spec["<"] = function($aString) { + return $SC.Boolean( + this.compare($aString, $false).valueOf() < 0 + ); + }; + + spec[">"] = function($aString) { + return $SC.Boolean( + this.compare($aString, $false).valueOf() > 0 + ); + }; + + spec["<="] = function($aString) { + return $SC.Boolean( + this.compare($aString, $false).valueOf() <= 0 + ); + }; + + spec[">="] = function($aString) { + return $SC.Boolean( + this.compare($aString, $false).valueOf() >= 0 + ); + }; + + spec["=="] = function($aString) { + return $SC.Boolean( + this.compare($aString, $false).valueOf() === 0 + ); + }; + + spec["!="] = function($aString) { + return $SC.Boolean( + this.compare($aString, $false).valueOf() !== 0 + ); + }; + + // TODO: implements hash + + spec.performBinaryOpOnSimpleNumber = function($aSelector, $aNumber) { + return $aNumber.asString().perform($aSelector, this); + }; + + spec.performBinaryOpOnComplex = function($aSelector, $aComplex) { + return $aComplex.asString().perform($aSelector, this); + }; + + spec.multiChannelPerform = function() { + throw new Error("String:multiChannelPerform. Cannot expand strings."); + }; + + spec.isString = utils.alwaysReturn$true; + + spec.asString = utils.nop; + + spec.asCompileString = function() { + return $SC.String("\"" + this.__str__() + "\""); + }; + + spec.species = function() { + return $SC("String"); + }; + + // TODO: implements postln + // TODO: implements post + // TODO: implements postcln + // TODO: implements postc + // TODO: implements postf + // TODO: implements format + // TODO: implements matchRegexp + // TODO: implements fformat + // TODO: implements die + // TODO: implements error + // TODO: implements warn + // TODO: implements inform + + spec["++"] = function($anObject) { + return $SC.String( + this.toString() + $anObject.asString().toString() + ); + }; + + spec["+"] = function($anObject) { + return $SC.String( + this.toString() + " " + $anObject.asString().toString() + ); + }; + + // TODO: implements catArgs + // TODO: implements scatArgs + // TODO: implements ccatArgs + // TODO: implements catList + // TODO: implements scatList + // TODO: implements ccatList + // TODO: implements split + // TODO: implements containsStringAt + // TODO: implements icontainsStringAt + // TODO: implements contains + // TODO: implements containsi + // TODO: implements findRegexp + // TODO: implements findAllRegexp + // TODO: implements find + // TODO: implements findBackwards + // TODO: implements endsWith + // TODO: implements beginsWith + // TODO: implements findAll + // TODO: implements replace + // TODO: implements escapeChar + // TODO: implements shellQuote + // TODO: implements quote + // TODO: implements tr + // TODO: implements insert + // TODO: implements wrapExtend + // TODO: implements zeroPad + // TODO: implements padLeft + // TODO: implements padRight + // TODO: implements underlined + // TODO: implements scramble + // TODO: implements rotate + // TODO: implements compile + // TODO: implements interpret + // TODO: implements interpretPrint + // TODO: implements $readNew + // TODO: implements printOn + // TODO: implements storeOn + // TODO: implements inspectorClass + // TODO: implements standardizePath + // TODO: implements realPath + // TODO: implements withTrailingSlash + // TODO: implements withoutTrailingSlash + // TODO: implements absolutePath + // TODO: implements pathMatch + // TODO: implements load + // TODO: implements loadPaths + // TODO: implements loadRelative + // TODO: implements resolveRelative + // TODO: implements include + // TODO: implements exclude + // TODO: implements basename + // TODO: implements dirname + // TODO: implements splittext + // TODO: implements +/+ + // TODO: implements asRelativePath + // TODO: implements asAbsolutePath + // TODO: implements systemCmd + // TODO: implements gethostbyname + // TODO: implements getenv + // TODO: implements setenv + // TODO: implements unsetenv + // TODO: implements codegen_UGenCtorArg + // TODO: implements ugenCodeString + // TODO: implements asSecs + // TODO: implements speak + // TODO: implements toLower + // TODO: implements toUpper + // TODO: implements mkdir + // TODO: implements parseYAML + // TODO: implements parseYAMLFile + }); + +})(sc); + +// src/sc/lang/classlib/Collections/Set.js +(function(sc) { + + function SCSet() { + this.__initializeWith__("Collection"); + } + + sc.lang.klass.define(SCSet, "Set : Collection", function() { + // TODO: implements species + // TODO: implements copy + // TODO: implements do + // TODO: implements clear + // TODO: implements makeEmpty + // TODO: implements includes + // TODO: implements findMatch + // TODO: implements add + // TODO: implements remove + // TODO: implements choose + // TODO: implements pop + // TODO: implements powerset + // TODO: implements unify + // TODO: implements sect + // TODO: implements union + // TODO: implements difference + // TODO: implements symmetricDifference + // TODO: implements isSubsetOf + // TODO: implements initSet + // TODO: implements putCheck + // TODO: implements fullCheck + // TODO: implements grow + // TODO: implements noCheckAdd + // TODO: implements scanFor + // TODO: implements fixCollisionsFrom + // TODO: implements keyAt + // TODO: implements asSet + }); + +})(sc); + +// src/sc/lang/classlib/Collections/Dictionary.js +(function(sc) { + + function SCDictionary() { + this.__initializeWith__("Set"); + this._ = {}; + } + + sc.lang.klass.define(SCDictionary, "Dictionary : Set", function() { + // TODO: implements $newFrom + // TODO: implements at + // TODO: implements atFail + // TODO: implements matchAt + // TODO: implements trueAt + // TODO: implements add + // TODO: implements put + // TODO: implements putAll + // TODO: implements putPairs + // TODO: implements getPairs + // TODO: implements associationAt + // TODO: implements associationAtFail + // TODO: implements keys + // TODO: implements values + // TODO: implements includes + // TODO: implements includesKey + // TODO: implements removeAt + // TODO: implements removeAtFail + // TODO: implements remove + // TODO: implements removeFail + // TODO: implements keysValuesDo + // TODO: implements keysValuesChange + // TODO: implements do + // TODO: implements keysDo + // TODO: implements associationsDo + // TODO: implements pairsDo + // TODO: implements collect + // TODO: implements select + // TODO: implements reject + // TODO: implements invert + // TODO: implements merge + // TODO: implements blend + // TODO: implements findKeyForValue + // TODO: implements sortedKeysValuesDo + // TODO: implements choose + // TODO: implements order + // TODO: implements powerset + // TODO: implements transformEvent + // TODO: implements embedInStream + // TODO: implements asSortedArray + // TODO: implements asKeyValuePairs + // TODO: implements keysValuesArrayDo + // TODO: implements grow + // TODO: implements fixCollisionsFrom + // TODO: implements scanFor + // TODO: implements storeItemsOn + // TODO: implements printItemsOn + }); + + function SCIdentityDictionary() { + this.__initializeWith__("Dictionary"); + } + + sc.lang.klass.define(SCIdentityDictionary, "IdentityDictionary : Dictionary", function() { + // TODO: implements at + // TODO: implements put + // TODO: implements putGet + // TODO: implements includesKey + // TODO: implements findKeyForValue + // TODO: implements scanFor + // TODO: implements freezeAsParent + // TODO: implements insertParent + // TODO: implements storeItemsOn + // TODO: implements doesNotUnderstand + // TODO: implements nextTimeOnGrid + // TODO: implements asQuant + // TODO: implements timingOffset + }); + +})(sc); + +// src/sc/lang/classlib/Collections/Environment.js +(function(sc) { + + function SCEnvironment() { + this.__initializeWith__("IdentityDictionary"); + } + + sc.lang.klass.define(SCEnvironment, "Environment : IdentityDictionary", function() { + // TODO: implements $make + // TODO: implements $use + // TODO: implements make + // TODO: implements use + // TODO: implements eventAt + // TODO: implements composeEvents + // TODO: implements $pop + // TODO: implements $push + // TODO: implements pop + // TODO: implements push + // TODO: implements linkDoc + // TODO: implements unlinkDoc + }); + +})(sc); + +// src/sc/lang/classlib/Collections/Event.js +(function(sc) { + + function SCEvent() { + this.__initializeWith__("Environment"); + } + + sc.lang.klass.define(SCEvent, "Event : Environment", function() { + // TODO: implements $default + // TODO: implements $silent + // TODO: implements $addEventType + // TODO: implements next + // TODO: implements delta + // TODO: implements play + // TODO: implements isRest + // TODO: implements isPlaying_ + // TODO: implements isRunning_ + // TODO: implements playAndDelta + // TODO: implements synchWithQuant + // TODO: implements asControlInput + // TODO: implements asUGenInput + // TODO: implements printOn + // TODO: implements storeOn + // TODO: implements $initClass + // TODO: implements $makeDefaultSynthDef + // TODO: implements $makeParentEvents + }); + +})(sc); + +// src/sc/lang/classlib/Collections/Array.js +(function(sc) { + + var slice = [].slice; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; + var rand = sc.libs.random; + var mathlib = sc.libs.mathlib; + + sc.lang.klass.refine("Array", function(spec, utils) { + var BOOL = utils.BOOL; + var $nil = utils.$nil; + var SCArray = $SC("Array"); + + spec.$with = function() { + return $SC.Array(slice.call(arguments)); + }; + + spec.reverse = function() { + // <-- _ArrayReverse --> + return $SC.Array(this._.slice().reverse()); + }; + + spec.scramble = function() { + var a, tmp, i, j, m; + + // <-- _ArrayScramble --> + a = this._.slice(); + m = a.length; + if (m > 1) { + for (i = 0; m > 0; ++i, --m) { + j = i + (rand.next() * m)|0; + tmp = a[i]; + a[i] = a[j]; + a[j] = tmp; + } + } + + return $SC.Array(a); + }; + + spec.mirror = function() { + var raw = this._; + var size, i, j, imax, a; + + // <-- _ArrayMirror --> + size = raw.length * 2 - 1; + if (size < 2) { + return $SC.Array(raw.slice(0)); + } + + a = new Array(size); + for (i = 0, imax = raw.length; i < imax; ++i) { + a[i] = raw[i]; + } + for (j = imax - 2, imax = size; i < imax; ++i, --j) { + a[i] = raw[j]; + } + + return $SC.Array(a); + }; + + spec.mirror1 = function() { + var raw = this._; + var size, i, j, imax, a; + + // <-- _ArrayMirror1 --> + size = raw.length * 2 - 2; + if (size < 2) { + return $SC.Array(raw.slice(0)); + } + + a = new Array(size); + for (i = 0, imax = raw.length; i < imax; ++i) { + a[i] = raw[i]; + } + for (j = imax - 2, imax = size; i < imax; ++i, --j) { + a[i] = raw[j]; + } + + return $SC.Array(a); + }; + + spec.mirror2 = function() { + var raw = this._; + var size, i, j, imax, a; + + // <-- _ArrayMirror2 --> + size = raw.length * 2; + if (size < 2) { + return $SC.Array(raw.slice(0)); + } + + a = new Array(size); + for (i = 0, imax = raw.length; i < imax; ++i) { + a[i] = raw[i]; + } + for (j = imax - 1, imax = size; i < imax; ++i, --j) { + a[i] = raw[j]; + } + + return $SC.Array(a); + }; + + spec.stutter = fn(function($n) { + var raw = this._; + var n, a, i, j, imax, k; + + // <-- _ArrayStutter --> + n = Math.max(0, $n.__int__()); + a = new Array(raw.length * n); + for (i = 0, j = 0, imax = raw.length; i < imax; ++i) { + for (k = 0; k < n; ++k, ++j) { + a[j] = raw[i]; + } + } + + return $SC.Array(a); + }, "n=2"); + + spec.rotate = fn(function($n) { + var raw = this._; + var n, a, size, i, j; + + // <-- _ArrayRotate --> + n = $n.__int__(); + a = new Array(raw.length); + size = a.length; + n %= size; + if (n < 0) { + n += size; + } + for (i = 0, j = n; i < size; ++i) { + a[j] = raw[i]; + if (++j >= size) { + j = 0; + } + } + + return $SC.Array(a); + }, "n=1"); + + spec.pyramid = fn(function($patternType) { + var patternType; + var obj1, obj2, i, j, k, n, numslots, x; + + obj1 = this._; + obj2 = []; + + patternType = Math.max(1, Math.min($patternType.__int__(), 10)); + x = numslots = obj1.length; + + switch (patternType) { + case 1: + n = (x * x + x) >> 1; + for (i = 0, k = 0; i < numslots; ++i) { + for (j = 0; j <= i; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + case 2: + n = (x * x + x) >> 1; + for (i = 0, k = 0; i < numslots; ++i) { + for (j = numslots - 1 - i; j <= numslots - 1; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + case 3: + n = (x * x + x) >> 1; + for (i = 0, k = 0; i < numslots; ++i) { + for (j = 0; j <= numslots - 1 - i; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + case 4: + n = (x * x + x) >> 1; + for (i = 0, k = 0; i < numslots; ++i) { + for (j = i; j <= numslots - 1; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + case 5: + n = x * x; + for (i = k = 0; i < numslots; ++i) { + for (j = 0; j <= i; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + for (i = 0; i < numslots - 1; ++i) { + for (j = 0; j <= numslots - 2 - i; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + case 6: + n = x * x; + for (i = 0, k = 0; i < numslots; ++i) { + for (j = numslots - 1 - i; j <= numslots - 1; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + for (i = 0; i < numslots - 1; ++i) { + for (j = i + 1; j <= numslots - 1; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + case 7: + n = x * x + x - 1; + for (i = 0, k = 0; i < numslots; ++i) { + for (j = 0; j <= numslots - 1 - i; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + for (i = 1; i < numslots; ++i) { + for (j = 0; j <= i; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + case 8: + n = x * x + x - 1; + for (i = 0, k = 0; i < numslots; ++i) { + for (j = i; j <= numslots - 1; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + for (i = 1; i < numslots; ++i) { + for (j = numslots - 1 - i; j <= numslots - 1; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + case 9: + n = x * x; + for (i = 0, k = 0; i < numslots; ++i) { + for (j = 0; j <= i; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + for (i = 0; i < numslots - 1; ++i) { + for (j = i + 1; j <= numslots - 1; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + case 10: + n = x * x; + for (i = 0, k = 0; i < numslots; ++i) { + for (j = numslots - 1 - i; j <= numslots - 1; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + for (i = 0; i < numslots - 1; ++i) { + for (j = 0; j <= numslots - 2 - i; ++j, ++k) { + obj2[k] = obj1[j]; + } + } + break; + } + + return $SC.Array(obj2); + }, "n=1"); + + spec.pyramidg = fn(function($patternType) { + var raw = this._; + var patternType; + var list = [], lastIndex, i; + + patternType = Math.max(1, Math.min($patternType.__int__(), 10)); + lastIndex = raw.length - 1; + + switch (patternType) { + case 1: + for (i = 0; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(0, i + 1))); + } + break; + case 2: + for (i = 0; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(lastIndex - i, lastIndex + 1))); + } + break; + case 3: + for (i = lastIndex; i >= 0; --i) { + list.push($SC.Array(raw.slice(0, i + 1))); + } + break; + case 4: + for (i = 0; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(i, lastIndex + 1))); + } + break; + case 5: + for (i = 0; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(0, i + 1))); + } + for (i = lastIndex - 1; i >= 0; --i) { + list.push($SC.Array(raw.slice(0, i + 1))); + } + break; + case 6: + for (i = 0; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(lastIndex - i, lastIndex + 1))); + } + for (i = lastIndex - 1; i >= 0; --i) { + list.push($SC.Array(raw.slice(lastIndex - i, lastIndex + 1))); + } + break; + case 7: + for (i = lastIndex; i >= 0; --i) { + list.push($SC.Array(raw.slice(0, i + 1))); + } + for (i = 1; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(0, i + 1))); + } + break; + case 8: + for (i = 0; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(i, lastIndex + 1))); + } + for (i = lastIndex - 1; i >= 0; --i) { + list.push($SC.Array(raw.slice(i, lastIndex + 1))); + } + break; + case 9: + for (i = 0; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(0, i + 1))); + } + for (i = 1; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(i, lastIndex + 1))); + } + break; + case 10: + for (i = 0; i <= lastIndex; ++i) { + list.push($SC.Array(raw.slice(lastIndex - i, lastIndex + 1))); + } + for (i = lastIndex - 1; i >= 0; --i) { + list.push($SC.Array(raw.slice(0, i + 1))); + } + break; + } + + return $SC.Array(list); + }, "n=1"); + + spec.sputter = fn(function($probability, $maxlen) { + var list, prob, maxlen, i, length; - for (i = 0, imax = args.length; i < imax; ++i) { - this.scope.add("var", args[i]); + list = []; + prob = 1.0 - $probability.__num__(); + maxlen = $maxlen.__int__(); + i = 0; + length = this._.length; + while (i < length && list.length < maxlen) { + list.push(this._[i]); + if (rand.next() < prob) { + i += 1; + } } - syncBlockScope = this.state.syncBlockScope; - this.state.syncBlockScope = this.scope.peek(); - - functionBodies = this.withIndent(function() { - var fragments = []; - var i = 0, imax = elements.length; - var lastIndex = imax - 1; + return $SC.Array(list); + }, "probability=0.25; maxlen=100"); - fragments.push("\n"); + spec.lace = fn(function($length) { + var raw = this._; + var length, wrap = raw.length; + var a, i, $item; - var loop = function() { - var fragments = []; - var stmt; - var count = 0; + if ($length === $nil) { + $length = $SC.Integer(wrap); + } - while (i < imax) { - if (i === 0) { - if (args.length) { - stmt = this.stitchWith(args, "; ", assignArguments); - fragments.push([ this.addIndent(stmt), ";", "\n" ]); - } - } else if (count) { - fragments.push("\n"); - } + length = $length.__int__(); + a = new Array(length); - this.state.calledSegmentedMethod = false; - stmt = this.generate(elements[i]); + for (i = 0; i < length; ++i) { + $item = raw[i % wrap]; + if (Array.isArray($item._)) { + a[i] = $item._[ ((i / wrap)|0) % $item._.length ]; + } else { + a[i] = $item; + } + } - if (stmt.length) { - if (i === lastIndex || this.state.calledSegmentedMethod) { - stmt = [ "return ", stmt ]; - } - fragments.push([ this.addIndent(stmt), ";" ]); - count += 1; - } + return $SC.Array(a); + }, "length"); - i += 1; - if (this.state.calledSegmentedMethod) { - break; - } - } + spec.permute = fn(function($nthPermutation) { + var raw = this._; + var obj1, obj2, size, $item; + var nthPermutation, i, imax, j; - return fragments; - }; + obj1 = raw; + obj2 = raw.slice(); + size = raw.length; + nthPermutation = $nthPermutation.__int__(); - while (i < imax) { - if (i) { - fragments.push(",", "\n", this.addIndent(this.withFunction([], loop))); - } else { - fragments.push(this.addIndent(this.withFunction(fargs, loop))); - } - } + for (i = 0, imax = size - 1; i < imax; ++i) { + j = i + nthPermutation % (size - i); + nthPermutation = (nthPermutation / (size - i))|0; - fragments.push("\n"); + $item = obj2[i]; + obj2[i] = obj2[j]; + obj2[j] = $item; + } - return fragments; - }); + return $SC.Array(obj2); + }, "nthPermutation=0"); - fragments.push("return [", functionBodies, this.addIndent("];")); + spec.allTuples = fn(function($maxTuples) { + var maxSize; + var obj1, obj2, obj3, obj4, newSize, tupSize; + var i, j, k; - result.push([ this.addIndent(fragments) ]); + maxSize = $maxTuples.__int__(); - this.state.syncBlockScope = syncBlockScope; + obj1 = this._; + newSize = 1; + tupSize = obj1.length; + for (i = 0; i < tupSize; ++i) { + if (Array.isArray(obj1[i]._)) { + newSize *= obj1[i]._.length; + } + } + newSize = Math.min(newSize, maxSize); - return result; - }); + obj2 = new Array(newSize); - return [ "$SC.SegFunction(", body ]; - }; + for (i = 0; i < newSize; ++i) { + k = i; + obj3 = new Array(tupSize); + for (j = tupSize - 1; j >= 0; --j) { + if (Array.isArray(obj1[j]._)) { + obj4 = obj1[j]._; + obj3[j] = obj4[k % obj4.length]; + k = (k / obj4.length)|0; + } else { + obj3[j] = obj1[j]; + } + } + obj2[i] = $SC.Array(obj3); + } - CodeGen.prototype.Identifier = function(node, opts) { - var name = node.name; + return $SC.Array(obj2); + }, "maxTuples=16384"); - if (isClassName(name)) { - return "$SC('" + name + "')"; - } + spec.wrapExtend = fn(function($size) { + var raw = this._; + var size, a, i; - if (this.scope.find(name)) { - return $id(name); - } + size = Math.max(0, $size.__int__()); + if (raw.length < size) { + a = new Array(size); + for (i = 0; i < size; ++i) { + a[i] = raw[i % raw.length]; + } + } else { + a = raw.slice(0, size); + } - if (name.length === 1) { - return this._InterpreterVariable(node, opts); - } + return $SC.Array(a); + }, "size"); - this.throwError(null, Message.VariableNotDefined, name); - }; + spec.foldExtend = fn(function($size) { + var raw = this._; + var size, a, i; - CodeGen.prototype._InterpreterVariable = function(node, opts) { - var name; + size = Math.max(0, $size.__int__()); - if (opts) { - // setter - name = [ - "$this." + node.name + "_(", this.generate(opts.right), ")" - ]; - opts.used = true; - } else { - // getter - name = "$this." + node.name + "()"; - } + if (raw.length < size) { + a = new Array(size); + for (i = 0; i < size; ++i) { + a[i] = raw[mathlib.fold_idx(i, raw.length)]; + } + } else { + a = raw.slice(0, size); + } - return name; - }; + return $SC.Array(a); + }, "size"); - CodeGen.prototype.ListExpression = function(node) { - var result; + spec.clipExtend = fn(function($size) { + var raw = this._; + var size, a, i, imax, b; - result = [ - "$SC.Array(", - this.insertArrayElement(node.elements), - ]; + size = Math.max(0, $size.__int__()); - if (node.immutable) { - result.push(", ", "true"); - } + if (raw.length < size) { + a = new Array(size); + for (i = 0, imax = raw.length; i < imax; ++i) { + a[i] = raw[i]; + } + for (b = a[i - 1]; i < size; ++i) { + a[i] = b; + } + } else { + a = raw.slice(0, size); + } - result.push(")"); + return $SC.Array(a); + }, "size"); - return result; - }; + spec.slide = fn(function($windowLength, $stepSize) { + var raw = this._; + var windowLength, stepSize; + var obj1, obj2, m, n, numwin, numslots; + var i, j, h, k; - CodeGen.prototype.Literal = function(node) { - switch (node.valueType) { - case Token.IntegerLiteral: - return "$SC.Integer(" + node.value + ")"; - case Token.FloatLiteral: - return "$SC.Float(" + node.value + ")"; - case Token.CharLiteral: - return "$SC.Char('" + node.value + "')"; - case Token.SymbolLiteral: - return "$SC.Symbol('" + node.value + "')"; - case Token.StringLiteral: - return "$SC.String('" + node.value + "')"; - case Token.TrueLiteral: - return "$SC.True()"; - case Token.FalseLiteral: - return "$SC.False()"; - } + windowLength = $windowLength.__int__(); + stepSize = $stepSize.__int__(); + obj1 = raw; + obj2 = []; + m = windowLength; + n = stepSize; + numwin = ((raw.length + n - m) / n)|0; + numslots = numwin * m; - return "$SC.Nil()"; - }; + for (i = h = k = 0; i < numwin; ++i,h += n) { + for (j = h; j < m + h; ++j) { + obj2[k++] = obj1[j]; + } + } - CodeGen.prototype.ObjectExpression = function(node) { - return [ - "$SC.Event(", this.insertArrayElement(node.elements), ")" - ]; - }; + return $SC.Array(obj2); + }, "windowLength=3; stepSize=1"); - CodeGen.prototype.Program = function(node) { - var result, body; + spec.containsSeqColl = function() { + var raw = this._; + var i, imax; - if (node.body.length) { - body = this.withFunction([ "this", "SC" ], function() { - return this._Statements(node.body); - }); + for (i = 0, imax = raw.length; i < imax; ++i) { + if (BOOL(raw[i].isSequenceableCollection())) { + return $SC.True(); + } + } - result = [ "(", body, ")" ]; + return $SC.False(); + }; - if (!this.opts.bare) { - result = [ "SCScript", result, ";" ]; - } - } else { - result = []; - } + spec.unlace = fn(function($clumpSize, $numChan) { + var raw = this._; + var clumpSize, numChan; + var a, b, size, i, j, k; - return result; - }; + clumpSize = $clumpSize.__int__(); + numChan = $numChan .__int__(); + size = (raw.length / clumpSize)|0; + size = size - (size % numChan); + if (size) { + a = new Array(clumpSize); + for (i = 0; i < clumpSize; ++i) { + b = new Array(size); + for (j = 0; j < size; j += numChan) { + for (k = 0; k < numChan; ++k) { + b[j + k] = raw[i * numChan + k + j * clumpSize]; + } + } + a[i] = $SC.Array(b); + } + } else { + a = []; + } - CodeGen.prototype.ThisExpression = function(node) { - if (node.name === "this") { - return "$this"; - } + return $SC.Array(a); + }, "clumpSize=2; numChan=1"); - return [ "$this." + node.name + "()" ]; - }; + // TODO: implements interlace + // TODO: implements deinterlace - CodeGen.prototype.UnaryExpression = function(node) { - /* istanbul ignore else */ - if (node.operator === "`") { - return [ "$SC.Ref(", this.generate(node.arg), ")" ]; - } + spec.flop = function() { + return this.multiChannelExpand(); + }; - /* istanbul ignore next */ - throw new Error("Unknown UnaryExpression: " + node.operator); - }; + spec.multiChannelExpand = function() { + var raw = this._; + var maxSize, size, obj1, obj2, obj3; + var i, j; - CodeGen.prototype.VariableDeclaration = function(node) { - var scope = this.state.syncBlockScope; + obj1 = raw; + maxSize = obj1.reduce(function(len, $elem) { + return Math.max(len, Array.isArray($elem._) ? $elem._.length : 1); + }, 0); - return this.stitchWith(node.declarations, ", ", function(item) { - this.scope.add("var", item.id.name, scope); + obj2 = new Array(maxSize); + size = obj1.length; - if (!item.init) { - return; + if (size === 0) { + obj2[0] = $SC.Array([]); + } else { + for (i = 0; i < maxSize; ++i) { + obj3 = new Array(size); + for (j = 0; j < size; ++j) { + if (Array.isArray(obj1[j]._)) { + obj3[j] = obj1[j]._[i % obj1[j]._.length]; + } else { + obj3[j] = obj1[j]; + } + } + obj2[i] = $SC.Array(obj3); + } } - return [ this.generate(item.id), " = ", this.generate(item.init) ]; - }); - }; - - CodeGen.prototype._Statements = function(elements) { - var lastIndex = elements.length - 1; + return $SC.Array(obj2); + }; - return this.stitchWith(elements, "\n", function(item, i) { - var stmt; + // TODO: implements envirPairs - stmt = this.generate(item); + spec.shift = fn(function($n, $filler) { + var $fill, $remain; - if (stmt.length === 0) { - return; - } + $fill = SCArray.fill($n.abs(), $filler); + $remain = this.drop($n.neg()); - if (i === lastIndex) { - stmt = [ "return ", stmt ]; + if ($n < 0) { + return $remain ["++"] ($fill); } - return [ this.addIndent(stmt), ";" ]; - }); - }; - - var $id = function(id) { - var ch = id.charAt(0); + return $fill ["++"] ($remain); + }, "n; fillter=0.0"); - if (ch !== "_" && ch !== "$") { - id = "$" + id; - } + spec.powerset = function() { + var raw = this._; + var arrSize, powersize; + var result, elemArr, mod, i, j; - return id; - }; + arrSize = this.size().__int__(); + powersize = Math.pow(2, arrSize); - var getInformationOfFunction = function(node) { - var args = []; - var keys, vals, remain; - var list, i, imax; + result = []; + for (i = 0; i < powersize; ++i) { + elemArr = []; + for (j = 0; j < arrSize; ++j) { + mod = Math.pow(2, j); + if (((i / mod)|0) % 2) { + elemArr.push(raw[j]); + } + } + result[i] = $SC.Array(elemArr); + } - keys = []; - vals = []; - remain = null; + return $SC.Array(result); + }; - if (node.args) { - list = node.args.list; - for (i = 0, imax = list.length; i < imax; ++i) { - args.push(list[i].id.name); - keys.push(list[i].id.name); - vals.push(list[i].init); - } - if (node.args.remain) { - remain = node.args.remain.name; - args.push(remain); - } - } + // TODO: implements source - if (node.partial) { - keys = []; - } + spec.asUGenInput = function($for) { + return this.collect($SC.Function(function($_) { + return $_.asUGenInput($for); + })); + }; - return { - args : args, - keys : keys, - vals : vals, - remain: remain, - closed: node.closed + spec.asAudioRateInput = function($for) { + return this.collect($SC.Function(function($_) { + return $_.asAudioRateInput($for); + })); }; - }; - var isClassName = function(name) { - var ch0 = name.charAt(0); - return "A" <= ch0 && ch0 <= "Z"; - }; + spec.asControlInput = function() { + return this.collect($SC.Function(function($_) { + return $_.asControlInput(); + })); + }; - var isNode = function(node, key) { - return key !== "range" && key !== "loc" && typeof node[key] === "object"; - }; + spec.isValidUGenInput = utils.alwaysReturn$true; - var isSegmentedBlock = function(node) { - if (isSegmentedMethod(node)) { - return true; - } - return Object.keys(node).some(function(key) { - if (isNode(node, key)) { - return isSegmentedBlock(node[key]); - } - return false; - }); - }; + spec.numChannels = function() { + return this.size(); + }; - var isSegmentedMethod = function(node) { - return node.type === Syntax.CallExpression && !!SegmentedMethod[node.method.name]; - }; + // TODO: implements poll + // TODO: implements dpoll + // TODO: implements evnAt + // TODO: implements atIdentityHash + // TODO: implements atIdentityHashInPairs + // TODO: implements asSpec + // TODO: implements fork - codegen.compile = function(ast, opts) { - return new CodeGen(opts).generate(ast); - }; + spec.madd = fn(function($mul, $add) { + return $SC("MulAdd").new(this, $mul, $add); + }, "mul=1.0; add=0.0"); - sc.lang.codegen = codegen; + // TODO: implements asRawOSC + // TODO: implements printOn + // TODO: implements storeOn + }); })(sc); diff --git a/package.json b/package.json index 18e0fdd..4c750cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scscript", - "version": "0.0.18", + "version": "0.0.19", "author": "Nao Yonamine ", "homepage": "http://mohayonao.github.io/SCScript/", "bugs": "https://github.com/mohayonao/SCScript/issues", diff --git a/src/sc/lang/classlib.js b/src/sc/lang/classlib.js index 9d59466..54e7630 100644 --- a/src/sc/lang/classlib.js +++ b/src/sc/lang/classlib.js @@ -1,10 +1,8 @@ (function(sc) { "use strict"; - require("./klass"); - require("./klass-constructors"); - require("./klass-utils"); require("./dollarSC"); + require("./klass"); require("./iterator"); require("./fn"); require("../libs/random"); diff --git a/src/sc/lang/compiler.js b/src/sc/lang/compiler.js index adb7d9a..e401d4d 100644 --- a/src/sc/lang/compiler.js +++ b/src/sc/lang/compiler.js @@ -1,184 +1,3 @@ -(function(sc) { - "use strict"; - - require("./sc"); - - var compiler = {}; - - compiler.Token = { - CharLiteral: "Char", - EOF: "", - FalseLiteral: "False", - FloatLiteral: "Float", - Identifier: "Identifier", - IntegerLiteral: "Integer", - Keyword: "Keyword", - Label: "Label", - NilLiteral: "Nil", - Punctuator: "Punctuator", - StringLiteral: "String", - SymbolLiteral: "Symbol", - TrueLiteral: "True" - }; - - compiler.Syntax = { - AssignmentExpression: "AssignmentExpression", - BinaryExpression: "BinaryExpression", - BlockExpression: "BlockExpression", - CallExpression: "CallExpression", - FunctionExpression: "FunctionExpression", - GlobalExpression: "GlobalExpression", - Identifier: "Identifier", - ListExpression: "ListExpression", - Label: "Label", - Literal: "Literal", - ObjectExpression: "ObjectExpression", - Program: "Program", - ThisExpression: "ThisExpression", - UnaryExpression: "UnaryExpression", - VariableDeclaration: "VariableDeclaration", - VariableDeclarator: "VariableDeclarator" - }; - - var Message = compiler.Message = { - ArgumentAlreadyDeclared: "argument '%0' already declared", - InvalidLHSInAssignment: "invalid left-hand side in assignment", - NotImplemented: "not implemented %0", - UnexpectedEOS: "unexpected end of input", - UnexpectedIdentifier: "unexpected identifier", - UnexpectedChar: "unexpected char", - UnexpectedLabel: "unexpected label", - UnexpectedNumber: "unexpected number", - UnexpectedString: "unexpected string", - UnexpectedSymbol: "unexpected symbol", - UnexpectedToken: "unexpected token %0", - VariableAlreadyDeclared: "variable '%0' already declared", - VariableNotDefined: "variable '%0' not defined" - }; - - compiler.Keywords = { - var: "keyword", - arg: "keyword", - const: "keyword", - this: "function", - thisThread: "function", - thisProcess: "function", - thisFunction: "function", - thisFunctionDef: "function", - }; - - var Scope = (function() { - function Scope(methods) { - var f = function(parent) { - this.parent = parent; - this.stack = []; - }; - - function F() {} - F.prototype = Scope; - f.prototype = new F(); - - Object.keys(methods).forEach(function(key) { - f.prototype[key] = methods[key]; - }); - - return f; - } - - Scope.add = function(type, id, scope) { - var peek = this.stack[this.stack.length - 1]; - var vars, args, declared, stmt, indent; - - if (scope) { - vars = scope.vars; - args = scope.args; - declared = scope.declared; - stmt = scope.stmt; - indent = scope.indent; - } else { - vars = peek.vars; - args = peek.args; - declared = peek.declared; - stmt = peek.stmt; - indent = peek.indent; - } - - if (args[id]) { - this.parent.throwError({}, Message.ArgumentAlreadyDeclared, id); - } - - if (vars[id] && id.charAt(0) !== "_") { - this.parent.throwError({}, Message.VariableAlreadyDeclared, id); - } - - switch (type) { - case "var": - if (!vars[id]) { - this.add_delegate(stmt, id, indent, peek, scope); - vars[id] = true; - delete declared[id]; - } - break; - case "arg": - args[id] = true; - delete declared[id]; - break; - } - }; - - Scope.add_delegate = function() { - }; - - Scope.end = function() { - this.stack.pop(); - }; - - Scope.getDeclaredVariable = function() { - var peek = this.stack[this.stack.length - 1]; - var declared = {}; - - if (peek) { - Array.prototype.concat.apply([], [ - peek.declared, peek.args, peek.vars - ].map(Object.keys)).forEach(function(key) { - declared[key] = true; - }); - } - - return declared; - }; - - Scope.find = function(id) { - var peek = this.stack[this.stack.length - 1]; - return peek.vars[id] || peek.args[id] || peek.declared[id]; - }; - - Scope.peek = function() { - return this.stack[this.stack.length - 1]; - }; - - return Scope; - })(); - - compiler.Scope = Scope; - - sc.lang.compiler = compiler; - - var SCScript = sc.SCScript; - - SCScript.tokenize = function(source, opts) { - opts = opts || /* istanbul ignore next */ {}; - opts.tokens = true; - return sc.lang.parser.parse(source, opts).tokens || /* istanbul ignore next */ []; - }; - - SCScript.parse = function(source, opts) { - return sc.lang.parser.parse(source, opts); - }; - - SCScript.compile = function(source, opts) { - var ast = SCScript.parse(source, opts); - return sc.lang.codegen.compile(ast, opts); - }; - -})(sc); +require("./compiler/compiler"); +require("./compiler/parser"); +require("./compiler/codegen"); diff --git a/src/sc/lang/codegen.js b/src/sc/lang/compiler/codegen.js similarity index 99% rename from src/sc/lang/codegen.js rename to src/sc/lang/compiler/codegen.js index b65cd11..792e1ab 100644 --- a/src/sc/lang/codegen.js +++ b/src/sc/lang/compiler/codegen.js @@ -2,7 +2,7 @@ "use strict"; require("./sc"); - require("./parser"); + require("./scope"); var codegen = {}; var Syntax = sc.lang.compiler.Syntax; @@ -218,7 +218,7 @@ CodeGen.prototype.throwError = function(obj, messageFormat) { var args, message; - args = Array.prototype.slice.call(arguments, 1); + args = Array.prototype.slice.call(arguments, 2); message = messageFormat.replace(/%(\d)/g, function(whole, index) { return args[index]; }); diff --git a/src/sc/lang/codegen_test.js b/src/sc/lang/compiler/codegen_test.js similarity index 93% rename from src/sc/lang/codegen_test.js rename to src/sc/lang/compiler/codegen_test.js index 67a6cbf..64b5cc9 100644 --- a/src/sc/lang/codegen_test.js +++ b/src/sc/lang/compiler/codegen_test.js @@ -7,7 +7,6 @@ var codegen = sc.lang.codegen; var Syntax = sc.lang.compiler.Syntax; var Token = sc.lang.compiler.Message; - var SCScript = sc.SCScript; describe("sc.lang.codegen", function() { function s(str) { @@ -54,8 +53,18 @@ }); describe("compile", function() { it("with bare", function() { - var source = "nil"; - var test = esprima.parse(SCScript.compile(source, { bare: true })); + var ast = { + type: Syntax.Program, + body: [ + { + type: Syntax.Literal, + value: "null", + valueType: Token.NilLiteral + } + ] + }; + var source = codegen.compile(ast, { bare: true }); + var test = esprima.parse(source); var compiled = esprima.parse( "(function($this, $SC) { return $SC.Nil(); })" ); diff --git a/src/sc/lang/compiler/compiler.js b/src/sc/lang/compiler/compiler.js new file mode 100644 index 0000000..5c2f108 --- /dev/null +++ b/src/sc/lang/compiler/compiler.js @@ -0,0 +1,89 @@ +(function(sc) { + "use strict"; + + require("./sc"); + + var compiler = {}; + + compiler.Token = { + CharLiteral: "Char", + EOF: "", + FalseLiteral: "False", + FloatLiteral: "Float", + Identifier: "Identifier", + IntegerLiteral: "Integer", + Keyword: "Keyword", + Label: "Label", + NilLiteral: "Nil", + Punctuator: "Punctuator", + StringLiteral: "String", + SymbolLiteral: "Symbol", + TrueLiteral: "True" + }; + + compiler.Syntax = { + AssignmentExpression: "AssignmentExpression", + BinaryExpression: "BinaryExpression", + BlockExpression: "BlockExpression", + CallExpression: "CallExpression", + FunctionExpression: "FunctionExpression", + GlobalExpression: "GlobalExpression", + Identifier: "Identifier", + ListExpression: "ListExpression", + Label: "Label", + Literal: "Literal", + ObjectExpression: "ObjectExpression", + Program: "Program", + ThisExpression: "ThisExpression", + UnaryExpression: "UnaryExpression", + VariableDeclaration: "VariableDeclaration", + VariableDeclarator: "VariableDeclarator" + }; + + compiler.Message = { + ArgumentAlreadyDeclared: "argument '%0' already declared", + InvalidLHSInAssignment: "invalid left-hand side in assignment", + NotImplemented: "not implemented %0", + UnexpectedEOS: "unexpected end of input", + UnexpectedIdentifier: "unexpected identifier", + UnexpectedChar: "unexpected char", + UnexpectedLabel: "unexpected label", + UnexpectedNumber: "unexpected number", + UnexpectedString: "unexpected string", + UnexpectedSymbol: "unexpected symbol", + UnexpectedToken: "unexpected token %0", + VariableAlreadyDeclared: "variable '%0' already declared", + VariableNotDefined: "variable '%0' not defined" + }; + + compiler.Keywords = { + var: "keyword", + arg: "keyword", + const: "keyword", + this: "function", + thisThread: "function", + thisProcess: "function", + thisFunction: "function", + thisFunctionDef: "function", + }; + + sc.lang.compiler = compiler; + + var SCScript = sc.SCScript; + + SCScript.tokenize = function(source, opts) { + opts = opts || /* istanbul ignore next */ {}; + opts.tokens = true; + return sc.lang.parser.parse(source, opts).tokens || /* istanbul ignore next */ []; + }; + + SCScript.parse = function(source, opts) { + return sc.lang.parser.parse(source, opts); + }; + + SCScript.compile = function(source, opts) { + var ast = SCScript.parse(source, opts); + return sc.lang.codegen.compile(ast, opts); + }; + +})(sc); diff --git a/src/sc/lang/compiler_test.js b/src/sc/lang/compiler/compiler_test.js similarity index 99% rename from src/sc/lang/compiler_test.js rename to src/sc/lang/compiler/compiler_test.js index 4bc7e24..946d3d2 100644 --- a/src/sc/lang/compiler_test.js +++ b/src/sc/lang/compiler/compiler_test.js @@ -1,7 +1,7 @@ (function(sc) { "use strict"; - require("../lang/compiler"); + require("./compiler"); var Token = sc.lang.compiler.Token; var Syntax = sc.lang.compiler.Syntax; @@ -5563,10 +5563,10 @@ method: { type: Syntax.Identifier, name: "new", - range: [ 7, 7 ], + range: [ 9, 9 ], loc: { - start: { line: 1, column: 7 }, - end : { line: 1, column: 7 } + start: { line: 1, column: 9 }, + end : { line: 1, column: 9 } } }, args: { @@ -5769,10 +5769,10 @@ method: { type: Syntax.Identifier, name: "newFrom", - range: [ 5, 5 ], + range: [ 6, 6 ], loc: { - start: { line: 1, column: 5 }, - end : { line: 1, column: 5 } + start: { line: 1, column: 6 }, + end : { line: 1, column: 6 } } }, args: { @@ -5882,10 +5882,10 @@ method: { type: Syntax.Identifier, name: "newFrom", - range: [ 5, 5 ], + range: [ 6, 6 ], loc: { - start: { line: 1, column: 5 }, - end : { line: 1, column: 5 } + start: { line: 1, column: 6 }, + end : { line: 1, column: 6 } } }, args: { diff --git a/src/sc/lang/compiler/lexer.js b/src/sc/lang/compiler/lexer.js new file mode 100644 index 0000000..d287fcd --- /dev/null +++ b/src/sc/lang/compiler/lexer.js @@ -0,0 +1,727 @@ +(function(sc) { + "use strict"; + + require("./compiler"); + + var Token = sc.lang.compiler.Token; + var Message = sc.lang.compiler.Message; + var Keywords = sc.lang.compiler.Keywords; + + function Lexer(source, opts) { + /* istanbul ignore next */ + if (typeof source !== "string") { + if (typeof source === "undefined") { + source = ""; + } + source = String(source); + } + source = source.replace(/\r\n?/g, "\n"); + this.source = source; + this.opts = opts; + this.length = source.length; + this.index = 0; + this.lineNumber = this.length ? 1 : 0; + this.lineStart = 0; + this.reverted = null; + this.tokens = opts.tokens ? [] : null; + this.errors = opts.tolerant ? [] : null; + + this.peek(); + } + + function char2num(ch) { + var n = ch.charCodeAt(0); + + if (48 <= n && n <= 57) { + return n - 48; + } + if (65 <= n && n <= 90) { + return n - 55; + } + return n - 87; // if (97 <= n && n <= 122) + } + + function isNumber(ch) { + return "0" <= ch && ch <= "9"; + } + + Lexer.prototype.skipComment = function() { + var source = this.source; + var length = this.length; + var index = this.index; + var ch; + + LOOP: while (index < length) { + ch = source.charAt(index); + + if (ch === " " || ch === "\t") { + index += 1; + continue; + } + + if (ch === "\n") { + index += 1; + this.lineNumber += 1; + this.lineStart = index; + continue; + } + + if (ch === "/") { + ch = source.charAt(index + 1); + if (ch === "/") { + index = this.skipLineComment(index + 2); + continue; + } + if (ch === "*") { + index = this.skipBlockComment(index + 2); + continue; + } + } + + break; + } + + this.index = index; + }; + + Lexer.prototype.skipLineComment = function(index) { + var source = this.source; + var length = this.length; + var ch; + + while (index < length) { + ch = source.charAt(index); + index += 1; + if (ch === "\n") { + this.lineNumber += 1; + this.lineStart = index; + break; + } + } + + return index; + }; + + Lexer.prototype.skipBlockComment = function(index) { + var source = this.source; + var length = this.length; + var ch, depth; + + depth = 1; + while (index < length) { + ch = source.charAt(index); + + if (ch === "\n") { + this.lineNumber += 1; + this.lineStart = index; + } else { + ch = ch + source.charAt(index + 1); + if (ch === "/*") { + depth += 1; + index += 1; + } else if (ch === "*/") { + depth -= 1; + index += 1; + if (depth === 0) { + return index + 1; + } + } + } + + index += 1; + } + this.throwError({}, Message.UnexpectedToken, "ILLEGAL"); + + return index; + }; + + Lexer.prototype.collectToken = function() { + var loc, token, source, t; + + this.skipComment(); + + loc = { + start: { + line: this.lineNumber, + column: this.index - this.lineStart + } + }; + + token = this.advance(); + + loc.end = { + line: this.lineNumber, + column: this.index - this.lineStart + }; + + if (token.type !== Token.EOF) { + source = this.source; + t = { + type: token.type, + value: source.slice(token.range[0], token.range[1]) + }; + if (this.opts.range) { + t.range = [ token.range[0], token.range[1] ]; + } + if (this.opts.loc) { + t.loc = loc; + } + this.tokens.push(t); + } + + return token; + }; + + Lexer.prototype.advance = function() { + var ch, token; + + this.skipComment(); + + if (this.length <= this.index) { + return this.EOFToken(); + } + + ch = this.source.charAt(this.index); + + // Symbol literal starts with back slash + if (ch === "\\") { + return this.scanSymbolLiteral(); + } + + // Char literal starts with dollar + if (ch === "$") { + return this.scanCharLiteral(); + } + + // String literal starts with single quote or double quote + if (ch === "'" || ch === "\"") { + return this.scanStringLiteral(); + } + + // for partial application + if (ch === "_") { + return this.scanUnderscore(); + } + + if (ch === "-") { + token = this.scanNegativeNumericLiteral(); + if (token) { + return token; + } + } + + // Identifier starts with alphabet + if (("A" <= ch && ch <= "Z") || ("a" <= ch && ch <= "z")) { + return this.scanIdentifier(); + } + + // Number literal starts with number + if (isNumber(ch)) { + return this.scanNumericLiteral(); + } + + return this.scanPunctuator(); + }; + + Lexer.prototype.expect = function(value) { + var token = this.lex(); + if (token.type !== Token.Punctuator || token.value !== value) { + this.throwUnexpected(token, value); + } + }; + + Lexer.prototype.peek = function() { + var index, lineNumber, lineStart; + + index = this.index; + lineNumber = this.lineNumber; + lineStart = this.lineStart; + + if (this.opts.tokens) { + this.lookahead = this.collectToken(); + } else { + this.lookahead = this.advance(); + } + + this.index = index; + this.lineNumber = lineNumber; + this.lineStart = lineStart; + }; + + Lexer.prototype.lex = function(saved) { + var that = this; + var token = this.lookahead; + + if (saved) { + saved = [ this.lookahead, this.index, this.lineNumber, this.lineStart ]; + } + + this.index = token.range[1]; + this.lineNumber = token.lineNumber; + this.lineStart = token.lineStart; + + if (this.opts.tokens) { + this.lookahead = this.collectToken(); + } else { + this.lookahead = this.advance(); + } + + this.index = token.range[1]; + this.lineNumber = token.lineNumber; + this.lineStart = token.lineStart; + + if (saved) { + token.revert = function() { + that.lookahead = saved[0]; + that.index = saved[1]; + that.lineNumber = saved[2]; + that.lineStart = saved[3]; + if (that.tokens) { + that.tokens.pop(); + } + }; + } + + return token; + }; + + Lexer.prototype.EOFToken = function() { + return { + type: Token.EOF, + value: "", + lineNumber: this.lineNumber, + lineStart: this.lineStart, + range: [ this.index, this.index ] + }; + }; + + Lexer.prototype.scanCharLiteral = function() { + var start, value; + + start = this.index; + value = this.source.charAt(this.index + 1); + + this.index += 2; + + return { + type : Token.CharLiteral, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + }; + + Lexer.prototype.scanIdentifier = function() { + var source = this.source.slice(this.index); + var start = this.index; + var value, type; + + value = /^[a-zA-Z][a-zA-Z0-9_]*/.exec(source)[0]; + + this.index += value.length; + + if (this.source.charAt(this.index) === ":") { + this.index += 1; + return { + type: Token.Label, + value: value, + lineNumber: this.lineNumber, + lineStart: this.lineStart, + range: [ start, this.index ] + }; + } else if (this.isKeyword(value)) { + type = Token.Keyword; + } else { + switch (value) { + case "inf": + type = Token.FloatLiteral; + value = "Infinity"; + break; + case "pi": + type = Token.FloatLiteral; + value = String(Math.PI); + break; + case "nil": + type = Token.NilLiteral; + value = "null"; + break; + case "true": + type = Token.TrueLiteral; + break; + case "false": + type = Token.FalseLiteral; + break; + default: + type = Token.Identifier; + break; + } + } + + return { + type: type, + value: value, + lineNumber: this.lineNumber, + lineStart: this.lineStart, + range: [ start, this.index ] + }; + }; + + Lexer.prototype.scanNumericLiteral = function(neg) { + return this.scanNAryNumberLiteral(neg) || this.scanDecimalNumberLiteral(neg); + }; + + Lexer.prototype.scanNegativeNumericLiteral = function() { + var token, ch1, ch2, ch3; + var start, value = null; + + start = this.index; + ch1 = this.source.charAt(this.index + 1); + + if (isNumber(ch1)) { + this.index += 1; + token = this.scanNumericLiteral(true); + token.range[0] = start; + return token; + } + + ch2 = this.source.charAt(this.index + 2); + ch3 = this.source.charAt(this.index + 3); + + if (ch1 + ch2 === "pi") { + this.index += 3; + value = String(-Math.PI); + } else if (ch1 + ch2 + ch3 === "inf") { + this.index += 4; + value = "-Infinity"; + } + + if (value !== null) { + return { + type : Token.FloatLiteral, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + } + + return null; + }; + + Lexer.prototype.scanNAryNumberLiteral = function(neg) { + var re, start, items; + var base, integer, frac, pi; + var value, type; + + re = /^(\d+)r((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+)(?:\.((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+))?/; + start = this.index; + items = re.exec(this.source.slice(this.index)); + + if (!items) { + return; + } + + base = items[1].replace(/^0+(?=\d)/g, "")|0; + integer = items[2].replace(/(^0+(?=\d)|_)/g, ""); + frac = items[3] && items[3].replace(/_/g, ""); + + if (!frac && base < 26 && integer.substr(-2) === "pi") { + integer = integer.substr(0, integer.length - 2); + pi = true; + } + + type = Token.IntegerLiteral; + value = this.calcNBasedInteger(integer, base); + + if (frac) { + type = Token.FloatLiteral; + value += this.calcNBasedFrac(frac, base); + } + + if (neg) { + value *= -1; + } + + if (pi) { + type = Token.FloatLiteral; + value = value * Math.PI; + } + + if (type === Token.FloatLiteral && value === (value|0)) { + value = value + ".0"; + } else { + value = String(value); + } + + this.index += items[0].length; + + return { + type : type, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + }; + + Lexer.prototype.char2num = function(ch, base) { + var x = char2num(ch, base); + if (x >= base) { + this.throwError({}, Message.UnexpectedToken, ch); + } + return x; + }; + + Lexer.prototype.calcNBasedInteger = function(integer, base) { + var value, i, imax; + + for (i = value = 0, imax = integer.length; i < imax; ++i) { + value *= base; + value += this.char2num(integer[i], base); + } + + return value; + }; + + Lexer.prototype.calcNBasedFrac = function(frac, base) { + var value, i, imax; + + for (i = value = 0, imax = frac.length; i < imax; ++i) { + value += this.char2num(frac[i], base) * Math.pow(base, -(i + 1)); + } + + return value; + }; + + Lexer.prototype.scanDecimalNumberLiteral = function(neg) { + var re, start, items, integer, frac, pi; + var value, type; + + re = /^((?:\d(?:_(?=\d))?)+((?:\.(?:\d(?:_(?=\d))?)+)?(?:e[-+]?(?:\d(?:_(?=\d))?)+)?))(pi)?/; + start = this.index; + items = re.exec(this.source.slice(this.index)); + + integer = items[1]; + frac = items[2]; + pi = items[3]; + + type = (frac || pi) ? Token.FloatLiteral : Token.IntegerLiteral; + value = +integer.replace(/(^0+(?=\d)|_)/g, ""); + + if (neg) { + value *= -1; + } + + if (pi) { + value = value * Math.PI; + } + + if (type === Token.FloatLiteral && value === (value|0)) { + value = value + ".0"; + } else { + value = String(value); + } + + this.index += items[0].length; + + return { + type : type, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + }; + + Lexer.prototype.scanPunctuator = function() { + var re, start, items; + + re = /^(\.{1,3}|[(){}[\]:;,~#`]|[-+*\/%<=>!?&|@]+)/; + start = this.index; + items = re.exec(this.source.slice(this.index)); + + if (items) { + this.index += items[0].length; + return { + type : Token.Punctuator, + value: items[0], + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + } + + this.throwError({}, Message.UnexpectedToken, this.source.charAt(this.index)); + + this.index = this.length; + + return this.EOFToken(); + }; + + Lexer.prototype.scanStringLiteral = function() { + var source, start; + var length, index; + var quote, ch, value, type; + + source = this.source; + length = this.length; + index = start = this.index; + quote = source.charAt(start); + type = (quote === '"') ? Token.StringLiteral : Token.SymbolLiteral; + + index += 1; + while (index < length) { + ch = source.charAt(index); + index += 1; + if (ch === quote) { + value = source.substr(start + 1, index - start - 2); + value = value.replace(/\n/g, "\\n"); + this.index = index; + return { + type : type, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + } else if (ch === "\n") { + this.lineNumber += 1; + this.lineStart = index; + } else if (ch === "\\") { + index += 1; + } + } + + this.index = index; + this.throwError({}, Message.UnexpectedToken, "ILLEGAL"); + + return this.EOFToken(); + }; + + Lexer.prototype.scanSymbolLiteral = function() { + var re, start, items; + var value; + + re = /^\\([a-z_]\w*)?/i; + start = this.index; + items = re.exec(this.source.slice(this.index)); + + value = items[1]; + + this.index += items[0].length; + + return { + type : Token.SymbolLiteral, + value: value, + lineNumber: this.lineNumber, + lineStart : this.lineStart, + range: [ start, this.index ] + }; + }; + + Lexer.prototype.scanUnderscore = function() { + var start = this.index; + + this.index += 1; + + return { + type: Token.Identifier, + value: "_", + lineNumber: this.lineNumber, + lineStart: this.lineStart, + range: [ start, this.index ] + }; + }; + + Lexer.prototype.isKeyword = function(value) { + return !!Keywords[value] || false; + }; + + Lexer.prototype.match = function(value) { + return this.lookahead.value === value; + }; + + Lexer.prototype.matchAny = function(list) { + var value, i, imax; + + value = this.lookahead.value; + for (i = 0, imax = list.length; i < imax; ++i) { + if (list[i] === value) { + return value; + } + } + + return null; + }; + + Lexer.prototype.getLocItems = function() { + return [ this.index, this.lineNumber, this.index - this.lineStart ]; + }; + + Lexer.prototype.throwError = function(token, messageFormat) { + var args, message; + var error, index, lineNumber, column; + var prev; + + args = Array.prototype.slice.call(arguments, 2); + message = messageFormat.replace(/%(\d)/g, function(whole, index) { + return args[index]; + }); + + if (typeof token.lineNumber === "number") { + index = token.range[0]; + lineNumber = token.lineNumber; + column = token.range[0] - token.lineStart + 1; + } else { + index = this.index; + lineNumber = this.lineNumber; + column = this.index - this.lineStart + 1; + } + + error = new Error("Line " + lineNumber + ": " + message); + error.index = index; + error.lineNumber = lineNumber; + error.column = column; + error.description = message; + + if (this.errors) { + prev = this.errors[this.errors.length - 1]; + if (!(prev && error.index <= prev.index)) { + this.errors.push(error); + } + } else { + throw error; + } + }; + + Lexer.prototype.throwUnexpected = function(token) { + switch (token.type) { + case Token.EOF: + this.throwError(token, Message.UnexpectedEOS); + break; + case Token.FloatLiteral: + case Token.IntegerLiteral: + this.throwError(token, Message.UnexpectedNumber); + break; + case Token.CharLiteral: + this.throwError(token, Message.UnexpectedChar); + break; + case Token.StringLiteral: + this.throwError(token, Message.UnexpectedString); + break; + case Token.SymbolLiteral: + this.throwError(token, Message.UnexpectedSymbol); + break; + case Token.Identifier: + this.throwError(token, Message.UnexpectedIdentifier); + break; + default: + this.throwError(token, Message.UnexpectedToken, token.value); + break; + } + }; + + sc.lang.compiler.lexer = Lexer; + +})(sc); diff --git a/src/sc/lang/compiler/marker.js b/src/sc/lang/compiler/marker.js new file mode 100644 index 0000000..92a6077 --- /dev/null +++ b/src/sc/lang/compiler/marker.js @@ -0,0 +1,89 @@ +(function(sc) { + "use strict"; + + require("./compiler"); + + function Marker(lexer, locItems) { + this.lexer = lexer; + this.startLocItems = locItems; + this.endLocItems = null; + } + + Marker.create = function(lexer, node) { + var locItems; + + if (!lexer.opts.loc && !lexer.opts.range) { + return nopMarker; + } + + if (node) { + locItems = [ node.range[0], node.loc.start.line, node.loc.start.column ]; + } else { + lexer.skipComment(); + locItems = lexer.getLocItems(); + } + + return new Marker(lexer, locItems); + }; + + Marker.prototype.update = function(node) { + var locItems; + + if (node) { + locItems = [ node.range[1], node.loc.end.line, node.loc.end.column ]; + } else { + locItems = this.lexer.getLocItems(); + } + this.endLocItems = locItems; + + return this; + }; + + Marker.prototype.apply = function(node, force) { + var startLocItems, endLocItems; + + if (Array.isArray(node)) { + return node; + } + + if (force || !node.range || !node.loc) { + startLocItems = this.startLocItems; + if (this.endLocItems) { + endLocItems = this.endLocItems; + } else { + endLocItems = this.startLocItems; + } + /* istanbul ignore else */ + if (this.lexer.opts.range) { + node.range = [ startLocItems[0], endLocItems[0] ]; + } + /* istanbul ignore else */ + if (this.lexer.opts.loc) { + node.loc = { + start: { + line : startLocItems[1], + column: startLocItems[2] + }, + end: { + line : endLocItems[1], + column: endLocItems[2] + } + }; + } + } + + return node; + }; + + var nopMarker = { + apply: function(node) { + return node; + }, + update: function() { + return this; + } + }; + + sc.lang.compiler.marker = Marker; + +})(sc); diff --git a/src/sc/lang/compiler/marker_test.js b/src/sc/lang/compiler/marker_test.js new file mode 100644 index 0000000..4041b18 --- /dev/null +++ b/src/sc/lang/compiler/marker_test.js @@ -0,0 +1,174 @@ +(function() { + "use strict"; + + require("./marker"); + + var Marker = sc.lang.compiler.marker; + + function Lexer(opts) { + this.opts = opts || {}; + this.i = 0; + } + + Lexer.prototype.skipComment = function() { + }; + + Lexer.prototype.getLocItems = function() { + return [ this.i++, this.i++, this.i++ ]; + }; + + describe("sc.lang.compiler.marker", function() { + it("mark with updated location", function() { + var lexer, marker, node; + + lexer = new Lexer({ range: true, loc: true }); + marker = Marker.create(lexer); + + node = {}; + node = marker.update().apply(node); + + expect(node).to.eql({ + range: [ 0, 3 ], + loc: { + start: { line: 1, column: 2 }, + end : { line: 4, column: 5 }, + } + }); + }); + it("mark without updated location", function() { + var lexer, marker, node; + + lexer = new Lexer({ range: true, loc: true }); + marker = Marker.create(lexer); + + node = {}; + node = marker.apply(node); + + expect(node).to.eql({ + range: [ 0, 0 ], + loc: { + start: { line: 1, column: 2 }, + end : { line: 1, column: 2 }, + } + }); + }); + it("create a marker that applied start location when receive node", function() { + var lexer, marker, node; + + lexer = new Lexer({ range: true, loc: true }); + + node = { + range: [ 100, 103 ], + loc: { + start: { line: 101, column: 102 }, + end : { line: 104, column: 105 }, + } + }; + + marker = Marker.create(lexer, node); + + node = {}; + node = marker.update().apply(node); + + expect(node).to.eql({ + range: [ 100, 0 ], + loc: { + start: { line: 101, column: 102 }, + end : { line: 1, column: 2 }, + } + }); + }); + it("update a marker that applied end location when receive node", function() { + var lexer, marker, node; + + lexer = new Lexer({ range: true, loc: true }); + marker = Marker.create(lexer); + + node = {}; + node = marker.update({ + range: [ 100, 103 ], + loc: { + start: { line: 101, column: 102 }, + end : { line: 104, column: 105 }, + } + }).apply(node); + + expect(node).to.eql({ + range: [ 0, 103 ], + loc: { + start: { line: 1, column: 2 }, + end : { line: 104, column: 105 }, + } + }); + }); + it("not update when location exists already", function() { + var lexer, marker, node; + + lexer = new Lexer({ range: true, loc: true }); + marker = Marker.create(lexer); + + node = { + range: [ 100, 103 ], + loc: { + start: { line: 101, column: 102 }, + end : { line: 104, column: 105 }, + } + }; + node = marker.update().apply(node); + + expect(node).to.eql({ + range: [ 100, 103 ], + loc: { + start: { line: 101, column: 102 }, + end : { line: 104, column: 105 }, + } + }); + }); + it("if 2nd argument is true, update regardless when location exists", function() { + var lexer, marker, node; + + lexer = new Lexer({ range: true, loc: true }); + marker = Marker.create(lexer); + + node = { + range: [ 100, 103 ], + loc: { + start: { line: 101, column: 102 }, + end : { line: 104, column: 105 }, + } + }; + node = marker.update().apply(node, true); + + expect(node).to.eql({ + range: [ 0, 3 ], + loc: { + start: { line: 1, column: 2 }, + end : { line: 4, column: 5 }, + } + }); + }); + it("ignore when receive an array", function() { + var lexer, marker, node; + + lexer = new Lexer({ range: true, loc: true }); + marker = Marker.create(lexer); + + node = []; + node = marker.update().apply(node); + + expect(node).to.eql([]); + }); + it("should no operate when options are false", function() { + var lexer, marker, node; + + lexer = new Lexer({ range: false, loc: false }); + marker = Marker.create(lexer); + + node = {}; + node = marker.update().apply(node); + + expect(node).to.eql({}); + }); + }); + +})(); diff --git a/src/sc/lang/compiler/node.js b/src/sc/lang/compiler/node.js new file mode 100644 index 0000000..b8a8616 --- /dev/null +++ b/src/sc/lang/compiler/node.js @@ -0,0 +1,157 @@ +(function(sc) { + "use strict"; + + require("./compiler"); + + var Syntax = sc.lang.compiler.Syntax; + + var Node = { + createAssignmentExpression: function(operator, left, right, remain) { + var node = { + type: Syntax.AssignmentExpression, + operator: operator, + left: left, + right: right + }; + if (remain) { + node.remain = remain; + } + return node; + }, + createBinaryExpression: function(operator, left, right) { + var node = { + type: Syntax.BinaryExpression, + operator: operator.value, + left: left, + right: right + }; + if (operator.adverb) { + node.adverb = operator.adverb; + } + return node; + }, + createBlockExpression: function(body) { + return { + type: Syntax.BlockExpression, + body: body + }; + }, + createCallExpression: function(callee, method, args, stamp) { + var node; + + node = { + type: Syntax.CallExpression, + callee: callee, + method: method, + args : args, + }; + + if (stamp) { + node.stamp = stamp; + } + + return node; + }, + createGlobalExpression: function(id) { + return { + type: Syntax.GlobalExpression, + id: id + }; + }, + createFunctionExpression: function(args, body, closed, partial, blocklist) { + var node; + + node = { + type: Syntax.FunctionExpression, + body: body + }; + if (args) { + node.args = args; + } + if (closed) { + node.closed = true; + } + if (partial) { + node.partial = true; + } + if (blocklist) { + node.blocklist = true; + } + return node; + }, + createIdentifier: function(name) { + return { + type: Syntax.Identifier, + name: name + }; + }, + createLabel: function(name) { + return { + type: Syntax.Label, + name: name + }; + }, + createListExpression: function(elements, immutable) { + var node = { + type: Syntax.ListExpression, + elements: elements + }; + if (immutable) { + node.immutable = !!immutable; + } + return node; + }, + createLiteral: function(token) { + return { + type: Syntax.Literal, + value: token.value, + valueType: token.type + }; + }, + createObjectExpression: function(elements) { + return { + type: Syntax.ObjectExpression, + elements: elements + }; + }, + createProgram: function(body) { + return { + type: Syntax.Program, + body: body + }; + }, + createThisExpression: function(name) { + return { + type: Syntax.ThisExpression, + name: name + }; + }, + createUnaryExpression: function(operator, arg) { + return { + type: Syntax.UnaryExpression, + operator: operator, + arg: arg + }; + }, + createVariableDeclaration: function(declarations, kind) { + return { + type: Syntax.VariableDeclaration, + declarations: declarations, + kind: kind + }; + }, + createVariableDeclarator: function(id, init) { + var node = { + type: Syntax.VariableDeclarator, + id: id + }; + if (init) { + node.init = init; + } + return node; + } + }; + + sc.lang.compiler.node = Node; + +})(sc); diff --git a/src/sc/lang/parser.js b/src/sc/lang/compiler/parser.js similarity index 50% rename from src/sc/lang/parser.js rename to src/sc/lang/compiler/parser.js index b059b13..2dbd184 100644 --- a/src/sc/lang/parser.js +++ b/src/sc/lang/compiler/parser.js @@ -3,7 +3,10 @@ require("./sc"); require("./compiler"); - + require("./scope"); + require("./lexer"); + require("./marker"); + require("./node"); var parser = {}; @@ -11,6 +14,9 @@ var Syntax = sc.lang.compiler.Syntax; var Message = sc.lang.compiler.Message; var Keywords = sc.lang.compiler.Keywords; + var Lexer = sc.lang.compiler.lexer; + var Marker = sc.lang.compiler.marker; + var Node = sc.lang.compiler.node; var binaryPrecedenceDefaults = { "?" : 1, @@ -40,22 +46,6 @@ "!" : 12, }; - function char2num(ch) { - var n = ch.charCodeAt(0); - - if (48 <= n && n <= 57) { - return n - 48; - } - if (65 <= n && n <= 90) { - return n - 55; - } - return n - 87; // if (97 <= n && n <= 122) - } - - function isNumber(ch) { - return "0" <= ch && ch <= "9"; - } - var Scope = sc.lang.compiler.Scope({ begin: function() { var declared = this.getDeclaredVariable(); @@ -68,878 +58,59 @@ } }); - function LocationMarker(parser) { - this.parser = parser; - this.marker = [ - parser.index, - parser.lineNumber, - parser.index - parser.lineStart - ]; - } - - LocationMarker.prototype.apply = function(node) { - var parser = this.parser; - var marker = this.marker; - - /* istanbul ignore else */ - if (this.parser.opts.range) { - node.range = [ marker[0], parser.index ]; - } - /* istanbul ignore else */ - if (this.parser.opts.loc) { - node.loc = { - start: { - line: marker[1], - column: marker[2] - }, - end: { - line: parser.lineNumber, - column: parser.index - parser.lineStart - } - }; - } - }; - function SCParser(source, opts) { - /* istanbul ignore next */ - if (typeof source !== "string") { - if (typeof source === "undefined") { - source = ""; - } - source = String(source); - } - source = source.replace(/\r\n?/g, "\n"); - this.source = source; - this.opts = opts; - this.length = source.length; - this.index = 0; - this.lineNumber = this.length ? 1 : 0; - this.lineStart = 0; - this.reverted = null; + var binaryPrecedence; + + this.opts = opts || /* istanbul ignore next */ {}; + this.lexer = new Lexer(source, opts); this.scope = new Scope(this); - this.marker = []; - this.tokens = opts.tokens ? [] : null; - this.errors = opts.tolerant ? [] : null; this.state = { - closedFunction: false, - disallowGenerator: false, - innerElements: false, - immutableList: false, - underscore: [] - }; - } - - SCParser.prototype.parse = function() { - return this.parseProgram(); - }; - - SCParser.prototype.skipComment = function() { - var source = this.source; - var length = this.length; - var index = this.index; - var ch; - - LOOP: while (index < length) { - ch = source.charAt(index); - - if (ch === " " || ch === "\t") { - index += 1; - continue; - } - - if (ch === "\n") { - index += 1; - this.lineNumber += 1; - this.lineStart = index; - continue; - } - - if (ch === "/") { - ch = source.charAt(index + 1); - if (ch === "/") { - index = this.skipLineComment(index + 2); - continue; - } - if (ch === "*") { - index = this.skipBlockComment(index + 2); - continue; - } - } - - break; - } - - this.index = index; - }; - - SCParser.prototype.skipLineComment = function(index) { - var source = this.source; - var length = this.length; - var ch; - - while (index < length) { - ch = source.charAt(index); - index += 1; - if (ch === "\n") { - this.lineNumber += 1; - this.lineStart = index; - break; - } - } - - return index; - }; - - SCParser.prototype.skipBlockComment = function(index) { - var source = this.source; - var length = this.length; - var ch, depth; - - depth = 1; - while (index < length) { - ch = source.charAt(index); - - if (ch === "\n") { - this.lineNumber += 1; - this.lineStart = index; - } else { - ch = ch + source.charAt(index + 1); - if (ch === "/*") { - depth += 1; - index += 1; - } else if (ch === "*/") { - depth -= 1; - index += 1; - if (depth === 0) { - return index + 1; - } - } - } - - index += 1; - } - this.throwError({}, Message.UnexpectedToken, "ILLEGAL"); - - return index; - }; - - SCParser.prototype.collectToken = function() { - var loc, token, source, t; - - this.skipComment(); - - loc = { - start: { - line: this.lineNumber, - column: this.index - this.lineStart - } - }; - - token = this.advance(); - - loc.end = { - line: this.lineNumber, - column: this.index - this.lineStart - }; - - if (token.type !== Token.EOF) { - source = this.source; - t = { - type: token.type, - value: source.slice(token.range[0], token.range[1]) - }; - if (this.opts.range) { - t.range = [ token.range[0], token.range[1] ]; - } - if (this.opts.loc) { - t.loc = loc; - } - this.tokens.push(t); - } - - return token; - }; - - SCParser.prototype.advance = function() { - var ch, token; - - this.skipComment(); - - if (this.length <= this.index) { - return this.EOFToken(); - } - - ch = this.source.charAt(this.index); - - // Symbol literal starts with back slash - if (ch === "\\") { - return this.scanSymbolLiteral(); - } - - // Char literal starts with dollar - if (ch === "$") { - return this.scanCharLiteral(); - } - - // String literal starts with single quote or double quote - if (ch === "'" || ch === "\"") { - return this.scanStringLiteral(); - } - - // for partial application - if (ch === "_") { - return this.scanUnderscore(); - } - - if (ch === "-") { - token = this.scanNegativeNumericLiteral(); - if (token) { - return token; - } - } - - // Identifier starts with alphabet - if (("A" <= ch && ch <= "Z") || ("a" <= ch && ch <= "z")) { - return this.scanIdentifier(); - } - - // Number literal starts with number - if (isNumber(ch)) { - return this.scanNumericLiteral(); - } - - return this.scanPunctuator(); - }; - - SCParser.prototype.expect = function(value) { - var token = this.lex(); - if (token.type !== Token.Punctuator || token.value !== value) { - this.throwUnexpected(token, value); - } - }; - - SCParser.prototype.peek = function() { - var index, lineNumber, lineStart; - - index = this.index; - lineNumber = this.lineNumber; - lineStart = this.lineStart; - - if (this.opts.tokens) { - this.lookahead = this.collectToken(); - } else { - this.lookahead = this.advance(); - } - - this.index = index; - this.lineNumber = lineNumber; - this.lineStart = lineStart; - }; - - SCParser.prototype.lex = function(saved) { - var that = this; - var token = this.lookahead; - - if (saved) { - saved = [ this.lookahead, this.index, this.lineNumber, this.lineStart ]; - } - - this.index = token.range[1]; - this.lineNumber = token.lineNumber; - this.lineStart = token.lineStart; - - if (this.opts.tokens) { - this.lookahead = this.collectToken(); - } else { - this.lookahead = this.advance(); - } - - this.index = token.range[1]; - this.lineNumber = token.lineNumber; - this.lineStart = token.lineStart; - - if (saved) { - token.restore = function() { - that.lookahead = saved[0]; - that.index = saved[1]; - that.lineNumber = saved[2]; - that.lineStart = saved[3]; - if (that.tokens) { - that.tokens.pop(); - } - }; - } - - return token; - }; - - SCParser.prototype.EOFToken = function() { - return { - type: Token.EOF, - value: "", - lineNumber: this.lineNumber, - lineStart: this.lineStart, - range: [ this.index, this.index ] - }; - }; - - SCParser.prototype.scanCharLiteral = function() { - var start, value; - - start = this.index; - value = this.source.charAt(this.index + 1); - - this.index += 2; - - return { - type : Token.CharLiteral, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - }; - - SCParser.prototype.scanIdentifier = function() { - var source = this.source.slice(this.index); - var start = this.index; - var value, type; - - value = /^[a-zA-Z][a-zA-Z0-9_]*/.exec(source)[0]; - - this.index += value.length; - - if (this.source.charAt(this.index) === ":") { - this.index += 1; - return { - type: Token.Label, - value: value, - lineNumber: this.lineNumber, - lineStart: this.lineStart, - range: [ start, this.index ] - }; - } else if (this.isKeyword(value)) { - type = Token.Keyword; - } else { - switch (value) { - case "inf": - type = Token.FloatLiteral; - value = "Infinity"; - break; - case "pi": - type = Token.FloatLiteral; - value = String(Math.PI); - break; - case "nil": - type = Token.NilLiteral; - value = "null"; - break; - case "true": - type = Token.TrueLiteral; - break; - case "false": - type = Token.FalseLiteral; - break; - default: - type = Token.Identifier; - break; - } - } - - return { - type: type, - value: value, - lineNumber: this.lineNumber, - lineStart: this.lineStart, - range: [ start, this.index ] - }; - }; - - SCParser.prototype.scanNumericLiteral = function(neg) { - return this.scanNAryNumberLiteral(neg) || this.scanDecimalNumberLiteral(neg); - }; - - SCParser.prototype.scanNegativeNumericLiteral = function() { - var token, ch1, ch2, ch3; - var start, value = null; - - start = this.index; - ch1 = this.source.charAt(this.index + 1); - - if (isNumber(ch1)) { - this.index += 1; - token = this.scanNumericLiteral(true); - token.range[0] = start; - return token; - } - - ch2 = this.source.charAt(this.index + 2); - ch3 = this.source.charAt(this.index + 3); - - if (ch1 + ch2 === "pi") { - this.index += 3; - value = String(-Math.PI); - } else if (ch1 + ch2 + ch3 === "inf") { - this.index += 4; - value = "-Infinity"; - } - - if (value !== null) { - return { - type : Token.FloatLiteral, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - } - - return null; - }; - - SCParser.prototype.scanNAryNumberLiteral = function(neg) { - var re, start, items; - var base, integer, frac, pi; - var value, type; - - re = /^(\d+)r((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+)(?:\.((?:[\da-zA-Z](?:_(?=[\da-zA-Z]))?)+))?/; - start = this.index; - items = re.exec(this.source.slice(this.index)); - - if (!items) { - return; - } - - base = items[1].replace(/^0+(?=\d)/g, "")|0; - integer = items[2].replace(/(^0+(?=\d)|_)/g, ""); - frac = items[3] && items[3].replace(/_/g, ""); - - if (!frac && base < 26 && integer.substr(-2) === "pi") { - integer = integer.substr(0, integer.length - 2); - pi = true; - } - - type = Token.IntegerLiteral; - value = this.calcNBasedInteger(integer, base); - - if (frac) { - type = Token.FloatLiteral; - value += this.calcNBasedFrac(frac, base); - } - - if (neg) { - value *= -1; - } - - if (pi) { - type = Token.FloatLiteral; - value = value * Math.PI; - } - - if (type === Token.FloatLiteral && value === (value|0)) { - value = value + ".0"; - } else { - value = String(value); - } - - this.index += items[0].length; - - return { - type : type, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - }; - - SCParser.prototype.char2num = function(ch, base) { - var x = char2num(ch, base); - if (x >= base) { - this.throwError({}, Message.UnexpectedToken, ch); - } - return x; - }; - - SCParser.prototype.calcNBasedInteger = function(integer, base) { - var value, i, imax; - - for (i = value = 0, imax = integer.length; i < imax; ++i) { - value *= base; - value += this.char2num(integer[i], base); - } - - return value; - }; - - SCParser.prototype.calcNBasedFrac = function(frac, base) { - var value, i, imax; - - for (i = value = 0, imax = frac.length; i < imax; ++i) { - value += this.char2num(frac[i], base) * Math.pow(base, -(i + 1)); - } - - return value; - }; - - SCParser.prototype.scanDecimalNumberLiteral = function(neg) { - var re, start, items, integer, frac, pi; - var value, type; - - re = /^((?:\d(?:_(?=\d))?)+((?:\.(?:\d(?:_(?=\d))?)+)?(?:e[-+]?(?:\d(?:_(?=\d))?)+)?))(pi)?/; - start = this.index; - items = re.exec(this.source.slice(this.index)); - - integer = items[1]; - frac = items[2]; - pi = items[3]; - - type = (frac || pi) ? Token.FloatLiteral : Token.IntegerLiteral; - value = +integer.replace(/(^0+(?=\d)|_)/g, ""); - - if (neg) { - value *= -1; - } - - if (pi) { - value = value * Math.PI; - } - - if (type === Token.FloatLiteral && value === (value|0)) { - value = value + ".0"; - } else { - value = String(value); - } - - this.index += items[0].length; - - return { - type : type, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - }; - - SCParser.prototype.scanPunctuator = function() { - var re, start, items; - - re = /^(\.{1,3}|[(){}[\]:;,~#`]|[-+*\/%<=>!?&|@]+)/; - start = this.index; - items = re.exec(this.source.slice(this.index)); - - if (items) { - this.index += items[0].length; - return { - type : Token.Punctuator, - value: items[0], - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - } - - this.throwError({}, Message.UnexpectedToken, this.source.charAt(this.index)); - - this.index = this.length; - - return this.EOFToken(); - }; - - SCParser.prototype.scanStringLiteral = function() { - var source, start; - var length, index; - var quote, ch, value, type; - - source = this.source; - length = this.length; - index = start = this.index; - quote = source.charAt(start); - type = (quote === '"') ? Token.StringLiteral : Token.SymbolLiteral; - - index += 1; - while (index < length) { - ch = source.charAt(index); - index += 1; - if (ch === quote) { - value = source.substr(start + 1, index - start - 2); - value = value.replace(/\n/g, "\\n"); - this.index = index; - return { - type : type, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - } else if (ch === "\n") { - this.lineNumber += 1; - this.lineStart = index; - } else if (ch === "\\") { - index += 1; - } - } - - this.index = index; - this.throwError({}, Message.UnexpectedToken, "ILLEGAL"); - - return this.EOFToken(); - }; - - SCParser.prototype.scanSymbolLiteral = function() { - var re, start, items; - var value; - - re = /^\\([a-z_]\w*)?/i; - start = this.index; - items = re.exec(this.source.slice(this.index)); - - value = items[1]; - - this.index += items[0].length; - - return { - type : Token.SymbolLiteral, - value: value, - lineNumber: this.lineNumber, - lineStart : this.lineStart, - range: [ start, this.index ] - }; - }; - - SCParser.prototype.scanUnderscore = function() { - var start = this.index; - - this.index += 1; - - return { - type: Token.Identifier, - value: "_", - lineNumber: this.lineNumber, - lineStart: this.lineStart, - range: [ start, this.index ] - }; - }; - - SCParser.prototype.createAssignmentExpression = function(operator, left, right, remain) { - var node = { - type: Syntax.AssignmentExpression, - operator: operator, - left: left, - right: right - }; - if (remain) { - node.remain = remain; - } - return node; - }; - - SCParser.prototype.createBinaryExpression = function(operator, left, right) { - var node = { - type: Syntax.BinaryExpression, - operator: operator.value, - left: left, - right: right - }; - if (operator.adverb) { - node.adverb = operator.adverb; - } - return node; - }; - - SCParser.prototype.createBlockExpression = function(body) { - return { - type: Syntax.BlockExpression, - body: body - }; - }; - - SCParser.prototype.createCallExpression = function(callee, method, args, stamp) { - var node; - - node = { - type: Syntax.CallExpression, - callee: callee, - method: method, - args : args, - }; - - if (stamp) { - node.stamp = stamp; - } - - return node; - }; - - SCParser.prototype.createGlobalExpression = function(id) { - return { - type: Syntax.GlobalExpression, - id: id - }; - }; - - SCParser.prototype.createFunctionExpression = function(args, body, closed, partial, blocklist) { - var node; - - node = { - type: Syntax.FunctionExpression, - body: body - }; - if (args) { - node.args = args; - } - if (closed) { - node.closed = true; - } - if (partial) { - node.partial = true; - } - if (blocklist) { - node.blocklist = true; - } - return node; - }; - - SCParser.prototype.createIdentifier = function(name) { - return { - type: Syntax.Identifier, - name: name - }; - }; - - SCParser.prototype.createLabel = function(name) { - return { - type: Syntax.Label, - name: name - }; - }; - - SCParser.prototype.createListExpression = function(elements, immutable) { - var node = { - type: Syntax.ListExpression, - elements: elements - }; - if (immutable) { - node.immutable = !!immutable; - } - return node; - }; - - SCParser.prototype.createLiteral = function(token) { - return { - type: Syntax.Literal, - value: token.value, - valueType: token.type - }; - }; - - SCParser.prototype.createLocationMarker = function() { - if (this.opts.loc || this.opts.range) { - this.skipComment(); - return new LocationMarker(this); - } - }; - - SCParser.prototype.createObjectExpression = function(elements) { - return { - type: Syntax.ObjectExpression, - elements: elements - }; - }; - - SCParser.prototype.createProgram = function(body) { - return { - type: Syntax.Program, - body: body - }; - }; - - SCParser.prototype.createThisExpression = function(name) { - return { - type: Syntax.ThisExpression, - name: name - }; - }; - - SCParser.prototype.createUnaryExpression = function(operator, arg) { - return { - type: Syntax.UnaryExpression, - operator: operator, - arg: arg - }; - }; - - SCParser.prototype.createVariableDeclaration = function(declarations, kind) { - return { - type: Syntax.VariableDeclaration, - declarations: declarations, - kind: kind + closedFunction: false, + disallowGenerator: false, + innerElements: false, + immutableList: false, + underscore: [] }; - }; - SCParser.prototype.createVariableDeclarator = function(id, init) { - var node = { - type: Syntax.VariableDeclarator, - id: id - }; - if (init) { - node.init = init; + if (this.opts.binaryPrecedence) { + if (typeof this.opts.binaryPrecedence === "object") { + binaryPrecedence = this.opts.binaryPrecedence; + } else { + binaryPrecedence = binaryPrecedenceDefaults; + } } - return node; - }; - SCParser.prototype.isClassName = function(node) { - var name, ch; + this.binaryPrecedence = binaryPrecedence || {}; + } - if (node.type === Syntax.Identifier) { - name = node.value || node.name; - ch = name.charAt(0); - return "A" <= ch && ch <= "Z"; + Object.defineProperty(SCParser.prototype, "lookahead", { + get: function() { + return this.lexer.lookahead; } + }); - return false; - }; - - SCParser.prototype.isKeyword = function(value) { - return !!Keywords[value] || false; - }; - - SCParser.prototype.isLeftHandSide = function(expr) { - switch (expr.type) { - case Syntax.Identifier: - case Syntax.GlobalExpression: - return true; - } - return false; + SCParser.prototype.lex = function() { + return this.lexer.lex(); }; - SCParser.prototype._match = function(value, type) { - var token = this.lookahead; - return token.type === type && token.value === value; + SCParser.prototype.expect = function(value) { + return this.lexer.expect(value); }; SCParser.prototype.match = function(value) { - return this._match(value, Token.Punctuator); - }; - - SCParser.prototype.matchKeyword = function(value) { - return this._match(value, Token.Keyword); + return this.lexer.match(value); }; SCParser.prototype.matchAny = function(list) { - var value, i, imax; + return this.lexer.matchAny(list); + }; - if (this.lookahead.type === Token.Punctuator) { - value = this.lookahead.value; - for (i = 0, imax = list.length; i < imax; ++i) { - if (list[i] === value) { - return value; - } - } - } + SCParser.prototype.throwError = function() { + return this.lexer.throwError.apply(this.lexer, arguments); + }; - return null; + SCParser.prototype.throwUnexpected = function(token) { + return this.lexer.throwUnexpected(token); }; SCParser.prototype.withScope = function(fn) { @@ -952,13 +123,15 @@ return result; }; + SCParser.prototype.parse = function() { + return this.parseProgram(); + }; + // 1. Program SCParser.prototype.parseProgram = function() { - var node; + var node, marker; - this.skipComment(); - this.markStart(); - this.peek(); + marker = Marker.create(this.lexer); node = this.withScope(function() { var body; @@ -968,10 +141,10 @@ body = body[0].body; } - return this.createProgram(body); + return Node.createProgram(body); }); - return this.markEnd(node); + return marker.update().apply(node); }; // 2. Function @@ -984,12 +157,12 @@ if (this.match("|")) { args = this.parseFunctionArgument("|"); - } else if (this.matchKeyword("arg")) { + } else if (this.match("arg")) { args = this.parseFunctionArgument(";"); } body = this.parseFunctionBody("}"); - return this.createFunctionExpression(args, body, closed, false, blocklist); + return Node.createFunctionExpression(args, body, closed, false, blocklist); }); return node; @@ -997,7 +170,7 @@ // 2.2 Function Argument SCParser.prototype.parseFunctionArgument = function(expect) { - var args = { list: [] }, lookahead; + var args = { list: [] }; this.lex(); @@ -1013,7 +186,6 @@ if (this.match("...")) { this.lex(); - lookahead = this.lookahead; args.remain = this.parseVariableIdentifier(); this.scope.add("arg", args.remain.name); } @@ -1025,9 +197,10 @@ SCParser.prototype._parseArgVarElement = function(type, method) { var init = null, id; + var marker, declarator; + + marker = Marker.create(this.lexer); - this.skipComment(); - this.markStart(); id = this.parseVariableIdentifier(); this.scope.add(type, id.name); @@ -1036,7 +209,9 @@ init = this[method](); } - return this.markEnd(this.createVariableDeclarator(id, init)); + declarator = Node.createVariableDeclarator(id, init); + + return marker.update().apply(declarator); }; SCParser.prototype.parseFunctionArgumentElement = function() { @@ -1053,7 +228,7 @@ SCParser.prototype.parseFunctionBody = function(match) { var elements = []; - while (this.matchKeyword("var")) { + while (this.match("var")) { elements.push(this.parseVariableDeclaration()); } @@ -1072,18 +247,18 @@ // 3. Variable Declarations SCParser.prototype.parseVariableDeclaration = function() { var declaration; + var marker; - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); this.lex(); // var - declaration = this.markEnd( - this.createVariableDeclaration( - this.parseVariableDeclarationList(), "var" - ) + declaration = Node.createVariableDeclaration( + this.parseVariableDeclarationList(), "var" ); + declaration = marker.update().apply(declaration); + this.expect(";"); return declaration; @@ -1122,11 +297,14 @@ } while (this.lookahead.type !== Token.EOF && !this.matchAny([ ",", ")", "]", ".." ])) { - this.skipComment(); - this.markStart(); - node = this.parseAssignmentExpression(); - this.markEnd(node); + var marker; + + marker = Marker.create(this.lexer); + node = this.parseAssignmentExpression(); + node = marker.update().apply(node); + nodes.push(node); + if (this.match(";")) { this.lex(); } @@ -1141,19 +319,18 @@ // 4.2 Assignment Expression SCParser.prototype.parseAssignmentExpression = function(node) { - var token; + var token, marker; if (node) { return this.parsePartialExpression(node); } - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); if (this.match("#")) { - token = this.lex(true); + token = this.lexer.lex(true); if (this.matchAny([ "[", "{" ])) { - token.restore(); + token.revert(); } else { node = this.parseDestructuringAssignmentExpression(); } @@ -1163,7 +340,7 @@ node = this.parseSimpleAssignmentExpression(); } - return this.markEnd(node); + return marker.update().apply(node); }; SCParser.prototype.parseDestructuringAssignmentExpression = function() { @@ -1174,7 +351,7 @@ this.expect("="); right = this.parseAssignmentExpression(); - node = this.createAssignmentExpression( + node = Node.createAssignmentExpression( token.value, left.list, right, left.remain ); @@ -1182,37 +359,28 @@ }; SCParser.prototype.parseSimpleAssignmentExpression = function() { - var node, left, right, token; + var node, left, right, token, marker; node = left = this.parsePartialExpression(); if (this.match("=")) { if (node.type === Syntax.CallExpression) { + marker = Marker.create(this.lexer, left); + token = this.lex(); right = this.parseAssignmentExpression(); - left.method.name = this.getAssignMethod(left.method.name); - left.args.list = node.args.list.concat(right); - /* istanbul ignore else */ - if (this.opts.range) { - left.range[1] = this.index; - } - /* istanbul ignore else */ - if (this.opts.loc) { - left.loc.end = { - line: this.lineNumber, - column: this.index - this.lineStart - }; - } - node = left; + left.method.name = renameGetterToSetter(left.method.name); + left.args.list = node.args.list.concat(right); + + node = marker.update().apply(left, true); } else { - // TODO: fix - if (!this.isLeftHandSide(left)) { + if (!isLeftHandSide(left)) { this.throwError({}, Message.InvalidLHSInAssignment); } token = this.lex(); right = this.parseAssignmentExpression(); - node = this.createAssignmentExpression( + node = Node.createAssignmentExpression( token.value, left, right ); } @@ -1221,22 +389,12 @@ return node; }; - SCParser.prototype.getAssignMethod = function(methodName) { - switch (methodName) { - case "at": - return "put"; - case "copySeries": - return "putSeries"; - } - return methodName + "_"; - }; - SCParser.prototype.parseDestructuringAssignmentLeft = function() { var params = { list: [] }, element; do { element = this.parseLeftHandSideExpression(); - if (!this.isLeftHandSide(element)) { + if (!isLeftHandSide(element)) { this.throwError({}, Message.InvalidLHSInAssignment); } params.list.push(element); @@ -1245,7 +403,7 @@ } else if (this.match("...")) { this.lex(); params.remain = this.parseLeftHandSideExpression(); - if (!this.isLeftHandSide(params.remain)) { + if (!isLeftHandSide(params.remain)) { this.throwError({}, Message.InvalidLHSInAssignment); } break; @@ -1274,20 +432,12 @@ args = new Array(this.state.underscore.length); for (i = 0, imax = args.length; i < imax; ++i) { x = this.state.underscore[i]; - y = this.createVariableDeclarator(x); - /* istanbul ignore else */ - if (x.range) { - y.range = x.range; - } - /* istanbul ignore else */ - if (x.loc) { - y.loc = x.loc; - } - args[i] = y; + y = Node.createVariableDeclarator(x); + args[i] = Marker.create(this.lexer, x).update(x).apply(y); this.scope.add("arg", this.state.underscore[i].name); } - return this.createFunctionExpression( + return Node.createFunctionExpression( { list: args }, [ node ], false, true, false ); }); @@ -1304,13 +454,11 @@ SCParser.prototype.parseBinaryExpression = function(node) { var marker, left, token, prec; - this.skipComment(); - - marker = this.createLocationMarker(); + marker = Marker.create(this.lexer); left = this.parseUnaryExpression(node); token = this.lookahead; - prec = this.binaryPrecedence(token); + prec = calcBinaryPrecedence(token, this.binaryPrecedence); if (prec === 0) { if (node) { return this.parseUnaryExpression(node); @@ -1319,7 +467,7 @@ } this.lex(); - token.prec = prec; + token.prec = prec; token.adverb = this.parseAdverb(); return this.sortByBinaryPrecedence(left, token, marker); @@ -1331,26 +479,25 @@ var markers, i; var right, stack; - markers = [ marker, this.createLocationMarker() ]; + markers = [ marker, Marker.create(this.lexer) ]; right = this.parseUnaryExpression(); stack = [ left, operator, right ]; - while ((prec = this.binaryPrecedence(this.lookahead)) > 0) { + while ((prec = calcBinaryPrecedence(this.lookahead, this.binaryPrecedence)) > 0) { // Reduce: make a binary expression from the three topmost entries. while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { right = stack.pop(); operator = stack.pop(); left = stack.pop(); - expr = this.createBinaryExpression(operator, left, right); + expr = Node.createBinaryExpression(operator, left, right); markers.pop(); marker = markers.pop(); - /* istanbul ignore else */ - if (marker) { - marker.apply(expr); - } + marker.update().apply(expr); + stack.push(expr); + markers.push(marker); } @@ -1360,7 +507,8 @@ token.adverb = this.parseAdverb(); stack.push(token); - markers.push(this.createLocationMarker()); + + markers.push(Marker.create(this.lexer)); expr = this.parseUnaryExpression(); stack.push(expr); } @@ -1370,48 +518,15 @@ expr = stack[i]; markers.pop(); while (i > 1) { - expr = this.createBinaryExpression(stack[i - 1], stack[i - 2], expr); + expr = Node.createBinaryExpression(stack[i - 1], stack[i - 2], expr); i -= 2; marker = markers.pop(); - /* istanbul ignore else */ - if (marker) { - marker.apply(expr); - } + marker.update().apply(expr); } return expr; }; - SCParser.prototype.binaryPrecedence = function(token) { - var table, prec = 0; - - if (this.opts.binaryPrecedence) { - if (typeof this.opts.binaryPrecedence === "object") { - table = this.opts.binaryPrecedence; - } else { - table = binaryPrecedenceDefaults; - } - } else { - table = {}; - } - switch (token.type) { - case Token.Punctuator: - if (token.value !== "=") { - if (table.hasOwnProperty(token.value)) { - prec = table[token.value]; - } else if (/^[-+*\/%<=>!?&|@]+$/.test(token.value)) { - prec = 255; - } - } - break; - case Token.Label: - prec = 255; - break; - } - - return prec; - }; - SCParser.prototype.parseAdverb = function() { var adverb, lookahead; @@ -1442,18 +557,19 @@ // 4.6 Unary Expressions SCParser.prototype.parseUnaryExpression = function(node) { var token, expr; + var marker; - this.markStart(); + marker = Marker.create(this.lexer); if (this.match("`")) { token = this.lex(); expr = this.parseUnaryExpression(); - expr = this.createUnaryExpression(token.value, expr); + expr = Node.createUnaryExpression(token.value, expr); } else { expr = this.parseLeftHandSideExpression(node); } - return this.markEnd(expr); + return marker.update().apply(expr); }; // 4.7 LeftHandSide Expressions @@ -1461,10 +577,8 @@ var marker, expr, prev, lookahead; var blocklist, stamp; - this.skipComment(); - - marker = this.createLocationMarker(); - expr = this.parsePrimaryExpression(node); + marker = Marker.create(this.lexer); + expr = this.parsePrimaryExpression(node); blocklist = false; @@ -1490,11 +604,8 @@ expr = this.parseLeftHandSideDot(expr); break; } + marker.update().apply(expr, true); - /* istanbul ignore else */ - if (marker) { - marker.apply(expr); - } prev = stamp; } @@ -1502,7 +613,7 @@ }; SCParser.prototype.parseLeftHandSideParenthesis = function(expr) { - if (this.isClassName(expr)) { + if (isClassName(expr)) { return this.parseLeftHandSideClassNew(expr); } @@ -1512,10 +623,12 @@ SCParser.prototype.parseLeftHandSideClassNew = function(expr) { var method, args; - method = this.markTouch(this.createIdentifier("new")); + method = Node.createIdentifier("new"); + method = Marker.create(this.lexer).apply(method); + args = this.parseCallArgument(); - return this.createCallExpression(expr, method, args, "("); + return Node.createCallExpression(expr, method, args, "("); }; SCParser.prototype.parseLeftHandSideMethodCall = function(expr) { @@ -1541,7 +654,7 @@ } // max(0, 1) -> 0.max(1) - return this.createCallExpression(expr, method, args, "("); + return Node.createCallExpression(expr, method, args, "("); }; SCParser.prototype.parseLeftHandSideClosedBrace = function(expr) { @@ -1564,11 +677,12 @@ this.throwUnexpected(this.lookahead); } if (expr.type === Syntax.Identifier) { - if (this.isClassName(expr)) { - method = this.markTouch(this.createIdentifier("new")); - expr = this.createCallExpression(expr, method, { list: [] }, "{"); + if (isClassName(expr)) { + method = Node.createIdentifier("new"); + method = Marker.create(this.lexer).apply(method); + expr = Node.createCallExpression(expr, method, { list: [] }, "{"); } else { - expr = this.createCallExpression(null, expr, { list: [] }); + expr = Node.createCallExpression(null, expr, { list: [] }); } } lookahead = this.lookahead; @@ -1593,7 +707,7 @@ this.throwUnexpected(this.lookahead); } - if (this.isClassName(expr)) { + if (isClassName(expr)) { expr = this.parseLeftHandSideNewFrom(expr); } else { expr = this.parseLeftHandSideListAt(expr); @@ -1604,21 +718,23 @@ SCParser.prototype.parseLeftHandSideNewFrom = function(expr) { var node, method; + var marker; - method = this.markTouch(this.createIdentifier("newFrom")); + method = Node.createIdentifier("newFrom"); + method = Marker.create(this.lexer).apply(method); - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); + node = this.parseListInitialiser(); + node = marker.update().apply(node); - node = this.markEnd(this.parseListInitialiser()); - - return this.createCallExpression(expr, method, { list: [ node ] }, "["); + return Node.createCallExpression(expr, method, { list: [ node ] }, "["); }; SCParser.prototype.parseLeftHandSideListAt = function(expr) { var indexes, method; - method = this.markTouch(this.createIdentifier("at")); + method = Node.createIdentifier("at"); + method = Marker.create(this.lexer).apply(method); indexes = this.parseListIndexer(); if (indexes) { @@ -1629,7 +745,7 @@ this.throwUnexpected(this.lookahead); } - return this.createCallExpression(expr, method, { list: indexes }, "["); + return Node.createCallExpression(expr, method, { list: indexes }, "["); }; SCParser.prototype.parseLeftHandSideDot = function(expr) { @@ -1649,38 +765,34 @@ if (this.match("(")) { // expr.method(args) args = this.parseCallArgument(); - return this.createCallExpression(expr, method, args); + return Node.createCallExpression(expr, method, args); } // expr.method - return this.createCallExpression(expr, method, { list: [] }); + return Node.createCallExpression(expr, method, { list: [] }); }; SCParser.prototype.parseLeftHandSideDotValue = function(expr) { var method, args; - method = this.markTouch(this.createIdentifier("value")); + method = Node.createIdentifier("value"); + method = Marker.create(this.lexer).apply(method); + args = this.parseCallArgument(); - return this.createCallExpression(expr, method, args, "."); + return Node.createCallExpression(expr, method, args, "."); }; SCParser.prototype.parseLeftHandSideDotBracket = function(expr) { - var save, method; + var method, marker; - save = expr; - method = this.markTouch(this.createIdentifier("value")); - expr = this.markTouch(this.createCallExpression(expr, method, { list: [] }, ".")); + marker = Marker.create(this.lexer, expr); - /* istanbul ignore else */ - if (this.opts.range) { - expr.range[0] = save.range[0]; - } + method = Node.createIdentifier("value"); + method = Marker.create(this.lexer).apply(method); - /* istanbul ignore else */ - if (this.opts.loc) { - expr.loc.start = save.loc.start; - } + expr = Node.createCallExpression(expr, method, { list: [] }, "."); + expr = marker.update().apply(expr); return this.parseLeftHandSideListAt(expr); }; @@ -1827,25 +939,27 @@ }; SCParser.prototype.parseProperty = function() { - var token; + var token, id; + var marker; - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); token = this.lex(); - if (token.type !== Token.Identifier || this.isClassName(token)) { + if (token.type !== Token.Identifier || isClassName(token)) { this.throwUnexpected(token); } - return this.markEnd(this.createIdentifier(token.value)); + id = Node.createIdentifier(token.value); + + return marker.update().apply(id); }; // 4.8 Primary Expressions SCParser.prototype.parseArgumentableValue = function() { var expr, stamp; + var marker; - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); stamp = this.matchAny([ "(", "{", "[", "#" ]) || this.lookahead.type; @@ -1860,7 +974,7 @@ case Token.NilLiteral: case Token.SymbolLiteral: case Token.TrueLiteral: - expr = this.createLiteral(this.lex()); + expr = Node.createLiteral(this.lex()); break; } @@ -1869,22 +983,22 @@ this.throwUnexpected(this.lex()); } - return this.markEnd(expr); + return marker.update().apply(expr); }; SCParser.prototype.parsePrimaryExpression = function(node) { var expr, stamp; + var marker; if (node) { return node; } - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); if (this.match("~")) { this.lex(); - expr = this.createGlobalExpression(this.parseIdentifier()); + expr = Node.createGlobalExpression(this.parseIdentifier()); } else { stamp = this.matchAny([ "(", "{", "[", "#" ]) || this.lookahead.type; switch (stamp) { @@ -1907,20 +1021,12 @@ expr = this.parsePrimaryStringExpression(); break; default: - // case "#": - // case Token.CharLiteral: - // case Token.FloatLiteral: - // case Token.FalseLiteral: - // case Token.IntegerLiteral: - // case Token.NilLiteral: - // case Token.SymbolLiteral: - // case Token.TrueLiteral: expr = this.parseArgumentableValue(stamp); break; } } - return this.markEnd(expr); + return marker.update().apply(expr); }; SCParser.prototype.parsePrimaryHashedExpression = function() { @@ -1981,7 +1087,7 @@ this.throwUnexpected(this.lookahead); } - return this.createThisExpression(this.lex().value); + return Node.createThisExpression(this.lex().value); }; SCParser.prototype.parsePrimaryIdentifier = function() { @@ -2013,7 +1119,7 @@ return this.parseInterpolatedString(token.value); } - return this.createLiteral(token); + return Node.createLiteral(token); }; SCParser.prototype.parseInterpolatedString = function(value) { @@ -2052,60 +1158,16 @@ code = items.join("++"); parser = new SCParser(code, {}); - parser.peek(); return parser.parseExpression(); }; - var findString$InterpolatedString = function(value, index) { - var len, ch; - - len = value.length; - - while (index < len) { - ch = value.charAt(index); - if (ch === "#") { - if (value.charAt(index + 1) === "{") { - break; - } - } else if (ch === "\\") { - index += 1; - } - index += 1; - } - - return index; - }; - - var findExpression$InterpolatedString = function(value, index) { - var len, depth, ch; - - len = value.length; - - depth = 0; - while (index < len) { - ch = value.charAt(index); - if (ch === "}") { - if (depth === 0) { - break; - } - depth -= 1; - } else if (ch === "{") { - depth += 1; - } - index += 1; - } - - return index; - }; - // ( ... ) SCParser.prototype.parseParentheses = function() { - var marker, expr, generator, items; + var marker, expr, generator; - this.skipComment(); + marker = Marker.create(this.lexer); - marker = this.createLocationMarker(); this.expect("("); if (this.match(":")) { @@ -2115,33 +1177,28 @@ if (this.lookahead.type === Token.Label) { expr = this.parseObjectInitialiser(); - } else if (this.matchKeyword("var")) { + } else if (this.match("var")) { expr = this.withScope(function() { var body; body = this.parseFunctionBody(")"); - return this.createBlockExpression(body); + return Node.createBlockExpression(body); }); } else if (this.match("..")) { expr = this.parseSeriesInitialiser(null, generator); } else if (this.match(")")) { - expr = this.createObjectExpression([]); + expr = Node.createObjectExpression([]); } else { - items = this.parseParenthesesGuess(generator, marker); - expr = items[0]; - marker = items[1]; + expr = this.parseParenthesesGuess(generator, marker); } this.expect(")"); - /* istanbul ignore else */ - if (marker) { - marker.apply(expr); - } + marker.update().apply(expr); return expr; }; - SCParser.prototype.parseParenthesesGuess = function(generator, marker) { + SCParser.prototype.parseParenthesesGuess = function(generator) { var node, expr; node = this.parseExpression(); @@ -2154,13 +1211,11 @@ if (this.matchAny([ ",", ".." ])) { expr = this.parseSeriesInitialiser(expr, generator); } - marker = null; } else { expr = this.parseExpression(node); - marker = null; } - return [ expr, marker ]; + return expr; }; SCParser.prototype.parseObjectInitialiser = function(node) { @@ -2195,7 +1250,7 @@ this.state.innerElements = innerElements; - return this.createObjectExpression(elements); + return Node.createObjectExpression(elements); }; SCParser.prototype.parseSeriesInitialiser = function(node, generator) { @@ -2205,9 +1260,8 @@ innerElements = this.state.innerElements; this.state.innerElements = true; - method = this.markTouch(this.createIdentifier( - generator ? "seriesIter" : "series" - )); + method = Node.createIdentifier(generator ? "seriesIter" : "series"); + method = Marker.create(this.lexer).apply(method); if (node === null) { // (..), (..last) @@ -2218,18 +1272,19 @@ this.state.innerElements = innerElements; - return this.createCallExpression(items.shift(), method, { list: items }); + return Node.createCallExpression(items.shift(), method, { list: items }); }; SCParser.prototype.parseSeriesInitialiserWithoutFirst = function(generator) { var first, last = null; // (..last) - first = this.markTouch({ + first = { type: Syntax.Literal, value: "0", valueType: Token.IntegerLiteral - }); + }; + first = Marker.create(this.lexer).apply(first); this.expect(".."); if (this.match(")")) { @@ -2302,15 +1357,15 @@ this.state.innerElements = innerElements; - return this.createListExpression(elements, this.state.immutableList); + return Node.createListExpression(elements, this.state.immutableList); }; // { ... } SCParser.prototype.parseBraces = function(blocklist) { var expr; + var marker; - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); this.expect("{"); @@ -2328,7 +1383,7 @@ this.expect("}"); - return this.markEnd(expr); + return marker.update().apply(expr); }; SCParser.prototype.parseGeneratorInitialiser = function() { @@ -2344,17 +1399,23 @@ } } - return this.createLiteral({ value: "null", valueType: Token.NilLiteral }); + return Node.createLiteral({ value: "null", valueType: Token.NilLiteral }); }; SCParser.prototype.parseLabel = function() { - this.skipComment(); - this.markStart(); - return this.markEnd(this.createLabel(this.lex().value)); + var label, marker; + + marker = Marker.create(this.lexer); + + label = Node.createLabel(this.lex().value); + + return marker.update().apply(label); }; SCParser.prototype.parseLabelAsSymbol = function() { - var label, node; + var marker, label, node; + + marker = Marker.create(this.lexer); label = this.parseLabel(); node = { @@ -2363,38 +1424,32 @@ valueType: Token.SymbolLiteral }; - /* istanbul ignore else */ - if (label.range) { - node.range = label.range; - } - /* istanbul ignore else */ - if (label.loc) { - node.loc = label.loc; - } + node = marker.update().apply(node); return node; }; SCParser.prototype.parseIdentifier = function() { var expr; + var marker; - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); if (this.lookahead.type !== Syntax.Identifier) { this.throwUnexpected(this.lookahead); } expr = this.lex(); + expr = Node.createIdentifier(expr.value); - return this.markEnd(this.createIdentifier(expr.value)); + return marker.update().apply(expr); }; SCParser.prototype.parseVariableIdentifier = function() { var token, value, ch; + var id, marker; - this.skipComment(); - this.markStart(); + marker = Marker.create(this.lexer); token = this.lex(); value = token.value; @@ -2408,143 +1463,62 @@ } } - return this.markEnd(this.createIdentifier(value)); - }; + id = Node.createIdentifier(value); - SCParser.prototype.markStart = function() { - /* istanbul ignore else */ - if (this.opts.loc) { - this.marker.push( - this.index - this.lineStart, - this.lineNumber - ); - } - /* istanbul ignore else */ - if (this.opts.range) { - this.marker.push( - this.index - ); - } + return marker.update().apply(id); }; - SCParser.prototype.markEnd = function(node) { - if (Array.isArray(node) || node.range || node.loc) { - /* istanbul ignore else */ - if (this.opts.range) { - this.marker.pop(); - } - /* istanbul ignore else */ - if (this.opts.loc) { - this.marker.pop(); - this.marker.pop(); - } - } else { - /* istanbul ignore else */ - if (this.opts.range) { - node.range = [ this.marker.pop(), this.index ]; - } - /* istanbul ignore else */ - if (this.opts.loc) { - node.loc = { - start: { - line: this.marker.pop(), - column: this.marker.pop() - }, - end: { - line: this.lineNumber, - column: this.index - this.lineStart - } - }; - } + var renameGetterToSetter = function(methodName) { + switch (methodName) { + case "at" : return "put"; + case "copySeries": return "putSeries"; } - return node; + return methodName + "_"; }; - SCParser.prototype.markTouch = function(node) { - /* istanbul ignore else */ - if (this.opts.range) { - node.range = [ this.index, this.index ]; - } - /* istanbul ignore else */ - if (this.opts.loc) { - node.loc = { - start: { - line: this.lineNumber, - column: this.index - this.lineStart - }, - end: { - line: this.lineNumber, - column: this.index - this.lineStart + var calcBinaryPrecedence = function(token, binaryPrecedence) { + var prec = 0; + + switch (token.type) { + case Token.Punctuator: + if (token.value !== "=") { + if (binaryPrecedence.hasOwnProperty(token.value)) { + prec = binaryPrecedence[token.value]; + } else if (/^[-+*\/%<=>!?&|@]+$/.test(token.value)) { + prec = 255; } - }; + } + break; + case Token.Label: + prec = 255; + break; } - return node; - }; - SCParser.prototype.throwError = function(token, messageFormat) { - var args, message; - var error, index, lineNumber, column; - var prev; + return prec; + }; - args = Array.prototype.slice.call(arguments, 2); - message = messageFormat.replace(/%(\d)/g, function(whole, index) { - return args[index]; - }); + var isClassName = function(node) { + var name, ch; - if (typeof token.lineNumber === "number") { - index = token.range[0]; - lineNumber = token.lineNumber; - column = token.range[0] - token.lineStart + 1; - } else { - index = this.index; - lineNumber = this.lineNumber; - column = this.index - this.lineStart + 1; + if (node.type === Syntax.Identifier) { + name = node.value || node.name; + ch = name.charAt(0); + return "A" <= ch && ch <= "Z"; } - error = new Error("Line " + lineNumber + ": " + message); - error.index = index; - error.lineNumber = lineNumber; - error.column = column; - error.description = message; - - if (this.errors) { - prev = this.errors[this.errors.length - 1]; - if (!(prev && error.index <= prev.index)) { - this.errors.push(error); - } - } else { - throw error; - } + return false; }; - SCParser.prototype.throwUnexpected = function(token) { - switch (token.type) { - case Token.EOF: - this.throwError(token, Message.UnexpectedEOS); - break; - case Token.FloatLiteral: - case Token.IntegerLiteral: - this.throwError(token, Message.UnexpectedNumber); - break; - case Token.CharLiteral: - this.throwError(token, Message.UnexpectedChar); - break; - case Token.StringLiteral: - this.throwError(token, Message.UnexpectedString); - break; - case Token.SymbolLiteral: - this.throwError(token, Message.UnexpectedSymbol); - break; - case Token.Identifier: - this.throwError(token, Message.UnexpectedIdentifier); - break; - default: - this.throwError(token, Message.UnexpectedToken, token.value); - break; + var isLeftHandSide = function(expr) { + switch (expr.type) { + case Syntax.Identifier: + case Syntax.GlobalExpression: + return true; } + return false; }; - function isValidArgumentValue(node) { + var isValidArgumentValue = function(node) { if (node.type === Syntax.Literal) { return true; } @@ -2555,7 +1529,49 @@ } return false; - } + }; + + var findString$InterpolatedString = function(value, index) { + var len, ch; + + len = value.length; + + while (index < len) { + ch = value.charAt(index); + if (ch === "#") { + if (value.charAt(index + 1) === "{") { + break; + } + } else if (ch === "\\") { + index += 1; + } + index += 1; + } + + return index; + }; + + var findExpression$InterpolatedString = function(value, index) { + var len, depth, ch; + + len = value.length; + + depth = 0; + while (index < len) { + ch = value.charAt(index); + if (ch === "}") { + if (depth === 0) { + break; + } + depth -= 1; + } else if (ch === "{") { + depth += 1; + } + index += 1; + } + + return index; + }; parser.parse = function(source, opts) { var instance, ast; @@ -2565,11 +1581,11 @@ instance = new SCParser(source, opts); ast = instance.parse(); - if (!!opts.tokens && typeof instance.tokens !== "undefined") { - ast.tokens = instance.tokens; + if (!!opts.tokens && typeof instance.lexer.tokens !== "undefined") { + ast.tokens = instance.lexer.tokens; } - if (!!opts.tolerant && typeof instance.errors !== "undefined") { - ast.errors = instance.errors; + if (!!opts.tolerant && typeof instance.lexer.errors !== "undefined") { + ast.errors = instance.lexer.errors; } return ast; diff --git a/src/sc/lang/parser_test.js b/src/sc/lang/compiler/parser_test.js similarity index 100% rename from src/sc/lang/parser_test.js rename to src/sc/lang/compiler/parser_test.js diff --git a/src/sc/lang/compiler/sc.js b/src/sc/lang/compiler/sc.js new file mode 100644 index 0000000..88d99f2 --- /dev/null +++ b/src/sc/lang/compiler/sc.js @@ -0,0 +1 @@ +require("../sc"); diff --git a/src/sc/lang/compiler/scope.js b/src/sc/lang/compiler/scope.js new file mode 100644 index 0000000..0e995a6 --- /dev/null +++ b/src/sc/lang/compiler/scope.js @@ -0,0 +1,99 @@ +(function(sc) { + "use strict"; + + require("./compiler"); + + var Message = sc.lang.compiler.Message; + + function Scope(methods) { + var f = function(parent) { + this.parent = parent; + this.stack = []; + }; + + function F() {} + F.prototype = Scope; + f.prototype = new F(); + + Object.keys(methods).forEach(function(key) { + f.prototype[key] = methods[key]; + }); + + return f; + } + + Scope.add = function(type, id, scope) { + var peek = this.stack[this.stack.length - 1]; + var vars, args, declared, stmt, indent; + + if (scope) { + vars = scope.vars; + args = scope.args; + declared = scope.declared; + stmt = scope.stmt; + indent = scope.indent; + } else { + vars = peek.vars; + args = peek.args; + declared = peek.declared; + stmt = peek.stmt; + indent = peek.indent; + } + + if (args[id]) { + this.parent.throwError({}, Message.ArgumentAlreadyDeclared, id); + } + + if (vars[id] && id.charAt(0) !== "_") { + this.parent.throwError({}, Message.VariableAlreadyDeclared, id); + } + + switch (type) { + case "var": + if (!vars[id]) { + this.add_delegate(stmt, id, indent, peek, scope); + vars[id] = true; + delete declared[id]; + } + break; + case "arg": + args[id] = true; + delete declared[id]; + break; + } + }; + + Scope.add_delegate = function() { + }; + + Scope.end = function() { + this.stack.pop(); + }; + + Scope.getDeclaredVariable = function() { + var peek = this.stack[this.stack.length - 1]; + var declared = {}; + + if (peek) { + Array.prototype.concat.apply([], [ + peek.declared, peek.args, peek.vars + ].map(Object.keys)).forEach(function(key) { + declared[key] = true; + }); + } + + return declared; + }; + + Scope.find = function(id) { + var peek = this.stack[this.stack.length - 1]; + return peek.vars[id] || peek.args[id] || peek.declared[id]; + }; + + Scope.peek = function() { + return this.stack[this.stack.length - 1]; + }; + + sc.lang.compiler.Scope = Scope; + +})(sc); diff --git a/src/sc/lang/fn.js b/src/sc/lang/fn.js index bdcd8cc..f12434f 100644 --- a/src/sc/lang/fn.js +++ b/src/sc/lang/fn.js @@ -2,8 +2,6 @@ "use strict"; require("./dollarSC"); - require("./compiler"); - require("./parser"); var slice = [].slice; var $SC = sc.lang.$SC; diff --git a/src/sc/lang/fn_test.js b/src/sc/lang/fn_test.js index 0091daf..75e82d9 100644 --- a/src/sc/lang/fn_test.js +++ b/src/sc/lang/fn_test.js @@ -2,7 +2,7 @@ "use strict"; require("./fn"); - require("./klass-utils"); + require("./klass/utils"); var fn = sc.lang.fn; var $SC = sc.lang.$SC; diff --git a/src/sc/lang/installer.js b/src/sc/lang/installer.js index d231ebc..5c0a3d0 100644 --- a/src/sc/lang/installer.js +++ b/src/sc/lang/installer.js @@ -1,3 +1,2 @@ -require("./parser"); -require("./codegen"); require("./classlib/installer"); +require("./compiler"); diff --git a/src/sc/lang/iterator.js b/src/sc/lang/iterator.js index dfa4658..f6add26 100644 --- a/src/sc/lang/iterator.js +++ b/src/sc/lang/iterator.js @@ -3,7 +3,7 @@ require("./sc"); require("./dollarSC"); - require("./klass-utils"); + require("./klass/utils"); var iterator = {}; var $SC = sc.lang.$SC; diff --git a/src/sc/lang/klass.js b/src/sc/lang/klass.js index 3e382e1..0818633 100644 --- a/src/sc/lang/klass.js +++ b/src/sc/lang/klass.js @@ -1,278 +1,3 @@ -(function(sc) { - "use strict"; - - require("./sc"); - require("./dollarSC"); - - var slice = [].slice; - var $SC = sc.lang.$SC; - - var klass = {}; - var metaClasses = {}; - var classes = klass.classes = {}; - - var createClassInstance = function(MetaSpec) { - var instance = new SCClass(); - instance._MetaSpec = MetaSpec; - return instance; - }; - - var extend = function(constructor, superMetaClass) { - function F() {} - F.prototype = superMetaClass._Spec.prototype; - constructor.prototype = new F(); - - function Meta_F() {} - Meta_F.prototype = superMetaClass._MetaSpec.prototype; - - function MetaSpec() {} - MetaSpec.prototype = new Meta_F(); - - constructor.metaClass = createClassInstance(MetaSpec); - }; - - var def = function(className, constructor, fn, opts) { - var classMethods, instanceMethods, setMethod, spec; - - classMethods = constructor.metaClass._MetaSpec.prototype; - instanceMethods = constructor.prototype; - - setMethod = function(methods, methodName, func) { - var bond; - if (methods.hasOwnProperty(methodName) && !opts.force) { - bond = methods === classMethods ? "." : "#"; - throw new Error( - "sc.lang.klass.refine: " + - className + bond + methodName + " is already defined." - ); - } - methods[methodName] = func; - }; - - if (typeof fn === "function") { - fn(spec = {}, klass.utils); - } else { - spec = fn; - } - - Object.keys(spec).forEach(function(methodName) { - if (methodName.charCodeAt(0) === 0x24) { // u+0024 is '$' - setMethod(classMethods, methodName.substr(1), spec[methodName]); - } else { - setMethod(instanceMethods, methodName, spec[methodName]); - } - }); - }; - - var throwIfInvalidArgument = function(constructor, className) { - if (typeof constructor !== "function") { - throw new Error( - "sc.lang.klass.define: " + - "first argument must be a constructor, but got: " + typeof(constructor) - ); - } - - if (typeof className !== "string") { - throw new Error( - "sc.lang.klass.define: " + - "second argument must be a string, but got: " + String(className) - ); - } - }; - - var throwIfInvalidClassName = function(className, superClassName) { - var ch0 = className.charCodeAt(0); - - if (ch0 < 0x41 || 0x5a < ch0) { // faster test than !/^[A-Z]/.test(className) - throw new Error( - "sc.lang.klass.define: " + - "classname should be CamelCase, but got '" + className + "'" - ); - } - - if (metaClasses.hasOwnProperty(className)) { - throw new Error( - "sc.lang.klass.define: " + - "class '" + className + "' is already registered." - ); - } - - if (className !== "Object") { - if (!metaClasses.hasOwnProperty(superClassName)) { - throw new Error( - "sc.lang.klass.define: " + - "superclass '" + superClassName + "' is not registered." - ); - } - } - }; - - var buildClass = function(className, constructor) { - var newClass, metaClass; - - metaClass = constructor.metaClass; - - newClass = new metaClass._MetaSpec(); - newClass._name = className; - newClass._Spec = constructor; - constructor.prototype.__class = newClass; - constructor.prototype.__Spec = constructor; - - metaClass._Spec = constructor; - metaClass._isMetaClass = true; - metaClass._name = "Meta_" + className; - - classes["Meta_" + className] = metaClass; - classes[className] = newClass; - - if (newClass.initClass) { - newClass.initClass(); - } - - metaClasses[className] = metaClass; - }; - - klass.define = function(constructor, className, fn) { - var items, superClassName; - - throwIfInvalidArgument(constructor, className); - - items = className.split(":"); - className = items[0].trim(); - superClassName = (items[1] || "Object").trim(); - - throwIfInvalidClassName(className, superClassName); - - if (className !== "Object") { - extend(constructor, metaClasses[superClassName]); - } - - fn = fn || {}; - - def(className, constructor, fn, {}); - - buildClass(className, constructor); - }; - - klass.refine = function(className, fn, opts) { - var constructor; - - if (!metaClasses.hasOwnProperty(className)) { - throw new Error( - "sc.lang.klass.refine: " + - "class '" + className + "' is not registered." - ); - } - - constructor = metaClasses[className]._Spec; - - def(className, constructor, fn, opts || {}); - }; - - klass.get = function(name) { - if (!classes[name]) { - throw new Error( - "sc.lang.klass.get: " + - "class '" + name + "' is not registered." - ); - } - return classes[name]; - }; - - klass.exists = function(name) { - return !!classes[name]; - }; - - // basic classes - function SCObject() { - this._ = this; - } - - function SCClass() { - this._ = this; - this._name = "Class"; - this._Spec = null; - this._isMetaClass = false; - } - - SCObject.metaClass = createClassInstance(function() {}); - klass.define(SCObject, "Object", { - __tag: sc.C.TAG_OBJ, - __initializeWith__: function(className, args) { - metaClasses[className]._Spec.apply(this, args); - }, - $initClass: function() {} - }); - - klass.define(SCClass, "Class"); - - SCObject.metaClass._MetaSpec.prototype = classes.Class = createClassInstance(); - classes.Class._Spec = SCClass; - classes.Object = new SCObject.metaClass._MetaSpec(); - classes.Object._name = "Object"; - classes.Object._Spec = SCObject.metaClass._Spec; - classes.Object._Spec.prototype.__class = classes.Object; - classes.Object._Spec.prototype.__Spec = classes.Object._Spec; - - klass.refine("Object", function(spec) { - spec.$new = function() { - if (this._Spec === SCClass) { - return $SC.Nil(); - } - return new this._Spec(slice.call(arguments)); - }; - - spec.class = function() { - return this.__class; - }; - - spec.isClass = function() { - return $SC.False(); - }; - - spec.isKindOf = function($aClass) { - return $SC.Boolean(this instanceof $aClass._Spec); - }; - - spec.isMemberOf = function($aClass) { - return $SC.Boolean(this.__class === $aClass); - }; - - spec.toString = function() { - var name = this.__class._name; - if (/^[AEIOU]/.test(name)) { - return String("an " + name); - } else { - return String("a " + name); - } - }; - - spec.valueOf = function() { - return this._; - }; - }); - - klass.refine("Class", function(spec) { - spec.name = function() { - return $SC.String(this._name); - }; - - spec.class = function() { - if (this._isMetaClass) { - return classes.Class; - } - return $SC("Meta_" + this._name); - }; - - spec.isClass = function() { - return $SC.True(); - }; - - spec.toString = function() { - return String(this._name); - }; - }); - - sc.lang.klass = klass; - -})(sc); +require("./klass/klass"); +require("./klass/utils"); +require("./klass/constructors"); diff --git a/src/sc/lang/klass-constructors.js b/src/sc/lang/klass/constructors.js similarity index 98% rename from src/sc/lang/klass-constructors.js rename to src/sc/lang/klass/constructors.js index 88870b8..af8b857 100644 --- a/src/sc/lang/klass-constructors.js +++ b/src/sc/lang/klass/constructors.js @@ -1,11 +1,9 @@ (function(sc) { "use strict"; - require("./sc"); require("./klass"); - require("./dollarSC"); - var $SC = sc.lang.$SC; + var $SC = sc.lang.$SC; var klass = sc.lang.klass; function SCNil() { diff --git a/src/sc/lang/klass-constructors_test.js b/src/sc/lang/klass/constructors_test.js similarity index 98% rename from src/sc/lang/klass-constructors_test.js rename to src/sc/lang/klass/constructors_test.js index 5b99a60..ca1885aea 100644 --- a/src/sc/lang/klass-constructors_test.js +++ b/src/sc/lang/klass/constructors_test.js @@ -1,7 +1,7 @@ (function() { "use strict"; - require("./klass-constructors"); + require("./constructors"); var $SC = sc.lang.$SC; diff --git a/src/sc/lang/klass/klass.js b/src/sc/lang/klass/klass.js new file mode 100644 index 0000000..e4811b4 --- /dev/null +++ b/src/sc/lang/klass/klass.js @@ -0,0 +1,278 @@ +(function(sc) { + "use strict"; + + require("../sc"); + require("../dollarSC"); + + var slice = [].slice; + var $SC = sc.lang.$SC; + + var klass = {}; + var metaClasses = {}; + var classes = klass.classes = {}; + + var createClassInstance = function(MetaSpec) { + var instance = new SCClass(); + instance._MetaSpec = MetaSpec; + return instance; + }; + + var extend = function(constructor, superMetaClass) { + function F() {} + F.prototype = superMetaClass._Spec.prototype; + constructor.prototype = new F(); + + function Meta_F() {} + Meta_F.prototype = superMetaClass._MetaSpec.prototype; + + function MetaSpec() {} + MetaSpec.prototype = new Meta_F(); + + constructor.metaClass = createClassInstance(MetaSpec); + }; + + var def = function(className, constructor, fn, opts) { + var classMethods, instanceMethods, setMethod, spec; + + classMethods = constructor.metaClass._MetaSpec.prototype; + instanceMethods = constructor.prototype; + + setMethod = function(methods, methodName, func) { + var bond; + if (methods.hasOwnProperty(methodName) && !opts.force) { + bond = methods === classMethods ? "." : "#"; + throw new Error( + "sc.lang.klass.refine: " + + className + bond + methodName + " is already defined." + ); + } + methods[methodName] = func; + }; + + if (typeof fn === "function") { + fn(spec = {}, klass.utils); + } else { + spec = fn; + } + + Object.keys(spec).forEach(function(methodName) { + if (methodName.charCodeAt(0) === 0x24) { // u+0024 is '$' + setMethod(classMethods, methodName.substr(1), spec[methodName]); + } else { + setMethod(instanceMethods, methodName, spec[methodName]); + } + }); + }; + + var throwIfInvalidArgument = function(constructor, className) { + if (typeof constructor !== "function") { + throw new Error( + "sc.lang.klass.define: " + + "first argument must be a constructor, but got: " + typeof(constructor) + ); + } + + if (typeof className !== "string") { + throw new Error( + "sc.lang.klass.define: " + + "second argument must be a string, but got: " + String(className) + ); + } + }; + + var throwIfInvalidClassName = function(className, superClassName) { + var ch0 = className.charCodeAt(0); + + if (ch0 < 0x41 || 0x5a < ch0) { // faster test than !/^[A-Z]/.test(className) + throw new Error( + "sc.lang.klass.define: " + + "classname should be CamelCase, but got '" + className + "'" + ); + } + + if (metaClasses.hasOwnProperty(className)) { + throw new Error( + "sc.lang.klass.define: " + + "class '" + className + "' is already registered." + ); + } + + if (className !== "Object") { + if (!metaClasses.hasOwnProperty(superClassName)) { + throw new Error( + "sc.lang.klass.define: " + + "superclass '" + superClassName + "' is not registered." + ); + } + } + }; + + var buildClass = function(className, constructor) { + var newClass, metaClass; + + metaClass = constructor.metaClass; + + newClass = new metaClass._MetaSpec(); + newClass._name = className; + newClass._Spec = constructor; + constructor.prototype.__class = newClass; + constructor.prototype.__Spec = constructor; + + metaClass._Spec = constructor; + metaClass._isMetaClass = true; + metaClass._name = "Meta_" + className; + + classes["Meta_" + className] = metaClass; + classes[className] = newClass; + + if (newClass.initClass) { + newClass.initClass(); + } + + metaClasses[className] = metaClass; + }; + + klass.define = function(constructor, className, fn) { + var items, superClassName; + + throwIfInvalidArgument(constructor, className); + + items = className.split(":"); + className = items[0].trim(); + superClassName = (items[1] || "Object").trim(); + + throwIfInvalidClassName(className, superClassName); + + if (className !== "Object") { + extend(constructor, metaClasses[superClassName]); + } + + fn = fn || {}; + + def(className, constructor, fn, {}); + + buildClass(className, constructor); + }; + + klass.refine = function(className, fn, opts) { + var constructor; + + if (!metaClasses.hasOwnProperty(className)) { + throw new Error( + "sc.lang.klass.refine: " + + "class '" + className + "' is not registered." + ); + } + + constructor = metaClasses[className]._Spec; + + def(className, constructor, fn, opts || {}); + }; + + klass.get = function(name) { + if (!classes[name]) { + throw new Error( + "sc.lang.klass.get: " + + "class '" + name + "' is not registered." + ); + } + return classes[name]; + }; + + klass.exists = function(name) { + return !!classes[name]; + }; + + // basic classes + function SCObject() { + this._ = this; + } + + function SCClass() { + this._ = this; + this._name = "Class"; + this._Spec = null; + this._isMetaClass = false; + } + + SCObject.metaClass = createClassInstance(function() {}); + klass.define(SCObject, "Object", { + __tag: sc.C.TAG_OBJ, + __initializeWith__: function(className, args) { + metaClasses[className]._Spec.apply(this, args); + }, + $initClass: function() {} + }); + + klass.define(SCClass, "Class"); + + SCObject.metaClass._MetaSpec.prototype = classes.Class = createClassInstance(); + classes.Class._Spec = SCClass; + classes.Object = new SCObject.metaClass._MetaSpec(); + classes.Object._name = "Object"; + classes.Object._Spec = SCObject.metaClass._Spec; + classes.Object._Spec.prototype.__class = classes.Object; + classes.Object._Spec.prototype.__Spec = classes.Object._Spec; + + klass.refine("Object", function(spec) { + spec.$new = function() { + if (this._Spec === SCClass) { + return $SC.Nil(); + } + return new this._Spec(slice.call(arguments)); + }; + + spec.class = function() { + return this.__class; + }; + + spec.isClass = function() { + return $SC.False(); + }; + + spec.isKindOf = function($aClass) { + return $SC.Boolean(this instanceof $aClass._Spec); + }; + + spec.isMemberOf = function($aClass) { + return $SC.Boolean(this.__class === $aClass); + }; + + spec.toString = function() { + var name = this.__class._name; + if (/^[AEIOU]/.test(name)) { + return String("an " + name); + } else { + return String("a " + name); + } + }; + + spec.valueOf = function() { + return this._; + }; + }); + + klass.refine("Class", function(spec) { + spec.name = function() { + return $SC.String(this._name); + }; + + spec.class = function() { + if (this._isMetaClass) { + return classes.Class; + } + return $SC("Meta_" + this._name); + }; + + spec.isClass = function() { + return $SC.True(); + }; + + spec.toString = function() { + return String(this._name); + }; + }); + + sc.lang.klass = klass; + +})(sc); diff --git a/src/sc/lang/klass_test.js b/src/sc/lang/klass/klass_test.js similarity index 100% rename from src/sc/lang/klass_test.js rename to src/sc/lang/klass/klass_test.js diff --git a/src/sc/lang/klass-utils.js b/src/sc/lang/klass/utils.js similarity index 87% rename from src/sc/lang/klass-utils.js rename to src/sc/lang/klass/utils.js index 98c69b2..7035a5f 100644 --- a/src/sc/lang/klass-utils.js +++ b/src/sc/lang/klass/utils.js @@ -1,12 +1,10 @@ (function(sc) { "use strict"; - require("./sc"); - require("./dollarSC"); require("./klass"); - require("./klass-constructors"); + require("./constructors"); - var $SC = sc.lang.$SC; + var $SC = sc.lang.$SC; var klass = sc.lang.klass; var utils = { diff --git a/src/sc/lang/klass-utils_test.js b/src/sc/lang/klass/utils_test.js similarity index 97% rename from src/sc/lang/klass-utils_test.js rename to src/sc/lang/klass/utils_test.js index abe2e65..f6b868c 100644 --- a/src/sc/lang/klass-utils_test.js +++ b/src/sc/lang/klass/utils_test.js @@ -1,9 +1,9 @@ (function() { "use strict"; - require("./klass-utils"); + require("./utils"); - var $SC = sc.lang.$SC; + var $SC = sc.lang.$SC; var utils = sc.lang.klass.utils; describe("sc.lang.klass.utils", function() {