Skip to content
satyr edited this page Jul 21, 2012 · 143 revisions

Assignment

nondeclaring assignment :=

Assigns to an existing variable without declaring a new one.

$ coco -bce 'a = 1; new -> a := 2; b = 3'
var a;
a = 1;
new function(){
  var b;
  a = 2;
  b = 3;
};

$ coco -ce 'x := 0'
assignment to undeclared variable "x" on line 1

This is essential because (unlike CoffeeScript) our = always declares the variable in current scope.

ref. https://github.com/jashkenas/coffee-script/issues/712

conditional assignments && +=

Any assignment operator can be conditional when prefixed with &&, ||, ? or !?.

$ coco -bce 'a.b |||= c.d ?= e'
var __ref;
a.b || (a.b |= (__ref = c.d) != null ? __ref : c.d = e);

$ coco -bce '[x, y] &&= z'
var x, y;
x && (x = z[0]), y && (y = z[1]);

unary assignments ! = x

Compound assignments for unary operators.

$ coco -bcs
  # syntax: UnaryOp+ AssignOp LHSExpression
  +    = x  # numberize
  !!   = x  # enbool
  -~-~ = x  # intcasting bicrement

var x;
x = +x;
x = !!x;
x = -~-~x;

assignment defaults (o || {}) -> [a ? b] = c

You can specify a default value to each function parameter or destructuring target, using one of logical operators (|| && ? !?).

$ coco -bce '(o || {}) -> [@a ? b] = c'
(function(o){
  var __ref;
  o || (o = {});
  return this.a = (__ref = c[0]) != null ? __ref : b, c;
});
  • For parameters, = can be used in place of ?.

subdestructuring x[a, b] = y x{a, b} = y

Performs destructuring into an object's properties.

$ coco -bcs
  x[a, b] = y
  x{a, b} = y

x[a] = y[0], x[b] = y[1];
x.a = y.a, x.b = y.b;

named destructuring ({p, q}:o) ->

You can provide a name to a destructuring pattern, which will be used for the intermediate variable instead of a temporary one.

$ coco -bcs
  ([p, q]:a) ->
  {{x}:b, [y]:c} = o  # short for {b: {x}:b, c: [y]:c}

var b, x, c, y;
(function(a){
  var p, q;
  p = a[0], q = a[1];
});
b = o.b, x = b.x, c = o.c, y = c[0];

soak assign a? = b

Performs assignment only when the right-hand-side value exists.

$ coco -bcs
  [, left, rite]? = /(\w+)\s*=\s*(\w+)/exec input
  ({when, where}?) -> go where, when

var left, rite, __ref;
if ((__ref = /(\w+)\s*=\s*(\w+)/.exec(input)) != null) {
  left = __ref[1], rite = __ref[2];
}
(function(__arg){
  var when, where;
  if (__arg != null) {
    when = __arg.when, where = __arg.where;
  }
  return go(where, when);
});

explicit variable declarations

constant const x = 0

Performs assignments and flags the declared variables as read-only. Redeclaration/reassignment on them will be a compile error.

$ coco -s
  const a = 0
  a++

SyntaxError: increment of constant "a" on line 2

uninitialized var x

Plain ol' var without initialization and redeclaration.

$ coco -ps
  var a, b
  a is b is undefined

true

$ coco -e '(x) -> var x'
SyntaxError: redeclaration of "x" on line 1

exporting export x, y

Creates the same name property of target expression(s) on exports or top-level this.

$ coco -bcs
  export a, b.c
  export
    d: e
    function f then g
  export const h = 1

var h, __out = typeof exports != 'undefined' && exports || this;
__out.a = a;
__out.c = b.c;
__out.d = e;
__out.f = f;
function f(){
  return g;
}
__out.h = h = 1;
  • Named expressions (variable, dot-identifier access, function declaration, class) are exported with those names.
  • All other expressions get all own properties exported.

Property Access

dot X

In addition to identifiers, our dot operators accept many more things as their right operand. Numbers, strings, parentheses, brackets and braces.

$ coco -bce 'x.0."1".(2).[3]'
x[0]["1"][2][3];

Therefore we don't technically have the bracket notation for property access in {Java,Coffee}Script sense. o[x] is just short for o.[x].

accessignment .= .+=

Compound assignments for ..

