GorillaScript

Fork on Github

a compile-to-JavaScript language designed to empower the user while attempting to prevent some common errors.

Table of contents

Why not use JavaScript?

JavaScript has a lot of warts in the language and while the ECMAScript community is doing a good job with its upcoming revisions, it’s still left with a lot of the old cruft. Also, for those who want to code for older browsers, all the shiny new features that the newer versions of ECMAScript provide are for all intents and purposes unreachable.

Some of the features that are available in GorillaScript that are not (or are otherwise unwieldy) in vanilla JavaScript are:

Strict vs unstrict equality

== and != perform type coercion behind the scenes and can be the cause of some subtle bugs.

// JavaScript, all the following are true
1 == "1"
0 != ""
3 == "03"
[] == ""
[] == 0
[] == ![]
[] == false
null != false
null == undefined

These could all be fixed by adding an extra equal sign, but GorillaScript makes == and != strict by default. If one really wants the unstrict equality operators, one can use ~= and !~=, but it is not recommended except for comparing against null or undefined, which has the nice postfix operator ?.

The + operator (and +=)

+ can mean one of two things in JavaScript, addition or string concatenation. There is no way to know at compile-time what the consequences of using + is unless one is 100% certain what each type is.

x + y

Is addition unless both x and both y are numbers, booleans, undefined, or null, with any mixed variation. If either is not one of those, at which point it performs string concatenation.

// JavaScript
1 + 2 === 3 // as expected
"hello, " + "world" === "hello, world" // as expected
"hello, " + 123 === "hello, 123" // sure, I can accept this
"1" + 2 === "12"
1 + "2" === "12"

// and for some oddities
false + false === 0
false + true === 1
true + true === 2
null + null === 0
isNaN(undefined + undefined)
[] + [] === ""
{} + {} === "[object Object][object Object]"
true + [] === "true"
new Date() + 1 === "Tue Jan 29 2013 20:25:58 GMT-0800 (PST)1" // or something like it
new Date() - 1 === 1359519958072 // or some other number
var foo = {
  toString: function () { return 5; }
  valueOf: function () { return "foo"; }
};
foo.toString() + 1 === 6
foo + 1 === "foo1"

GorillaScript solves this by splitting the operator into two: both + for addition and & for string concatenation.

// GorillaScript
1 + 2 == 3
"hello, " & "world" == "hello, world"
"hello, " & 123 == "hello, 123" // concatenation with numbers still works perfectly fine
1 & 2 == "12" // despite both being numbers, & always makes a string.

1 + "2" // TypeError
"1" + 2 // TypeError
"1" + "2" // TypeError
false + false // TypeError
null + null // TypeError
void + void // TypeError
[] + [] // TypeError
{} + {} // TypeError
new Date() + 1 // TypeError
new Date().getTime() + 1 == 1359519958072 // or some other number

As can be seen, the operators which don’t fit the proper types exactly fail immediately, allowing one to catch bugs as early as possible rather than allowing them to permeate through one’s programs.

Don’t worry about losing the bitwise and operator &. It is now called bitand.

Strict mode by default

All GorillaScript code is wrapped in an immediately-invoked function expression (IIFE) which has the declaration of "use strict". This ensures that on the engines that support it, strict semantics will be followed, meaning fewer bugs in the long run.

Type safety of operators

All operators check the types of their operands to assure that there will be no improper inputs and that any errors that do occur are caught as early as possible. This is highly similar to the custom "use restrict" mode.

No other operators’ types are checked.

If one really wishes to work in an environment where the operands’ types are not checked, one can always prepend the operator with ~, so there is a ~* operator which performs multiplication without checking. It is recommended to instead parse input data into conforming types before performing operations on them.

Thankfully, GorillaScript is able to tell which types most values are, so in the general case, there should be little to no runtime type checking occurring.

Immutable by default

GorillaScript uses two separate tokens for declaration as compared to assignment. Also, instead of JavaScript’s var keyword, GorillaScript uses let. Unless one specifies let mutable, the local cannot be reset to any other value. This can prevent some errors and often helps with the clarity of one’s code.

Note: An object assigned to a local using let can still be mutated, such as pushing values into an Array. The reference to the array cannot be mutated, though.

Also, no undeclared variables can be altered, preventing unexpected global pollution (and typos).

let x = 5
x := 6 // Error
let mutable y = 5
y := 6 // perfectly fine
z := 6 // never declared, this is an error

As you may have noticed, there are two different operators for declaration = as compared to assignment :=. This is to clarify the difference. In an ideal program, having as little mutable state as possible is best, so if := jumps out, that’s a good thing.

Constants

Separate from the let statement, const allows you to specify that a value is a specific literal value. Any references in-code will be replaced by the value, allowing for pragma-like control over your code.

const DEBUG = false

DEBUG and assert(some-expensive-check())

Constants do not need to be in CONST_CASE, though it is recommended.

There are also a few constant-like values provided:

__FILE__
A string representing the current full file path. If unknown, the empty string.
__LINE__
A number representing the current line in the GorillaScript file being parsed. 1-indexed.
__COLUMN__
A number representing the current column in the GorillaScript file being parsed. 1-indexed.
__DATEMSEC__
A number representing the milliseconds since epoch.
__VERSION__
A string representing the "version" key from the closest package.json.
print __FILE__
print __LINE__
print __COLUMN__
print new Date(__DATEMSEC__)
MyProject.version := __VERSION__ // would work if we weren't in the browser

Also, if you’d like to have namespaced constants such as in other languages’ enum, you can specify an object (or array) as the value. Any unknown access into these objects will result in a compile-time error rather than a value of undefined

const FRUIT_TYPES = {
  apple: 1
  banana: 2
  cherry: 3
}

print FRUIT_TYPES.apple // 1
print FRUIT_TYPES.banana // 2
print FRUIT_TYPES.cherry // 3
// print FRUIT_TYPES.date // would error if uncommented

You don't have to use numbers as the values. In fact, you can use it as a way to compile-time check constant string values:

const FRUIT_TYPES = {
  "apple"
  "banana"
  "cherry"
}

print FRUIT_TYPES.apple // "apple"
print FRUIT_TYPES.banana // "banana"
print FRUIT_TYPES.cherry // "cherry"
// print FRUIT_TYPES.date // would error if uncommented

