8 people like it.

Turtle procedures

Minimal Logo implementation using FParsec with support for procedures.

  1: 
  2: 
  3: 
  4: 
  5: 
  6: 
  7: 
  8: 
  9: 
 10: 
 11: 
 12: 
 13: 
 14: 
 15: 
 16: 
 17: 
 18: 
 19: 
 20: 
 21: 
 22: 
 23: 
 24: 
 25: 
 26: 
 27: 
 28: 
 29: 
 30: 
 31: 
 32: 
 33: 
 34: 
 35: 
 36: 
 37: 
 38: 
 39: 
 40: 
 41: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
121: 
122: 
123: 
124: 
125: 
126: 
127: 
128: 
129: 
130: 
module AST =
   type distance = int
   type degrees = int
   type count = int
   type name = string
   type command =
      | Forward of distance
      | Turn of degrees
      | SetRandomPosition
      | Repeat of count * command list
      | Call of name
      | Proc of name * command list

Windows Forms references

module Interpreter =
   open AST
   open System
   open System.Drawing
   open System.Windows.Forms

   type Turtle = { X:float; Y:float; A:int }

   let execute commands =
      let procs = ref Map.empty
      let width, height = 640, 480
      let form = new Form (Text="Turtle", Width=width, Height=height)
      let image = new Bitmap(width, height)
      let picture = new PictureBox(Dock=DockStyle.Fill, Image=image)
      do  form.Controls.Add(picture)
      let turtle = { X=float width/2.0; Y=float height/2.0; A = -90 }
      use pen = new Pen(Color.Red)
      let rand = let r = Random() in fun n -> r.Next(n) |> float
      let drawLine (x1,y1) (x2,y2) =
         use graphics = Graphics.FromImage(image)
         graphics.DrawLine(pen,int x1,int y1,int x2, int y2)
      let rec perform turtle = function
         | Forward n ->
            let r = float turtle.A * Math.PI / 180.0
            let dx, dy = float n * cos r, float n * sin r
            let x, y =  turtle.X, turtle.Y
            let x',y' = x + dx, y + dy
            drawLine (x,y) (x',y')
            { turtle with X = x'; Y = y' }
         | Turn n -> { turtle with A=turtle.A + n }
         | SetRandomPosition -> { turtle with X=rand width; Y=rand height }
         | Repeat(n,commands) ->
            let rec repeat turtle = function
               | 0 -> turtle
               | n -> repeat (performAll turtle commands) (n-1)
            repeat turtle n
         | Proc(name,commands) -> procs := Map.add name commands !procs; turtle
         | Call(name) -> (!procs).[name] |> performAll turtle
      and performAll = List.fold perform
      performAll turtle commands |> ignore
      form.ShowDialog() |> ignore

FParsec references

module Parser =

   open AST
   open FParsec

   let procs = ref []

   let pforward = 
      (pstring "forward" <|> pstring "fd") >>. spaces1 >>. pfloat 
      |>> fun x -> Forward(int x)
   let pleft = 
      (pstring "left" <|> pstring "lt") >>. spaces1 >>. pfloat 
      |>> fun x -> Turn(int -x)
   let pright = 
      (pstring "right" <|> pstring "rt") >>. spaces1 >>. pfloat 
      |>> fun x -> Turn(int x)
   let prandom = 
      pstring "set-random-position"
      |>> fun _ -> SetRandomPosition
   let prepeat, prepeatimpl = createParserForwardedToRef ()
   let pcall, pcallimpl = createParserForwardedToRef ()

   let pcommand = pforward <|> pleft <|> pright <|> prandom <|> prepeat <|> pcall

   let updateCalls () =
      pcallimpl := 
         choice [for name in !procs -> pstring name |>> fun _ -> Call(name)]
   updateCalls()

   let block = between (pstring "[" .>> spaces) (pstring "]") 
                       (sepEndBy pcommand spaces1)
   
   prepeatimpl := 
      pstring "repeat" >>. spaces1 >>. pfloat .>> spaces .>>. block
      |>> fun (n,commands) -> Repeat(int n, commands)

   let pidentifier =
      let isIdentifierFirstChar c = isLetter c || c = '-'
      let isIdentifierChar c = isLetter c || isDigit c || c = '-'
      many1Satisfy2L isIdentifierFirstChar isIdentifierChar  "identifier"

   let pheader = pstring "to" >>. spaces1 >>. pidentifier .>> spaces1
   let pbody = many (pcommand .>> spaces1)
   let pfooter = pstring "end"

   let pproc =
      pheader .>>. pbody .>> pfooter
      |>> fun (name,body) -> procs := name::!procs; updateCalls(); Proc(name, body)

   let parser =
      spaces >>. (sepEndBy (pcommand <|> pproc) spaces1)

   let parse code =
      match run parser code with
      | Success(result,_,_) -> result
      | Failure(msg,_,_) -> failwith msg