$ coco -bce 'location.href.=replace /^http:/, \https:'
location.href = location.href.replace(/^http:/, 'https:');

$ coco -bce 'a.+=0'
a += a[0];

$ coco -bce '[b, c].=reverse()'
var b, c, __ref;
__ref = [b, c].reverse(), b = __ref[0], c = __ref[1];
  • Is at same precedence as ..
  • Consumes all dot/call chains to the right.

binding access .~

Retrieves an object's method as bound to the object.

$ coco -bce o.p.~m
__bind(o.p, 'm');
function __bind(obj, key){
  return function(){ return obj[key].apply(obj, arguments) };
}

$ coco -ps
  [2 5 9]map 42~toString
  # desugars to: [2, 5, 9].map -> 42.toString.apply 42, arguments

[ '101010', '132', '46' ]

In other words, o.~m sort of emulates o.m.bind(o) in ES5.

array slice x[a, b]

Postfix brackets can take multiple arguments, in which case they collect retrieved values into an array.

$ coco -bce 'x[a, b]'
[x[a], x[b]];

object slice x{a, b: c} _

Postfix curly braces extract the selected properties into a new object.

$ coco -bce 'x{a: b, c}'
({
  a: x.b,
  c: x.c
});

length star [*]

An asterisk within an indexer represents the length of the indexee.

$ coco -bce 'a[*] = b'
a[a.length] = b;

$ coco -bce 'arr()[* - 1]'
var __ref;
(__ref = arr())[__ref.length - 1];

$ coco -bce '{(* >> 1): mid} = a'
var mid;
mid = a[a.length >> 1];

semiautovivification .@ .@@

Ensures that the property exists as an object or array. x.@y desugars to (x.y ||= {}).

$ coco -bcs
  root.@obj.@@arr.push it
  A@D@@I

var __ref;
((__ref = root.obj || (root.obj = {})).arr || (__ref.arr = [])).push(it);
(__ref = A.D || (A.D = {})).I || (__ref.I = []);

Function

extended parameters

$ coco -se
  setXYZ = (
    point  # target object to set the coordinates
    point.x, point.y, point.z
  ) -> point

  setXYZ {} 2 3 5

{ x: 2, y: 3, z: 5 }

Function parameters can be:

  • written multi-line, using the same syntax as array items.
  • any assignable expressions.

function declaration function f(a, b) then

Declares or creates a named function, without touching the nasty JScript bug.

$ coco -bcs
  # syntax: "function" Name? ParameterList? Block
  function equal a, b
    a is b
  add = function (x, y)
    x + y
  ~function bound then this

var add, __this = this;
function equal(a, b){
  return a === b;
}
add = (function(){
  function add(x, y){
    return x + y;
  }
  return add;
}());
function bound(){
  return __this;
}

$ coco -s
  function f
    f = ->

SyntaxError: redeclaration of function "f" on line 2
  • Parentheses around parameter list can be omitted in the same way that f(a) can be f a.
  • Prefix ~ to bind parent this.
  • The declared variable is read-only.

implicit argument it

Represents the first argument of the current function, like in Groovy's closure. Available only when no parameters are declared.

$ coco -bce 'I = -> it'
var I;
I = function(it){
  return it;
};

do-call do f

A unary operator that calls a function with no arguments, helping you write less parentheses.

$ coco -bce 'do f; do ->'
f();
(function(){})();

bang-call f!

Sugar for ().

$ coco -bce 'f! g?!'
f()(typeof g == 'function' ? g() : void 8);

IIFE sugars

with arguments let a = b then ...

Short for the idiom (function(a){...}.call(this, b)).

$ coco -bcs
  # syntax: "let" Assignments? Block
  let (a = b)
    assert a is b

  r = let      # short for `let ()`
    let c, @d  # short for `let (c = c, d = @d)`
      c + d

var r;
(function(a){
  assert(a === b);
}.call(this, b));
r = (function(){
  return (function(c, d){
    return c + d;
  }.call(this, c, this.d));
}.call(this));

with context with x then ...

Short for the idiom (function(){...}.call(x)).

$ coco -bcs
  # syntax: "with" Expression Block
  xhr = with new XMLHttpRequest
    @open \GET \data
    @send null
    this

var xhr;
xhr = (function(){
  this.open('GET', 'data');
  this.send(null);
  return this;
}.call(new XMLHttpRequest));