Indentation to mark code blocks

Instead of using braces to dictate code blocks, GorillaScript opts for whitespace indentation as a way to mark blocks. Although this may be jarring at first and one may be skeptical, any good programmer properly indents his or her code to have a consistent whitespace anyway. GorillaScript does not dictate how many spaces or tabs are used, as long as it is consistent within any given block.

if hello
  if loudly
    "HELLO!"
  else
    "hi"
else
  "Goodbye."

You may have also noticed the lack of semicolons. The parser is able to tell when the end of a statement is without them, so they are unnecessary.

Changed operators

Many of the operators have changed to provide more clarity or to free up the usage of certain symbols.

Kept the same:

Added:

Slightly nicer function syntax

There are two ways to specify functions, one directly using let, and one as an anonymous function. Also, unlike JavaScript, the last expression is automatically returned (unless tagging the function with !). Functions can be called optionally without parentheses, as long as it is unambiguous.

let increment(x)
  x + 1

assert increment(0) == 1
assert (increment 1) == 2
assert 3 == increment 2

let run(callback)
  callback()

assert run(# "hello") == "hello"
assert (run # "there") == "there"
assert run(#
  "you") == "you"
assert run(#
  let x = "guys"
  "good " & x) == "good guys"

// this syntax also works
let f() Math.random()
console.log f()

The outer this can also be captured by appending @ to the head of the function declaration, creating a “bound” function. This is similar to ECMAScript 5’s Function.prototype.bind, but more efficient since a hidden _this variable is used rather than an extra function call.

let func()
  let inner()@
    this

  assert func() == this

String interpolation

Inside double-quoted strings ("like this"), not single-quoted strings ('like this'), one can specify string interpolations using the $ symbol, followed by an identifier or a parenthetical expression.

let hello(name)
  "Hello, $name"

assert hello("World") == "Hello, World"
assert hello("Universe") == "Hello, Universe"

// or

let greet(names)
  "Hello, $(names.join ', ')"

assert greet(["World", "Universe"]) == "Hello, World, Universe"

Optional parameters

To specify an optional parameter, one simply need to specify = value in the function parameter list.

let hello(name = "World")
  "Hello, $name"

assert hello() == "Hello, World"
assert hello("Universe") == "Hello, Universe"

If a value is passed in that is null or undefined, it will be automatically turned into the default value.

Spread parameters

Instead of using JavaScript’s atrociously broken arguments special, one can specify a spread parameter by prefixing .... Only one can occur in a function parameter list, but it can be at any position.

let hello(...names)
  if names.length == 0
    "No one is here"
  else
    "Hello, $(names.join ', ')"

hello() == "No one is here"
hello("World") == "Hello, World"
hello("World", "Universe") == "Hello, World, Universe"

And so that callers don’t feel bad about themselves, you can call with spread as well.

let f(a, b, c) [a, b, c]

let items = [1, 2]

f(0, ...f(...items, 3), 4)

Dashed-identifiers

Although completely optional to use if you prefer using camelScript-style identifiers, one can now specify identifiers with dashes, such as my-name or gorillas-are-awesome. They are turned into myName and gorillasAreAwesome, respectively.

let gorillas-are-awesome = "Yes, they are."

Nicer number syntax

All numbers can have arbitrary underscores in the middle of them, which can be used for thousands separators or bitwise n-bit separators. (1_234_567 or 0x1234_5678_90ab_cdef)

Decimal numbers can have inline-comments appended to them after an underscore. (1000_ms)

Octals use the format 0o12345670 instead of 01234567, to help with clarity.

Binary numbers are available with the format 0b10101010.

Arbitrary-radix numbers are available by specifying a decimal number between 2 and 36, r, and the number. (4r01230123, 36rjdhremn)

let time = 10_000_ms
let hex = 0x1234_5678
let octal = 0o070
let binary = 0b1010010101
let radix = 36rNFfdH45
let float = 123_456.789_012

Non-decimals do support floating point unlike JavaScript, though that is a lesser-used feature.

Some new string syntaxes

Aside from the already-seen string interpolation, there are also triple-quoted strings, which allow for multi-line and are indentation-friendly.

let name = "Jimmy"
let string = """
  Hello there.
    I have a story to tell you, $name.
  I can't think of it right now, though.
  """

The indentation is stripped (but not line 2’s, since that's deliberate), and interpolation is done for $name, and the first and last newlines are removed. This all occurs at compile-time, so your result code will be as fast as possible.

There are also string like '''this''' which do not have interpolation, if you wish to use it.

There is also a short syntax for single-word strings that also convert dashed-names to camelCase just as normal identifiers do.

assert "Jimmy" == \Jimmy
assert object.some-key == object[\some-key]
assert "someKey" == \some-key

GorillaScript includes all the escape sequences you may be familiar with in JavaScript, such as "\0", "\t", "\v", and many more. You needn’t worry about some non-standard escape codes such as "\v", as GorillaScript compiles to the lowest common denominator.

It also includes the \u unicode escape sequence which takes 4 hex characters trailing it and \x which takes 2 hex characters trailing it.

Unlike JavaScript, GorillaScript can properly handle unicode code points greater than 0xFFFF, by having the \u{} syntax. Inside the braces, one can put between 1 and 6 hex characters as long as the representation doesn't exceed 0x10FFFF. This will be split up into two UTF-16 code points if exceeding 0xFFFF.

let escapes = "\b\f\r\n\t\v\0\1\2\3\4\5\6\7"
let hex = "\xe9"
let unicode = "\u1d25"
let large-unicode = "\u{20bb7}"
assert large-unicode.length == 2 // takes up 2 UTF-16 characters, one Unicode code point

Nicer syntaxes for objects and arrays

Although the standard JavaScript-style syntaxes work, there are a few other ways to specify objects and arrays.

let list = [1, 2, 3]
let other-list = [...list, 4, 5, 6] // now contains [1, 2, 3, 4, 5, 6]

// another way to specify an array
let items =
  * "Apples"
  * "Bananas"
  * "Cherries"

let obj = {
  list // same as list: list
  sum: 6
  f()
    "result" // same as f: # "result"
}

let great-apes =
  bonobos:
    awesomeness: "pretty cool"
    population: 40_000
  humans:
    awesomeness: "let's not say anything bad about these guys"
    population: 7_000_000_000
  gorillas:
    awesomeness: "clearly the best"
    population: 100_000

let special = {
  [1 + 2]: "three"
  "key$i": "interpolated key"
  class: "JavaScript would fail on the 'class' key."
}

To specify the prototype of an object:

let parent = { hello: \there }
let child = { extends parent
  value: 1
}
assert child.hello == \there
assert child.value == 1

First-class support for Maps and Sets

Map and Set are available in the upcoming ECMAScript 6 and in some existing JavaScript engines. There are also shims to provide support in engines that do not support it out-of-the-box.

GorillaScript will automatically provide a shim if used, but it is recommended to use either a native implementation or a more-efficient shim.

Map is similar to Object, except its keys are not required to be Strings, but can be any type. Also, access and assignment is done through method calls rather than raw dot-access.

Set is a collection of unique values, similar to a Map where all values are true.

GorillaScript has syntax for declaring both of these as literals, just as one can do with Arrays or Objects.

let obj = {}
let other = {}
let map = %{
  [obj]: 1
  [other]: "hello"
}
assert map.get(obj) == 1
assert map.get(other) == "hello"
map.delete other
assert map.has obj
assert not map.has other
map.set other, "there"
assert map.has other
assert map.get(other) == "there"

let set = %[obj, other]
assert set.has(obj)
assert set.has(other)
set.delete(other)
assert not set.has(other)
set.add other
assert set.has(other)
set.add other // does nothing, already in the set.

Unless statement

To correlate with the if statement, there is also an unless statement which works as its exact opposite.

if hates-bananas
  "You monster."
else unless loves-gorillas
  "How could you?"
else if likes-the-gorillaz
  "Fire comes out of the monkey's head."
else
  "Well, at least you love gorillas and don't hate bananas."

Loops

GorillaScript provides many different looping constructs, all fitted for their own purposes.

Normal while loop, same as JavaScript:

let mutable i = 0
while i < 10
  console.log i
  i += 1

Opposite of a while loop, until:

let mutable i = 0
until i >= 10
  console.log i
  i += 1

Better version of the above, acts like JavaScript’s for(;;)

let mutable i = 0
while i < 10, i += 1
  console.log i

Even better version:

for i in 0 til 10
  console.log i

Or if you want to go in reverse,

for i in 9 to 0 by -1
  console.log i

Or by twos:

for i in 0 til 10 by 2
  console.log i

You don’t have to use literal numbers, they can be any expression in GorillaScript. You can also use to, til, and by to make arrays outside of loops.

The difference between til and to is that til goes up until it hits the end, but to includes the end.

To iterate over an array,

for food in ["Apples", "Bananas", "Cherries"]
  console.log food

If you want its index,

for food, index in ["Apples", "Bananas", "Cherries"]
  console.log "$index: $food"

You can also get the total length of the array, if you need it:

for value, index, length in some-array
  f()

To iterate an array in reverse (slightly more efficient):

for value, index in some-array by -1
  // index goes from some-array.length - 1 down to 0.
  console.log value

To iterate only a part of the array:

for value in some-array[2 to 5]
  console.log value

It works similarly to some-array.slice(2, 6). You can slice outside of loops as well.

To iterate over objects, you can use of instead of in:

for key, value of some-object
  console.log key, value

GorillaScript automatically runs an Object.prototype.hasOwnProperty check on the key. To avoid this, use:

for key, value ofall some-object
  console.log key, value

Any loop can be an expression simply by returning it or assigning it to a variable. This will create an array.

let squares = for value in 0 to 10
  value ^ 2

// squares now contains [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Single-line loops can be specified as so:

let squares = for value in 0 to 10; value ^ 2

There are also reducing loops which work by placing one of the reducers (first, every, some, filter, reduce) on the for or while loop.

let all-good = for every item in array; item.is-good()
let has-bad = for some item in array; item.is-bad()
let best-value = for first item in array
  if item.is-best()
    item.value
let only-good = for filter item in array; item.is-good()
let sum = for reduce value in [0, 1, 2, 3, 4], current = 0
  current + value

These work on any for or while loop.

Lexical scoping in loops

A common bug in JavaScript is when one creates a function inside a loop that refers to the current index or value or some other variable that changes each loop iteration. GorillaScript solves this problem by wrapping any for loop that creates a function in another function, so the following works by seemingly lexically-scoping the inside of the for loop rather than abiding by JavaScript’s normal function scoping.

let funcs = []
for i in 0 til 10
  funcs.push # i

funcs[0]() == 0
funcs[5]() == 5

With no extra work on the developer’s part.

Array slicing

As mentioned briefly earlier, one can use the to, til, and by syntaxes to slice on arrays.

let array = [\a, \b, \c, \d, \e]
assert-arr array[1 to 3], [\a, \b, \c]
assert-arr array[1 to 5 by 2], [\a, \c, \e]
assert-arr array[5 to 1 by -2], [\e, \c, \a]
assert-arr array by -1, [\e, \d, \c, \b, \a]
assert-arr array[0 to -1], [\a, \b, \c, \d, \e]
assert-arr array[0 to Infinity], [\a, \b, \c, \d, \e]

The negative values work just as they do in .slice, unlike normal accessing

Array negative indexing

GorillaScript can’t wrap every single access in a check to see if the child is a negative value (as Python or some other languages do), due to efficiency, but it does provide a nice syntax to make it easier.

let array = [\a, \b, \c, \d, \e]
assert array[* - 1] == \e
assert array[* - 2] == \d
assert array[* \ 2] == \c // halfway point in the array

When a standalone * is encountered in an index, it is converted to the current array’s .length.

Cascades

Cascades are a way to repeatedly send messages to an object that would otherwise not have a fluent interface (i.e. returning this).

let array = [\a, \b, \c, \d, \e]
  ..push \f
  ..reverse()
  ..sort()

assert array.length == 6

Cascades can also be nested as such:

document.query-selector \h1
  ..style
    ..color := \red
    ..font-size := "200%"
  ..inner-HTML := "Hello, world!"

Everything is an expression (mostly)

Unlike JavaScript, for loops, while loops, try blocks, and if constructs can be used as expressions.

let array = for i in 0 to 10; i
let trial = try
  throw Error()
catch e
  "Caught something"
assert trial == "Caught something"
let check = if youre-amazing
  "It's true"
else
  "Not amazing."

Existential operator

If one wishes to check a value against null or undefined, the existential operator (?) can be used.

let exists = value?

It can also be used for access soaking

let inner = value?.which.might?.not.exist

Turns into

let inner = if value?
  let _ref = value.which.might
  if _ref?
    _ref.not.exist

It can also be used for function checking

let result = f?()

Turns into

let result = if typeof f == \function
  f()

in operator

To correlate with array iteration, in checks if a value is in an array, in a similar way to array.indexOf(value) != -1 would.

assert \hello in [\hi, \there, \hello]

To check if an object contains a key, see the haskey operator.

haskey and ownskey

Instead of using JavaScript’s in, haskey is used to verify a key’s existence on an object. Also, ownskey is also available to check if a key exists on an object without prototype-checking.

let parent = { alpha: \bravo }
let child = { extends parent, charlie: \delta }

assert parent haskey \alpha
assert parent ownskey \alpha
assert parent not haskey \charlie
assert parent not ownskey \charlie
assert child haskey \alpha
assert child not ownskey \alpha
assert child haskey \charlie
assert child ownskey \charlie

Access with ownership

Sometimes you may have an object where you want to access its key but only in the case of the object owning the key as a property.

assert if parent ownskey key
  parent[key]

// functionally equivalent to

assert parent![key]

Or, for a known key:

assert if parent ownskey \key
  parent.key

assert parent!.key

Apply syntax

In JavaScript, if you wish to specify the this argument passed to a function, one must use either .call or .apply. GorillaScript provides the @ syntax:

let f() this

let obj = {}
assert obj == f@ obj
assert obj == f@(obj)

Array.prototype.slice@ arguments, 1

It transparently converts to .call or .apply (whichever is more appropriate), using the first argument as its this.

Binding access

ECMAScript 5 supplies Function.prototype.bind as a way to bind functions to a specific this (and specify arguments). To do a similar binding in GorillaScript, one need only use the familiar @ syntax with access.

let obj = {
  f: # this
}

let bound = obj@.f
assert bound() == obj
let unbound = obj.f
assert unbound() == window

Classes

GorillaScript provides a way to make classical-style classes. JavaScript does not have classes normally, so GorillaScript’s creation is slightly hackish, but works for the general case.

class Animal
  def constructor(@name) ->

  def eat() "$(@name) eats"

class GreatApe extends Animal
  // no constructor, Animal's is automatically called
  def eat(food="fruit") super.eat() & " a " & food

class Gorilla extends GreatApe
  def constructor(@name, @favorite-food)
    // important to call the super constructor.
    super(@name)

  def eat() super.eat(@favorite-food)

class Chimp extends GreatApe
  def eat() super.eat("banana")

let bobo = Chimp("Bobo") // new is not required on GorillaScript-made classes
assert bobo.eat() == "Bobo eats a banana"

let toko = Gorilla("Toko", "cherry")
assert toko.eat() == "Toko eats a cherry"

// set a method on the Gorilla constructor
Gorilla::barrel := # @name & " throws a barrel!"

assert toko.barrel() == "Toko throws a barrel!"

Classes can extend other classes and call into their superclass with super. The constructor functions automatically check the this argument and if it is not the current class’s type (such as when called without new), it will create a new one on-the-fly.

Destructuring

GorillaScript, like ECMAScript 6, provides a destructuring declaration.

let [x, y] = [1, 2]
assert x == 1
assert y == 2

let {a, b: c} = {a: 3, b: 4}
assert a == 3
assert c == 4

These can be nested like so:

let [a, {b, c: [d]}] = get-data()

And the spread operator (...) can be used once per array destructure:

let [value, ...rest] = array

Switch

Like JavaScript, GorillaScript provides switch. The only major exception is that JavaScript is fallthrough-by-default, and GorillaScript is break-by-default. GorillaScript can also specify multiple values to check at once instead of having multiple cases. switch can also be used as an expression.

switch value
case 0, 1, 2
  "small"
case 3, 4, 5
  fallthrough // in the last position of the case, causes the case to fall through to the next case.
case 6, 7, 8
  "large"
default
  "unknown"

Unlike JavaScript, GorillaScript provides a topicless switch, which works by checking if each case is truthy rather than comparing against a value. fallthrough works the same way as a normal switch statement. This does not generate a JavaScript switch statement, as it is extremely inefficient to use switch in JavaScript without constant case checks.

switch
case is-good()
  "good"
case is-bad()
  "bad"
default
  "neutral"

Unlike JavaScript, GorillaScript requires a default block as part of the switch. If not specified, then it will cause an error if it is ever reached. Granted, if you cover all possible situations in your cases, this is not a problem. Also, you can always just specify default; void in order for nothing to occur.

switch value
case 0, 1
  "low"
case 2, 3
  "high"
// no default

Try-catch-else-finally

Try-catch also works similarly to JavaScript, except that catches can have type-checks and the notable exception of the else statement, for when no error was caught, but occurs before the finally statement.

try
  something-dangerous()
catch e as SpecificError
  handle-error(e)
catch e
  uh-oh()
else
  whew()
finally
  cleanup()

At least one of catch, else, or finally must be used, but so can all three.

Regular Expressions

Unlike JavaScript, Regular Expressions borrow the string syntax simply prefixed with r and postfixed with any RegExp flags deemed necessary. Also, if triple-quoted strings (e.g. r"""reg"""g) are used, all spaces are ignored as well as any # hash comments

r"l".test "apple"
let regex = r"""
  This is a large regex, $name
  And all the space is ignored # and this is ignored, too!
  """gim

Custom interpolation strings

If one doesn’t wish to use simple string concatenation with string interpolation, % can be prefixed to any double-quoted string to return an array instead which can then be interpolated in a custom manner. All even-numbered strings are guaranteed to be source literals and all odd-numbered values (might not be strings) are interpolated input.

The following is an example of automatic HTML escaping, but the same concept could be applied to SQL strings or practically any string with unsafe input.

class SafeHTML
  def constructor(@text as String) ->
  def to-string() @text
let to-HTML = do
  let escapes = {
    "&": "&amp;"
    "<": "&lt;"
    ">": "&gt;"
    '"': "&quot;"
    "'": "&#39;"
  }
  let replacer(x) escapes[x]
  let regex = r"[&<>""']"g
  let escape(text) text.replace(regex, replacer)
  #(arr)
    (for x, i in arr
      if i %% 2 or x instanceof SafeHTML
        x
      else
        escape String(x)).join ""

assert "<h1>normal</h1>" == to-HTML %"<h1>normal</h1>"
let evil-name = "<\"bob\" the 'great' & powerful>"
assert "&lt;&quot;bob&quot; the &#39;great&#39; &amp; powerful&gt;" == to-HTML %"$evil-name"
assert "<span>&lt;&quot;bob&quot; the &#39;great&#39; &amp; powerful&gt;</span>" == to-HTML %"<span>$evil-name</span>"
assert "<span><\"bob\" the 'great' & powerful></span>" == to-HTML %"<span>$(SafeHTML evil-name)</span>"

Iterators

Iterators are an ECMAScript 6 feature that have been in Mozilla’s JavaScript since 1.7. GorillaScript can both produce and consume such iterators. The API used is specified in the TC39 March 2013 notes

for value, index from some-iterable
  console.log value

Turns into the following code:

let _iter = some-iterable.iterator()
let mutable index = 0
try
  while true, index += 1
    let _item = _iter.next()
    if _item.done
      break
    let value = _item.value
    console.log value
finally
  try
    _iter.close()
  catch e
    void

Which means that any object that implements the iterator method acts as an iterable. That return value merely needs to implement the next method.

If Array.prototype were to implement iterator, which the ECMAScript 6 draft is recommending, one could iterate over Arrays, Sets, Maps, but one can iterate over any custom type now or by simply adding an Array.prototype.iterator method.

Production of iterators is easy as well in GorillaScript. You need merely append the * to your function declaration and use the yield (or yield*) expression.

let fib()*
  let mutable a = 0
  let mutable b = 1
  while true
    yield b
    let tmp = a
    a := b
    b += tmp

Produces an iterable which returns the infinite sequence of fibonacci numbers. The resultant code looks something along the lines of:

let fib()
  let mutable a = 0
  let mutable b = 1
  let mutable _state = 0
  {
    iterator: # this
    next: #
      while true
        switch _state
        case 0
          _state := 1
          return { -done, value: b }
        case 1
          let tmp = a
          a := b
          b += tmp
          _state := 0
  }

The state machine is made for you and any GorillaScript construct can be used inside a generator function.

One could then easily then use the fib iterator:

for value from fib()
  console.log value
  if value > 4000000
    break

One can also use yield in an expression position, not just return as a way to receive information from custom iterators. This isn’t affected by a for-from loop, but some libraries and patterns can rely on it.

Promises

GorillaScript provides mechanisms for Promises/A+-compliant Promises to be used and joined and manipulated together.

Promises provide a whole slew of asynchronous-capable syntax to make dealing with “callback hell” just a little easier. Since JavaScript fundamentals and frameworks (node.js, for example) tend to be very async-friendly, this is especially helpful.

Promises have the benefit over simple callbacks in that they are well-defined by the Promises/A+ spec, have easy-to-follow resolve/reject rules, and can be combined together shockingly easily.

Piggy-backing on the yield syntax, since yield can be used as an expression and not just a statement, information can be sent back to the iterator every time a yield occurs. GorillaScript takes advantage of this with the promise! keyword, which converts a generator function into a Promise factory or allows an arbitrary block of code to turn into a Promise, all one needs to do is yield other Promises until complete.

let make-promise = promise! #(filename)*
  // here, read-file returns a Promise
  let text = yield read-file(filename)
  return text.to-upper-case()

let promise = make-promise()
  .then(on-success, on-failure)

It practically reads like synchronous code, and it's guaranteed to execute in the same order as if it were synchronous.

You can also use promise! with a body of code instead of just a function:

let do-stuff(filename)
  let my-promise = promise!
    let text = yield read-file(filename)
    return text.to-upper-case()
  
  my-promise.then(on-success, on-failure)

to-promise!

To facilitate the easy use of Promises, there is also a to-promise! macro that converts a node.js-style function call into a self-fulfilled Promise, as long as that function takes a callback as its last argument with the signature function (error, result).

let do-stuff(filename)
  let my-promise = promise!
    let p = to-promise! fs.read-file filename, "utf8"
    let text = yield p
    return text.to-upper-case()
  
  my-promise.then(on-success, on-failure)

from-promise!

Along with to-promise! is from-promise!, which converts a Promise into a function which takes a node.js-style callback with the signature function (error, result).

let do-stuff(filename)
  let my-promise = promise!
    let p = to-promise! fs.read-file filename, "utf8"
    let text = yield p
    return text.to-upper-case()
  
  let node-func = from-promise! my-promise
  node-func #(err, value)
    if err?
      handle-error err
    else
      handle-success value

It is recommended to use promises over node.js-style callbacks in nearly all cases, due to the composability of code.

fulfilled! and rejected!

As a convenience, you can use fulfilled! value to produce an already-fulfilled promise or rejected! reason to produce an already-rejected promise.

delay!

In case you wish to have a Promise that fulfills after a known amount of time, delay! is available.

let take-a-while = promise! #()*
  for i in 0 til 10
    calculate(i)
    yield delay! 100_ms

You can also give delay! a value to be fulfilled with:

let promise = delay! 1000_ms, "hello!"
promise.then #(value)
  assert value == "hello!"

some-promise!

If you want to easily create several Promises and get a result when any Promise within either fulfills or rejects, some-promise! is perfect for the task.

let read-file-or-timeout(filename)
  some-promise! [
    read-file filename
    delay! 1000_ms
  ]

Which very nicely returns a Promise that either reads a file or times out after waiting too long. The same could be done for fetching pages from the internet or waiting on another process to complete.

every-promise!

If you wish to execute several Promises in parallel and do something after they all complete (or any of them fail), every-promise! is there for you.

let read-many-files(filenames)
  let file-promises = []
  for filename in filenames
    file-promises.push read-file filename
  every-promise! file-promises

Parallel Loops

Piggy-backing on the Promise system, GorillaScript provides the promisefor looping construct that allows for asynchronous execution. It can be used either inside of other Promises or in any normal function, it will return a Promise in both cases.

If you need to run a loop serially (as opposed to in-parallel), you should simply make a promise! and use the standard looping construct.

let loop = promisefor(3) filename in filenames
  let text = yield read-file filename
  return text.to-upper-case()

loop.then on-success, on-error

The previous code will run the body of the loop in parallel up to 3 at a time, in-order, until the loop is exhausted. The result will be an array, also in-order, regardless of the individual loop bodies' completion time.

Async

If you wish to use an async-friendly syntax without resorting to Promises, there are several syntax constructs to allow for this.

It is recommended to use the Promise-based syntax over this, as it is more composable. In it, you can use all the standard language constructs such as loops, conditionals, etc. and GorillaScript will convert it all nicely for you. The following examples require some mangling of your code and manual callback invocation to provide the illusion of top-to-bottom code execution.

Calling an async function

async err, text <- fs.read-file "somefile.txt", "utf8"
throw? err
console.log text

No indentation necessary, every line following the async call will be part of the implicitly-constructed callback. The resultant code looks like:

fs.read-file "somefile.txt", "utf8", #(err, text)@
  throw? err
  console.log text

Although that may not seem too useful with only one block of indentation, when dealing with complex database access or multiple sources of input, it is very easy to become overwhelmed.

Async function with automatic error handling

In the case where you don’t want to throw the error, but instead need to pass it to a callback (which takes the error as the first argument), you have one of two options:

let run(callback)
  async err, text <- fs.read-file "somefile.txt", "utf8"
  if err?
    return callback err
  callback null, do-something(text)

Or you can use

let run(callback)
  async! callback, text <- fs.read-file "somefile.txt", "utf8"
  callback null, do-something(text)

Which is functionally equivalent.

Async loops

Often, one may use a library to manage asynchronous handling of loops and iteration over arrays, but that is all built right into GorillaScript. Every loop construct you’ve seen works seamlessly by prepending async and specifying the next callback.

The next callback expects 0 to 2 arguments, if it receives a non-null-or-undefined value as its first argument, it will halt the loop immediately, as an error or other break has occurred. If it receives a second argument, it will append that value to an array, which may be optionally requested by the developer.

asyncfor loops can specify their level of parallelism manually (defaulting to 1, which runs serially). If parallelism is set to 0, it runs completely parallel.

// read two files at a time
asyncfor(2) err, array <- next, filename in ["a.txt", "b.txt", "c.txt", "d.txt"]
  async err, text <- fs.read-file filename, "utf8"
  if err?
    // if an error occurs, it propagates up and no more files will be read, and post-async execution
    // will occur immediately.
    return next err
  next null, { filename, text }
throw? err
console.log array // array will now be filled with objects like { filename: "a.txt", text: "lots of text here" }

Which turns into a device which looks something like:

let _array = ["a.txt", "b.txt", "c.txt", "d.txt"]
__async-result 2, array.length,
  #(i, next)@
    let filename = array[i]
    fs.read-file filename, "utf8", #(err, text)@
      if err?
        return next err
      next null, { filename, text }
  #(err, array)@
    throw? err
    console.log array

There is also asyncwhile and asyncuntil, which work similarly to their normal constructs but require the next argument to inform the code that its execution has completed one way or another.

Async conditionals

If you wish to use conditionals which may or may not have asynchronous code inside their bodies, asyncif or asyncunless is necessary.

asyncif text <- next, some-boolean()
  async err, text <- fs.read-file filename, "utf8"
  throw? err
  next(text)
// else is optional, will automatically call next() if not provided
console.log text

Which turns into something like:

let next(text)
  console.log text
if some-boolean()
  fs.read-file filename, "utf8", #(err, text)@
    throw? err
    next(text)
else
  next()

Returning

Occasionally, one needs to return a value such as false in an event handler, while using an asynchronous command. This is where the returning statement comes in. It works just like return, only it runs at the end of its block rather than where it is placed.

register-event #(filename)
  returning false
  async err, text <- fs.read-file filename, "utf8"
  throw? err
  console.log text

Which turns into:

register-event #(filename)
  fs.read-file filename, "utf8", #(err, text)
    throw? err
    console.log
  return false

Optional typing

On parameters, one can put types in the format param as Type. These will be checked at runtime and the type inference engine will be aware of them.

The types String, Number, Boolean, and Function will check against typeof, not with instanceof.

null, void, and undefined (alias for void) are also seen as valid types.

Arrays can be made using the syntax of [] for an array of any type or [Type] for a specific array. The contents will be checked at runtime.

Objects can be made using the standard object syntax with all values being types, e.g. {x: Number, y: String}

Functions with specific parameters or return value can be defined with -> for any parameter, any return. -> Type for a specific return value, Type -> Type for a single-argument with a specific return value, or (Type, Type) -> Type for two arguments with a specific return value.

Type unions can be made with |, e.g. Number|String. Order is irrelevant.

One can also place a return type on functions, which is not checked at runtime, but is used by the type inference engine.

Some examples:

let increment(x as Number) x + 1
let greet(x as String|Number) "Hello, $x"
let run(x as -> Number) x()
let get-number() as Number -> num
let join(x as [String]) x.join ", "
let use-object(o as {x: Number, y: Number}) o.x + o.y

Types can also be placed on let statements, to help with the type inference engine.

let x as Number = f()

Operators, accesses, and method calls as functions

In functional programming, it is often handy to use one of the built-in operators as a function, and GorillaScript provides this capability.

let add = (+) // same as #(x, y) x + y
assert add(5, 6) == 11

let square = (^ 2) // same as #(x) x ^ 2
assert square(10) == 100

let double = (2 *) // same as #(x) 2 * x
assert double(5) == 10

let invert = (not) // same as #(x) not x
assert invert(true) == false
assert invert(false) == true

assert 10 == [1, 2, 3, 4].reduce (+)

Any binary operator can be used this way, and any unary operator can be used as long as it does not share the same token as a binary operator.

One can also use the same syntax for accesses and method calls

let get-length = (.length) // same as #(x) x.length
assert get-length("hello") == 5

let to-hex = (.to-string(16)) // same as #(x) x.to-string(16)
assert to-hex(255) == "ff"

Getters and setters

Getter and setter support is up to the engine to support, and is therefore not recommended to be used in the general case. Even Internet Explorer 8 only provides support if the object is a DOM Element, so I cannot recommend using them on a broad scale unless you control the JavaScript engine (such as in node.js).

There are two ways to define getters and setters:

let obj =
  _x: 0
  get x: # @_x
  set x: #(value)! @_x := value

  _y: 0
  property y:
    get: # @_y
    set: #(value)! @_y := value
    configurable: true
    enumerable: true

When using the get and set pair, they must be defined next to each other (order is irrelevant). One can also supply only get or only set.

When using the property syntax, the value is the same as one calling Object.defineProperty, in fact, that is all that is happening behind the scenes.

If Object.defineProperty is not available, then it will try to sanely fallback, but it might not be successful. If specifying value instead of get/set, it will always work, but the configurable/enumerable/writable attributes are not guaranteed.

Note: this might throw an Exception at runtime if specifying get/set and the engine does not support getters/setters. If always using value, this will not fail, even if Object.defineProperty is not defined.

Labels and break/continue

JavaScript provides a way to add a label to a block and then be able to break or continue (if it is a loop) on that block.

Although it is not recommended practice, as one is probably better served by rethinking the algorithm, GorillaScript does provide the capability.

let mutable sum = 0
label! outer for i in 0 to 10
  for j in 0 to 10
    if i == j
      continue outer
    sum += i

As you can see, the outer loop has the label outer on it and the continue also references that same label, meaning it will continue on the outer loop rather than the inner.

The GLOBAL identifier

Since JavaScript can be run outside the browser now, one cannot rely on window always being the global object, so GorillaScript provides the GLOBAL identifier for just this case, which will be window or global or the outermost this, whichever one works best. It is UPPER-CASE and therefore loud for a reason: try to avoid globals when possible.

assert Math == GLOBAL.Math

Curried functions

Currying is a technique of transforming a function which takes multiple arguments in such a way that it can be called as a chain of functions. GorillaScript provides a nice way to automatically curry functions.

let add(a, b, c)^
  a + b + c

assert add(1, 2, 3) == 6 // same as before
let add-one = add 1
assert add-one(2, 3) == 6
let add-two = add 2
assert add-two(1, 3) == 6
let add-one-and-two = add-one 2 // or add 1, 2
assert add-one-and-two(3) == 6

Simply by putting the caret (^) after the function parameters, functions are automatically curried. Curried functions work extremely well in functional-style programming and are extremely handy when coupled with the compose operators (<< and >>)

let sort-by(key, array)^
  array.sort #(a, b) a[key] <=> b[key]

let sort-by-id = sort-by \id
let sort-by-name = sort-by \name

let items =
  * id: 0
    name: "Dog"
  * id: 1
    name: "Car"
  * id: 2
    name: "Robot"
  * id: 3
    name: "Guitar"

let items-by-name = sort-by-name items
let items-by-id = sort-by-id items

It is not recommended to use currying with default arguments (e.g. #(x = 0)^) or spread arguments (e.g. #(...args)), since the curry helper checks a function's .length to determine how many arguments before calling the actual function, and it may be non-obvious.

Generics

GorillaScript supports reified generic classes and functions, which are similar to .NET's generics or C++'s templates.

If a function is called with generic arguments, it is converted as so:

func<String>("hello")
// turns into
func.generic(String)("hello")

How the function handles the .generic call can vary, but GorillaScript's classes handle it elegantly:

class MyClass<T>
  def constructor(@value as T) ->
  
  def get-value() as T
    @value

let wrapped-string = MyClass<String>("hello")
let wrapped-number = MyClass<Number>(1234)
let wrapped-any = MyClass({})

In the previous case, MyClass<String>, MyClass<Number>, and MyClass (same as MyClass<null>) all refer to three independent classes which have different semantics.

You may be wondering about MyClass<null>, but it simply refers to absolutely any type being accepted, whether it's a String, Object, void, or some custom object.

Type-checking works as expected and T (or whatever type you call it) is available at runtime to refer to the bound generic type.

Here is an example of a simple function (rather than a class) with generic arguments:

let func<T>(value as T) value

assert func<String>("hello") == "hello"
assert func<Number>(1234) == 1234
assert func(true) == true // no type specified, so any type is allowed
assert func(null) == null

It is recommended that you name your generic argument T or have it start with the letter T, as in TKey or TResult.

GNOME bindings

GNOME now has JavaScript bindings with Gjs and GorillaScript supports this to the best of its ability.

As long as you have gjs installed locally, there are three ways to run Gjs with GorillaScript:

Aside from being written in GorillaScript rather than JavaScript, it will then function as any other Gjs script

Here is the standard Hello World Gjs code converted to GorillaScript:

#!/usr/bin/env gjs-gorilla

let {Gtk, GLib} = imports.gi

// Initialize the gtk
Gtk.init null, 0

let mwindow = new Gtk.Window type: Gtk.WindowType.TOPLEVEL
let label = new Gtk.Label label: "Hello World"

// Set the window title
mwindow.title := "Hello World!"
mwindow.connect "destroy", # Gtk.main_quit()

// Add the label
mwindow.add label

// Show the widgets
label.show()
mwindow.show()

Gtk.main()

Build support

The recommended way to automatically build GorillaScript files is through Grunt. It is easy to add GorillaScript support with the grunt-gorilla plugin.

Here is a very simple example of how to compile three .gs files into a single .js file:

grunt.initConfig({
  gorilla: {
    dist: {
      options: {
        sourceMap: true
      }
      files: {
        "lib/code.js": ["src/one.gs", "src/two.gs", "src/three.gs"]
      }
    }
  }
});

And to compile all your files in src/ to lib/:

grunt.initConfig({
  gorilla: {
    dist: {
      options: {
        sourceMap: true
      },
      files: [{
        expand: true,
        cwd: 'src/',
        src: ['**/*.gs'],
        dest: 'lib/',
        ext: '.js'
      }]
    }
  }
});

Coverage

GorillaScript has built-in coverage instrumentation, that can be picked up by any tool that already supports JSCoverage, including Mocha.

All you have to do is pass in the --coverage option on the command line or coverage: true in the Grunt task.

Browser support

Although not recommended for production code, there is a browser-specific version of the GorillaScript compiler as extras/gorillascript.js. Once this is included on your page, it will run any <script type="text/gorillascript"> tags, with or without src="filename.gs". It compiles and runs each tag in-order.

Caveat: this will not run synchronously like normal <script> tags, so it is recommended to wait for an onload event or something similar.

Once extras/gorillascript.js is included, the global GorillaScript object will be available, allowing you to use GorillaScript.compile("code", { options: "here" }) and several other commands.

In fact, this is exactly how this page is designed. Most of the JavaScript (including in the code examples) is compiled on-the-fly, and the Try it out environment is handled in this very way.

If define and define.amd are properly defined, they will be used instead of setting window.Gorilla, as a proper module definition

Note: any compiled GorillaScript file should work in all browsers, including older browsers. Certain features like getters and setters may not work in engines that do not support them, but this will cause a runtime exception on defining such properties rather than a compile-time error.

Code Editor/IDE support

If you would like to contribute to this list, please file an issue.

Macros

Note: This is probably the most complex section in this documentation.

Macros are a way to write some code that will be used to write other code. They are similar to functions, except that they are evaluated during compile-time rather than run-time. This means that the macros themselves won't appear in the resultant JavaScript source code, as they will have been executed as-necessary and not need to exist when the JavaScript itself is executed.

Macro names can be normal identifiers, custom symbols, or a mixture of the two.

Due to GorillaScript's flexible syntax, there are five types of macro syntaxes.

All the macros you can write are just as capable as any built-in GorillaScript syntax construct, as nearly all of GorillaScript's syntax are actually defined as macros.

Unary operators (both prefix and postfix)

Say you want an operator that evaluates whether or not an expression is a positive number:

macro operator unary +?
  // the following idents are available:
  //   op (which will always be "+?" in this case)
  //   node (which will be the node to the right of this unary prefix macro)

  // we'll use an inline AST expression rather than building the nodes ourself:
  // the '$' before 'node' means we will interpolate it, just like with strings.
  ASTE $node > 0

assert(+?500)
assert(not +?(-500))
assert(+?f())

Binary operators

Say you have a set (from type theory) and it would be nice to have a 'union' operator instead of relying on methods:

macro operator binary union, ∪
  // the following idents are available:
  //   op (which will either be "union" or "∪", i.e. "\u222a")
  //   left (which will be the node to the left of the binary operator)
  //   right (which will be the node to the right of the binary operator)

  ASTE $left.union($right)

assert(alpha union bravo union charlie)
assert(alpha ∪ bravo ∪ charlie ∪ delta)

Assign operators

Mostly used when paired with a binary operator, I'll reuse the previous example

macro operator assign union=, ∪=
  // the following idents are available:
  //   op (which will either be "union=" or "∪=", i.e. "\u222a=")
  //   left (which will be the node to the left of the assign operator)
  //   right (which will be the node to the right of the assign operator)

  // here we need to potentially cache parts of the left-hand-side, since we're referencing it twice.
  // this will convert a()[b()] to tmp1[tmp2].
  @maybe-cache-access left, #(set-left, left)
    ASTE $set-left := $left.union($right)

let mutable alpha = some-set()
alpha union= bravo
alpha ∪= charlie
get-obj()[key()] union= delta()

Call-like macros

Unlike the previous operators, this has a syntax that looks like any ordinary function call, with the exception that the name could also be or contain a symbol.

Here is an example of what you shouldn't do in a macro:

macro square(value)
  ASTE $value * $value

assert(square(3) == 9)
let mutable i = 0
let f()
  i += 1
assert(square(f()) == 2) // what? 2 isn't a square, but f() was called twice, uh-oh.
square(Math.random()) // two different random numbers multiplied together.

Here is how the previous macro should have been written:

macro square(value)
  @maybe-cache value, #(set-value, value)
    ASTE $set-value * $value

assert(square(3) == 9)
let mutable i = 0
let f()
  i += 1
assert(square(f()) == 1) // huzzah, 1 * 1 == 1
assert(square(f()) == 4) // huzzah, 2 * 2 == 4
square(Math.random()) // the same random number squared with itself.

There are practically no restrictions on how you use the arguments passed to your macros, so you have to be aware of edge cases and cache when necessary.

Another nice trick about macros is that not all of the arguments have to be evaluated, for example:

macro get-or-add(cache, key, value)
  @maybe-cache cache, #(set-cache, cache)
    @maybe-cache key, #(set-key, key)
      let tmp = @tmp() // we need a temporary variable to store things into.
      AST
        let mutable $tmp = $set-cache[$set-key]
        if $tmp == void
          $cache[$key] := $tmp := $value
        $tmp

let some-cache = {}
let x = get-or-add some-cache, key, something-expensive()
let y = get-or-add some-cache, key, something-expensive() // something-expensive() is never executed

Even though it appears that something-expensive() would be executed twice, it is only executed the first time, even without wrapping it in a function or anything like that (as would be necessary without macros).

Custom-syntax macros

This is where the real magic sets in. By allowing custom syntax, you can design any syntax construct that your mind can think up. In fact, nearly all GorillaScript syntax uses this, from the lowly if statement to the prestigous try-catch.

macro timer!
  // either an indented Body or a single-line Statement are accepted
  syntax body as (Body | Statement)
    // the following idents are available:
    //   macro-name (which will always be "timer!" for this macro)
    //   body (which will contain the Body or Statement)
    let time = @tmp \time
    AST
      let mutable $time = new Date().get-time()
      $body
      new Date().get-time() - $time

  // other syntaxes could be specified here

let time = timer! something-expensive()
let other-time = timer!
  various()
  expensive()
  calls()

Or if you’re pining for a when statement separate from if,

macro when
  syntax test as Logic, "then", body as Expression
    // the following idents are available:
    //   macro-name (which will always be "when" for this macro)
    //   test
    //   body
    ASTE if $test then $body

  syntax test as Logic, body as Body
    // same idents as before, since they are named the same.
    AST
      if $test
        $body

when is-hungry() or is-bored() then eat()

when is-angry()
  calm-down()

The syntaxes are defined by a comma-separated list of names (with the type of node attached) or literal strings.

The built-in sequences are as-follows:

Literal strings (such as "then") can be used as well without issue. Spacing around all elements is implicit.

Note: It is important to end a macro with "end" when using BodyNoEnd or GeneratorBodyNoEnd.

Chat with others (IRC)

IRC is a way to chat with other GorillaScript developers, likely including the author (depending on) and other contributors. Feel free to join us at #gorillascript on irc.freenode.net.