VideoBar

This content is not yet available over encrypted connections.

Wednesday, October 29, 2014

Creating a lightweight DSL using Dart operator overrides.

I'm working on a game called "hakk.it".  hakk.it is 100% written in Dart and when launched, will be widely available as a modern web application for most devices that support the Chrome browser (I'm looking at you with a raised eyebrow IOS...).

The premise of the game is pretty simple, you write special software code called "hakks".  These hakks take physical form and combat each other in arenas.  In addition to code, the hakks are made up of different types of blocks, each of which the code can make use of in various ways. It's a lot of fun, if I do say so myself.

Early screenshot of a hakk.it arena match in action.

What!? Nobody wants to write code in assembly?

This post isn't really about hakk.it.  That that will come at a later time.  This post is about one challenging aspect of the project that I needed to address.  The hakk.it virtual machine (VM) runs a machine language very similar to assembly for any CPU, and and most efficient hakks can be written directly in this machine language I call "hakkML".  I want hakk.it to be accessible to beginners and experts alike, and I realized lot of people will find the prospect of coding in assembly quite daunting.  I decided that I needed to create an easier way for people to express their code, but at this stage I didn't want to develop a full-fledged DSL, with tooling and everything else that comes along with it (that will come, later).

Dart To The Rescue!

Among the many features I love about Dart, one of my favorite is the ability of override operators.  I decided to attempt a DSL that is imbedded in Dart itself.  Technically this isn't a DSL.  It's more accurately described as a macro language, but as you'll see, it does offer enough expressiveness that it feels like a DSL.

I wanted the API to feel flexible and accessible like BASIC, and yet still allow access to the underlying power of the hakk.it hakkML instruction set.  This meant that I needed to support inline expressions that, to the programmer, felt like real expressions.  I also needed looping and conditional mechanisms that work intuitively.  Thanks to the power of Dart, I was able to accomplish all of these goals, and more.

Using Dart I'm able to provide an "DSL" to players, and yet still allow them to take advantage of all the great Dart tooling that's already in place, such as the Dart Editor.

How It Looks

Building a hakk with the basic API starts by declaring a name and something called a "system block" which is basically a block containing some memory for the compiler to use.  So the basic form of all hakks looks like this:

 hakk('hello_world');  
 Block.system(0, -1);  

This is valid Dart code, by the way.  All you have to do is import the API library (linked later) and start writing a hakk.

Next we want the hakk to do something.  Well in a normal hakk, we would write code for moving it around, firing weapons, activating defensive capabilities and so on.  For the purposes of this article, we'll keep things a bit more simple.  Here is an example of "hello world":

 hakk('hello_world');  
   Block.system(0, -1);  
   output('Hello World!');  
 print(build());  

That's it.  What the build() function does behind the scenes is activate the compiler, which compiles the following hakkML into a string:

 name: "hello_world"  
 blocks:  
  - core:  
  - mem: {name: "__sys0__", loc: [0, 1]}  
 code:  
  core:  
   - out: "Hello World!"  