with new context new then ...

Short for new ->, saving precious two key strokes.

$ coco -bcs
  point = new
    @x = 9
    @y = 6

var point;
point = new function(){
  this.x = 9;
  this.y = 6;
};

$ coco -se
  # pseudo object comprehension
  flip = (obj) -> new
    @[v] = k for own k, v in obj

  flip \flop

{ f: '0', l: '1', o: '2', p: '3' }    

As in class constructor, no implicit return is generated.

thisplat f(...) _

Shorthand to delegate the current context to a function.

$ coco -bce 'f ...'
f.apply(this, arguments);

backcall <- f _

Sugar for flattening nested callbacks.

$ coco -bcs
  <~ f
  e <- g this
  return if e
  ( x
    y
  ) <- h ..., z

var __this = this;
f(function(){
  return g(__this, function(e){
    if (e) {
      return;
    }
    return h(function(x, y){}, z);
  });
});
  • Can only appear on the top level of a block.
  • Consumes all consecutive lines of that block as the function body.
  • <- for plain, <~ for bound.
  • ... can be used to specify the callback position when there are other arguments after it.

hushing auto-return !(a) -> a <-! f

Negating a function literal or the callee of a backcall suppresses the implicit return.

$ coco -bcs
  setTimeout !->
    !function say
      console.log it
    <-! setTimeout
    say \yes

setTimeout(function(){
  function say(it){
    console.log(it);
  }
  setTimeout(function(){
    say('yes');
  });
});

paren-free method chains o.f x, y .g z

Implicit calls don't cross spaced dots, allowing you to chain methods without parentheses.

$ coco -bcs
  obj.foo x, y .bar z
     .baz!

obj.foo(x, y).bar(z).baz();

Function literals within loops are defined outside.

$ coco -bcs
  sums = for a of as
    a.reduce (x, y) -> x + y

var a, sums, __res, __i, __ref, __len;
__res = [];
for (__i = 0, __len = (__ref = as).length; __i < __len; ++__i) {
  a = __ref[__i];
  __res.push(a.reduce(__fn));
}
sums = __res;
function __fn(x, y){
  return x + y;
}

Construction

do-block do then

do with an indented block represents a pair of parenthesis (unless followed by while/until), either as grouping or calling.

$ coco -bcs
  r = do
    f do
      a
      b
    g()

var r;
r = (f(a, b), g());

simple class

Our class defines the constructor as a bare function and properties (prototype members) as bare objects, both on top level under the class block.

$ coco -bcs
  class exports.C extends P
    /* constructor */
    -> super ...

    /* properties */
    member: 'U'
    method: -> super @member, ..static()

    /* any code */
    $private = 42
    @static = -> $private

var C;
exports.C = C = (function(superclass){
  /* constructor */
  C.displayName = 'C';
  var $private, prototype = __extend(C, superclass).prototype, constructor = C;
  function C(){
    superclass.apply(this, arguments);
  }
  /* properties */
  prototype.member = 'U';
  prototype.method = function(){
    return superclass.prototype.method.call(this, this.member, constructor['static']());
  };
  /* any code */;
  $private = 42;
  C['static'] = function(){
    return $private;
  };
  return C;
}(P));
function __extend(sub, sup){
  function fun(){} fun.prototype = (sub.superclass = sup).prototype;
  (sub.prototype = new fun).constructor = sub;
  if (typeof sup.extended == 'function') sup.extended(sub);
  return sub;
}

In short,

class C extends P
  -> /* constructor */
  {'properties'}
  ...

desugars roughly to

C = do ->
  function C
    /* constructor */
  C.displayName = 'C'
  with {prototype} = constructor = C extends superclass = P
    prototype import {'properties'}
    ...
  C

The constructor:

  • receives displayName property, useful for debugging, reflection etc.
  • is accessible as constructor (or ..).

bound constructor

Using the wavy arrow ~> for constructor makes the class to be new-free, like native Array which works with or without new.

$ coco -bce 'Bound = class then (@name) ~>'
var Bound;
Bound = (function(){
  Bound.displayName = 'Bound';
  var prototype = Bound.prototype, constructor = Bound;
  function Bound(name){
    var __this = this instanceof __ctor ? this : new __ctor;
    __this.name = name;
    return __this;
  } function __ctor(){} __ctor.prototype = prototype;
  return Bound;
}());

