// REFERENCE: Nemerle.Compiler using Nemerle.Compiler; using Nemerle.Compiler.Parsetree; using Nemerle.Collections; public class Robot { public mutable Orientation : byte; public mutable X : int; public mutable Y : int; public IsDown : bool { get { Orientation == 1 } } public override ToString () : string { $"($X, $Y)" } } public variant Expr { | MoveBy { steps : int; } | Left | Right | Value { prop : string; } | If { cond : Expr.Value; then : Expr; els : Expr; } } public module Scripts { public Run (obj : Robot, expr : Expr) : void { def check_value (val) { System.Convert.ToBoolean (obj.GetType ().GetProperty (val.prop).GetValue (obj, null)) } match (expr) { | Expr.MoveBy (steps) => match (obj.Orientation) { | 0 => obj.X += steps | 1 => obj.Y += steps | 2 => obj.X -= steps | _ => obj.Y -= steps } | Expr.Left => obj.Orientation = ((obj.Orientation + 3) % 4) :> byte; | Expr.Right => obj.Orientation = ((obj.Orientation + 1) % 4) :> byte; | Expr.Value as val => _ = check_value (val) | Expr.If (val, e1, e2) => if (check_value (val)) Run (obj, e1) else Run (obj, e2) } } public Run (obj : Robot, name : string) : void { def script = GetScript (name); foreach (e in script) Run (obj, e) } public GetScript (name : string) : list [Expr] { | "myscript1" => [Expr.Right (), Expr.MoveBy (5), Expr.If (Expr.Value ("IsDown"), Expr.MoveBy (3), Expr.Left ())] | _ => throw System.ArgumentException ($"unknown script $name") } public GenerateRun (obj : PExpr, expr : Expr) : PExpr { def check_value (val) { <[ $obj.$(val.prop : dyn) ]> } match (expr) { | Expr.MoveBy (steps) => <[ match ($obj.Orientation) { | 0 => $obj.X += $(steps : int) | 1 => $obj.Y += $(steps : int) | 2 => $obj.X -= $(steps : int) | _ => $obj.Y -= $(steps : int) } ]> | Expr.Left => <[ $obj.Orientation = (($obj.Orientation + 3) % 4) :> byte ]>; | Expr.Right => <[ $obj.Orientation = (($obj.Orientation + 1) % 4) :> byte ]>; | Expr.Value as val => <[ _ = $(check_value (val)) ]> | Expr.If (val, e1, e2) => <[ if ($(check_value (val))) $(GenerateRun (obj, e1)) else $(GenerateRun (obj, e2)) ]> } } } macro GenerateRun (obj, name : string) { def script = Scripts.GetScript (name); def exprs = List.Map (script, fun (e) { Scripts.GenerateRun (obj, e) }); <[ { ..$exprs } ]> }