Lets do something a little more advanced.  Lets say we want to perform some complex O(n^2) operation (you really, really wouldn't want to do this in a hakk meant for the arena, by the way).  In hakk.it every hakk comes standard with a "core" block, which contains 16 memory spaces to store and retrieve information during runtime.  The basic API provides an easy way to access these spaces, so lets use them to create something more complex with the API.  What follows is a nested FOR/NEXT loop construct, written with the API:

  hakk('for_example');  
  Block.system(0, 1);  
  FOR(core0 << 0, core0 < 10, () => inc(core0));  
   output('in core 0');  
   FOR(core1 << 0, core1 < 5, () => inc(core1));  
    output('  in core 1');  
   NEXT();   
  NEXT();  
  print(build());  

It's very similar to any FOR loop you might be familiar with.  The "<<" operator takes the place of "=" because Dart doesn't allow us to override "=".  But I think it's better anyway, because in the API what you are actually expressing with that operator is "store the result of the expression on the right side, into the memory location on the left side".  In that context, it is more precise than the general purpose "=" is.

In the first parameter of the FOR construct, we initialize core0 with 0.  In the second parameter we describe our iterator condition (core0 < 10).  And in the third parameter, we pass a function that will be called at the end of the loop, in this case simply incrementing the value in core0.  Pretty straightforward on the surface.  What's happening under the hood though is quite interesting.  Each of those operators like "<" is actually an override which looks in code like this:

  _ExpressionNode operator <(next){  
   next = _wrapLiteral(next);  
   final node = new _ExpressionNode(this, next, 'jl');  
   _expressionQueue.add(node);  
   return node;  
  }  

The API override "<" in this case, and instead constructs a node that becomes part of an expression tree, which will in turn generate all the hakkML necessary to achieve that expression in the hakk.it VM.  I do the same thing with all the other common operators (+, -, *, /, <, >, &, |) and voila! thanks to Dart we have an fully realized expression syntax.

In case your wondering, here is the hakkML that our example from above generates on behalf of the programmer:

 name: "for_example"
 blocks:  
  - core:  
  - mem: {name: "__sys0__", loc: [0, 1]}  
 code:  
  core:  
   - set: [core.0, 0]  
   - lbl: __while_test_1__  
   - jl: [core.0, 10, __istrue_5__]  
   - set: [__sys0__.0, false]  
   - goto: __logic_finish_5__  
   - lbl: __istrue_5__  
   - set: [__sys0__.0, true]  
   - lbl: __logic_finish_5__  
   - je: [__sys0__.0, true, __iftrue_3__]  
   - goto: __endif_3__  
   - lbl: __iftrue_3__  
   - out: "in core 0"  
   - set: [core.1, 0]  
   - lbl: __while_test_8__  
   - jl: [core.1, 5, __istrue_12__]  
   - set: [__sys0__.2, false]  
   - goto: __logic_finish_12__  
   - lbl: __istrue_12__  
   - set: [__sys0__.2, true]  
   - lbl: __logic_finish_12__  
   - je: [__sys0__.2, true, __iftrue_10__]  
   - goto: __endif_10__  
   - lbl: __iftrue_10__  
   - out: "  in core 1"  
   - add: [__sys0__.4, core.1, 1]  
   - set: [core.1, __sys0__.4]  
   - goto: __while_test_8__  
   - lbl: __endif_10__  
   - add: [__sys0__.3, core.0, 1]  
   - set: [core.0, __sys0__.3]  
   - goto: __while_test_1__  
   - lbl: __endif_3__  

After viewing the machine language generated, the benefits of having a higher level language become readily apparent.  Here is the runtime output of the code itself:

 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
 in core 0  
   in core 1  
   in core 1  
   in core 1  
   in core 1  
   in core 1  

The Real Power of Using Dart, Combinators

I won't go into other aspects of the API itself.  I'll do that in some video tutorials later.  But the API supports other standard constructs like WHILE/ENDWHILE, IF/THEN, and GOSUB/RETURN.  GOSUB incidentally, provides support for passing parameters and returning a result.  It's not as robust as closures, but good enough for the limited purpose of what hakks are intended for.

With all this in place, it's now possible to use the API itself to construct higher-level functionality.  Convenience functions, that would help beginners and people getting to know hakk.it get up and running faster.  Take for example this function provided by the API:

 faceEnemy();  

faceEnemy() orients the hakk toward the nearest opponent in the arena.  It uses the API itself to build up the logic necessary to perform the action it is intended for.  Here's the actual function:

 _HakkObject faceEnemy(){  
  _HakkObject result = _MMU.malloc('face_enemy');  
  _HakkObject x = _MMU.malloc('face_enemy');  
  _HakkObject y = _MMU.malloc('face_enemy');  
  locateEnemy();  
  x << pop();  
  y << pop();  
  output('enemy location');  
  vectorTo(x, y);  
  x << pop();  
  y << pop();  
  output('vector to enemy:');  
  result << faceDirection(vectorToDirection(x, y));  
  _MMU.dalloc('face_enemy');  
  return result;  
 }  

It allocates some temporary memory space in `result`, `x`, and `y`, and then uses other functions and expressions provided by the API to generate all the code necessary to locate and turn the hakk toward the opponent.  The trade-off of using something like this is speed, because the hakkML generated is quite a bit larger than a programmer could write directly in ML.  But it is convenient, and far easier to express.  Using faceEnemy() we could write a simpl logic routine that tries to move toward the enemy in the arena:

 core0 << true;  
 WHILE(core0);  
  core0 << faceEnemy();  
  IF(core0);  
   moveForward();  
   moveForward();  
  ENDIF();  
 ENDWHILE();  

These few lines generate a ton of hakkML code on behalf of the programmer.  I'm including it below so you can get a sense of the power of how using simple Dart combinators can yield very complex results!  Prepare yourselves for a lot of scrolling...

 name: "finder"  
 blocks:  
  - core:  
  - mem: {name: "__sys0__", loc: [0, 1]}  
 code:  
  core:  
   - set: [core.0, true]  
   - lbl: __while_test_0__  
   - set: [__sys0__.0, core.0]  
   - je: [__sys0__.0, true, __iftrue_2__]  
   - goto: __endif_2__  
   - lbl: __iftrue_2__  
   - find:  
   - pop: __sys0__.4  
   - set: [__sys0__.2, __sys0__.4]  
   - pop: __sys0__.5  
   - set: [__sys0__.3, __sys0__.5]  
   - out: "enemy location"  
   - tvec: [__sys0__.2, __sys0__.3]  
   - pop: __sys0__.6  
   - set: [__sys0__.2, __sys0__.6]  
   - pop: __sys0__.7  
   - set: [__sys0__.3, __sys0__.7]  
   - out: "vector to enemy:"  
   - set: [__sys0__.8, -1]  
   - je: [__sys0__.2, 0, __istrue_7__]  
   - set: [__sys0__.9, false]  
   - goto: __logic_finish_7__  
   - lbl: __istrue_7__  
   - set: [__sys0__.9, true]  
   - lbl: __logic_finish_7__  
   - je: [__sys0__.3, -1, __istrue_8__]  
   - set: [__sys0__.10, false]  
   - goto: __logic_finish_8__  
   - lbl: __istrue_8__  
   - set: [__sys0__.10, true]  
   - lbl: __logic_finish_8__  
   - je: [__sys0__.9, true, __istrue_10__]  
   - set: [__sys0__.11, false]  
   - goto: __and_finish_9__  
   - lbl: __istrue_10__  
   - je: [__sys0__.10, true, __istrue_11__]  
   - set: [__sys0__.11, false]  
   - goto: __and_finish_9__  
   - lbl: __istrue_11__  
   - set: [__sys0__.11, true]  
   - lbl: __and_finish_9__  
   - je: [__sys0__.11, true, __iftrue_5__]  
   - goto: __endif_5__  
   - lbl: __iftrue_5__  
   - set: [__sys0__.8, 0]  
   - lbl: __endif_5__  
   - je: [__sys0__.2, 0, __istrue_15__]  
   - set: [__sys0__.12, false]  
   - goto: __logic_finish_15__  
   - lbl: __istrue_15__  
   - set: [__sys0__.12, true]  
   - lbl: __logic_finish_15__  
   - je: [__sys0__.3, 1, __istrue_16__]  
   - set: [__sys0__.13, false]  
   - goto: __logic_finish_16__  
   - lbl: __istrue_16__  
   - set: [__sys0__.13, true]  
   - lbl: __logic_finish_16__  
   - je: [__sys0__.12, true, __istrue_18__]  
   - set: [__sys0__.14, false]  
   - goto: __and_finish_17__  
   - lbl: __istrue_18__  
   - je: [__sys0__.13, true, __istrue_19__]  
   - set: [__sys0__.14, false]  
   - goto: __and_finish_17__  
   - lbl: __istrue_19__  
   - set: [__sys0__.14, true]  
   - lbl: __and_finish_17__  
   - je: [__sys0__.14, true, __iftrue_13__]  
   - goto: __endif_13__  
   - lbl: __iftrue_13__  
   - set: [__sys0__.8, 2]  
   - lbl: __endif_13__  
   - je: [__sys0__.2, 1, __istrue_23__]  
   - set: [__sys0__.15, false]  
   - goto: __logic_finish_23__  
   - lbl: __istrue_23__  
   - set: [__sys0__.15, true]  
   - lbl: __logic_finish_23__  
   - je: [__sys0__.3, 0, __istrue_24__]  
   - set: [__sys0__.16, false]  
   - goto: __logic_finish_24__  
   - lbl: __istrue_24__  
   - set: [__sys0__.16, true]  
   - lbl: __logic_finish_24__  
   - je: [__sys0__.15, true, __istrue_26__]  
   - set: [__sys0__.17, false]  
   - goto: __and_finish_25__  
   - lbl: __istrue_26__  
   - je: [__sys0__.16, true, __istrue_27__]  
   - set: [__sys0__.17, false]  
   - goto: __and_finish_25__  
   - lbl: __istrue_27__  
   - set: [__sys0__.17, true]  
   - lbl: __and_finish_25__  
   - je: [__sys0__.17, true, __iftrue_21__]  
   - goto: __endif_21__  
   - lbl: __iftrue_21__  
   - set: [__sys0__.8, 1]  
   - lbl: __endif_21__  
   - je: [__sys0__.2, -1, __istrue_31__]  
   - set: [__sys0__.18, false]  
   - goto: __logic_finish_31__  
   - lbl: __istrue_31__  
   - set: [__sys0__.18, true]  
   - lbl: __logic_finish_31__  
   - je: [__sys0__.3, 0, __istrue_32__]  
   - set: [__sys0__.19, false]  
   - goto: __logic_finish_32__  
   - lbl: __istrue_32__  
   - set: [__sys0__.19, true]  
   - lbl: __logic_finish_32__  
   - je: [__sys0__.18, true, __istrue_34__]  
   - set: [__sys0__.20, false]  
   - goto: __and_finish_33__  
   - lbl: __istrue_34__  
   - je: [__sys0__.19, true, __istrue_35__]  
   - set: [__sys0__.20, false]  
   - goto: __and_finish_33__  
   - lbl: __istrue_35__  
   - set: [__sys0__.20, true]  
   - lbl: __and_finish_33__  
   - je: [__sys0__.20, true, __iftrue_29__]  
   - goto: __endif_29__  
   - lbl: __iftrue_29__  
   - set: [__sys0__.8, 3]  
   - lbl: __endif_29__  
   - set: [__sys0__.21, false]  
   - hvec:  
   - pop: __sys0__.25  
   - set: [__sys0__.22, __sys0__.25]  
   - pop: __sys0__.26  
   - set: [__sys0__.23, __sys0__.26]  
   - je: [__sys0__.22, 0, __istrue_39__]  
   - set: [__sys0__.27, false]  
   - goto: __logic_finish_39__  
   - lbl: __istrue_39__  
   - set: [__sys0__.27, true]  
   - lbl: __logic_finish_39__  
   - je: [__sys0__.23, -1, __istrue_40__]  
   - set: [__sys0__.28, false]  
   - goto: __logic_finish_40__  
   - lbl: __istrue_40__  
   - set: [__sys0__.28, true]  
   - lbl: __logic_finish_40__  
   - je: [__sys0__.27, true, __istrue_42__]  
   - set: [__sys0__.29, false]  
   - goto: __and_finish_41__  
   - lbl: __istrue_42__  
   - je: [__sys0__.28, true, __istrue_43__]  
   - set: [__sys0__.29, false]  
   - goto: __and_finish_41__  
   - lbl: __istrue_43__  
   - set: [__sys0__.29, true]  
   - lbl: __and_finish_41__  
   - je: [__sys0__.29, true, __iftrue_37__]  
   - goto: __endif_37__  
   - lbl: __iftrue_37__  
   - set: [__sys0__.24, 0]  
   - lbl: __endif_37__  
   - je: [__sys0__.22, 0, __istrue_47__]  
   - set: [__sys0__.30, false]  
   - goto: __logic_finish_47__  
   - lbl: __istrue_47__  
   - set: [__sys0__.30, true]  
   - lbl: __logic_finish_47__  
   - je: [__sys0__.23, 1, __istrue_48__]  
   - set: [__sys0__.31, false]  
   - goto: __logic_finish_48__  
   - lbl: __istrue_48__  
   - set: [__sys0__.31, true]  
   - lbl: __logic_finish_48__  
   - je: [__sys0__.30, true, __istrue_50__]  
   - set: [__sys0__.32, false]  
   - goto: __and_finish_49__  
   - lbl: __istrue_50__  
   - je: [__sys0__.31, true, __istrue_51__]  
   - set: [__sys0__.32, false]  
   - goto: __and_finish_49__  
   - lbl: __istrue_51__  
   - set: [__sys0__.32, true]  
   - lbl: __and_finish_49__  
   - je: [__sys0__.32, true, __iftrue_45__]  
   - goto: __endif_45__  
   - lbl: __iftrue_45__  
   - set: [__sys0__.24, 2]  
   - lbl: __endif_45__  
   - je: [__sys0__.22, 1, __istrue_55__]  
   - set: [__sys0__.33, false]  
   - goto: __logic_finish_55__  
   - lbl: __istrue_55__  
   - set: [__sys0__.33, true]  
   - lbl: __logic_finish_55__  
   - je: [__sys0__.23, 0, __istrue_56__]  
   - set: [__sys0__.34, false]  
   - goto: __logic_finish_56__  
   - lbl: __istrue_56__  
   - set: [__sys0__.34, true]  
   - lbl: __logic_finish_56__  
   - je: [__sys0__.33, true, __istrue_58__]  
   - set: [__sys0__.35, false]  
   - goto: __and_finish_57__  
   - lbl: __istrue_58__  
   - je: [__sys0__.34, true, __istrue_59__]  
   - set: [__sys0__.35, false]  
   - goto: __and_finish_57__  
   - lbl: __istrue_59__  
   - set: [__sys0__.35, true]  
   - lbl: __and_finish_57__  
   - je: [__sys0__.35, true, __iftrue_53__]  
   - goto: __endif_53__  
   - lbl: __iftrue_53__  
   - set: [__sys0__.24, 1]  
   - lbl: __endif_53__  
   - je: [__sys0__.22, -1, __istrue_63__]  
   - set: [__sys0__.36, false]  
   - goto: __logic_finish_63__  
   - lbl: __istrue_63__  
   - set: [__sys0__.36, true]  
   - lbl: __logic_finish_63__  
   - je: [__sys0__.23, 0, __istrue_64__]  
   - set: [__sys0__.37, false]  
   - goto: __logic_finish_64__  
   - lbl: __istrue_64__  
   - set: [__sys0__.37, true]  
   - lbl: __logic_finish_64__  
   - je: [__sys0__.36, true, __istrue_66__]  
   - set: [__sys0__.38, false]  
   - goto: __and_finish_65__  
   - lbl: __istrue_66__  
   - je: [__sys0__.37, true, __istrue_67__]  
   - set: [__sys0__.38, false]  
   - goto: __and_finish_65__  
   - lbl: __istrue_67__  
   - set: [__sys0__.38, true]  
   - lbl: __and_finish_65__  
   - je: [__sys0__.38, true, __iftrue_61__]  
   - goto: __endif_61__  
   - lbl: __iftrue_61__  
   - set: [__sys0__.24, 3]  
   - lbl: __endif_61__  
   - set: [__sys0__.8, __sys0__.24]  
   - je: [__sys0__.8, 0, __istrue_72__]  
   - set: [__sys0__.22, false]  
   - goto: __logic_finish_72__  
   - lbl: __istrue_72__  
   - set: [__sys0__.22, true]  
   - lbl: __logic_finish_72__  
   - je: [__sys0__.22, true, __iftrue_70__]  
   - goto: __endif_70__  
   - lbl: __iftrue_70__  
   - je: [__sys0__.8, 2, __istrue_76__]  
   - set: [__sys0__.24, false]  
   - goto: __logic_finish_76__  
   - lbl: __istrue_76__  
   - set: [__sys0__.24, true]  
   - lbl: __logic_finish_76__  
   - je: [__sys0__.24, true, __iftrue_74__]  
   - goto: __endif_74__  
   - lbl: __iftrue_74__  
   - turn: [__sys0__.40, 1]  
   - set: [__sys0__.21, __sys0__.40]  
   - turn: [__sys0__.41, 1]  
   - set: [__sys0__.21, __sys0__.41]  
   - lbl: __endif_74__  
   - je: [__sys0__.8, 1, __istrue_80__]  
   - set: [__sys0__.39, false]  
   - goto: __logic_finish_80__  
   - lbl: __istrue_80__  
   - set: [__sys0__.39, true]  
   - lbl: __logic_finish_80__  
   - je: [__sys0__.39, true, __iftrue_78__]  
   - goto: __endif_78__  
   - lbl: __iftrue_78__  
   - turn: [__sys0__.41, 0]  
   - set: [__sys0__.21, __sys0__.41]  
   - lbl: __endif_78__  
   - je: [__sys0__.8, 3, __istrue_84__]  
   - set: [__sys0__.40, false]  
   - goto: __logic_finish_84__  
   - lbl: __istrue_84__  
   - set: [__sys0__.40, true]  
   - lbl: __logic_finish_84__  
   - je: [__sys0__.40, true, __iftrue_82__]  
   - goto: __endif_82__  
   - lbl: __iftrue_82__  
   - turn: [__sys0__.42, 1]  
   - set: [__sys0__.21, __sys0__.42]  
   - lbl: __endif_82__  
   - goto: __end_turn_to_69__  
   - lbl: __endif_70__  
   - je: [__sys0__.8, 1, __istrue_88__]  
   - set: [__sys0__.23, false]  
   - goto: __logic_finish_88__  
   - lbl: __istrue_88__  
   - set: [__sys0__.23, true]  
   - lbl: __logic_finish_88__  
   - je: [__sys0__.23, true, __iftrue_86__]  
   - goto: __endif_86__  
   - lbl: __iftrue_86__  
   - je: [__sys0__.8, 0, __istrue_92__]  
   - set: [__sys0__.39, false]  
   - goto: __logic_finish_92__  
   - lbl: __istrue_92__  
   - set: [__sys0__.39, true]  
   - lbl: __logic_finish_92__  
   - je: [__sys0__.39, true, __iftrue_90__]  
   - goto: __endif_90__  
   - lbl: __iftrue_90__  
   - turn: [__sys0__.41, 1]  
   - set: [__sys0__.21, __sys0__.41]  
   - lbl: __endif_90__  
   - je: [__sys0__.8, 2, __istrue_96__]  
   - set: [__sys0__.40, false]  
   - goto: __logic_finish_96__  
   - lbl: __istrue_96__  
   - set: [__sys0__.40, true]  
   - lbl: __logic_finish_96__  
   - je: [__sys0__.40, true, __iftrue_94__]  
   - goto: __endif_94__  
   - lbl: __iftrue_94__  
   - turn: [__sys0__.42, 0]  
   - set: [__sys0__.21, __sys0__.42]  
   - lbl: __endif_94__  
   - je: [__sys0__.8, 3, __istrue_100__]  
   - set: [__sys0__.41, false]  
   - goto: __logic_finish_100__  
   - lbl: __istrue_100__  
   - set: [__sys0__.41, true]  
   - lbl: __logic_finish_100__  
   - je: [__sys0__.41, true, __iftrue_98__]  
   - goto: __endif_98__  
   - lbl: __iftrue_98__  
   - turn: [__sys0__.43, 1]  
   - set: [__sys0__.21, __sys0__.43]  
   - turn: [__sys0__.44, 1]  
   - set: [__sys0__.21, __sys0__.44]  
   - lbl: __endif_98__  
   - goto: __end_turn_to_69__  
   - lbl: __endif_86__  
   - je: [__sys0__.8, 2, __istrue_104__]  
   - set: [__sys0__.24, false]  
   - goto: __logic_finish_104__  
   - lbl: __istrue_104__  
   - set: [__sys0__.24, true]  
   - lbl: __logic_finish_104__  
   - je: [__sys0__.24, true, __iftrue_102__]  
   - goto: __endif_102__  
   - lbl: __iftrue_102__  
   - je: [__sys0__.8, 0, __istrue_108__]  
   - set: [__sys0__.40, false]  
   - goto: __logic_finish_108__  
   - lbl: __istrue_108__  
   - set: [__sys0__.40, true]  
   - lbl: __logic_finish_108__  
   - je: [__sys0__.40, true, __iftrue_106__]  
   - goto: __endif_106__  
   - lbl: __iftrue_106__  
   - turn: [__sys0__.42, 1]  
   - set: [__sys0__.21, __sys0__.42]  
   - turn: [__sys0__.43, 1]  
   - set: [__sys0__.21, __sys0__.43]  
   - lbl: __endif_106__  
   - je: [__sys0__.8, 1, __istrue_112__]  
   - set: [__sys0__.41, false]  
   - goto: __logic_finish_112__  
   - lbl: __istrue_112__  
   - set: [__sys0__.41, true]  
   - lbl: __logic_finish_112__  
   - je: [__sys0__.41, true, __iftrue_110__]  
   - goto: __endif_110__  
   - lbl: __iftrue_110__  
   - turn: [__sys0__.43, 1]  
   - set: [__sys0__.21, __sys0__.43]  
   - lbl: __endif_110__  
   - je: [__sys0__.8, 3, __istrue_116__]  
   - set: [__sys0__.42, false]  
   - goto: __logic_finish_116__  
   - lbl: __istrue_116__  
   - set: [__sys0__.42, true]  
   - lbl: __logic_finish_116__  
   - je: [__sys0__.42, true, __iftrue_114__]  
   - goto: __endif_114__  
   - lbl: __iftrue_114__  
   - turn: [__sys0__.44, 0]  
   - set: [__sys0__.21, __sys0__.44]  
   - lbl: __endif_114__  
   - goto: __end_turn_to_69__  
   - lbl: __endif_102__  
   - je: [__sys0__.8, 3, __istrue_120__]  
   - set: [__sys0__.39, false]  
   - goto: __logic_finish_120__  
   - lbl: __istrue_120__  
   - set: [__sys0__.39, true]  
   - lbl: __logic_finish_120__  
   - je: [__sys0__.39, true, __iftrue_118__]  
   - goto: __endif_118__  
   - lbl: __iftrue_118__  
   - je: [__sys0__.8, 0, __istrue_124__]  
   - set: [__sys0__.41, false]  
   - goto: __logic_finish_124__  
   - lbl: __istrue_124__  
   - set: [__sys0__.41, true]  
   - lbl: __logic_finish_124__  
   - je: [__sys0__.41, true, __iftrue_122__]  
   - goto: __endif_122__  
   - lbl: __iftrue_122__  
   - turn: [__sys0__.43, 0]  
   - set: [__sys0__.21, __sys0__.43]  
   - lbl: __endif_122__  
   - je: [__sys0__.8, 1, __istrue_128__]  
   - set: [__sys0__.42, false]  
   - goto: __logic_finish_128__  
   - lbl: __istrue_128__  
   - set: [__sys0__.42, true]  
   - lbl: __logic_finish_128__  
   - je: [__sys0__.42, true, __iftrue_126__]  
   - goto: __endif_126__  
   - lbl: __iftrue_126__  
   - turn: [__sys0__.44, 0]  
   - set: [__sys0__.21, __sys0__.44]  
   - turn: [__sys0__.45, 0]  
   - set: [__sys0__.21, __sys0__.45]  
   - lbl: __endif_126__  
   - je: [__sys0__.8, 2, __istrue_132__]  
   - set: [__sys0__.43, false]  
   - goto: __logic_finish_132__  
   - lbl: __istrue_132__  
   - set: [__sys0__.43, true]  
   - lbl: __logic_finish_132__  
   - je: [__sys0__.43, true, __iftrue_130__]  
   - goto: __endif_130__  
   - lbl: __iftrue_130__  
   - turn: [__sys0__.45, 1]  
   - set: [__sys0__.21, __sys0__.45]  
   - lbl: __endif_130__  
   - lbl: __endif_118__  
   - lbl: __end_turn_to_69__  
   - set: [__sys0__.1, __sys0__.21]  
   - set: [core.0, __sys0__.1]  
   - set: [__sys0__.1, core.0]  
   - je: [__sys0__.1, true, __iftrue_134__]  
   - goto: __endif_134__  
   - lbl: __iftrue_134__  
   - lbl: __endif_134__  
   - goto: __while_test_0__  
   - lbl: __endif_2__  
   - find:  
   - movef: __sys0__.2  
   - movef: __sys0__.3  
   - pop: __sys0__.3  
   - set: [__sys0__.1, __sys0__.3]  
   - pop: __sys0__.3  
   - set: [__sys0__.2, __sys0__.3]  
   - out: "enemy location"  
   - tvec: [__sys0__.1, __sys0__.2]  
   - pop: __sys0__.3  
   - set: [__sys0__.1, __sys0__.3]  
   - pop: __sys0__.3  
   - set: [__sys0__.2, __sys0__.3]  
   - out: "vector to enemy:"  
   - set: [__sys0__.3, -1]  
   - je: [__sys0__.1, 0, __istrue_139__]  
   - set: [__sys0__.4, false]  
   - goto: __logic_finish_139__  
   - lbl: __istrue_139__  
   - set: [__sys0__.4, true]  
   - lbl: __logic_finish_139__  
   - je: [__sys0__.2, -1, __istrue_140__]  
   - set: [__sys0__.5, false]  
   - goto: __logic_finish_140__  
   - lbl: __istrue_140__  
   - set: [__sys0__.5, true]  
   - lbl: __logic_finish_140__  
   - je: [__sys0__.4, true, __istrue_142__]  
   - set: [__sys0__.6, false]  
   - goto: __and_finish_141__  
   - lbl: __istrue_142__  
   - je: [__sys0__.5, true, __istrue_143__]  
   - set: [__sys0__.6, false]  
   - goto: __and_finish_141__  
   - lbl: __istrue_143__  
   - set: [__sys0__.6, true]  
   - lbl: __and_finish_141__  
   - je: [__sys0__.6, true, __iftrue_137__]  
   - goto: __endif_137__  
   - lbl: __iftrue_137__  
   - set: [__sys0__.3, 0]  
   - lbl: __endif_137__  
   - je: [__sys0__.1, 0, __istrue_147__]  
   - set: [__sys0__.4, false]  
   - goto: __logic_finish_147__  
   - lbl: __istrue_147__  
   - set: [__sys0__.4, true]  
   - lbl: __logic_finish_147__  
   - je: [__sys0__.2, 1, __istrue_148__]  
   - set: [__sys0__.5, false]  
   - goto: __logic_finish_148__  
   - lbl: __istrue_148__  
   - set: [__sys0__.5, true]  
   - lbl: __logic_finish_148__  
   - je: [__sys0__.4, true, __istrue_150__]  
   - set: [__sys0__.6, false]  
   - goto: __and_finish_149__  
   - lbl: __istrue_150__  
   - je: [__sys0__.5, true, __istrue_151__]  
   - set: [__sys0__.6, false]  
   - goto: __and_finish_149__  
   - lbl: __istrue_151__  
   - set: [__sys0__.6, true]  
   - lbl: __and_finish_149__  
   - je: [__sys0__.6, true, __iftrue_145__]  
   - goto: __endif_145__  
   - lbl: __iftrue_145__  
   - set: [__sys0__.3, 2]  
   - lbl: __endif_145__  
   - je: [__sys0__.1, 1, __istrue_155__]  
   - set: [__sys0__.4, false]  
   - goto: __logic_finish_155__  
   - lbl: __istrue_155__  
   - set: [__sys0__.4, true]  
   - lbl: __logic_finish_155__  
   - je: [__sys0__.2, 0, __istrue_156__]  
   - set: [__sys0__.5, false]  
   - goto: __logic_finish_156__  
   - lbl: __istrue_156__  
   - set: [__sys0__.5, true]  
   - lbl: __logic_finish_156__  
   - je: [__sys0__.4, true, __istrue_158__]  
   - set: [__sys0__.6, false]  
   - goto: __and_finish_157__  
   - lbl: __istrue_158__  
   - je: [__sys0__.5, true, __istrue_159__]  
   - set: [__sys0__.6, false]  
   - goto: __and_finish_157__  
   - lbl: __istrue_159__  
   - set: [__sys0__.6, true]  
   - lbl: __and_finish_157__  
   - je: [__sys0__.6, true, __iftrue_153__]  
   - goto: __endif_153__  
   - lbl: __iftrue_153__  
   - set: [__sys0__.3, 1]  
   - lbl: __endif_153__  
   - je: [__sys0__.1, -1, __istrue_163__]  
   - set: [__sys0__.4, false]  
   - goto: __logic_finish_163__  
   - lbl: __istrue_163__  
   - set: [__sys0__.4, true]  
   - lbl: __logic_finish_163__  
   - je: [__sys0__.2, 0, __istrue_164__]  
   - set: [__sys0__.5, false]  
   - goto: __logic_finish_164__  
   - lbl: __istrue_164__  
   - set: [__sys0__.5, true]  
   - lbl: __logic_finish_164__  
   - je: [__sys0__.4, true, __istrue_166__]  
   - set: [__sys0__.6, false]  
   - goto: __and_finish_165__  
   - lbl: __istrue_166__  
   - je: [__sys0__.5, true, __istrue_167__]  
   - set: [__sys0__.6, false]  
   - goto: __and_finish_165__  
   - lbl: __istrue_167__  
   - set: [__sys0__.6, true]  
   - lbl: __and_finish_165__  
   - je: [__sys0__.6, true, __iftrue_161__]  
   - goto: __endif_161__  
   - lbl: __iftrue_161__  
   - set: [__sys0__.3, 3]  
   - lbl: __endif_161__  
   - set: [__sys0__.4, false]  
   - hvec:  
   - pop: __sys0__.8  
   - set: [__sys0__.5, __sys0__.8]  
   - pop: __sys0__.8  
   - set: [__sys0__.6, __sys0__.8]  
   - je: [__sys0__.5, 0, __istrue_171__]  
   - set: [__sys0__.8, false]  
   - goto: __logic_finish_171__  
   - lbl: __istrue_171__  
   - set: [__sys0__.8, true]  
   - lbl: __logic_finish_171__  
   - je: [__sys0__.6, -1, __istrue_172__]  
   - set: [__sys0__.9, false]  
   - goto: __logic_finish_172__  
   - lbl: __istrue_172__  
   - set: [__sys0__.9, true]  
   - lbl: __logic_finish_172__  
   - je: [__sys0__.8, true, __istrue_174__]  
   - set: [__sys0__.10, false]  
   - goto: __and_finish_173__  
   - lbl: __istrue_174__  
   - je: [__sys0__.9, true, __istrue_175__]  
   - set: [__sys0__.10, false]  
   - goto: __and_finish_173__  
   - lbl: __istrue_175__  
   - set: [__sys0__.10, true]  
   - lbl: __and_finish_173__  
   - je: [__sys0__.10, true, __iftrue_169__]  
   - goto: __endif_169__  
   - lbl: __iftrue_169__  
   - set: [__sys0__.7, 0]  
   - lbl: __endif_169__  
   - je: [__sys0__.5, 0, __istrue_179__]  
   - set: [__sys0__.8, false]  
   - goto: __logic_finish_179__  
   - lbl: __istrue_179__  
   - set: [__sys0__.8, true]  
   - lbl: __logic_finish_179__  
   - je: [__sys0__.6, 1, __istrue_180__]  
   - set: [__sys0__.9, false]  
   - goto: __logic_finish_180__  
   - lbl: __istrue_180__  
   - set: [__sys0__.9, true]  
   - lbl: __logic_finish_180__  
   - je: [__sys0__.8, true, __istrue_182__]  
   - set: [__sys0__.10, false]  
   - goto: __and_finish_181__  
   - lbl: __istrue_182__  
   - je: [__sys0__.9, true, __istrue_183__]  
   - set: [__sys0__.10, false]  
   - goto: __and_finish_181__  
   - lbl: __istrue_183__  
   - set: [__sys0__.10, true]  
   - lbl: __and_finish_181__  
   - je: [__sys0__.10, true, __iftrue_177__]  
   - goto: __endif_177__  
   - lbl: __iftrue_177__  
   - set: [__sys0__.7, 2]  
   - lbl: __endif_177__  
   - je: [__sys0__.5, 1, __istrue_187__]  
   - set: [__sys0__.8, false]  
   - goto: __logic_finish_187__  
   - lbl: __istrue_187__  
   - set: [__sys0__.8, true]  
   - lbl: __logic_finish_187__  
   - je: [__sys0__.6, 0, __istrue_188__]  
   - set: [__sys0__.9, false]  
   - goto: __logic_finish_188__  
   - lbl: __istrue_188__  
   - set: [__sys0__.9, true]  
   - lbl: __logic_finish_188__  
   - je: [__sys0__.8, true, __istrue_190__]  
   - set: [__sys0__.10, false]  
   - goto: __and_finish_189__  
   - lbl: __istrue_190__  
   - je: [__sys0__.9, true, __istrue_191__]  
   - set: [__sys0__.10, false]  
   - goto: __and_finish_189__  
   - lbl: __istrue_191__  
   - set: [__sys0__.10, true]  
   - lbl: __and_finish_189__  
   - je: [__sys0__.10, true, __iftrue_185__]  
   - goto: __endif_185__  
   - lbl: __iftrue_185__  
   - set: [__sys0__.7, 1]  
   - lbl: __endif_185__  
   - je: [__sys0__.5, -1, __istrue_195__]  
   - set: [__sys0__.8, false]  
   - goto: __logic_finish_195__  
   - lbl: __istrue_195__  
   - set: [__sys0__.8, true]  
   - lbl: __logic_finish_195__  
   - je: [__sys0__.6, 0, __istrue_196__]  
   - set: [__sys0__.9, false]  
   - goto: __logic_finish_196__  
   - lbl: __istrue_196__  
   - set: [__sys0__.9, true]  
   - lbl: __logic_finish_196__  
   - je: [__sys0__.8, true, __istrue_198__]  
   - set: [__sys0__.10, false]  
   - goto: __and_finish_197__  
   - lbl: __istrue_198__  
   - je: [__sys0__.9, true, __istrue_199__]  
   - set: [__sys0__.10, false]  
   - goto: __and_finish_197__  
   - lbl: __istrue_199__  
   - set: [__sys0__.10, true]  
   - lbl: __and_finish_197__  
   - je: [__sys0__.10, true, __iftrue_193__]  
   - goto: __endif_193__  
   - lbl: __iftrue_193__  
   - set: [__sys0__.7, 3]  
   - lbl: __endif_193__  
   - set: [__sys0__.3, __sys0__.7]  
   - je: [__sys0__.3, 0, __istrue_204__]  
   - set: [__sys0__.5, false]  
   - goto: __logic_finish_204__  
   - lbl: __istrue_204__  
   - set: [__sys0__.5, true]  
   - lbl: __logic_finish_204__  
   - je: [__sys0__.5, true, __iftrue_202__]  
   - goto: __endif_202__  
   - lbl: __iftrue_202__  
   - je: [__sys0__.3, 2, __istrue_208__]  
   - set: [__sys0__.7, false]  
   - goto: __logic_finish_208__  
   - lbl: __istrue_208__  
   - set: [__sys0__.7, true]  
   - lbl: __logic_finish_208__  
   - je: [__sys0__.7, true, __iftrue_206__]  
   - goto: __endif_206__  
   - lbl: __iftrue_206__  
   - turn: [__sys0__.9, 1]  
   - set: [__sys0__.4, __sys0__.9]  
   - turn: [__sys0__.10, 1]  
   - set: [__sys0__.4, __sys0__.10]  
   - lbl: __endif_206__  
   - je: [__sys0__.3, 1, __istrue_212__]  
   - set: [__sys0__.8, false]  
   - goto: __logic_finish_212__  
   - lbl: __istrue_212__  
   - set: [__sys0__.8, true]  
   - lbl: __logic_finish_212__  
   - je: [__sys0__.8, true, __iftrue_210__]  
   - goto: __endif_210__  
   - lbl: __iftrue_210__  
   - turn: [__sys0__.10, 0]  
   - set: [__sys0__.4, __sys0__.10]  
   - lbl: __endif_210__  
   - je: [__sys0__.3, 3, __istrue_216__]  
   - set: [__sys0__.9, false]  
   - goto: __logic_finish_216__  
   - lbl: __istrue_216__  
   - set: [__sys0__.9, true]  
   - lbl: __logic_finish_216__  
   - je: [__sys0__.9, true, __iftrue_214__]  
   - goto: __endif_214__  
   - lbl: __iftrue_214__  
   - turn: [__sys0__.11, 1]  
   - set: [__sys0__.4, __sys0__.11]  
   - lbl: __endif_214__  
   - goto: __end_turn_to_201__  
   - lbl: __endif_202__  
   - je: [__sys0__.3, 1, __istrue_220__]  
   - set: [__sys0__.5, false]  
   - goto: __logic_finish_220__  
   - lbl: __istrue_220__  
   - set: [__sys0__.5, true]  
   - lbl: __logic_finish_220__  
   - je: [__sys0__.5, true, __iftrue_218__]  
   - goto: __endif_218__  
   - lbl: __iftrue_218__  
   - je: [__sys0__.3, 0, __istrue_224__]  
   - set: [__sys0__.7, false]  
   - goto: __logic_finish_224__  
   - lbl: __istrue_224__  
   - set: [__sys0__.7, true]  
   - lbl: __logic_finish_224__  
   - je: [__sys0__.7, true, __iftrue_222__]  
   - goto: __endif_222__  
   - lbl: __iftrue_222__  
   - turn: [__sys0__.9, 1]  
   - set: [__sys0__.4, __sys0__.9]  
   - lbl: __endif_222__  
   - je: [__sys0__.3, 2, __istrue_228__]  
   - set: [__sys0__.8, false]  
   - goto: __logic_finish_228__  
   - lbl: __istrue_228__  
   - set: [__sys0__.8, true]  
   - lbl: __logic_finish_228__  
   - je: [__sys0__.8, true, __iftrue_226__]  
   - goto: __endif_226__  
   - lbl: __iftrue_226__  
   - turn: [__sys0__.10, 0]  
   - set: [__sys0__.4, __sys0__.10]  
   - lbl: __endif_226__  
   - je: [__sys0__.3, 3, __istrue_232__]  
   - set: [__sys0__.9, false]  
   - goto: __logic_finish_232__  
   - lbl: __istrue_232__  
   - set: [__sys0__.9, true]  
   - lbl: __logic_finish_232__  
   - je: [__sys0__.9, true, __iftrue_230__]  
   - goto: __endif_230__  
   - lbl: __iftrue_230__  
   - turn: [__sys0__.11, 1]  
   - set: [__sys0__.4, __sys0__.11]  
   - turn: [__sys0__.12, 1]  
   - set: [__sys0__.4, __sys0__.12]  
   - lbl: __endif_230__  
   - goto: __end_turn_to_201__  
   - lbl: __endif_218__  
   - je: [__sys0__.3, 2, __istrue_236__]  
   - set: [__sys0__.5, false]  
   - goto: __logic_finish_236__  
   - lbl: __istrue_236__  
   - set: [__sys0__.5, true]  
   - lbl: __logic_finish_236__  
   - je: [__sys0__.5, true, __iftrue_234__]  
   - goto: __endif_234__  
   - lbl: __iftrue_234__  
   - je: [__sys0__.3, 0, __istrue_240__]  
   - set: [__sys0__.7, false]  
   - goto: __logic_finish_240__  
   - lbl: __istrue_240__  
   - set: [__sys0__.7, true]  
   - lbl: __logic_finish_240__  
   - je: [__sys0__.7, true, __iftrue_238__]  
   - goto: __endif_238__  
   - lbl: __iftrue_238__  
   - turn: [__sys0__.9, 1]  
   - set: [__sys0__.4, __sys0__.9]  
   - turn: [__sys0__.10, 1]  
   - set: [__sys0__.4, __sys0__.10]  
   - lbl: __endif_238__  
   - je: [__sys0__.3, 1, __istrue_244__]  
   - set: [__sys0__.8, false]  
   - goto: __logic_finish_244__  
   - lbl: __istrue_244__  
   - set: [__sys0__.8, true]  
   - lbl: __logic_finish_244__  
   - je: [__sys0__.8, true, __iftrue_242__]  
   - goto: __endif_242__  
   - lbl: __iftrue_242__  
   - turn: [__sys0__.10, 1]  
   - set: [__sys0__.4, __sys0__.10]  
   - lbl: __endif_242__  
   - je: [__sys0__.3, 3, __istrue_248__]  
   - set: [__sys0__.9, false]  
   - goto: __logic_finish_248__  
   - lbl: __istrue_248__  
   - set: [__sys0__.9, true]  
   - lbl: __logic_finish_248__  
   - je: [__sys0__.9, true, __iftrue_246__]  
   - goto: __endif_246__  
   - lbl: __iftrue_246__  
   - turn: [__sys0__.11, 0]  
   - set: [__sys0__.4, __sys0__.11]  
   - lbl: __endif_246__  
   - goto: __end_turn_to_201__  
   - lbl: __endif_234__  
   - je: [__sys0__.3, 3, __istrue_252__]  
   - set: [__sys0__.5, false]  
   - goto: __logic_finish_252__  
   - lbl: __istrue_252__  
   - set: [__sys0__.5, true]  
   - lbl: __logic_finish_252__  
   - je: [__sys0__.5, true, __iftrue_250__]  
   - goto: __endif_250__  
   - lbl: __iftrue_250__  
   - je: [__sys0__.3, 0, __istrue_256__]  
   - set: [__sys0__.7, false]  
   - goto: __logic_finish_256__  
   - lbl: __istrue_256__  
   - set: [__sys0__.7, true]  
   - lbl: __logic_finish_256__  
   - je: [__sys0__.7, true, __iftrue_254__]  
   - goto: __endif_254__  
   - lbl: __iftrue_254__  
   - turn: [__sys0__.9, 0]  
   - set: [__sys0__.4, __sys0__.9]  
   - lbl: __endif_254__  
   - je: [__sys0__.3, 1, __istrue_260__]  
   - set: [__sys0__.8, false]  
   - goto: __logic_finish_260__  
   - lbl: __istrue_260__  
   - set: [__sys0__.8, true]  
   - lbl: __logic_finish_260__  
   - je: [__sys0__.8, true, __iftrue_258__]  
   - goto: __endif_258__  
   - lbl: __iftrue_258__  
   - turn: [__sys0__.10, 0]  
   - set: [__sys0__.4, __sys0__.10]  
   - turn: [__sys0__.11, 0]  
   - set: [__sys0__.4, __sys0__.11]  
   - lbl: __endif_258__  
   - je: [__sys0__.3, 2, __istrue_264__]  
   - set: [__sys0__.9, false]  
   - goto: __logic_finish_264__  
   - lbl: __istrue_264__  
   - set: [__sys0__.9, true]  
   - lbl: __logic_finish_264__  
   - je: [__sys0__.9, true, __iftrue_262__]  
   - goto: __endif_262__  
   - lbl: __iftrue_262__  
   - turn: [__sys0__.11, 1]  
   - set: [__sys0__.4, __sys0__.11]  
   - lbl: __endif_262__  
   - lbl: __endif_250__  
   - lbl: __end_turn_to_201__  
   - set: [__sys0__.0, __sys0__.4]  
   - set: [core.0, __sys0__.0]  

Whew! You made it!

Conclusion

Using Dart to create an expressive embedded "DSL" API is entirely possible.  Operator overrides provide a gateway to building powerful expression combinators.  Keeping the API embedded in Dart allows developers to take advantage of Dart's existing tooling ecosystem.  For my project, using Dart allowed me to build an API that provides an easier entry point for potential gamers.

If you want to check out the library for yourself, you can import the hakk builder library into your own Dart project here.  It's pretty well documented, though not fully yet.  There are 3 libraries to choose from on import.  Chose the basic library with:

 import 'package:hakk_builder/basic.dart';  

The builder is not yet on github, but you can also download the source code from pub if you wish.


No comments:

Post a Comment