This form allows the constructor to return non-object values. Make sure to return this when you're returning the instance early.

simple super

super is an alias to superclass or the nearest method (a function defined via top-level object under class). When a super starts an access/call chain, the next call auto-passes current this.

$ coco -bce 'super.classMethod!'
superclass.classMethod.call(this);

mixins implements M, N

You can specify mixin objects to be merged into prototype at the tail of class line.

$ coco -bce 'class implements M, N then O'
(function(){
  var prototype = constructor.prototype;
  __importAll(prototype, arguments[0]);
  __importAll(prototype, arguments[1]);
  O;
  function constructor(){}
  return constructor;
}(M, N));
function __importAll(obj, src){
  for (var key in src) obj[key] = src[key];
  return obj;
}

loop

in range for from to til by

A generic way to loop within certain numeric ranges.

$ coco -bce 'for i from x to y then'
var i, __to;
for (i = x, __to = y; i <= __to; ++i) {}

$ coco -bce 'for i from x til y then'
var i, __to;
for (i = x, __to = y; i < __to; ++i) {}

$ coco -bce 'for i from x to y by -1 then'
var i, __to;
for (i = x, __to = y; i >= __to; --i) {}

$ coco -bce 'for i from x to y by z then'
var i, __step, __to;
for (i = x, __to = y, __step = z; __step < 0 ? i >= __to : i <= __to; i += __step) {}

$ coco -bce 'for i til x then'
var i, __to;
for (i = 0, __to = x; i < __to; ++i) {}
  • to for inclusive, til for exclusive.
  • by optionally specifies the step value.
  • from clause is optional, implying from 0.

scoped

IIFE sugars (let/with/new) within for body automatically capture the loop variables.

$ coco -s
  fs = for i til 3
    let then -> console.log i
  fs.forEach -> do it

0
1
2

$ coco -bce 'for k in o then new -> say k'
var k;
for (k in o) {
  new __fn(k);
}
function __fn(k){
  say(k);
}

postconditional _

The traditional do-while (or until) loop.

$ coco -bcs
  # syntax: "do" Block "while"|"until" Condition
  do
    do then
    while x
  until y

do {
  do {} while (x);
} while (!y);

with update clause while i < l, ++i

while/until accepts an update clause after test clause, enabling full emulation of JS for wrt continue.

$ coco -bcs
  until ok(), step()
    continue if hop()
    jump()

for (; !ok(); step()) {
  if (hop()) {
    continue;
  }
  jump();
}

with else clause while f() then g() else h() Preconditional loops can have an else clause, which runs when the loop body didn't run.

$ coco -bce 'while f() then g() else h()'
var __yet;
for (__yet = true; f();) {
  __yet = false;
  g();
} if (__yet) {
  h();
}

forever for ever

Sugar for while true.

$ coco -bce 'continue for ever'
for (;;) {}

switching switch case default

switch (as in JS) with multiple conditions and auto break insertions.

$ coco -bcs
  switch n
  case 1     then 2
  case 3 4   then 5
  case [6 7] then 8; fallthrough
  default 9

  switch
  case a
    break
  case c[d, e]
    f

switch (n) {
case 1:
  2;
  break;
case 3:
case 4:
  5;
  break;
case 6:
case 7:
  8;
  // fallthrough
default:
  9;
}
switch (false) {
case !a:
  break;
case !(c[d] || c[e]):
  f;
}

Basically the same as CoffeeScript's switch-when-else, except ours:

  • requires less indentation.
    • case/default are placed at the same level as switch.
  • supports falling through.
    • If the last expression of case block is fallthrough, it becomes a comment instead of break.
  • expands arrays in case conditions.

Incidentally this is pretty similar to Go's switch.

case can be omitted. This form is handy for refactoring nested ifs, e.g.:

unless a()
  b()
  unless c()
    d()

switch
  break if a()
  b()
  break if c()
  d()

anaphoric if/while/switch-case that

Each that in those blocks implicitly refers to the condition value.

$ coco -bcs
  that if a
  that while b?
  switch c case d then that
  switch case e then that

var that;
if (that = a) {
  that;
}
while ((that = b) != null) {
  that;
}
switch (that = c) {
case d:
  that;
}
switch (false) {
case !(that = e):
  that;
}

labels :label

