Install

First you will need to install:

Once node and npm are installed, run:

npm install -g earlgrey

This will install the earl command. Run earl with no arguments to start an interactive interpreter, or run an EG program as:

earl run file.eg

Runtime

Earl Grey scripts depend on the earlgrey-runtime package. You don't need to install it if you run a script with earl run, but for all the other methods you will need to do execute this in the root directory of your project:

npm install earlgrey-runtime --save

Workflow

At the moment you can use EG standalone, compile it, use it with gulp, and/or run it in the browser with browserify.

Standalone

Run a script like this:

earl run script.eg

earl will cache the result of compilation in egcache/script.js (and does so with all dependencies). This means that the second time you run your script, it should start running nearly instantaneously, and if you change one file, only that file will be recompiled.

If you run into issues, you may force recompilation of your script and of all of its dependencies with the -r flag:

earl run -r script.eg

If you only want to trigger recompilation of a single file, use the touch utility to change its last change date. That will do the trick :)

Compile

Compile a script or all the scripts within a directory with:

earl compile file.eg
earl compile -o dest.js file.eg
earl compile -o dest/ src/

The -s flag writes source maps, I recommend using it. By default earl generates EcmaScript 6 code. You can generate ES5 code instead with the 5 flag.

For instance, the command that follows verbosely compiles src/**/*.eg into ES5-compatible dest/**/*.js (using babel):

earl compile -5vso dest/ src/

As with earl run, earl compile avoids needless recompiling. Use the -r flag to force recompilation.

You will not be able to run the compiled scripts without first installing the runtime.

With gulp

The gulp-earl package defines a source transformer for use with gulp. It supports source maps via the gulp-sourcemaps package.

Here's a sample task for your gulpfile.js

var earl = require('gulp-earl');

gulp.task('earl', function() {
  gulp.src('./src/**/*.eg')
    .pipe(earl({}))
    .pipe(gulp.dest('./build/'))
});
You can also write gulpfiles in Earl Grey. The earl-gulp package defines a neat task macro for the purpose. Do note that this is the earl-gulp package and not the gulp-earl package. They are different (sorry for the confusion!)

Don't forget to also install the runtime.

In the browser

In order to use EG scripts in the browser, it is necessary to bundle them using browserify.

The earlify package defines a source transformer for use with browserify. Install it:

npm install earlify --save

Then run it like this:

browserify -t earlify script.eg > bundle.js

Don't forget to also install the runtime.

Global variables like document or window are not available by default in Earl Grey. You must declare them like this:
globals:
   document, window
The same goes if you include external scripts on your page and they declare global variables that you want to use: declare their existence in a globals block, then use them as you normally would.

What does it look like?

I am not sure what are the best examples to give here. Let's start with a straightforward example, counting all unique words in a paragraph of text:

countWords(text) =
   counts = new Map()
   words = text.split(R"\W+")
   words each word ->
      currentCount = counts.get(word) or 0
      counts.set(word, currentCount + 1)
   consume(counts.entries()).sort(compare) where
      compare({w1, c1}, {w2, c2}) = c2 - c1

Generators: the following defines a generator for the Fibonacci sequence and then prints all the even Fibonacci numbers less than 100. It shows off a little bit of everything:

gen fib() =
   var {a, b} = {0, 1}
   while true:
      yield a
      {a, b} = {b, a + b}

fib() each
   > 100 ->
      break
   n when n mod 2 == 0 ->
      print n

The each operator accepts multiple clauses, which makes it especially easy to work on heterogenous arrays.

Asynchronous: EG has async and await keywords to facilitate asynchronous programming, all based on Promises. Existing callback-based functionality can be converted to Promises using promisify:

require: request
g = promisify(request.get)
async getXKCD(n = 0) =
   response = await g('http://xkcd.com/info.{n}.json')
   JSON.parse(response.body)
async:
   print (await getXKCD()).alt

Also: classes:

class Person:

   ;; Instance @fields can be set directly in argument lists
   constructor(@name, @age, @job = "unemployed") =
      pass

   ;; Default arguments
   marchTowardsDeath(years = 1) =
      @age += years

   ;; Individual arguments can be matched on
   sayHello(match) =
      ;; This matches a Person instance and extracts its name field
      ;; or else it matches a String directly
      Person? {=> name} or String? name ->
         print 'Hello {name}, I am {@name}!'
      ElderGod? ->
         print "AAAAAAAHHHHHHHHHHHHHHHH!"
      else ->
         print "I don't know what to say."

;; .xyz is the same thing as "xyz", just a bit shorter to type
p1 = new Person(.Sylvie, 37, .accountant)
;; You don't have to use "new"
p2 = Person(.Michel, 43, .farmer)
p1.sayHello(p2)

Pattern matching is very useful. It makes it easier to work with regular expressions, for example:

mangle(match email) =

   ;; regexp! transforms the input into an array of match groups
   ;; (the first is always the whole match)
   R"^([a-z.]+)@([a-z.]+)$"! {_, name, host} ->
      '{name} AT {host}'

   ;; regexp? will just test if the regexp matches, but it won't
   ;; transform the input
   R"@"? ->
      "It looks like an email but I'm too daft to parse it."

   else ->
      "This is not an email at all!"

Tooling support

Click here for details about current support for editors and syntax highlighting.

Read this if you wish to contribute tools for popular editors or libraries.

Learn more