Comparing version 0.6.0 to 0.10.0
@@ -0,1 +1,15 @@ | ||
# 0.10.0 | ||
Many pending features and radical changes were added to Cor in this version, example, the new builtin function for regular expressions, also some syntax has changed. Croutines, concurrency, and parallelism were introduced in this version. | ||
* Add template literals (string interpolation) | ||
* Square brackets '[ and ']' is no longer part of the syntax in object constructors and arrays, now it uses parenthesis '(' and ')' | ||
* Add `regex` builtin function | ||
* Object and array constructors are now primary expressions | ||
* Add coroutines | ||
* Add the asynchronic operations | ||
* Add `timeout` builtin function | ||
* Add channels | ||
# 0.6.0 | ||
@@ -10,2 +24,3 @@ | ||
* Function now can be written in compact mode, e.g `func sum(a, b) a + b` | ||
* New for/in/range loop | ||
@@ -12,0 +27,0 @@ |
592
dist/crl.js
@@ -37,11 +37,13 @@ /* | ||
CRL = (typeof CRL === 'undefined' ? {} : CRL); | ||
var | ||
hasProp = Object.prototype.hasOwnProperty, | ||
slice = Array.prototype.slice; | ||
hasProp = Object.prototype.hasOwnProperty, | ||
toString = Object.prototype.toString, | ||
slice = Array.prototype.slice, | ||
CRL = (typeof CRL === 'undefined' ? {} : CRL); | ||
CRL.idSeed = 1; | ||
CRL.instancers = []; | ||
CRL.nativeTypes = { | ||
// store function that instantiate classes | ||
// whith different quantity of arguments | ||
instancers = [], | ||
nativeTypes = { | ||
'String' : String, | ||
@@ -56,16 +58,12 @@ 'Number' : Number, | ||
CRL.copyObj = function(from, to, strict) { | ||
// copy properties from an object to another | ||
// returns the object which properties has been copied to | ||
CRL.copyObj = function copyObj(from, to) { | ||
var name; | ||
if (strict) { | ||
for (name in from) { | ||
if (hasProp.call(from, name)) { | ||
to[name] = from[name]; | ||
} | ||
for (name in from) { | ||
if (hasProp.call(from, name)) { | ||
to[name] = from[name]; | ||
} | ||
} | ||
else { | ||
for (name in from) { | ||
to[name] = from[name]; | ||
} | ||
} | ||
@@ -75,3 +73,9 @@ return to; | ||
CRL.create = function(Class) { | ||
// creates an instance of a class | ||
// CRL.create(Class, arg1, arg2, ...) | ||
CRL.create = function create(Class) { | ||
if (typeof Class !== 'function') { | ||
throw Error('Runtime Error: trying to instanstiate no class'); | ||
} | ||
var | ||
@@ -82,34 +86,41 @@ instancerArgs, | ||
i = -1, | ||
instancer = this.instancers[argc]; | ||
instancer = instancers[argc]; | ||
if (! instancer) { | ||
instancerArgs = []; | ||
while (++i < argc) { | ||
instancerArgs.push('args[' + i + ']'); | ||
} | ||
this.instancers[argc] = instancer = new Function('cls', 'args', 'return new cls(' + instancerArgs.join(',') + ');'); | ||
} | ||
if (typeof Class === 'function') { | ||
return instancer(Class, args); | ||
instancer = instancers[argc] = new Function( | ||
'cls', | ||
'args', | ||
'return new cls(' + instancerArgs.join(',') + ');' | ||
); | ||
} | ||
throw Error('Runtime Error: trying to instanstiate no class'); | ||
return instancer(Class, args); | ||
}; | ||
CRL.extend = function(Cls, baseCls) { | ||
CRL.copyObj(baseCls, Cls, true); | ||
// convert a class in a subclass of other class | ||
// CRL.subclass(Subclass, Superclass) | ||
CRL.subclass = function subclass(subClass, superClass) { | ||
CRL.copyObj(superClass, subClass); | ||
function Proto() { | ||
this.constructor = Cls; | ||
this.constructor = subClass; | ||
} | ||
Proto.prototype = baseCls.prototype; | ||
Cls.prototype = new Proto(); | ||
Proto.prototype = superClass.prototype; | ||
subClass.prototype = new Proto(); | ||
} | ||
CRL.keys = function(obj) { | ||
// extract keys from an object or array | ||
// CRL.keys([5, 7, 3]) -> [0, 1, 2] | ||
// CRL.keys({x: 2, y: 4}) -> ['x', 'y'] | ||
CRL.keys = function keys(obj) { | ||
var keys, i, len; | ||
// is array | ||
if (obj instanceof Array) { | ||
@@ -123,49 +134,506 @@ i = -1; | ||
} | ||
return keys; | ||
} | ||
else { | ||
if (typeof Object.keys === 'function') { | ||
keys = Object.keys(obj); | ||
// if has key function | ||
if (typeof Object.keys === 'function') { | ||
return Object.keys(obj); | ||
} | ||
// otherwise polyfill it | ||
for (i in obj) { | ||
if (hasProp.call(obj, i)) { | ||
keys.push(i); | ||
} | ||
else { | ||
for (i in obj) { | ||
if (hasProp.call(obj, i)) { | ||
keys.push(i); | ||
} | ||
} | ||
} | ||
} | ||
return keys; | ||
}; | ||
CRL.assertType = function(obj, Class) { | ||
// whether a object is instance of a class or not | ||
// CRL.assertType({}, Object) | ||
// CRL.assertType(person, Person) | ||
CRL.assertType = function assertType(obj, Class) { | ||
var type; | ||
// Class is a Class? | ||
if (typeof Class === 'function') { | ||
// object is defined? | ||
if (typeof obj !== 'undefined') { | ||
if (obj instanceof Class) { | ||
return true; | ||
if (typeof Class !== 'function') { | ||
throw 'Trying to assert undefined class'; | ||
} | ||
if (typeof obj === 'undefined') { | ||
throw 'Trying to assert undefined object'; | ||
} | ||
// try with instanceof | ||
if (obj instanceof Class) { | ||
return true; | ||
} | ||
// try with finding the native type according to "Object.prototype.toString" | ||
type = toString.call(obj); | ||
type = type.substring(8, type.length - 1); | ||
if(hasProp.call(nativeTypes, type) && nativeTypes[type] === Class) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
CRL.regex = function regex(pattern, flags) { | ||
return new RegExp(pattern, flags); | ||
} | ||
})(); | ||
(function(global) { | ||
// Lightweight non standard compliant Promise | ||
function Promise(resolverFn) { | ||
if (typeof resolverFn !== 'function') { | ||
throw 'provided resolver must be a function'; | ||
} | ||
var p = this; | ||
// this.value; | ||
// this.reason; | ||
this.completed = false; | ||
this.thenListeners = []; | ||
this.catchListeners = []; | ||
resolverFn( | ||
function resolve(value){ | ||
Promise.doResolve(p, value); | ||
}, | ||
function reject(reason) { | ||
Promise.doReject(p, reason); | ||
} | ||
); | ||
} | ||
Promise.prototype = { | ||
then: function(fn) { | ||
this.thenListeners.push(fn); | ||
if (this.completed) { | ||
Promise.doResolve(this, this.value); | ||
} | ||
return this; | ||
}, | ||
catch: function(fn) { | ||
this.catchListeners.push(fn); | ||
if (this.completed) { | ||
Promise.doReject(this, this.reason); | ||
} | ||
return this; | ||
} | ||
}; | ||
Promise.doResolve = function resolve(p, value) { | ||
p.thenListeners.forEach(function(listener) { | ||
listener(value); | ||
}) | ||
p.completed = true; | ||
p.value = value; | ||
}; | ||
Promise.doReject = function reject(p, reason) { | ||
p.catchListeners.forEach(function(listener){ | ||
listener(reason); | ||
}) | ||
p.completed = true; | ||
p.reason = reason; | ||
}; | ||
Promise.all = function all(array) { | ||
var promise, | ||
i = -1, | ||
numPending = 0, | ||
result = [], | ||
len = array.length; | ||
return new Promise(function(resolve) { | ||
while (++i < len) { | ||
promise = array[i]; | ||
if (isPromise(promise)) { | ||
setupThen(promise, i); | ||
} | ||
// otherwise find the native type according to "Object.prototype.toString" | ||
else { | ||
type = Object.prototype.toString.call(obj); | ||
type = type.substring(8, type.length - 1); | ||
if(hasProp.call(this.nativeTypes, type) && this.nativeTypes[type] === Class) { | ||
return true; | ||
result[i] = array[i]; | ||
tryToResolve(); | ||
} | ||
} | ||
function setupThen(promise, i) { | ||
numPending++; | ||
// default value | ||
result[i] = void 0; | ||
promise.then(function(value) { | ||
result[i] = value; | ||
numPending--; | ||
tryToResolve(); | ||
}) | ||
} | ||
function tryToResolve() { | ||
if (numPending === 0) { | ||
resolve(result) | ||
} | ||
} | ||
}) | ||
} | ||
Promise.defer = function defer() { | ||
var deferred = {}; | ||
// use CRL.Promise | ||
deferred.promise = new CRL.Promise(function(resolve, reject) { | ||
deferred.resolve = resolve; | ||
deferred.reject = reject; | ||
}) | ||
return deferred; | ||
} | ||
CRL.Promise = Promise; | ||
// polyfill Promise | ||
if (typeof global.Promise !== 'function') { | ||
global.Promise = CRL.Promise; | ||
} | ||
// Coroutines | ||
// Schedule | ||
function schedule(fn, time) { | ||
if (time === void 0 && typeof global.setImmediate !== 'undefined') { | ||
setImmediate(fn); | ||
} else { | ||
setTimeout(fn, +time); | ||
} | ||
} | ||
function isPromise(p) { | ||
return p && typeof p.then === 'function'; | ||
} | ||
function isFunction(f) { | ||
return typeof f === 'function'; | ||
} | ||
function isObject(obj) { | ||
return obj && Object == obj.constructor; | ||
} | ||
function isArray(arr) { | ||
return Array.isArray(arr); | ||
} | ||
// Generator Runner | ||
CRL.go = function go(genf, ctx) { | ||
var state, gen = genf.apply(ctx || {}); | ||
return new CRL.Promise(function(resolve, reject) { | ||
//schedule(next); | ||
next(); | ||
function next(value) { | ||
if (state && state.done) { | ||
resolve(value); | ||
return; | ||
} | ||
state = gen.next(value); | ||
value = state.value; | ||
if (isPromise(value)) { | ||
value.then(function(value) { | ||
next(value); | ||
}) | ||
return; | ||
} | ||
next(value); | ||
} | ||
}) | ||
} | ||
// convert to promise as much possible | ||
function toPromise(obj) { | ||
if (isPromise(obj)) { | ||
return obj; | ||
} | ||
if (isArray(obj)) { | ||
return arrayToPromise(obj); | ||
} | ||
if (isObject(obj)) { | ||
return objectToPromise(obj); | ||
} | ||
} | ||
// convert array to promise | ||
function arrayToPromise(array) { | ||
var promise; | ||
return CRL.Promise.all(array.map(function(value) { | ||
promise = toPromise(value); | ||
if (isPromise(promise)) { | ||
return promise; | ||
} | ||
return value; | ||
})); | ||
} | ||
// convert object to promise | ||
function objectToPromise(obj) { | ||
var key, promise, ret, | ||
promises = [], | ||
result = {}, | ||
i = -1, | ||
keys = Object.keys(obj), | ||
len = keys.length; | ||
ret = new CRL.Promise(function(resolve) { | ||
while (++i < len) { | ||
key = keys[i]; | ||
promise = toPromise(obj[key]); | ||
if (isPromise(promise)) { | ||
setupThen(promise, key); | ||
} | ||
else { | ||
result[key] = obj[key]; | ||
} | ||
} | ||
CRL.Promise.all(promises).then(function() { | ||
resolve(result); | ||
}) | ||
function setupThen(promise, key) { | ||
// default value | ||
result[key] = void 0; | ||
promise.then(function(value) { | ||
result[key] = value; | ||
}) | ||
promises.push(promise); | ||
} | ||
}) | ||
return ret; | ||
} | ||
// receiver | ||
CRL.receive = function receive(obj) { | ||
var prom; | ||
if (obj && isFunction(obj.receive)) { | ||
return obj.receive(); | ||
} | ||
prom = toPromise(obj); | ||
if (isPromise(prom)) { | ||
return prom; | ||
} | ||
return obj; | ||
} | ||
// sender | ||
CRL.send = function send(obj, value) { | ||
if (obj && isFunction(obj.send)) { | ||
return obj.send(value); | ||
} | ||
throw 'unable to receive values'; | ||
} | ||
function timeout(time) { | ||
if (!isNaN(time) && time !== null) { | ||
return new CRL.Promise(function(resolve) { | ||
schedule(resolve, time) | ||
}) | ||
} | ||
throw 'Invalid time'; | ||
} | ||
CRL.timeout = timeout; | ||
// Buffer: simple array based buffer to use with channels | ||
function Buffer(size) { | ||
this.size = isNaN(size) ? 1 : size; | ||
this.array = []; | ||
} | ||
Buffer.prototype = { | ||
shift: function() { | ||
return this.array.shift(); | ||
}, | ||
push: function(value) { | ||
if (this.isFull()) { return false } | ||
this.array.push(value); | ||
return true; | ||
}, | ||
isFull: function() { | ||
return !(this.array.length < this.size); | ||
}, | ||
isEmpty: function() { | ||
return this.array.length === 0; | ||
} | ||
} | ||
// Channel: a structure to transport messages | ||
function indentityFn(x) {return x} | ||
function scheduledResolve(deferred, value) { | ||
schedule(function() { deferred.resolve(value) }) | ||
} | ||
function Channel(buffer, transform) { | ||
this.buffer = buffer; | ||
this.closed = false; | ||
this.data = void 0; | ||
this.senderPromises = []; | ||
this.receiverPromises = []; | ||
this.transform = transform || indentityFn; | ||
} | ||
Channel.prototype = { | ||
receive: function() { | ||
var data, deferred; | ||
// is unbuffered | ||
if (! this.buffer) { | ||
// there is data? | ||
if (this.data !== void 0) { | ||
// resume the first sender coroutine | ||
if (this.senderPromises[0]) { | ||
scheduledResolve(this.senderPromises.shift()); | ||
} | ||
// clean and return | ||
data = this.data; | ||
this.data = void 0; | ||
return data; | ||
// if no data | ||
} else { | ||
// suspend the coroutine wanting to receive | ||
deferred = Promise.defer(); | ||
this.receiverPromises.push(deferred); | ||
return deferred.promise; | ||
} | ||
} | ||
else { | ||
throw 'Trying to assert undefined object'; | ||
// if buffered | ||
// empty buffer? | ||
if (this.buffer.isEmpty()) { | ||
// suspend the coroutine wanting to receive | ||
deferred = Promise.defer(); | ||
this.receiverPromises.push(deferred); | ||
return deferred.promise; | ||
// some value in the buffer? | ||
} else { | ||
// resume the first sender coroutine | ||
if (this.senderPromises[0]) { | ||
scheduledResolve(this.senderPromises.shift()); | ||
} | ||
// clean and return | ||
return this.buffer.shift(); | ||
} | ||
}, | ||
send: function(data) { | ||
if (this.closed) { throw 'closed channel' } | ||
var deferred; | ||
// is unbuffered | ||
if (! this.buffer) { | ||
// some stored data? | ||
if (this.data !== void 0) { | ||
// deliver data to the first waiting coroutine | ||
if (this.receiverPromises[0]) { | ||
scheduledResolve(this.receiverPromises.shift(), this.data); | ||
} | ||
// no stored data? | ||
} else { | ||
// pass sent data directly to the first waiting for it | ||
if (this.receiverPromises[0]) { | ||
this.data = void 0; | ||
scheduledResolve(this.receiverPromises.shift(), this.transform(data)); | ||
// schedule the the sender coroutine | ||
return new timeout(0); | ||
} | ||
} | ||
// else, store the transformed data | ||
this.data = this.transform(data); | ||
deferred = Promise.defer(); | ||
this.senderPromises.push(deferred); | ||
return deferred.promise; | ||
} | ||
// if buffered | ||
// emty buffer? | ||
if (! this.buffer.isFull()) { | ||
// TODO: optimize below code | ||
// store sent value in the buffer | ||
this.buffer.push(this.transform(data)); | ||
// if any waiting for the data, give it | ||
if (this.receiverPromises[0]) { | ||
scheduledResolve(this.receiverPromises.shift(), this.buffer.shift()); | ||
} | ||
} | ||
// full buffer? | ||
if (this.buffer.isFull()) { | ||
// stop until the buffer start to be drained | ||
deferred = Promise.defer(); | ||
this.senderPromises.push(deferred); | ||
return deferred.promise; | ||
} | ||
}, | ||
close: function() { | ||
this.closed = true; | ||
this.senderPromises = []; | ||
while (this.receiverPromises.length) { | ||
scheduledResolve(this.receiverPromises.shift()); | ||
} | ||
} | ||
else { | ||
throw 'Trying to assert undefined class'; | ||
} | ||
CRL.Channel = Channel; | ||
CRL.chan = function chan(size, transform) { | ||
if (size instanceof Buffer) { | ||
return new Channel(size, transform); | ||
} | ||
return false; | ||
}; | ||
// isNaN(null) == false :O | ||
if (isNaN(size) || size === null) { | ||
return new Channel(null, transform); | ||
} | ||
})(); | ||
return new Channel(new Buffer(size), transform); | ||
} | ||
})(this); |
@@ -783,9 +783,9 @@ /* | ||
keyword: | ||
'new use class func me return ' + | ||
'use class func me return ' + | ||
'if else for in switch case default ' + | ||
'continue break catch', | ||
'continue break catch go', | ||
literal: | ||
'true false nil', | ||
built_in: | ||
'super error' | ||
'super regex error chan timeout' | ||
}; | ||
@@ -818,3 +818,3 @@ | ||
beginKeywords: 'class', end: /[{;=]/, excludeEnd: true, | ||
illegal: /["\[\]]/, | ||
illegal: /["\(\)]/, | ||
contains: [hljs.UNDERSCORE_TITLE_MODE] | ||
@@ -824,4 +824,4 @@ }, | ||
className: 'new', | ||
begin: '&', end: /[\[{;,\n]/, excludeEnd: true, | ||
illegal: /["\[\]]/, | ||
begin: '&', end: /[\({;,\n]/, excludeEnd: true, | ||
illegal: /["\(\)]/, | ||
contains: [hljs.UNDERSCORE_TITLE_MODE] | ||
@@ -828,0 +828,0 @@ } |
# Documentation | ||
This is a reference manual for the Cor programming language. It will guide you inside language aspects and concepts, including experimental features to be added in future releases. Cor is a language designed with large web development in mind. Hence, is possible to develop your software without to execute any command to compile the source code, CLI tools are only needed to make the product able for production environments. | ||
This is a reference manual for the Cor programming language. It will guide you inside language aspects and concepts. Cor is a language designed with large web development in mind. Hence, is possible to develop your software without to execute any command to compile the source code, CLI tools are only needed to make the product able for production environments. | ||
@@ -40,4 +40,57 @@ <toc/> | ||
### Slices/CoalesceOperator/ConditionalOperator | ||
### String Interpolation | ||
``` | ||
str = $'hello my name is {person.name}' | ||
``` | ||
### Concurrency/Parallelism/Coroutines/Channels | ||
``` | ||
// corotines starts with `go` | ||
go { | ||
// block until the result is resolved | ||
accounts = <- fetch.get($'http://rest-api.com/client/{id}/accounts') | ||
for account in accounts { | ||
//... | ||
} | ||
} | ||
``` | ||
``` | ||
// unbuffered channel | ||
ch = chan() | ||
go { | ||
for num in 1:100 { | ||
// send | ||
ch <- num | ||
} | ||
} | ||
go { | ||
// receive | ||
c = <- ch | ||
console.log(c) | ||
} | ||
``` | ||
``` | ||
// parallel | ||
go { | ||
result = <- ( | ||
books : fetch.get($'http://rest-api.com/client/{id}/books'), | ||
articles : fetch.get($'http://rest-api.com/client/{id}/books'), | ||
) | ||
for book in result.books { | ||
//.. | ||
} | ||
for articles in result.articles { | ||
//.. | ||
} | ||
} | ||
``` | ||
### Slices/Coalesce Operator/Conditional Operator | ||
``` | ||
--- | ||
@@ -47,3 +100,3 @@ Coalesce operator | ||
// if `null` or `undefined` then be an empty array | ||
panels = getPanels() ?? [] | ||
panels = getPanels() ?? (,) | ||
@@ -66,3 +119,3 @@ --- | ||
### Classes | ||
### Classes/Inheritance/Instance | ||
``` | ||
@@ -86,6 +139,5 @@ --- | ||
### Inheritance | ||
``` | ||
--- | ||
fully compatible with javascript prototypal inheritance | ||
inheritance is fully compatible with javascript prototypal inheritance | ||
--- | ||
@@ -107,3 +159,2 @@ class Person : Model { | ||
### Instances | ||
``` | ||
@@ -114,5 +165,2 @@ --- | ||
// empty object | ||
obj = &[] | ||
// new instance | ||
@@ -122,9 +170,9 @@ person = &Person | ||
// instance and properties setting | ||
person = &Person[ | ||
person = &Person( | ||
name : 'John', | ||
age : 23, | ||
] | ||
) | ||
// positional properties setting | ||
person = &Person['John', 23] | ||
person = &Person('John', 23) | ||
@@ -174,3 +222,3 @@ // person.name == 'John' | ||
### TypeAssertion/Errors | ||
### Type Assertion/Errors | ||
``` | ||
@@ -298,6 +346,5 @@ func init() { | ||
Objects are a collection of variables and functions. It may be created using `&` operator. | ||
> In previous versions it was possible to use `@` operator, but is now deprecated. | ||
``` | ||
// creates an empty object | ||
obj = &[] | ||
obj = &Object | ||
@@ -312,10 +359,10 @@ // filling properties | ||
``` | ||
client = &[ | ||
client = ( | ||
name : 'Aaron', | ||
age : 20, | ||
pet : &[ | ||
pet : ( | ||
name : 'Kitty', | ||
kind : 'Cat', | ||
], | ||
] | ||
), | ||
) | ||
``` | ||
@@ -335,17 +382,37 @@ | ||
### Literal Constructors | ||
### Parenthesis Expressions | ||
A literal constructor is a list of elements bounded by `[` and `]` symbols, used for creating objects and arrays. An element can be either, expression or a key-value pair. If one element is key-value type, all other elements has to be key-value in the same declaration. | ||
A parenthesis expression is bouded by `(` and `)` symbols, it evaluates depending on what is inside, followig the rules: | ||
Example using key-value pair elements: | ||
1. If there is just one value it evaluates to that value. | ||
2. If at least a `:` it returns an object. | ||
3. If at least a `,` it return an array. | ||
If one element inside is key-value type, all other elements has to be key-value in the same declaration. | ||
``` | ||
walter = &Client[ | ||
// rule #1, expression | ||
expr = (4) | ||
// rule #2, object | ||
obj = (:) | ||
obj = (name: 'john') | ||
// rule #3, array | ||
arr = (,) | ||
arr = (1,) | ||
arr = (1, 2, 3) | ||
``` | ||
Example using key-value pair list: | ||
``` | ||
walter = &Client( | ||
name : 'Walter', | ||
age : 12, | ||
] | ||
) | ||
``` | ||
Example using expression elements: | ||
Example using expression list: | ||
``` | ||
aaron = &Client['Aaron', 20] | ||
aaron = &Client('Aaron', 20) | ||
@@ -373,2 +440,15 @@ // aaron.name = 'Aaron' | ||
### Templates (String Interpolation) | ||
Templates or string interpolation is a string literal with trailing `$` symbol and curly braces to define the bounds of the expressions within the string, example: | ||
``` | ||
str = $'Hello {person.name}!' | ||
``` | ||
It is possible to use string delimiters inside the expressions: | ||
``` | ||
str = $'Hello {person['name']}!' | ||
``` | ||
### Numbers | ||
@@ -423,2 +503,5 @@ | ||
?? ? | ||
// Async Operator | ||
<- | ||
``` | ||
@@ -447,2 +530,3 @@ | ||
Example: | ||
``` | ||
@@ -453,6 +537,7 @@ someFunc?() | ||
The above example should be translated to: *if `someFunc` exists, call it*. It also can be used with indexes, selectors and slices. | ||
``` | ||
len = customers?.length; | ||
frst = customers?[0]; | ||
copy = customers?[:]; | ||
len = customers?.length | ||
frst = customers?[0] | ||
copy = customers?[:] | ||
``` | ||
@@ -465,5 +550,5 @@ | ||
``` | ||
empty = [] | ||
colors = ['red', 'green', 'blue'] | ||
foo = [bar(), 56, 'baz'] | ||
empty = (,) | ||
colors = ('red', 'green', 'blue') | ||
foo = (bar(), 56, 'baz') | ||
@@ -481,9 +566,9 @@ // accessing | ||
Slice expression is a syntactic sugar for the javascript `slice` method, in most of the cases it constructs an array from an existing array. | ||
Slice expression constructs an array from an existing array, it is a syntactic sugar for the javascript `slice` method. You can use the syntax: `array[start:length]`, start and length are optionals. | ||
``` | ||
colors = ['red', 'green', 'blue'] | ||
colors = ('red', 'green', 'blue') | ||
sliced = colors[1:3] // sliced is ['green', 'blue'] | ||
sliced = colors[:1] // sliced is ['red'] | ||
sliced = colors[1:] // sliced is ['green', 'blue'] | ||
sliced = colors[1:3] // sliced is ('green', 'blue') | ||
sliced = colors[:1] // sliced is ('red') | ||
sliced = colors[1:] // sliced is ('green', 'blue') | ||
``` | ||
@@ -512,2 +597,70 @@ | ||
### Coroutines | ||
Coroutines are blocks containig code that executes asynchronously. It can be defined using the `go` keyword followed by a block. Coroutines can be also used as expressions, its execution starts once evaluated, it returns a **Promise** object. | ||
> Coroutines uses generators underlying | ||
Example: | ||
``` | ||
go { | ||
// code... | ||
} | ||
``` | ||
Used as expression: | ||
``` | ||
prom = go { | ||
return 'yosbelms' | ||
} | ||
prom.then(func(s) console.log(s)) | ||
// will print 'yosbelms' | ||
``` | ||
### Asynchronic Operator | ||
The asynchronic operator `<-` allows to block coroutines and wait to receive future values (Promises) if a Promise is returned, also can be used to send values to [Channels](#chan). Asynchronic operators can be used only inside coroutines (`go` blocks) | ||
``` | ||
go { | ||
accounts = <- fetch.get($'http://rest-api.com/client/{id}/accounts') | ||
for account in accounts { | ||
//... | ||
} | ||
} | ||
``` | ||
If receiving a array or object of future values it will resolve all values in parallel. | ||
``` | ||
go { | ||
result = <- ( | ||
books : fetch.get($'http://rest-api.com/client/{id}/books'), | ||
articles : fetch.get($'http://rest-api.com/client/{id}/books'), | ||
) | ||
for book in result.books { | ||
//.. | ||
} | ||
for articles in result.articles { | ||
//.. | ||
} | ||
} | ||
``` | ||
Awaiting a coroutine (idiomatic Cor): | ||
``` | ||
// get articles in the las hour | ||
func findArticlesByTag(tag) go { | ||
articles <- fetch.get($'http://rest-api.com/article') | ||
return articles.filter(func(a) a.tag == tag) | ||
} | ||
func renderArticles() go { | ||
&ArticlesView(<- findArticlesByTag(tag)) | ||
} | ||
``` | ||
### Classes | ||
@@ -548,12 +701,12 @@ | ||
client = &Client[ | ||
client = &Client( | ||
firstName : 'Aaron', | ||
lastName : 20, | ||
accounts : [ | ||
&Account[ | ||
accounts : ( | ||
&Account( | ||
code : '3980-121970', | ||
ammount : 5000, | ||
], | ||
], | ||
] | ||
), | ||
), | ||
) | ||
``` | ||
@@ -576,3 +729,3 @@ | ||
### Initialization | ||
#### Initialization | ||
@@ -589,7 +742,7 @@ There is two ways to define class initialization. The first way is by declaring a property-set before any method declaration: | ||
// a = &Animal['snake', 'slithering'] | ||
// a = &Animal[ | ||
// a = &Animal('snake', 'slithering') | ||
// a = &Animal( | ||
// name: 'snake', | ||
// movement: 'slithering', | ||
// ] | ||
// ) | ||
``` | ||
@@ -611,3 +764,3 @@ | ||
// a = &Animal['snake', 'slithering'] | ||
// a = &Animal('snake', 'slithering') | ||
``` | ||
@@ -618,3 +771,3 @@ | ||
### Inheritance | ||
#### Inheritance | ||
@@ -641,6 +794,8 @@ Cor supports single inheritance by using `:` operator. It embraces javascript prototype chain, so it is safe to use it along with any javascript libraries. | ||
### Super (Builtin Function) | ||
## Builtin Functions | ||
The `super` builtin function calls a method of the super class. It will call the method with equal name to the current method where `super` is located. It should compile to `<SuperClass>.prototype.<Method>.apply(this, arguments)` | ||
### Super | ||
The `super` function calls a method of the super class. It will call the method with equal name to the current method where `super` is located. It should compile to `<SuperClass>.prototype.<Method>.apply(this, arguments)` | ||
Example: | ||
@@ -678,2 +833,89 @@ | ||
### Regex | ||
The `regex` function enables to use regular expressions in Cor. The regular expression syntax is equal to JavaScript regular expressions. | ||
``` | ||
reg = regex('pla[a-z]?') | ||
// with flags | ||
regf = regex('gi(t)+', 'ig') | ||
``` | ||
Furthemore, it can be multilined: | ||
``` | ||
r = regex(' | ||
pla | ||
[a-z]? | ||
') | ||
``` | ||
### Chan | ||
`chan(bufferSize, tranformer)` | ||
The `chan` function creates a channel. A channel is a structure to allow comunication and synchronization between coroutines. | ||
Channels can be buffered or unbuffered. When sending data through unbuffered channels it always blocks the sender until some other process receives. Once the data has been received, the sender will be unblocked and the receptor will be blocked until new data is received. Unbuffered channels are also known as _synchronic channels_. When some data is sent to a buffered channel it only blocks the coroutine if the buffer is full. The receiver only blocks if there is no data in the buffer. | ||
* The fist parameter defines the buffer size, if omitted or `nil` is provided the created channel will be unbuffered. | ||
* The transformer is a function that transfor values to send though the channel. | ||
The created channels are objects that can be closed using `.close()` method, to check whether is closed or not use `.closed` property. | ||
Example: | ||
``` | ||
// unbuffered channel | ||
ch = chan() | ||
go { | ||
for num in 1:100 { | ||
// send | ||
ch <- num | ||
} | ||
} | ||
go { | ||
// receive | ||
c = <- ch | ||
console.log(c) | ||
} | ||
``` | ||
Transformer example: | ||
``` | ||
// transform value*2 | ||
ch = chan(nil, func(s) s*2) | ||
go { | ||
for num in 1:3 { | ||
ch <- num | ||
} | ||
} | ||
go { | ||
console.log(<- ch) | ||
} | ||
--- | ||
output: | ||
2 | ||
4 | ||
--- | ||
``` | ||
### Timeout | ||
`timeout(msecs)` | ||
The timeout function blocks the execution of a coroutine during the specified milliseconds. | ||
Example: | ||
``` | ||
go { | ||
timeout(100) | ||
console.log('after 100 milliseconds') | ||
} | ||
``` | ||
## Modules | ||
@@ -743,3 +985,3 @@ | ||
func init() { | ||
u = &User['ragnar', 'secretpass'] | ||
u = &User('ragnar', 'secretpass') | ||
} | ||
@@ -764,3 +1006,3 @@ ``` | ||
``` | ||
fruits = ['orange', 'apple', 'pear'] | ||
fruits = ('orange', 'apple', 'pear') | ||
@@ -817,4 +1059,4 @@ for i = 0, len = fruits.length; i < len; i++ { | ||
``` | ||
array = [4, 3, 'Cor', 'PHP', 5, 'Go', 1, 7, 'Python'] | ||
langs = [] | ||
array = (4, 3, 'Cor', 'PHP', 5, 'Go', 1, 7, 'Python') | ||
langs = (,) | ||
@@ -834,4 +1076,4 @@ for item = array.shift() { | ||
``` | ||
arr = [1, 2, 3] | ||
sum = 0 | ||
arr = (1, 2, 3) | ||
sum = 0 | ||
@@ -847,3 +1089,3 @@ for value in arr { | ||
``` | ||
arr = ['Jeremy', 'Nolan', 'Brendan'] | ||
arr = ('Niña', 'Pinta', 'Santa María') | ||
@@ -854,5 +1096,5 @@ for index, value in arr { | ||
// 0 Jeremy | ||
// 1 Nolan | ||
// 2 Brendan | ||
// 0 Niña | ||
// 1 Pinta | ||
// 2 Santa María | ||
``` | ||
@@ -862,3 +1104,3 @@ | ||
``` | ||
obj = &[name: 'Bill', age: 50] | ||
obj = (name: 'Bill', age: 50) | ||
@@ -933,3 +1175,11 @@ for index, value in obj { | ||
Switch statement is even more generic, one can omit the main expression, the `true` value will be evaluated in place of the omitted expression: | ||
``` | ||
switch { | ||
case x > 2 : doSomething() | ||
case x < 2 : doOtherThing() | ||
} | ||
``` | ||
### Inc/Dec | ||
@@ -1036,3 +1286,33 @@ | ||
### Run | ||
`run` command reads and executes a `.cor` file containing Cor source code. | ||
Usage: | ||
``` | ||
cor run <path> [run options] | ||
``` | ||
<table> | ||
<tbody> | ||
<tr><td colspan="2">Arguments:</td></tr> | ||
<tr> | ||
<td class="cmd-arg"><code>path</code></td> | ||
<td>Specifies the path to the file to execute.</td> | ||
</tr> | ||
<tr><td colspan="2">Options:</td></tr> | ||
<tr> | ||
<td class="cmd-arg"><code>-conf</code></td> | ||
<td>Specifies the path to the <code>.json</code> file to use as configuration.</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
Example: | ||
``` | ||
cor run path/to/file.cor | ||
``` | ||
Runs a program located in the specified path. | ||
### Build | ||
@@ -1268,2 +1548,2 @@ | ||
The CRL is a small (~3Kb) library which makes possible to take advantage of features such as *for/in statements, inheritance, type assertions, among others*. Without the CRL, the javascript code obtained as the result of compilation could be repetitive and bigger in consequence, that's one of the reasons that CRL exits, to provide a small set of features that will be internally used by the javascript resulting code. | ||
The CRL is a small (~13Kb unminified and uncompressed) library which makes possible to take advantage of features such as *for/in statements, inheritance, type assertions, among others*. Without the CRL, the javascript code obtained as the result of compilation could be repetitive and bigger in consequence, that's one of the reasons that CRL exits, to provide a small set of features that will be internally used by the javascript resulting code. |
{ | ||
"author" : "Yosbel Marin", | ||
"name" : "cor-lang", | ||
"version" : "0.6.0", | ||
"version" : "0.10.0", | ||
"license" : "BSD", | ||
@@ -27,4 +27,9 @@ "description" : "The Language of the Web", | ||
"cor-lang", | ||
"compiler" | ||
"compiler", | ||
"concurrency", | ||
"parallel", | ||
"task", | ||
"coroutine", | ||
"channel" | ||
] | ||
} |
# Cor | ||
**making the web great again** | ||
 | ||
Cor is an opensource language that compiles to plain javascript. It is designed to make easy to write, build, and maintain large software for the web. Cor is an attemp to make myself more productive, it is what I think javascript should have been from the beginning. | ||
**The Language of the Web** | ||
Cor brings a fresh way of programming for web browsers and Node.js platform, it borrows good ideas from well known languages such as Go and C#, but it is a new language. It plays very well with the outer world; with Cor you can take advantage of the huge javascript ecosystem and reuse your preferred libraries. | ||
Cor is an opensource language that compiles to plain JavaScript. It is designed to make easy to write and maintain software for the asynchronous Web. | ||
Concurrency and parallelism are first class citizens in Cor, bringing a fresh way of programming for web browsers and Node.js platforms. It is a language inspired in _Go_, but runs in a world wide platform that is the Web. With Cor you can take advantage of the whole JavaScript ecosystem, use your preferred libraries, and ensure your application runs everywhere the Web is. | ||
## Installation | ||
@@ -39,9 +41,29 @@ | ||
## Platform Compatibility | ||
Cor coroutines are based in generators, so, if you plan to use it you must take the following in consideration. | ||
When using Node.js or browsers without generator support, you must use [gnode](https://github.com/TooTallNate/gnode) and/or [regenerator](http://facebook.github.io/regenerator/). | ||
### Server | ||
* Node.js 4+ | ||
When using Node.js 0.11.x or greater, you must use the `--harmony-generators` flag or just `--harmony` to get access to generators. | ||
### Browsers | ||
* Firefox 27+ | ||
* Chrome 39+ | ||
* Opera 26+ | ||
* Edge 13+ | ||
Chrome between 28 and 38 are supported by turning on an experimental flag. | ||
## Support | ||
* For documentation, and usage, see: http://yosbelms.github.io/cor | ||
* Search discussions: http://ost.io/@yosbelms/cor | ||
* To suggest a feature or report a bug: http://github.com/yosbelms/cor/issues | ||
* Contributors are listed here: http://github.com/yosbelms/cor/contributors | ||
Copyright 2015-2016 (c) Yosbel Marin. This software is licensed under the BSD License. |
@@ -8,2 +8,3 @@ # Making a new relase | ||
* Ensure master passes tests | ||
* Update CHANGELOG.md | ||
* Bump version in `package.json`. Any breaking change or new feature should bump minor (or even major). Non-breaking changes or fixes can just bump patch. | ||
@@ -13,29 +14,8 @@ * Update docs (`./docs/*.md`) manually. | ||
### Fine grained | ||
### Releasing | ||
*in the `master` branch* | ||
* Commit. | ||
* `$ git tag <version>` (see `git tag -l` for latest) | ||
* `$ git push origin master --tags` | ||
* `$ npm publish .` | ||
### Coarse grained | ||
> It should be used in case CICD server is no configured to deploy | ||
* Create a new branch with the version name `git checkout -b <version>` | ||
* Run `make release` | ||
* if every thing is ok, the you can savely delete version branch | ||
`git branch -D v<version>` | ||
### Asisted by CICD servers | ||
> Make sure the CICD server authenticates automatically in GitHub and NPM | ||
* Create a new branch with the version name `git checkout -b <version>` | ||
* `git push origin <version>` | ||
The CICD server should be able to execute `make release` command which publish the new version in NPM and will make a new tag named v<version>, eg. v1.3.0 | ||
* if every thing were fine, then you can savely delete version branch | ||
`git branch -D v<version>` | ||
* `$ npm publish .` |
@@ -18,8 +18,7 @@ require('../parser.js'); | ||
var | ||
cmd = new cor.CliCommand('run', 'ok'); | ||
cmd = new cor.CliCommand('run', 'reads a executes a file containing source code'); | ||
cmd.addArgument('path', 'path to the file to run', true); | ||
cmd.addArgument('path', 'path to the file to execute', true); | ||
cmd.addOption('conf', 'path to the .json file which contains environment variables for cor.Loader'); | ||
cmd.addOption('v', 'print additional information'); | ||
@@ -26,0 +25,0 @@ cmd.setAction(function (input, app) { |
592
src/crl.js
@@ -6,11 +6,13 @@ (function(){ | ||
CRL = (typeof CRL === 'undefined' ? {} : CRL); | ||
var | ||
hasProp = Object.prototype.hasOwnProperty, | ||
slice = Array.prototype.slice; | ||
hasProp = Object.prototype.hasOwnProperty, | ||
toString = Object.prototype.toString, | ||
slice = Array.prototype.slice, | ||
CRL = (typeof CRL === 'undefined' ? {} : CRL); | ||
CRL.idSeed = 1; | ||
CRL.instancers = []; | ||
CRL.nativeTypes = { | ||
// store function that instantiate classes | ||
// whith different quantity of arguments | ||
instancers = [], | ||
nativeTypes = { | ||
'String' : String, | ||
@@ -25,16 +27,12 @@ 'Number' : Number, | ||
CRL.copyObj = function(from, to, strict) { | ||
// copy properties from an object to another | ||
// returns the object which properties has been copied to | ||
CRL.copyObj = function copyObj(from, to) { | ||
var name; | ||
if (strict) { | ||
for (name in from) { | ||
if (hasProp.call(from, name)) { | ||
to[name] = from[name]; | ||
} | ||
for (name in from) { | ||
if (hasProp.call(from, name)) { | ||
to[name] = from[name]; | ||
} | ||
} | ||
else { | ||
for (name in from) { | ||
to[name] = from[name]; | ||
} | ||
} | ||
@@ -44,3 +42,9 @@ return to; | ||
CRL.create = function(Class) { | ||
// creates an instance of a class | ||
// CRL.create(Class, arg1, arg2, ...) | ||
CRL.create = function create(Class) { | ||
if (typeof Class !== 'function') { | ||
throw Error('Runtime Error: trying to instanstiate no class'); | ||
} | ||
var | ||
@@ -51,34 +55,41 @@ instancerArgs, | ||
i = -1, | ||
instancer = this.instancers[argc]; | ||
instancer = instancers[argc]; | ||
if (! instancer) { | ||
instancerArgs = []; | ||
while (++i < argc) { | ||
instancerArgs.push('args[' + i + ']'); | ||
} | ||
this.instancers[argc] = instancer = new Function('cls', 'args', 'return new cls(' + instancerArgs.join(',') + ');'); | ||
} | ||
if (typeof Class === 'function') { | ||
return instancer(Class, args); | ||
instancer = instancers[argc] = new Function( | ||
'cls', | ||
'args', | ||
'return new cls(' + instancerArgs.join(',') + ');' | ||
); | ||
} | ||
throw Error('Runtime Error: trying to instanstiate no class'); | ||
return instancer(Class, args); | ||
}; | ||
CRL.extend = function(Cls, baseCls) { | ||
CRL.copyObj(baseCls, Cls, true); | ||
// convert a class in a subclass of other class | ||
// CRL.subclass(Subclass, Superclass) | ||
CRL.subclass = function subclass(subClass, superClass) { | ||
CRL.copyObj(superClass, subClass); | ||
function Proto() { | ||
this.constructor = Cls; | ||
this.constructor = subClass; | ||
} | ||
Proto.prototype = baseCls.prototype; | ||
Cls.prototype = new Proto(); | ||
Proto.prototype = superClass.prototype; | ||
subClass.prototype = new Proto(); | ||
} | ||
CRL.keys = function(obj) { | ||
// extract keys from an object or array | ||
// CRL.keys([5, 7, 3]) -> [0, 1, 2] | ||
// CRL.keys({x: 2, y: 4}) -> ['x', 'y'] | ||
CRL.keys = function keys(obj) { | ||
var keys, i, len; | ||
// is array | ||
if (obj instanceof Array) { | ||
@@ -92,49 +103,506 @@ i = -1; | ||
} | ||
return keys; | ||
} | ||
else { | ||
if (typeof Object.keys === 'function') { | ||
keys = Object.keys(obj); | ||
// if has key function | ||
if (typeof Object.keys === 'function') { | ||
return Object.keys(obj); | ||
} | ||
// otherwise polyfill it | ||
for (i in obj) { | ||
if (hasProp.call(obj, i)) { | ||
keys.push(i); | ||
} | ||
else { | ||
for (i in obj) { | ||
if (hasProp.call(obj, i)) { | ||
keys.push(i); | ||
} | ||
} | ||
} | ||
} | ||
return keys; | ||
}; | ||
CRL.assertType = function(obj, Class) { | ||
// whether a object is instance of a class or not | ||
// CRL.assertType({}, Object) | ||
// CRL.assertType(person, Person) | ||
CRL.assertType = function assertType(obj, Class) { | ||
var type; | ||
// Class is a Class? | ||
if (typeof Class === 'function') { | ||
// object is defined? | ||
if (typeof obj !== 'undefined') { | ||
if (obj instanceof Class) { | ||
return true; | ||
if (typeof Class !== 'function') { | ||
throw 'Trying to assert undefined class'; | ||
} | ||
if (typeof obj === 'undefined') { | ||
throw 'Trying to assert undefined object'; | ||
} | ||
// try with instanceof | ||
if (obj instanceof Class) { | ||
return true; | ||
} | ||
// try with finding the native type according to "Object.prototype.toString" | ||
type = toString.call(obj); | ||
type = type.substring(8, type.length - 1); | ||
if(hasProp.call(nativeTypes, type) && nativeTypes[type] === Class) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
CRL.regex = function regex(pattern, flags) { | ||
return new RegExp(pattern, flags); | ||
} | ||
})(); | ||
(function(global) { | ||
// Lightweight non standard compliant Promise | ||
function Promise(resolverFn) { | ||
if (typeof resolverFn !== 'function') { | ||
throw 'provided resolver must be a function'; | ||
} | ||
var p = this; | ||
// this.value; | ||
// this.reason; | ||
this.completed = false; | ||
this.thenListeners = []; | ||
this.catchListeners = []; | ||
resolverFn( | ||
function resolve(value){ | ||
Promise.doResolve(p, value); | ||
}, | ||
function reject(reason) { | ||
Promise.doReject(p, reason); | ||
} | ||
); | ||
} | ||
Promise.prototype = { | ||
then: function(fn) { | ||
this.thenListeners.push(fn); | ||
if (this.completed) { | ||
Promise.doResolve(this, this.value); | ||
} | ||
return this; | ||
}, | ||
catch: function(fn) { | ||
this.catchListeners.push(fn); | ||
if (this.completed) { | ||
Promise.doReject(this, this.reason); | ||
} | ||
return this; | ||
} | ||
}; | ||
Promise.doResolve = function resolve(p, value) { | ||
p.thenListeners.forEach(function(listener) { | ||
listener(value); | ||
}) | ||
p.completed = true; | ||
p.value = value; | ||
}; | ||
Promise.doReject = function reject(p, reason) { | ||
p.catchListeners.forEach(function(listener){ | ||
listener(reason); | ||
}) | ||
p.completed = true; | ||
p.reason = reason; | ||
}; | ||
Promise.all = function all(array) { | ||
var promise, | ||
i = -1, | ||
numPending = 0, | ||
result = [], | ||
len = array.length; | ||
return new Promise(function(resolve) { | ||
while (++i < len) { | ||
promise = array[i]; | ||
if (isPromise(promise)) { | ||
setupThen(promise, i); | ||
} | ||
// otherwise find the native type according to "Object.prototype.toString" | ||
else { | ||
type = Object.prototype.toString.call(obj); | ||
type = type.substring(8, type.length - 1); | ||
if(hasProp.call(this.nativeTypes, type) && this.nativeTypes[type] === Class) { | ||
return true; | ||
result[i] = array[i]; | ||
tryToResolve(); | ||
} | ||
} | ||
function setupThen(promise, i) { | ||
numPending++; | ||
// default value | ||
result[i] = void 0; | ||
promise.then(function(value) { | ||
result[i] = value; | ||
numPending--; | ||
tryToResolve(); | ||
}) | ||
} | ||
function tryToResolve() { | ||
if (numPending === 0) { | ||
resolve(result) | ||
} | ||
} | ||
}) | ||
} | ||
Promise.defer = function defer() { | ||
var deferred = {}; | ||
// use CRL.Promise | ||
deferred.promise = new CRL.Promise(function(resolve, reject) { | ||
deferred.resolve = resolve; | ||
deferred.reject = reject; | ||
}) | ||
return deferred; | ||
} | ||
CRL.Promise = Promise; | ||
// polyfill Promise | ||
if (typeof global.Promise !== 'function') { | ||
global.Promise = CRL.Promise; | ||
} | ||
// Coroutines | ||
// Schedule | ||
function schedule(fn, time) { | ||
if (time === void 0 && typeof global.setImmediate !== 'undefined') { | ||
setImmediate(fn); | ||
} else { | ||
setTimeout(fn, +time); | ||
} | ||
} | ||
function isPromise(p) { | ||
return p && typeof p.then === 'function'; | ||
} | ||
function isFunction(f) { | ||
return typeof f === 'function'; | ||
} | ||
function isObject(obj) { | ||
return obj && Object == obj.constructor; | ||
} | ||
function isArray(arr) { | ||
return Array.isArray(arr); | ||
} | ||
// Generator Runner | ||
CRL.go = function go(genf, ctx) { | ||
var state, gen = genf.apply(ctx || {}); | ||
return new CRL.Promise(function(resolve, reject) { | ||
//schedule(next); | ||
next(); | ||
function next(value) { | ||
if (state && state.done) { | ||
resolve(value); | ||
return; | ||
} | ||
state = gen.next(value); | ||
value = state.value; | ||
if (isPromise(value)) { | ||
value.then(function(value) { | ||
next(value); | ||
}) | ||
return; | ||
} | ||
next(value); | ||
} | ||
}) | ||
} | ||
// convert to promise as much possible | ||
function toPromise(obj) { | ||
if (isPromise(obj)) { | ||
return obj; | ||
} | ||
if (isArray(obj)) { | ||
return arrayToPromise(obj); | ||
} | ||
if (isObject(obj)) { | ||
return objectToPromise(obj); | ||
} | ||
} | ||
// convert array to promise | ||
function arrayToPromise(array) { | ||
var promise; | ||
return CRL.Promise.all(array.map(function(value) { | ||
promise = toPromise(value); | ||
if (isPromise(promise)) { | ||
return promise; | ||
} | ||
return value; | ||
})); | ||
} | ||
// convert object to promise | ||
function objectToPromise(obj) { | ||
var key, promise, ret, | ||
promises = [], | ||
result = {}, | ||
i = -1, | ||
keys = Object.keys(obj), | ||
len = keys.length; | ||
ret = new CRL.Promise(function(resolve) { | ||
while (++i < len) { | ||
key = keys[i]; | ||
promise = toPromise(obj[key]); | ||
if (isPromise(promise)) { | ||
setupThen(promise, key); | ||
} | ||
else { | ||
result[key] = obj[key]; | ||
} | ||
} | ||
CRL.Promise.all(promises).then(function() { | ||
resolve(result); | ||
}) | ||
function setupThen(promise, key) { | ||
// default value | ||
result[key] = void 0; | ||
promise.then(function(value) { | ||
result[key] = value; | ||
}) | ||
promises.push(promise); | ||
} | ||
}) | ||
return ret; | ||
} | ||
// receiver | ||
CRL.receive = function receive(obj) { | ||
var prom; | ||
if (obj && isFunction(obj.receive)) { | ||
return obj.receive(); | ||
} | ||
prom = toPromise(obj); | ||
if (isPromise(prom)) { | ||
return prom; | ||
} | ||
return obj; | ||
} | ||
// sender | ||
CRL.send = function send(obj, value) { | ||
if (obj && isFunction(obj.send)) { | ||
return obj.send(value); | ||
} | ||
throw 'unable to receive values'; | ||
} | ||
function timeout(time) { | ||
if (!isNaN(time) && time !== null) { | ||
return new CRL.Promise(function(resolve) { | ||
schedule(resolve, time) | ||
}) | ||
} | ||
throw 'Invalid time'; | ||
} | ||
CRL.timeout = timeout; | ||
// Buffer: simple array based buffer to use with channels | ||
function Buffer(size) { | ||
this.size = isNaN(size) ? 1 : size; | ||
this.array = []; | ||
} | ||
Buffer.prototype = { | ||
shift: function() { | ||
return this.array.shift(); | ||
}, | ||
push: function(value) { | ||
if (this.isFull()) { return false } | ||
this.array.push(value); | ||
return true; | ||
}, | ||
isFull: function() { | ||
return !(this.array.length < this.size); | ||
}, | ||
isEmpty: function() { | ||
return this.array.length === 0; | ||
} | ||
} | ||
// Channel: a structure to transport messages | ||
function indentityFn(x) {return x} | ||
function scheduledResolve(deferred, value) { | ||
schedule(function() { deferred.resolve(value) }) | ||
} | ||
function Channel(buffer, transform) { | ||
this.buffer = buffer; | ||
this.closed = false; | ||
this.data = void 0; | ||
this.senderPromises = []; | ||
this.receiverPromises = []; | ||
this.transform = transform || indentityFn; | ||
} | ||
Channel.prototype = { | ||
receive: function() { | ||
var data, deferred; | ||
// is unbuffered | ||
if (! this.buffer) { | ||
// there is data? | ||
if (this.data !== void 0) { | ||
// resume the first sender coroutine | ||
if (this.senderPromises[0]) { | ||
scheduledResolve(this.senderPromises.shift()); | ||
} | ||
// clean and return | ||
data = this.data; | ||
this.data = void 0; | ||
return data; | ||
// if no data | ||
} else { | ||
// suspend the coroutine wanting to receive | ||
deferred = Promise.defer(); | ||
this.receiverPromises.push(deferred); | ||
return deferred.promise; | ||
} | ||
} | ||
else { | ||
throw 'Trying to assert undefined object'; | ||
// if buffered | ||
// empty buffer? | ||
if (this.buffer.isEmpty()) { | ||
// suspend the coroutine wanting to receive | ||
deferred = Promise.defer(); | ||
this.receiverPromises.push(deferred); | ||
return deferred.promise; | ||
// some value in the buffer? | ||
} else { | ||
// resume the first sender coroutine | ||
if (this.senderPromises[0]) { | ||
scheduledResolve(this.senderPromises.shift()); | ||
} | ||
// clean and return | ||
return this.buffer.shift(); | ||
} | ||
}, | ||
send: function(data) { | ||
if (this.closed) { throw 'closed channel' } | ||
var deferred; | ||
// is unbuffered | ||
if (! this.buffer) { | ||
// some stored data? | ||
if (this.data !== void 0) { | ||
// deliver data to the first waiting coroutine | ||
if (this.receiverPromises[0]) { | ||
scheduledResolve(this.receiverPromises.shift(), this.data); | ||
} | ||
// no stored data? | ||
} else { | ||
// pass sent data directly to the first waiting for it | ||
if (this.receiverPromises[0]) { | ||
this.data = void 0; | ||
scheduledResolve(this.receiverPromises.shift(), this.transform(data)); | ||
// schedule the the sender coroutine | ||
return new timeout(0); | ||
} | ||
} | ||
// else, store the transformed data | ||
this.data = this.transform(data); | ||
deferred = Promise.defer(); | ||
this.senderPromises.push(deferred); | ||
return deferred.promise; | ||
} | ||
// if buffered | ||
// emty buffer? | ||
if (! this.buffer.isFull()) { | ||
// TODO: optimize below code | ||
// store sent value in the buffer | ||
this.buffer.push(this.transform(data)); | ||
// if any waiting for the data, give it | ||
if (this.receiverPromises[0]) { | ||
scheduledResolve(this.receiverPromises.shift(), this.buffer.shift()); | ||
} | ||
} | ||
// full buffer? | ||
if (this.buffer.isFull()) { | ||
// stop until the buffer start to be drained | ||
deferred = Promise.defer(); | ||
this.senderPromises.push(deferred); | ||
return deferred.promise; | ||
} | ||
}, | ||
close: function() { | ||
this.closed = true; | ||
this.senderPromises = []; | ||
while (this.receiverPromises.length) { | ||
scheduledResolve(this.receiverPromises.shift()); | ||
} | ||
} | ||
else { | ||
throw 'Trying to assert undefined class'; | ||
} | ||
CRL.Channel = Channel; | ||
CRL.chan = function chan(size, transform) { | ||
if (size instanceof Buffer) { | ||
return new Channel(size, transform); | ||
} | ||
return false; | ||
}; | ||
// isNaN(null) == false :O | ||
if (isNaN(size) || size === null) { | ||
return new Channel(null, transform); | ||
} | ||
})(); | ||
return new Channel(new Buffer(size), transform); | ||
} | ||
})(this); |
@@ -186,3 +186,3 @@ (function(cor){ | ||
context: function(n) { | ||
if (isNaN(n)) { | ||
if (isNaN(n) || n === null) { | ||
n = this.contexts.length - 1; | ||
@@ -189,0 +189,0 @@ } |
@@ -84,2 +84,19 @@ (function(cor){ | ||
var builtinFn = [ | ||
'error', | ||
'super', | ||
'regex', | ||
'chan', | ||
'timeout' | ||
]; | ||
function isBuiltinFn(name) { | ||
return builtinFn.indexOf(name) !== -1; | ||
} | ||
function isEcmaReservedKeyWord(name) { | ||
return EcmaReservedKeywords.indexOf(name) !== -1; | ||
} | ||
yy.parseError = function parseError (msg, hash, replaceMsg) { | ||
@@ -293,2 +310,3 @@ var filename = yy.env.filename; | ||
this.children = this.children.concat(slice.call(arguments)); | ||
this.adopt(this.children); | ||
}, | ||
@@ -298,2 +316,3 @@ | ||
this.children = slice.call(arguments).concat(this.children); | ||
this.adopt(this.children); | ||
}, | ||
@@ -396,3 +415,2 @@ | ||
} | ||
} | ||
@@ -548,3 +566,2 @@ | ||
} | ||
} | ||
@@ -613,4 +630,4 @@ }); | ||
this.from = this.children[2]; | ||
this.to = this.children[4]; | ||
this.start = this.children[2]; | ||
this.len = this.children[4]; | ||
}, | ||
@@ -621,8 +638,8 @@ | ||
lit, | ||
from = this.from, | ||
to = this.to, | ||
ch = this.children; | ||
start = this.start, | ||
len = this.len, | ||
ch = this.children; | ||
if (from === undefined) { | ||
from = new yy.Lit('0', ch[1].lineno); | ||
if (start === undefined) { | ||
start = new yy.Lit('0', ch[1].lineno); | ||
} | ||
@@ -633,10 +650,10 @@ | ||
new yy.Lit('.slice(', ch[1].lineno), | ||
from | ||
start | ||
]; | ||
if (to !== undefined) { | ||
if (to instanceof yy.UnaryExprNode && typeof to.children[1].children === 'string') { | ||
lit = new yy.Lit(stringifyNode(to), to.lineno); | ||
lit.loc = to.children[1].loc; | ||
to = lit; | ||
if (len !== undefined) { | ||
if (len instanceof yy.UnaryExprNode && typeof len.children[1].children === 'string') { | ||
lit = new yy.Lit(stringifyNode(len), len.lineno); | ||
lit.loc = len.children[1].loc; | ||
len = lit; | ||
} | ||
@@ -646,3 +663,3 @@ | ||
new yy.Lit(', ', ch[3].lineno), | ||
to | ||
len | ||
); | ||
@@ -710,2 +727,5 @@ } | ||
if (!ch[1]) { | ||
this.keyValue = true; | ||
} | ||
if (ch[3]) { // key-value | ||
@@ -742,3 +762,3 @@ this.keyValue = true; | ||
this.children.splice(2, 0, new yy.Lit(', _conf: true', this.children[2].lineno)) | ||
} | ||
} | ||
} | ||
@@ -752,2 +772,15 @@ else { | ||
yy.ArrayConstructorNode = Class(yy.Node, { | ||
type: 'ArrayConstructorNode', | ||
compile: function() { | ||
var ch = this.children[1]; | ||
if (ch && (ch.children.length % 2) === 0) { | ||
ch.children.pop(); | ||
} | ||
} | ||
}) | ||
yy.TypeAssertNode = Class(yy.Node, { | ||
@@ -808,3 +841,3 @@ | ||
if (EcmaReservedKeywords.indexOf(this.name) !== -1) { | ||
if (isEcmaReservedKeyWord(this.name)) { | ||
this.children[0].children = this.name += '_'; | ||
@@ -1068,3 +1101,3 @@ } | ||
if (this.superClassName) { | ||
this.methodSet.children[0].children[1].children += this.runtimeFn('extend') + this.className + extendsStr +');'; | ||
this.methodSet.children[0].children[1].children += this.runtimeFn('subclass') + this.className + extendsStr +');'; | ||
} | ||
@@ -1110,3 +1143,3 @@ }, | ||
if (this.superClassName) { | ||
newNode = new yy.Lit(this.runtimeFn('extend') + this.className + extendsStr +');', this.propertySet.lineno); | ||
newNode = new yy.Lit(this.runtimeFn('subclass') + this.className + extendsStr +');', this.propertySet.lineno); | ||
newNode.loc = ch[2].loc; | ||
@@ -1254,7 +1287,5 @@ this.children.push(newNode); | ||
if (this.name) { | ||
builtin = this[this.name + 'Builtin']; | ||
if (builtin) { | ||
builtin.call(this); | ||
} | ||
builtin = this[this.name + 'Builtin']; | ||
if (this.name && isBuiltinFn(this.name) && builtin) { | ||
builtin.call(this); | ||
} | ||
@@ -1323,2 +1354,80 @@ | ||
} | ||
}, | ||
regexBuiltin: function() { | ||
if (!this.children[2]) { | ||
this.error('invalid regular expression pattern', this.children[0].lineno); | ||
} | ||
var | ||
flags, | ||
ch = this.children, | ||
params = ch[2], | ||
patternNode = params.children[0], | ||
flagsNode = params.children[2], | ||
regStart = /^\'/, | ||
regEnd = /\'$/, | ||
regDelim = /\//g, | ||
strDelim = "\\'", | ||
newLine = /\n(\s+)?/g, | ||
rFlags = /[gimy]+/, | ||
rEscape = /\\(?=[bBdDsSwW])/g; | ||
function cleanPattern(p) { | ||
return p.replace(newLine, '').replace(regDelim, '\\/'); | ||
} | ||
if (patternNode instanceof yy.StringNode && (flagsNode instanceof yy.StringNode || flagsNode == void 0)) { | ||
patternNode.children = cleanPattern(patternNode.children).replace(regStart, '\/') | ||
.replace(regEnd, '\/') | ||
.replace(newLine, '\\n') | ||
.replace(strDelim, "'"); | ||
if (patternNode.children === '//') { | ||
this.error('invalid regular expression pattern', patternNode.lineno); | ||
} | ||
if (flagsNode) { | ||
flags = flagsNode.children.replace(regStart, '').replace(regEnd, ''); | ||
if (flags !== '' && !rFlags.test(flags)) { | ||
this.error('invalid regular expression flags', flagsNode.lineno); | ||
} | ||
patternNode.children += flags; | ||
} | ||
this.children = [ | ||
patternNode | ||
]; | ||
return; | ||
} else { | ||
ch[0].children[0].children = this.runtimePrefix + 'regex'; | ||
} | ||
if (patternNode instanceof yy.StringNode) { | ||
// special symbols | ||
// bBdDsSwW | ||
patternNode.children = cleanPattern(patternNode.children).replace(rEscape, '\\\\'); | ||
} | ||
}, | ||
chanBuiltin: function() { | ||
var ch = this.children; | ||
ch[0].children[0].children = this.runtimePrefix + 'chan'; | ||
}, | ||
timeoutBuiltin: function() { | ||
if (! isInGoExpr(this)) { | ||
this.error('unexpected timeout operation', this.lineno); | ||
} | ||
var ch = this.children; | ||
ch[0].children[0].children = this.runtimePrefix + 'timeout'; | ||
ch.unshift(new yy.Lit('yield ', getLesserLineNumber(ch[0]))) | ||
} | ||
@@ -1394,2 +1503,3 @@ | ||
} | ||
}, | ||
@@ -1501,2 +1611,3 @@ | ||
} | ||
} | ||
@@ -1530,4 +1641,3 @@ | ||
ch[6], | ||
] | ||
]; | ||
} | ||
@@ -1590,3 +1700,3 @@ | ||
]; | ||
} | ||
} | ||
} | ||
@@ -1675,3 +1785,6 @@ }); | ||
]; | ||
} | ||
} | ||
// re-adopt | ||
this.adopt(this.children); | ||
} | ||
@@ -1681,2 +1794,102 @@ }) | ||
// check if a node is inside a `go` expression | ||
function isInGoExpr(node) { | ||
var goExprFound = false; | ||
while(node.parent) { | ||
node = node.parent; | ||
if (node instanceof yy.ContextAwareNode && !goExprFound) { | ||
return false; | ||
} | ||
if (node instanceof yy.GoExprNode) { | ||
goExprFound = true; | ||
} | ||
} | ||
return goExprFound; | ||
} | ||
yy.GoExprNode = Class(yy.Node, { | ||
type: 'GoExprNode', | ||
compile: function() { | ||
var | ||
ch = this.children, | ||
fnNode = ch[1]; | ||
ch[0].children = this.runtimePrefix + 'go(function* go()'; | ||
fnNode.children[fnNode.children.length - 1].children += ', this)'; | ||
} | ||
}) | ||
yy.SendAsyncNode = Class(yy.Node, { | ||
type: 'SendAsyncNode', | ||
compile: function() { | ||
if (! isInGoExpr(this)) { | ||
this.error('unexpected async operation', this.lineno); | ||
} | ||
var | ||
ch = this.children; | ||
ch[1].children = ','; | ||
ch.splice(0, 0, new yy.Lit('yield ' + this.runtimeFn('send'), ch[0].lineno)); | ||
ch.push(new yy.Lit(')', ch[ch.length - 1].lineno)); | ||
} | ||
}) | ||
yy.ReceiveAsyncNode = Class(yy.Node, { | ||
type: 'ReceiveAsyncNode', | ||
compile: function() { | ||
if (! isInGoExpr(this)) { | ||
this.error('unexpected async operation', this.lineno); | ||
} | ||
var | ||
ch = this.children; | ||
ch[0].children = 'yield ' + this.runtimeFn('receive'); | ||
ch.push(new yy.Lit(')', ch[ch.length - 1].lineno)); | ||
} | ||
}) | ||
yy.TemplateLiteralNode = Class(yy.Node, { | ||
type: 'TemplateLiteralNode', | ||
compile: function() { | ||
var str, list, i, len, item, | ||
ch = this.children; | ||
if (ch.length === 1) { | ||
// simple template | ||
ch[0] = new yy.StringNode(ch[0].children.substr(1), ch[0].loc); | ||
} else { | ||
// interpolation | ||
str = ch[0].children; | ||
ch[0] = new yy.StringNode(str.substring(1, str.length-1) + "' + ", ch[0].loc); | ||
str = ch[2].children; | ||
ch[2] = new yy.StringNode(" + '" + str.substring(1), ch[2].loc); | ||
list = ch[1]; | ||
for (i = -1, len = list.children.length-2; i < len;) { | ||
i+=2; | ||
item = list.children[i]; | ||
str = item.children; | ||
list.children[i] = new yy.StringNode(" + '" + str.substring(1, str.length-1) + "' + ", item.loc); | ||
} | ||
} | ||
} | ||
}) | ||
})(typeof cor === 'undefined' ? {} : cor); |
(function(cor){ | ||
// This is a version, | ||
// the original code can be found at | ||
// https://github.com/jashkenas/coffeescript/tree/master/src/sourcemap.litcoffee | ||
var | ||
@@ -4,0 +8,0 @@ Class = cor.Class, |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
872741
136
15033
68