$ coco -bce ':label block or expression'
label: {
  block || expression;
}

$ coco -se
:pythagoras for a from 1 to 9
  for b from a to 99
    h = Math.sqrt a*a + b*b
    if h % 1 == 0
      console.log a, b, h
      continue pythagoras
:plain
   \end
   break plain
   \unreachable

3 4 5
5 12 13
6 8 10
7 24 25
8 15 17
9 12 15
end

The syntax is backward from JS to disambiguate from the implicit object literal.

Labeling a function (literal, IIFE sugar, or backcall) gives it a name instead of creating a useless block.

$ coco -bcs
  :f ->
    :g let
       <-:h f

(function f(){
  return (function g(){
    return f(function h(){});
  }.call(this));
});

Operator

property copy <<< <<<<

Infix operators that copy enumerable properties from the right operand to left, and return the left value.

$ coco -bce 'x <<< y <<<< z'
__import(__importAll(x, y), z);
function __importAll(obj, src){
  for (var key in src) obj[key] = src[key];
  return obj;
}
function __import(obj, src){
  var own = {}.hasOwnProperty;
  for (var key in src) if (own.call(src, key)) obj[key] = src[key];
  return obj;
}

$ coco -bce 'import {y, (z)}'
this.y = y;
this[z] = z;
  • <<< is for own properties. It gets optimized to a series of assignments if the right operand is an object literal.
  • import and import all are available as aliases.

importing import a, b

Copies properties from target object(s) into current this.

$ coco -bcs
  import a, b
  import all c
  import
    f: g
    h: i
 
__import(this, a);
__import(this, b);
__importAll(this, c);
this.f = g;
this.h = i;
function __import(obj, src){
  var own = {}.hasOwnProperty;
  for (var key in src) if (own.call(src, key)) obj[key] = src[key];
  return obj;
}
function __importAll(obj, src){
  for (var key in src) obj[key] = src[key];
  return obj;
}

clone ^o

Unary ^ creates a prototypal child of the operand.

$ coco -pe '[c = ^(p = {0}); c.0; c.hasOwnProperty 0; p.isPrototypeOf c]'
[ {}, 0, false, true ]

$ coco -bce 'child = ^parent'
var child;
child = __clone(parent);
function __clone(it){
  function fun(){} fun.prototype = it;
  return new fun;
}

any-instanceof instanceof []

A bare array to the right of instanceof is expanded into or chains.

$ coco -bce 'o instanceof [C, K]'
o instanceof C || o instanceof K;

pluck v = delete o.p

If used in an expression, delete returns the original value as opposed to the useless JS behavior.

$ coco -bce 'f delete @x; delete @y'
var __ref;
f((__ref = this.x, delete this.x, __ref));
delete this.y;

inexistence !?

The counterpart of ?.

$ coco -bce '@a !?= b !? c.d!?'
this.a != null && (this.a = typeof b == 'undefined' || b === null
  ? b
  : c.d == null);

pow **

$ coco -bce '@x **= y ** z'
this.x = Math.pow(this.x, Math.pow(y, z));

The precedence and associativity are set to the same as *//, not to complicate the hierarchy further.

min/max <? >?

Binary operators that return the lesser/greater value of the two.

$ coco -bce 'r = a <? b; @c >?= d'
var r;
r = a < b ? a : b;
this.c >= d || (this.c = d);

$ coco -pe '4 >? 3 + 2'
5
  • Precedence is same as <<.

unary spread +[a, b]

Unary operators affect each item when applied to an array literal.

$ coco -bcs
  @inverts = ~[a, b]
  ++player<[str def]>

this.inverts = [~a, ~b];
++player['str'], ++player['def'];

Overloaded

string/array repetition 'x' * y

When the left operand of * is a (syntactically obvious) string/array, it is repeated [right operand] times.

$ coco -bcs
  [1] * 2
  'x' * 3
  'z' * y

[1, 1];
'xxx';
__repeatString('z', y);
function __repeatString(str, n){
  for (var r = ''; n > 0; (n >>= 1) && (str += str)) if (n & 1) r += str;
  return r;
}

The helper function (generated when the right operand is large or non-constant) uses O(logN) algorithm, making "#x" * y more efficient than the idiom Array(-~y).join('x').

array joining a * '-'

When the right operand of * is a string literal, [].join is called on the left.