let code = "
   to square
     repeat 4 [forward 50 right 90]
   end
   to flower
     repeat 36 [right 10 square]
   end
   to garden
     repeat 25 [set-random-position flower]
   end
   garden
   "
let program = Parser.parse code
Interpreter.execute program
type distance = int

Full name: Script.AST.distance
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
type degrees = int

Full name: Script.AST.degrees
type count = int

Full name: Script.AST.count
type name = string

Full name: Script.AST.name
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
type command =
  | Forward of distance
  | Turn of degrees
  | SetRandomPosition
  | Repeat of count * command list
  | Call of name
  | Proc of name * command list

Full name: Script.AST.command
union case command.Forward: distance -> command
union case command.Turn: degrees -> command
union case command.SetRandomPosition: command
union case command.Repeat: count * command list -> command
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
union case command.Call: name -> command
union case command.Proc: name * command list -> command
#if INTERACTIVE
#r "System.Drawing.dll"
#r "System.Windows.Forms.dll"
#endif
module AST

from Script
namespace System
namespace System.Drawing
namespace System.Windows
namespace System.Windows.Forms
type Turtle =
  {X: float;
   Y: float;
   A: int;}

Full name: Script.Interpreter.Turtle
Turtle.X: float
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
Turtle.Y: float
Turtle.A: int
val execute : commands:command list -> unit

Full name: Script.Interpreter.execute
val commands : command list
val procs : Map<name,command list> ref
Multiple items
val ref : value:'T -> 'T ref

Full name: Microsoft.FSharp.Core.Operators.ref

--------------------
type 'T ref = Ref<'T>

Full name: Microsoft.FSharp.Core.ref<_>
Multiple items
module Map

from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> =
  interface IEnumerable
  interface IComparable
  interface IEnumerable<KeyValuePair<'Key,'Value>>
  interface ICollection<KeyValuePair<'Key,'Value>>
  interface IDictionary<'Key,'Value>
  new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
  member Add : key:'Key * value:'Value -> Map<'Key,'Value>
  member ContainsKey : key:'Key -> bool
  override Equals : obj -> bool
  member Remove : key:'Key -> Map<'Key,'Value>
  ...

Full name: Microsoft.FSharp.Collections.Map<_,_>

--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
val empty<'Key,'T (requires comparison)> : Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.empty
val width : int
val height : int
val form : Form
Multiple items
type Form =
  inherit ContainerControl
  new : unit -> Form
  member AcceptButton : IButtonControl with get, set
  member Activate : unit -> unit
  member ActiveMdiChild : Form
  member AddOwnedForm : ownedForm:Form -> unit
  member AllowTransparency : bool with get, set
  member AutoScale : bool with get, set
  member AutoScaleBaseSize : Size with get, set
  member AutoScroll : bool with get, set
  member AutoSize : bool with get, set
  ...
  nested type ControlCollection

Full name: System.Windows.Forms.Form

