SeriousJS - A simple syntax for a complicated world
JavaScript Other
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


A simple syntax for a complicated world.

There is an interactive interpreter available at:


Feel free to skip to the bottom if you like - but a lot of people will ask why I bothered implementing a language that follows Coffee-Script 99% of the time.

Ok, why re-write Coffee-Script? My initial motivation was that the coffee-script parser has really weird limitations. For instance:

if someLongVariable
        + someOtherLongVariable == 8

Won't compile with Coffee-Script, despite making complete visual sense. I've also come across the scenario where, after several layers of lambda and object literal nesting, coffee-script broke. Granted I should have distilled the situation and fixed Coffee-Script's parser, but it was just a frustrating experience.

Since I was just learning PegJS for another project, I thought it would be fun to try to make a Coffee-Script parser that didn't have so many bizarre limitations in the syntax.

As I was looking at Coffee-Script's design choices and my past coffee-script projects, there were some other things that bugged me constantly:

  • -> vs => - Why? I mean, I understand how coffee-script uses them, but if a language introduces a beautiful syntax like @ to mean "the object that this method is operating on", why would you confuse the issue with function bindings? "@" should mean "the closest class object", and "this" should mean the context with which the current method was called.

  • Why are there so many aliases for true/false? It's because it's a scripting language. I get that. But for large, maintainable code bases, I don't feel that it's appropriate to have so many ways of saying "false" and "true". Feel free to correct me on that one.

  • Guess what this code does:

          .bind 'click', -> alert("Click!")

    If you guessed "It sure doesn't append that div to the body!" you were correct. Coffee-Script associates the .appendTo with the return value from calling alert(), quite contrary to what the indentation implies.

While those aren't huge, they got me thinking. What are some other ways that Coffee-Script could be improved? Well, how about:

  • Support for asynchronous fibers / continuations? Programming callbacks in an imperative manner rather than a bunch of nested functions

  • Tight integration with RequireJS, to provide a uniform, consistent language between NodeJS and the browser, that runs in all environments and "just works"

  • Doc strings! One of the best things about Python that really helped it be a great language was doc strings. An interactive language needs interactive help. So why not start encouraging them?

Anyway, that's the jist of it. I'm not expecting people to jump ship on this one - it started as an educational project but the focus is on production quality language constructs. I mostly just think it's a useful conversation starter.


SeriousJS is inspired by Coffee-Script, but aimed at making the parsing more dependable and providing extensions for a uniform code base across NodeJS and browser implementations. Emphasis is on making projects easy to manage and learn.

Remember, there is an interactive interpreter to play with at:


For a shorthand to run any SeriousJS script in NodeJS, just use:

seriousjs app.sjs

To start a new SeriousJS HTTP client/server project with RequireJS set up, run:

seriousjs create-app [appname]

This makes a folder named [appname] and sets up a skeleton hierarchy, with an example web server and configuration. It also comes with a stock .gitignore file, which suffices for simple projects.

To compile your new application, run:

seriousjs app.sjs --build

from your application directory. A "" directory will be created, which has compiled sources for your client.

To run the compiled version, run:

seriousjs app.sjs --built

Note that if you run --built without --build first, a build will be triggered anyway.

Usage in a browser

If you run seriousjs --compile-only (ran during install anyway), then the file lib/seriousjs-embed.js will be generated. This is a self-contained file that may be used in a browser (or NodeJS, of course).

Or just run "seriousjs --create-app myapp" and have a working demonstration.


Functions are lambdas:

(a, b) -> a + b

Long functions can have doc strings:

(a, b) ->
    """Returns the summation of a and b.
    return a + b

Dict unmapping:

{ a, b } = { a: 8, b: 9 }
{ a = 8, b } = { b: 9 }

# < means that the matched dict must contain a subset of the specified
# keys.  That is, the first will be OK (b is undefined), but the second
# will throw an error (c not allowed)!
{< a, b } = { a: 1 }
{< a, b } = { a: 1, c: 3 }

# > means at least the specified keys must be in the matched dict
{> a } = { a: 1, b: 2 }

# = means keys must match exactly
{= a, b } = { a: 3, b: 4 }

Dict unmapping as lambda arguments:

{ a, b } -> a + b
(a, b, {< add = true}) -> add ? a + b : a

Classes and @-binding:

class Test
  constructor: (@value = 5) ->

  inner: () ->
    console.log @value

  f: (g) ->

t = Test(42)
# This will log 42, which is t.value.  Note that the binding of the
# function was preserved when it was referenced with @.
# Useful for e.g. callbacks bound to events
t.f (method) -> method()


  • 2015-3-12 - Classes back to being aligned with javascript, :: operator for accessing prototype, more comprehensive testing, lambdas always start their own continuation (if they are in params, a one-line lambda has two indents rather than one), return statements are implied ONLY for one-line lambdas.

  • 2014-6-3 - Fix bug where (3+5) 4 is valid syntax for calling a nonsense function. Is an issue with e.g. (3 + 5) / 4 / 8.

  • 2014-5-8 - catch e instanceof Error shortcut for catching by type.

  • 2014-5-2 - Much improved class inheritance (changes proto hack so that the child function actually chains from the parent function). Added property support to classes. Added Event and EventMixin builtins on seriousjs module.


  • 2014-4-24 - Made create-static template, for sites with no server component. Check if callback is function for async, if it's not, unset it. Fixed parse error with arguments separated by newlines.


  • 2014-4-21 - Updated create-app template. Added test as a TODO. Updated version / npm registry with latest changes.
  • 2014-4-3 - Fixed list of lists parsing, async throwing, async catch blocks in a for loop, onAsyncUnhandledError, onAsyncSecondaryError


  • i[??] Random choose operator? Would be handy to have that built in... Probably should think that one through though.

  • for..else. Else is triggered when loop is completed without break.

Bitdeli Badge