$ coco -bce 'a * "-"'
var __join = [].join;
__join.call(a, "-");

string subtraction/division x - /r/ y / 's'

When the right operand of -// is a string/regex literal, ''.replace/''.split is called on the left.

$ coco -bce 'x - /r/; y / "s"'
var __replace = ''.replace, __split = ''.split;
__replace.call(x, /r/, '');
__split.call(y, "s");

$ coco -pe '({} - /\w/g) / " "'
[ '[', ']' ]

Literal

undefined/no-op literal void

A literal that compiles to an undefined value.

$ coco -bcs
  void
  (void, a) -> void
  [,,]

(function(__arg, a){});
[void 8, void 8];
  • Simply ignored when on top-level or returned.
  • Works as a placeholder within function parameters or destructuring assignment.
  • Implied when , follows (, [, or another ,.

word literal \plus \+

Simple strings without whitespace can be written by prefixing a backslash rather than enclosing with single quotes.

$ coco -bce '\\ + \" + \word + \"'
'\\' + '"' + 'word' + '"';

Note that closing symbols () ] } , ;) are disallowed after the first character, so that you can naturally write:

prompt(\?, \!)

quoted words <[ q w ]>

Sugar for a bracketted list of strings, either as an array or indexer.

$ coco -bce 'f <[ array of strings ]>'
f(["array", "of", "strings"]);

$ coco -bce 'o<[ property names ]>'
[o['property'], o['names']];

dynakey {(a+b): c} {"#key": val}

Property names within object literals can be dynamically set using parentheses or interpolated strings.

$ coco -bce 'o = {(paren): 1, "str#ing": 2, (shorthand = 3)}'
var shorthand, o, __ref, __ref2;
o = (__ref = {}, __ref[paren] = 1, __ref["str" + ing] = 2, __ref[__ref2 = shorthand = 3] = __ref2, __ref);

object splat {...o}

Mixes an object's own properties into the created object.

$ coco -bcs
  poq = {p, ...o, q}
  abc =
    a: true
    ...: b
    ...: c

var poq, abc, __ref;
poq = (__ref = {
  p: p
}, __import(__ref, o), __ref.q = q, __ref);
abc = (__ref = {
  a: true
}, __import(__ref, b), __import(__ref, c));
function __import(obj, src){
  var own = {}.hasOwnProperty;
  for (var key in src) if (own.call(src, key)) obj[key] = src[key];
  return obj;
}

flagging shorthand {+x, -y}

$ coco -bce '{+x, -y}'
({
  x: true,
  y: false
});

ref. https://github.com/jashkenas/coffee-script/issues/885

accessor properties x:~ ->

$ coco -bcs
  a:~ (@a) ->     # setter if the function has one argument
  b:~      -> @b  # getter if none
  c:~  # indent and line them up to define both
    -> @c
    (@c) ->
  # uses `Object.defineProperty` when imported 
  import x:~ ->

({
  set a(a){
    this.a = a;
  },
  get b(){
    return this.b;
  },
  get c(){
    return this.c;
  },
  set c(c){
    this.c = c;
  }
});
Object.defineProperty(this, 'x', {
  get: function(){},
  configurable: true,
  enumerable: true
});

implicit array a = then b; c _

Some operators (: = := <<< <<<< return throw ! ~ delete typeof) can take a block on the right, which becomes an array when it contains multiple items.

$ coco -bcs
  list =
    1
    2
  atom =
    3

var list, atom;
list = [1, 2];
atom = 3;

listing * x, y

Leading asterisk denotes an expression or array of expressions (thanks to implicit array).

$ coco -bcs
  points =
    * x:0 y:1
    * x:2 y:3

var points;
points = [
  {
    x: 0,
    y: 1
  }, {
    x: 2,
    y: 3
  }
];

$ coco -se
* * 0 1
  * 2

[ [ 0, 1 ], 2 ]

This is a sugar over an empty assignment taking an implicit block; the last example is same as writing:

[] =
  [] =
    0, 1
  [] =
    2

numbers with variable radix 25rCoco

Borrowing from Smalltalk, numbers can have any base between 2 to 36 in the form RRrXXX.

$ coco -bce '[2r101010, 8r52, 36r16]'
[42, 42, 42];

number comment 42_000_ms

Underscores within numbers, and alphabets after decimals are ignored.