--------------------
Form() : unit
Multiple items
namespace System.Drawing.Text

--------------------
namespace System.Text
val image : Bitmap
Multiple items
type Bitmap =
  inherit Image
  new : filename:string -> Bitmap + 11 overloads
  member Clone : rect:Rectangle * format:PixelFormat -> Bitmap + 1 overload
  member GetHbitmap : unit -> nativeint + 1 overload
  member GetHicon : unit -> nativeint
  member GetPixel : x:int * y:int -> Color
  member LockBits : rect:Rectangle * flags:ImageLockMode * format:PixelFormat -> BitmapData + 1 overload
  member MakeTransparent : unit -> unit + 1 overload
  member SetPixel : x:int * y:int * color:Color -> unit
  member SetResolution : xDpi:float32 * yDpi:float32 -> unit
  member UnlockBits : bitmapdata:BitmapData -> unit
  ...

Full name: System.Drawing.Bitmap

--------------------
Bitmap(filename: string) : unit
   (+0 other overloads)
Bitmap(stream: IO.Stream) : unit
   (+0 other overloads)
Bitmap(original: Image) : unit
   (+0 other overloads)
Bitmap(filename: string, useIcm: bool) : unit
   (+0 other overloads)
Bitmap(type: Type, resource: string) : unit
   (+0 other overloads)
Bitmap(stream: IO.Stream, useIcm: bool) : unit
   (+0 other overloads)
Bitmap(width: int, height: int) : unit
   (+0 other overloads)
Bitmap(original: Image, newSize: Size) : unit
   (+0 other overloads)
Bitmap(width: int, height: int, format: Imaging.PixelFormat) : unit
   (+0 other overloads)
Bitmap(width: int, height: int, g: Graphics) : unit
   (+0 other overloads)
val picture : PictureBox
Multiple items
type PictureBox =
  inherit Control
  new : unit -> PictureBox
  member AllowDrop : bool with get, set
  member BorderStyle : BorderStyle with get, set
  member CancelAsync : unit -> unit
  member CausesValidation : bool with get, set
  member ErrorImage : Image with get, set
  member Font : Font with get, set
  member ForeColor : Color with get, set
  member Image : Image with get, set
  member ImageLocation : string with get, set
  ...

Full name: System.Windows.Forms.PictureBox

--------------------
PictureBox() : unit
type DockStyle =
  | None = 0
  | Top = 1
  | Bottom = 2
  | Left = 3
  | Right = 4
  | Fill = 5

Full name: System.Windows.Forms.DockStyle
field DockStyle.Fill = 5
type Image =
  inherit MarshalByRefObject
  member Clone : unit -> obj
  member Dispose : unit -> unit
  member Flags : int
  member FrameDimensionsList : Guid[]
  member GetBounds : pageUnit:GraphicsUnit -> RectangleF
  member GetEncoderParameterList : encoder:Guid -> EncoderParameters
  member GetFrameCount : dimension:FrameDimension -> int
  member GetPropertyItem : propid:int -> PropertyItem
  member GetThumbnailImage : thumbWidth:int * thumbHeight:int * callback:GetThumbnailImageAbort * callbackData:nativeint -> Image
  member Height : int
  ...
  nested type GetThumbnailImageAbort

Full name: System.Drawing.Image
property Control.Controls: Control.ControlCollection
Control.ControlCollection.Add(value: Control) : unit
val turtle : Turtle
val pen : Pen
Multiple items
type Pen =
  inherit MarshalByRefObject
  new : color:Color -> Pen + 3 overloads
  member Alignment : PenAlignment with get, set
  member Brush : Brush with get, set
  member Clone : unit -> obj
  member Color : Color with get, set
  member CompoundArray : float32[] with get, set
  member CustomEndCap : CustomLineCap with get, set
  member CustomStartCap : CustomLineCap with get, set
  member DashCap : DashCap with get, set
  member DashOffset : float32 with get, set
  ...

