Introducing Chops

Recently I’ve been doing a lot of my exploratory programming in JavaScript, thanks to the fact that I know it’ll run on crazy platforms like the 3DS. -_^ I’ve programmed in JavaScript off and on since high school, and boi has my JavaScript style changed…. @_@

JavaScript’s lack of macros is a bit troubling for me, especially given the recent tendency for JavaScript programmers to deal in lots of nested asynchronous callbacks: “Unindented functions to clean callback mess.” “Asynchronous callbacks in JavaScript”

I haven’t felt the pain of the asynchronous callback nesting problem firsthand, but I know a horror close to that one: Monadic programming in Groovy. Observe the indentation.

In fact, that indentation-heavy part of the Blade code is what originally got me fed up with using Groovy and interested in making Penknife as an intermediate project on the way to Blade. Well, my work on Penknife has been so focused on modularity and hygiene that it’s slack on what really matters: Syntax. Oh, syntax, how you really really matter so! :-p

Blade and Penwhatsit?

Unless you’ve seen my hobby-horsing rants on the Arc Forum, you probably have no clue what I mean by Blade or Penknife. They’re projects I’ve been working on for quite some time now, but I never did get around to blogging about them until now. In summary, they’re both language ideas which take customizable syntax to the limit, approaching the vision I described in my last post.

What’s the difference between Penknife and Blade, you ask? Well, they both center around extensible/customizable syntax, but Blade projects are finite collections of declarations, and Penknife projects are discrete, potentially ongoing sequences of commands. Blade is the kind of language that could have good IDE integration, but Penknife is the kind of language that could have good REPL integration. I started working on Penknife after those indentation difficulties with Blade (as I just mentioned), and Penknife is now much farther along in development.

Penknife and Blade are based on square bracket syntaxes which are designed to work seamlessly inside of most other-language code. In most existing programming languages, square brackets are somewhat less common than parens and curly braces, and when they do appear, they’re usually balanced. (Sure, the half-open mathematical range syntax (0,4] isn’t balanced, but what programming language uses that? :-p ) So the premise of Penknife and Blade’s syntax extension is pretty simple: Parse out the square brackets, and compile the outermost ones according to some rule or other that splits off an operator and applies it to the rest of the (unparsed) body. Chops uses this kind of syntax too.

Chops, with flashy examples

Going back to my roots a bit, I’ve put together chops.js (and lumped it in with some other JavaScript tools here in Lathe/js). It makes no attempt at being a programming language. It’s merely a text preprocessor. In Chops, each outermost square bracket is compiled by splitting off all the non-whitespace characters at the beginning, and then invoking a particular JavaScript function (parseInlineChop) which takes three parameters: that string, the current Chops environment, and the “rest” of the form as an array of strings and arrays.

One of the first things I put together was a little tool to help out with those indentation issues I was talking about. Instead of writing this:

thisWill( function ( err, a ) {
    return becomeSome( function ( err, b ) {
        return horriblyNested( function ( err ) {
            return code( a, b );
        } );
    } );
} )

You can write this:

[> [just thisWill( [next] ); ]
   [just function ( err, a ) { return becomeSome( [next] ); } ]
   [just function ( err, b ) { return horriblyNested( [next] ); } ]
   [just function ( err ) { return code( a, b ); } ]]

The “>” macro parses the last whitespace-delimited word in its body, then parses the one before it with that result provided as a local “next” macro, and so on. The “just” macro does nothing; its only purpose is to keep the code inside from counting as multiple words.

On Thursday, I took that up a notch. I have a Haskell-style monad notation now:

[>-> g1 bar
     g2 baz
     [just foo( g1, g2 ) ]]

This expands into something equivalent to the following (modulo meaningless parentheses and whitespace):

monadBind( bar, function ( g1 ) {
    return monadBind( baz, function ( g2 ) {
        return foo( g1, g2 );
    } );
} )

I even have a crazy monad notation of my own devising:

[>- foo( [<- bar ], [<- baz ] ) ]

You can actually try out these syntaxes at http://rocketnia.github.com/lathe/chops.html, though that’s mostly a kind of unit testing sandbox page, and my REPL interface there is extremely unpolished.

>>> rocketnia.chops.parseChopcode( choppaScript, "[>- foo( [<- bar ], [<- baz ] ) ]" )
--> (monadBind( ( bar ), function ( gs8352752204_2 ) { return ((monadBind( ( baz ), function ( gs8352752204_3 ) { return ( foo( gs8352752204_2, gs8352752204_3 ) ); } ))); } ))

Here’s that output prettied up, with some unnecessary parentheses removed:

monadBind( bar, function ( gs8352752204_2 ) {
    return monadBind( baz, function ( gs8352752204_3 ) {
        return foo( gs8352752204_2, gs8352752204_3 );
    } );
} )

There’s also a “” macro, so that any opening square bracket followed immediately by whitespace is treated as though it were regular text:

>>> rocketnia.chops.parseChopcode( choppaScript, "[> foo[ [next] ] bar]" )
--> foo[ bar ]

Chops, with flashy hand-waving

Chops doesn’t parse JavaScript. This is totally a text-level translation (unless you go and write an ambitious macro that does the parsing itself), and any kind of malformed or browser-specific JavaScript is just fine. Output languages other than JavaScript are fair game too, including Arc and Haskell.

In fact, the output language doesn’t have to be textual. I personally intend to use Chops as yet another markup language for generating HTML, and I intend to use an intermediate AST representation. I was building my website in Penknife this way already, but I think Chops’ll be a bit more straightforward in the short term.

At this point, Chops is a JavaScript library, but there’s no good way to develop code that uses Chops as a preprocessor. That’s the next step, as I see it. Node.js is an option, but I’d mainly like to able to build in the browser, especially in a way that uses some existing online storage API. (Any storage recommendations? I’ve been trying the GitHub Gist API, but it uses unusual HTTP actions like PUT, which apparently can’t be sent by typical browsers. ^^; Now that I’ve seen JSPaste, I’m kinda thinking about RDBHost….)

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s