$ coco -pe '[0xDead_Beef 7_heads]'
[ 3735928559, 7 ]

character/number ranges \A to \C 1 til 10 by 2

Expands to comma separated characters or numbers.

$ coco -bce
  # syntax: CharOrNum ("to"|"til") CharOrNum ("by" Num)?
  [\A to \C]
  f 1 til 10 by 2

["A", "B", "C"];
f(1, 3, 5, 7, 9);

heregex dynamic flag //#x//?

$ coco -bcs
  //
    heregex
    #{ y } # normal interpolation
    #{ z } # sets the flag part
  //?

RegExp('heregex' + y, z);

regex string /\\/$

Same as .source, but on compile time. Useful for building large regexes piece by piece.

$ coco -bce '/\\/$; //\b#word\b//$'
'\\\\';
'\\b' + word + '\\b';

literal debugger (debugger)

debugger works as literal rather than statement, allowing you to suspend anywhere. The return value is undefined.

$ coco -bce 'f() or debugger'
f() || (function(){
  debugger;
}());

arguments shorthand @@

$ coco -bce '@@0 @@'
arguments[0](arguments);

Read: @rgument @rray

constructor shorthand .. _

$ coco -bce '@..classMethod ..'
this.constructor.classMethod(constructor);

empty splat [...]

... in an array desugars to ...[], which can be used to force implicit array or ignore intermediate items in destructuring.

$ coco -bcs
  empty =
    ...
  [first, ..., last] = array

var empty, first, last;
empty = [];
first = array[0], last = array[array.length - 1];

variable interpolation "#x"

Variables can be interpolated brace-free.

$ coco -bce '"(#id)"'
"(" + id + ")";

Miscellaneous

automatic comma insertion [0 1]

Commas after non-callable literals can be omitted when spaced.

$ coco -bce '[null true 1 "2" [3] ->]'
[null, true, 1, "2", [3], function(){}];

automatic dot insertion @0\! _

Dots before {number,string,identifier}s can be omitted unless spaced.

$ coco -bce '@1"!"l'
this[1]["!"].l;

Similarly:

  • ? becomes ?. if surrounded without space.
    • a?b => a?.b
    • c?=b => c?.=d
  • ~, @ and @@ become .~/.@/.@@ when they follow previous token without space.

unspacing o\ p

\ cancels subsequent whitespaces a la Perl6.

$ coco -bcs
  o\ p  # triggers ADI
  f \
    x   # continues line and triggers implicit call
o.p;
f(x);

Short for throw Error('unimplemented') on top level.

$ coco -e ...
Error: unimplemented

inline block comment f /* */ x

Works just as JS with no propagation to compilation.

$ coco -bce 'f /* implicit call */ x'
f(x);

CLI

compiled code in stack trace

$ coco ast
Failed at: ast.co
ReferenceError: typo is not defined
    at Object.<anonymous> (/usr/tmp/ast.co:248:1)
244|     }
245|   }
246|   return fromJSON;
247| }());
248+ typo;
249| Negatable = {
250|   show: function(){
251|     return this.negated && '!';
252|   },
    at Module._compile (module.js:402:26)
    at Object.run (/usr/local/lib/coco/lib/node.js:19:19)
    at compileScript (/usr/local/lib/coco/lib/command.js:172:14)
    at /usr/local/lib/coco/lib/command.js:130:18
    at /usr/local/lib/coco/lib/command.js:103:12
    at [object Object].<anonymous> (fs.js:107:5)
    at [object Object].emit (events.js:61:17)
    at afterRead (fs.js:878:12)
    at wrapper (fs.js:245:17)

interactive compilation

$ coco -icb
coco -cb> with document.body
.........   @style.color = \purple
.........   @textCentent = \rose
(function(){
  this.style.color = 'purple';
  this.textCentent = 'rose';
}.call(document.body));
coco -cb>

JSON output

--json modifies the behavior of --ast and --compile.

$ coco -aje 3
{
  "type": "Block",
  "lines": [
    {
      "type": "Literal",
      "value": "3",
      "line": 1
    }
  ]
}

$ coco -cje '{6 9}'
{
  "6": 6,
  "9": 9
}
  • -aj: Serializes the AST in JSON.
  • -cj: Evaluates input, then prints the result in JSON.
Clone this wiki locally