Full name: System.Drawing.Pen

--------------------
Pen(color: Color) : unit
Pen(brush: Brush) : unit
Pen(color: Color, width: float32) : unit
Pen(brush: Brush, width: float32) : unit
type Color =
  struct
    member A : byte
    member B : byte
    member Equals : obj:obj -> bool
    member G : byte
    member GetBrightness : unit -> float32
    member GetHashCode : unit -> int
    member GetHue : unit -> float32
    member GetSaturation : unit -> float32
    member IsEmpty : bool
    member IsKnownColor : bool
    ...
  end

Full name: System.Drawing.Color
property Color.Red: Color
val rand : (int -> float)
val r : Random
Multiple items
type Random =
  new : unit -> Random + 1 overload
  member Next : unit -> int + 2 overloads
  member NextBytes : buffer:byte[] -> unit
  member NextDouble : unit -> float

Full name: System.Random

--------------------
Random() : unit
Random(Seed: int) : unit
val n : int
Random.Next() : int
Random.Next(maxValue: int) : int
Random.Next(minValue: int, maxValue: int) : int
val drawLine : (float * float -> float * float -> unit)
val x1 : float
val y1 : float
val x2 : float
val y2 : float
val graphics : Graphics
type Graphics =
  inherit MarshalByRefObject
  member AddMetafileComment : data:byte[] -> unit
  member BeginContainer : unit -> GraphicsContainer + 2 overloads
  member Clear : color:Color -> unit
  member Clip : Region with get, set
  member ClipBounds : RectangleF
  member CompositingMode : CompositingMode with get, set
  member CompositingQuality : CompositingQuality with get, set
  member CopyFromScreen : upperLeftSource:Point * upperLeftDestination:Point * blockRegionSize:Size -> unit + 3 overloads
  member Dispose : unit -> unit
  member DpiX : float32
  ...
  nested type DrawImageAbort
  nested type EnumerateMetafileProc

Full name: System.Drawing.Graphics
Graphics.FromImage(image: Image) : Graphics
Graphics.DrawLine(pen: Pen, pt1: Point, pt2: Point) : unit
Graphics.DrawLine(pen: Pen, pt1: PointF, pt2: PointF) : unit
Graphics.DrawLine(pen: Pen, x1: int, y1: int, x2: int, y2: int) : unit
Graphics.DrawLine(pen: Pen, x1: float32, y1: float32, x2: float32, y2: float32) : unit
val perform : (Turtle -> command -> Turtle)
val n : distance
val r : float
type Math =
  static val PI : float
  static val E : float
  static member Abs : value:sbyte -> sbyte + 6 overloads
  static member Acos : d:float -> float
  static member Asin : d:float -> float
  static member Atan : d:float -> float
  static member Atan2 : y:float * x:float -> float
  static member BigMul : a:int * b:int -> int64
  static member Ceiling : d:decimal -> decimal + 1 overload
  static member Cos : d:float -> float
  ...

Full name: System.Math
field Math.PI = 3.14159265359
val dx : float
val dy : float
val cos : value:'T -> 'T (requires member Cos)

Full name: Microsoft.FSharp.Core.Operators.cos
val sin : value:'T -> 'T (requires member Sin)

Full name: Microsoft.FSharp.Core.Operators.sin
val x : float
val y : float
val x' : float
val y' : float
val n : degrees
val n : count
val repeat : (Turtle -> int -> Turtle)
val performAll : (Turtle -> command list -> Turtle)
Multiple items
val name : name

--------------------
type name = string

Full name: Script.AST.name
val add : key:'Key -> value:'T -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.add
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State

Full name: Microsoft.FSharp.Collections.List.fold
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
Form.ShowDialog() : DialogResult
Form.ShowDialog(owner: IWin32Window) : DialogResult
#if INTERACTIVE
#r @"..\packages\FParsec.1.0.1\lib\net40-client\FParsecCS.dll"
#r @"..\packages\FParsec.1.0.1\lib\net40-client\FParsec.dll"
#endif
namespace FParsec
val procs : string list ref

