For ant description we use the following language: Syntax ~~~~~~ stmt ::= "label" "(" ID ")" "goto" "(" assigns, ID ")" "vars" "(" vars, stmts ")" "mark" "(" INT ")" "unmark" "(" INT ")" "set" "(" assigns ")" "drop" "(" ")" "turn" "(" "left" ")" "turn" "(" "right" ")" "if" "(" expr ")" stmts "else" stmts "when" "(" expr ")" stmts "pickup" "(" ")" "move" "(" ")" stmts ::= stmt "{" (stmt ";") * "}" expr ::= expr "&&" expr expr "||" expr "!" expr const what "^" where // sense condition "pickup" // if picked up "move" // if moved ... "rand" "(" INT ")" // flip const ::= ID INT const "+" const const "-" const const "<" const const ">" const const "<=" const const ">=" const const "==" const const "!=" const what ::= "friend" "foe" "friend" "(" "food" ")" "foe" "(" "food" ")" "foe" "(" "marker" ")" "food" "rock" "marker" "(" INT ")" "home" "foe" "(" "home" ")" where ::= "here" "front" "left" "right" Semantics ~~~~~~~~~ "label" (ID); Define a new label. "goto" (assigns, ID); Jump to a label setting specified variables. "vars" (vars, stmts); Define local variables in scope of stmts. "mark" (INT); "unmark" (INT); "drop" (); Should be obvious. "set" (assigns); A shortcut for: goto (assigns, tmp); label (tmp); "turn" ("left"); "turn" ("right"); Both commands in addition to turning the ant also update compass variable that must be declared in toplevel vars. So each ant program looks like vars (compass(6), { ... }). "if" (expr) stmts "else" stmts; Evaluate given condition (either runtime or compile time) and execute stmts. "when" (expr) stmts; Shortcut for if (expr) stmts else {}; "pickup" (); Shortcut for if (pickup) {} else {} "move" (); Shortcut for if (move) {} else {} In addition we have defined a few macros. mark_compass: Puts encoding of current compass value on current field using markers. mark_rev_compass: Likewise, but mark reverse of compass. align_to_mark: Align ant so it heads in direction marked on the current field. align_to_rev_mark: Likewise, but opposite direction. Constants ~~~~~~~~~ Compilation of this language to DFA from the task isn't hard -- you just need to translate linear structure of the program and the gotos to graph. Of course it is important to optimize gotos to gotos, but it's rather trivial. The resulting DFA linear with respect to program size. The important part is how we use constants. Without constants we would need to write 10000 lines of source program to fully exercise power of ant's brain. Therefore we have added variable a.k.a. compile time constants. For example: vars (x(7), { ... }) Multiplies automata resulting from compilation of ... by 7, and inside the ... variables x can be used -- that is it can be used in conditionals (it's then constant folded) and modified using set and/or goto. The compilation of source program to automata proceeds as follows: 1. program is compiled to AST using standard Nemerle language parser 2. special set of macros transforms Nemerle AST to ants AST. 3. &&, || and ! are unfolded, special labels are added at the end of vars() blocks 4. vars() and constant if expression are unfolded 5. labels are bound and tree is changed to DFA form 6. optimization of DFA [not done] 7. serialization of DFA As for 1. and 2., Nemerle parser is easily extensible using macros. Some goes for semantics. We were therefore able to reuse this important part of the compiler As for 3., for example: if (c1 && c2) { s1 } else { s2 } is transformed to: if (c1) if (c2) { s1 } else { goto (lab); } } else { label (lab); s2 } also: vars (a, { s }) is transformed to: vars (a, { s; goto (end); }); label (end); to aid in later vars() unfolding. As for 4., for example: vars (x(3), y(2), { label (l); goto (x = 2, l); }) is transformed to: label (l_x=0_y=0); goto (l_x=2_y=0); label (l_x=0_y=1); goto (l_x=2_y=1); label (l_x=1_y=0); goto (l_x=2_y=0); ... Of course if can lead to lots of unused labels -- these are removed from the resulting DFA. Additionally when "if(const) {...} else {...}" is encountered const is evaluated and if it's really constant, special "if(1) {...} else {...}" construct is generated. This one is later recognized by phase 5 -- first branch is generated as usual, but second is compiled, but can be only referenced with labels from outside.