Full name: Script.Parser.procs
val pforward : Parser<command,unit>

Full name: Script.Parser.pforward
val pstring : string -> Parser<string,'u>

Full name: FParsec.CharParsers.pstring
val spaces1 : Parser<unit,'u>

Full name: FParsec.CharParsers.spaces1
val pfloat : Parser<float,'u>

Full name: FParsec.CharParsers.pfloat
val pleft : Parser<command,unit>

Full name: Script.Parser.pleft
val pright : Parser<command,unit>

Full name: Script.Parser.pright
val prandom : Parser<command,unit>

Full name: Script.Parser.prandom
val prepeat : Parser<command,unit>

Full name: Script.Parser.prepeat
val prepeatimpl : Parser<command,unit> ref

Full name: Script.Parser.prepeatimpl
val createParserForwardedToRef : unit -> Parser<'a,'u> * Parser<'a,'u> ref

Full name: FParsec.Primitives.createParserForwardedToRef
val pcall : Parser<command,unit>

Full name: Script.Parser.pcall
val pcallimpl : Parser<command,unit> ref

Full name: Script.Parser.pcallimpl
val pcommand : Parser<command,unit>

Full name: Script.Parser.pcommand
val updateCalls : unit -> unit

Full name: Script.Parser.updateCalls
val choice : seq<Parser<'a,'u>> -> Parser<'a,'u>

Full name: FParsec.Primitives.choice
Multiple items
val name : string

--------------------
type name = string

Full name: Script.AST.name
val block : Parser<command list,unit>

Full name: Script.Parser.block
val between : Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'c,'u>

Full name: FParsec.Primitives.between
val spaces : Parser<unit,'u>

Full name: FParsec.CharParsers.spaces
val sepEndBy : Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a list,'u>

Full name: FParsec.Primitives.sepEndBy
val n : float
val pidentifier : Parser<string,unit>

Full name: Script.Parser.pidentifier
val isIdentifierFirstChar : (char -> bool)
val c : char
val isLetter : char -> bool

Full name: FParsec.CharParsers.isLetter
val isIdentifierChar : (char -> bool)
val isDigit : char -> bool

Full name: FParsec.CharParsers.isDigit
val many1Satisfy2L : (char -> bool) -> (char -> bool) -> string -> Parser<string,'u>

Full name: FParsec.CharParsers.many1Satisfy2L
val pheader : Parser<string,unit>

Full name: Script.Parser.pheader
val pbody : Parser<command list,unit>

Full name: Script.Parser.pbody
val many : Parser<'a,'u> -> Parser<'a list,'u>

Full name: FParsec.Primitives.many
val pfooter : Parser<string,unit>

Full name: Script.Parser.pfooter
val pproc : Parser<command,unit>

Full name: Script.Parser.pproc
val body : command list
val parser : Parser<command list,unit>

Full name: Script.Parser.parser
val parse : code:string -> command list

Full name: Script.Parser.parse
val code : string
val run : Parser<'Result,unit> -> string -> ParserResult<'Result,unit>

Full name: FParsec.CharParsers.run
union case ParserResult.Success: 'Result * 'UserState * Position -> ParserResult<'Result,'UserState>
val result : command list
union case ParserResult.Failure: string * ParserError * 'UserState -> ParserResult<'Result,'UserState>
val msg : string
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
val code : string

Full name: Script.code
val program : AST.command list

Full name: Script.program
module Parser

from Script
val parse : code:string -> AST.command list

Full name: Script.Parser.parse
module Interpreter

from Script
val execute : commands:AST.command list -> unit

Full name: Script.Interpreter.execute
Next Version Raw view Test code New version

More information

Link:http://fssnip.net/nN
Posted:10 years ago
Author:Phillip Trelford
Tags: turtle , dsl , fparsec