Lindenmayer systems with luatex and TikZ

Table of Contents

Introduction

Lindenmayer system (or L-systems for short) are fun! TikZ is also fun! What fun it would be to program L-systems in TikZ! Well, you have come to the right post. Below we will go through the steps of implementing an algorithm for drawing L-systems using luatex and TikZ.

Turtles

The first thing we want to do is to implement a simple turtle. We want the turtle to follow a list of commands that we give to it, in this case we use a list of characters {“F”, “F”, “L”, “F”, “R”, “F”} in the lua code to represent the actions:

F
move forward in the direction that the turtle is currently facing, drawing a line as you go
L
turn the turtle 90° to the left
R
turn the turtle 90° to the right

The implementation is simple, we keep track of the current direction (in degrees) of the turtle in the dir variable. We create two closures inc and curr which capture a strictly increasing id count which allows us to generate unique labels for each node as we generate the TikZ lines. The TikZ generation is simple, we loop over the commands, either updating dir or drawing a line from the current label to a newly generated label in the case of an “F” command.

This is all pure lua code generating TikZ at this point, but it allows us to control a turtle:

\begin{tikzpicture}
  \begin{luacode}
    function createGensymCounter()
      local count = 0

      local inc = function()
        count = count + 1
        local ret = "gensym" .. count
        return ret
      end

      local curr = function()
        local ret = "gensym" .. count
        return ret
      end

      return inc, curr
    end
  \end{luacode}
  \directlua{
    local commands = {"F", "F", "L", "F", "R", "F"}
    local dir = 0
    local gensymInc, gensymCurr = createGensymCounter()
    for i, cmd in ipairs(commands) do
      if cmd == "F" then
        tex.print([[\noexpand\draw (]] .. gensymCurr() .. ") -- coordinate[pos=1] (" .. gensymInc() .. ") +(" .. dir .. ":1);")
      elseif cmd == "L" then
        dir = math.mod(dir + 90, 360)
      elseif cmd == "R" then
        dir = math.mod(dir - 90, 360)
      end
    end
  }
\end{tikzpicture}

Which renders to the following:

\begin{tikzpicture*}
  \begin{luacode}
    function createGensymCounter()
      local count = 0

      local inc = function()
        count = count + 1
        local ret = gensym .. count
        return ret
      end

      local curr = function()
        local ret = gensym .. count
        return ret
      end

      return inc, curr
    end
  \end{luacode}
  \directlua{
    local commands = {F, F, L, F, R, F}
    local dir = 0
    local gensymInc, gensymCurr = createGensymCounter()
    for i, cmd in ipairs(commands) do
      if cmd == F then
        tex.print([[\noexpand\draw (]] .. gensymCurr() .. ) -- coordinate[pos=1] ( .. gensymInc() .. ) +( .. dir .. :1);)
      elseif cmd == L then
        dir = math.mod(dir + 90, 360)
      elseif cmd == R then
        dir = math.mod(dir - 90, 360)
      end
    end
  }
\end{tikzpicture*}

We want to be able to factor out the lua code into a LaTeX interface, this is luatex after all. To do this we create a new command \turtle which takes a string of command characters as input and passes it directly to our lua function turtle. Care must be taken here, the argument is substituted raw by tex before the lua function is called so we have to wrap #1 in quotation marks to convert it to a lua string, i.e. turtle("#1").

\begin{tikzpicture}
  \begin{luacode}
    function createGensymCounter()
      local count = 0

      local inc = function()
        count = count + 1
        local ret = "gensym" .. count
        return ret
      end

      local curr = function()
        local ret = "gensym" .. count
        return ret
      end

      return inc, curr
    end

    function turtle(commands)
      local dir = 0
      local gensymInc, gensymCurr = createGensymCounter()
      for cmd in commands:gmatch(".") do
        if cmd == "F" then
          tex.print([[\noexpand\draw (]] .. gensymCurr() .. ") -- coordinate[pos=1] (" .. gensymInc() .. ") +(" .. dir .. ":1);")
        elseif cmd == "L" then
          dir = math.mod(dir + 90, 360)
        elseif cmd == "R" then
          dir = math.mod(dir - 90, 360)
        else
          tex.error("Unknown turtle command: " .. cmd)   
        end
      end
    end
  \end{luacode}

  \newcommand\turtle[1]{
    \directlua{turtle("#1")}
  }

  \turtle{FFLFRF}

This renders as:

\begin{tikzpicture*}
  \begin{luacode}
    function createGensymCounter()
      local count = 0

      local inc = function()
        count = count + 1
        local ret = gensym .. count
        return ret
      end

      local curr = function()
        local ret = gensym .. count
        return ret
      end

      return inc, curr
    end

    function turtle(commands)
      local dir = 0
      local gensymInc, gensymCurr = createGensymCounter()
      for cmd in commands:gmatch(.) do
        if cmd == F then
          tex.print([[\noexpand\draw (]] .. gensymCurr() .. ) -- coordinate[pos=1] ( .. gensymInc() .. ) +( .. dir .. :1);)
        elseif cmd == L then
          dir = math.mod(dir + 90, 360)
        elseif cmd == R then
          dir = math.mod(dir - 90, 360)
        else
          tex.error(Unknown turtle command:  .. cmd)   
        end
      end
    end
  \end{luacode}

  \newcommand\turtle[1]{
    \directlua{turtle(#1)}
  }

  \turtle{FFLFRF}
\end{tikzpicture*}

Connecting lua and LaTeX

The previous case was easy enough to send data from LaTeX to lua just be encoding it as a simple string. However, we will want to be able to pass more structured data from LaTeX to lua if we want to be able to define an L-System in LaTeX and have lua evaluate it.

There are many ways that this could be done. One could come up with a custom string encoding of the data that they wanted to send, then requiring it to be encoded and decoded on the LaTeX and lua ends respectively which would mean having to write a custom parser in lua, not ideal. One could use an existing format such as JSON, using already written parsers and a general and well thought out text format. However, this would mean relying on an external lua JSON decoding package, again not ideal. An ideal solution in this case is to use lua’s text representation of its own data structures!

To illustrate this, we can define custom LaTeX commands which allow us to specify more precisely the turtle movement commands. These commands look like LaTeX, but really they just return the textual representation of lua data structures that we pass straight into our lua function.

The below code prints the data we passed into it, from our LaTeX commands, to the compilation console, verifying that we can indeed pass data to lua using this technique.

\begin{luacode}
  function whatDidIGet(arg)
    print(arg)
    print(arg[1].dist)
    print(arg[2].angle)
    print(arg[3].dist)
  end
\end{luacode}

\newcommand\turtleForward[1]{{command="forward", dist=#1}}
\newcommand\turtleTurnL[1]{{command="turnL", angle=#1}}
\newcommand\turtleTurnR[1]{{command="turnR", angle=#1}}
\newcommand\turtleCommands[1]{{#1}}

\directlua{
  local cmd = \turtleCommands{\turtleForward{3}, \turtleTurnL{90}, \turtleForward{5}}
  whatDidIGet(cmd)
}

We can use our new ability to pass data from LaTeX to lua to implement a set of LaTeX macros to run a turtle on a list of commands such as the following:

\turtle{ \turtleForward{3},
         \turtleTurnL{90},
         \turtleForward{2},
         \turtleTurnR{45},
         \turtleForward{3}}

Notice here that we have a rather perculiar LaTeX syntax, the arguments to \turtle are separated by commas?! This is because the \turtle command is actually converting its argument into a lua table by wrapping it in parens before passing it to lua. It would be nice if I knew how to define vararg commands in LaTeX which would allow this to be written as a standard LaTeX command with multiple arguments which would internally be converted to the correct string representation. Alas, I do not yet know how to do that, so this will have to do.

The full code for this example is the following

\begin{tikzpicture}
  \begin{luacode}
    function createGensymCounter()
      local count = 0

      local inc = function()
        count = count + 1
        local ret = "gensym" .. count
        return ret
      end

      local curr = function()
        local ret = "gensym" .. count
        return ret
      end

      return inc, curr
    end

    function turtle(commands)
      local dir = 0
      local gensymInc, gensymCurr = createGensymCounter()
      for i, cmd in ipairs(commands) do
        if cmd.command == "forward" then
          tex.print([[\noexpand\draw (]] .. gensymCurr() .. ") -- coordinate[pos=1] (" .. gensymInc() .. ") +(" .. dir .. ":".. cmd.dist ..");")
        elseif cmd.command == "turnL" then
          dir = math.mod(dir + cmd.angle, 360)
        elseif cmd.command == "turnR" then
          dir = math.mod(dir - cmd.angle, 360)
        else
          tex.error("Unknown turtle command: " .. cmd)   
          print("Turtle command= " .. cmd.command)
        end
      end
    end
  \end{luacode}

  \newcommand\turtleForward[1]{{command="forward", dist=#1}}
  \newcommand\turtleTurnL[1]{{command="turnL", angle=#1}}
  \newcommand\turtleTurnR[1]{{command="turnR", angle=#1}}

  \NewDocumentCommand\turtle{m}{
    \directlua{
      turtle({#1})
    }
  }

  \turtle{ \turtleForward{3},
           \turtleTurnL{90},
           \turtleForward{2},
           \turtleTurnR{45},
           \turtleForward{3}}
\end{tikzpicture}

Which renders to the following turtle track

\begin{tikzpicture*}
  \begin{luacode}
    function createGensymCounter()
      local count = 0

      local inc = function()
        count = count + 1
        local ret = gensym .. count
        return ret
      end

      local curr = function()
        local ret = gensym .. count
        return ret
      end

      return inc, curr
    end

    function turtle(commands)
      local dir = 0
      local gensymInc, gensymCurr = createGensymCounter()
      for i, cmd in ipairs(commands) do
        if cmd.command == forward then
          tex.print([[\noexpand\draw (]] .. gensymCurr() .. ) -- coordinate[pos=1] ( .. gensymInc() .. ) +( .. dir .. :.. cmd.dist ..);)
        elseif cmd.command == turnL then
          dir = math.mod(dir + cmd.angle, 360)
        elseif cmd.command == turnR then
          dir = math.mod(dir - cmd.angle, 360)
        else
          tex.error(Unknown turtle command:  .. cmd)   
          print(Turtle command=  .. cmd.command)
        end
      end
    end
  \end{luacode}

  \newcommand\turtleForward[1]{{command=forward, dist=#1}}
  \newcommand\turtleTurnL[1]{{command=turnL, angle=#1}}
  \newcommand\turtleTurnR[1]{{command=turnR, angle=#1}}

  \NewDocumentCommand\turtle{m}{
    \directlua{
      turtle({#1})
    }
  }

  \turtle{ \turtleForward{3},
           \turtleTurnL{90},
           \turtleForward{2},
           \turtleTurnR{45},
           \turtleForward{3}}
\end{tikzpicture*}

L-Systems

We are almost there! We have a simple turtle system with a LaTeX interface. Now we just need to generate an L-System and feed it to the turtle system for rendering.

Let us start with the lua code. To run an L-System we need a starting string (for example “FlFlFlF”) and a set of rules for updating this string (for example ’F’ → “FFlFlFlFlFlFrF”). For some predetermined number of steps n we iterate through each character in the string, inserting the contents of the updating rule into the string every time a match is found, or leaving the character alone if no rule applies. The updated string is used as input for the next iteration or returned after the final iteration.

Because we intend to draw these systems with our turtle code, we also have a syntax table that maps characters to the data structures describing their turtle semantics. We map this syntax table over the final string, returning a list of turtle commands to draw.

The code is as follows:

rules = { F="FFlFlFlFlFlFrF" }
start = "FlFlFlF"
syntax = { F={command="forward", dist=0.04},
           l={command="turnL", angle=90},
           r={command="turnR", angle=90} }

function lind(start, rules, n)
   local tmp = start
   local ret = ""
   for i=1,n do
      ret = ""
      for c in tmp:gmatch(".") do
         if rules[c] == nil then
             ret = ret .. c
         else
             ret = ret .. rules[c]
         end
      end
      tmp = ret
   end
   ret = {}
   for c in tmp:gmatch(".") do
      if syntax[c] == nil then
              tex.error("Unknown syntax: " .. c)
      else
         table.insert(ret, syntax[c])
      end
   end
   return ret
end

Adding the above lua code to our turtle code and invoking \luaexec{turtle(lind(start, rules, 4))} we get our first L-System in LaTeX!

\begin{tikzpicture*}
  \begin{luacode}
    rules = { F=FFlFlFlFlFlFrF }
    start = FlFlFlF
    syntax = { F={command=forward, dist=0.04},
               l={command=turnL, angle=90},
               r={command=turnR, angle=90} }

    function lind(start, rules, n)
       local tmp = start
       local ret = 
       for i=1,n do
          ret = 
          for c in tmp:gmatch(.) do
             if rules[c] == nil then
                 ret = ret .. c
             else
                 ret = ret .. rules[c]
             end
          end
          tmp = ret
       end
       ret = {}
       for c in tmp:gmatch(.) do
          if syntax[c] == nil then
    	      tex.error(Unknown syntax:  .. c)
          else
    	 table.insert(ret, syntax[c])
          end
       end
       return ret
    end
  \end{luacode}

  \begin{luacode}
    function createGensymCounter()
      local count = 0

      local inc = function()
        count = count + 1
        local ret = gensym .. count
        return ret
      end

      local curr = function()
        local ret = gensym .. count
        return ret
      end

      return inc, curr
    end

    function turtle(commands)
      local dir = 0
      local gensymInc, gensymCurr = createGensymCounter()
      for i, cmd in ipairs(commands) do
        if cmd.command == forward then
          tex.print([[\noexpand\draw (]] .. gensymCurr() .. ) -- coordinate[pos=1] ( .. gensymInc() .. ) +( .. dir .. :.. cmd.dist ..);)
        elseif cmd.command == turnL then
          dir = math.mod(dir + cmd.angle, 360)
        elseif cmd.command == turnR then
          dir = math.mod(dir - cmd.angle, 360)
        else
          tex.error(Unknown turtle command:  .. cmd)   
          print(Turtle command=  .. cmd.command)
        end
      end
    end
  \end{luacode}

  \luaexec{ turtle(lind(start, rules, 4)) }
\end{tikzpicture*}

Adding a LaTeX interface

Now we just have to add a LaTeX interface. Luckily it is not too hard, we just have to add a few wrapper macros that generate lua data structures.

\newcommand\LSystem[4]{
  \luaexec{
    turtle(lind(#1, {#2}, #3, {#4}))
  }
}

\newcommand\turtleForward[1]{{command="forward", dist=#1}}
\newcommand\turtleTurnL[1]{{command="turnL", angle=#1}}
\newcommand\turtleTurnR[1]{{command="turnR", angle=#1}}
\newcommand\LSInitial[1]{"#1"}
\newcommand\LSExpansion[1]{"#1"}

We can then very simply specify our L-System with the following LaTeX code:

\begin{tikzpicture}
  \LSystem{\LSInitial{FlFlFlF}}
          {F=\LSExpansion{FFlFlFlFlFlFrF}}
          {4}
          {F=\turtleForward{0.04},
           l=\turtleTurnL{90},
           r=\turtleTurnR{90}}
\end{tikzpicture}

Some examples

With this simple interface, we can render some more L-Systems.

Example 1

\begin{tikzpicture}
  \LSystem{\LSInitial{FlFlFlF}}
          {F=\LSExpansion{FFlFlFlFlFF}}
          {4}
          {F=\turtleForward{0.04},
           l=\turtleTurnL{90},
           r=\turtleTurnR{90}}
\end{tikzpicture}
\begin{tikzpicture*}
  \begin{luacode}
    function lind(start, rules, n, syntax)
       local tmp = start
       local ret = 
       for i=1,n do
          ret = 
          for c in tmp:gmatch(.) do
             if rules[c] == nil then
                 ret = ret .. c
             else
                 ret = ret .. rules[c]
             end
          end
          tmp = ret
       end
       ret = {}
       for c in tmp:gmatch(.) do
          if syntax[c] == nil then
    	      tex.error(Unknown syntax:  .. c)
          else
    	 table.insert(ret, syntax[c])
          end
       end
       return ret
    end
  \end{luacode}
  
  \begin{luacode}
    function createGensymCounter()
      local count = 0
  
      local inc = function()
        count = count + 1
        local ret = gensym .. count
        return ret
      end
  
      local curr = function()
        local ret = gensym .. count
        return ret
      end
  
      return inc, curr
    end
  
    function turtle(commands)
      local dir = 0
      local gensymInc, gensymCurr = createGensymCounter()
      for i, cmd in ipairs(commands) do
        if cmd.command == forward then
          tex.print([[\noexpand\draw (]] .. gensymCurr() .. ) -- coordinate[pos=1] ( .. gensymInc() .. ) +( .. dir .. :.. cmd.dist ..);)
        elseif cmd.command == turnL then
          dir = math.mod(dir + cmd.angle, 360)
        elseif cmd.command == turnR then
          dir = math.mod(dir - cmd.angle, 360)
        else
          tex.error(Unknown turtle command:  .. cmd)   
          print(Turtle command=  .. cmd.command)
        end
      end
    end
  \end{luacode}
  
  \newcommand\LSystem[4]{
    \luaexec{
      turtle(lind(#1, {#2}, #3, {#4}))
    }
  }
  
  \newcommand\turtleForward[1]{{command=forward, dist=#1}}
  \newcommand\turtleTurnL[1]{{command=turnL, angle=#1}}
  \newcommand\turtleTurnR[1]{{command=turnR, angle=#1}}
  \newcommand\LSInitial[1]{#1}
  \newcommand\LSExpansion[1]{#1}

  \LSystem{\LSInitial{FlFlFlF}}
          {F=\LSExpansion{FFlFlFlFlFF}}
          {4}
          {F=\turtleForward{0.04},
           l=\turtleTurnL{90},
           r=\turtleTurnR{90}}
\end{tikzpicture*}

Example 2

\begin{tikzpicture}
  \LSystem{\LSInitial{FlFlFlF}}
          {F=\LSExpansion{FFlFllFlF}}
          {4}
          {F=\turtleForward{0.04},
           l=\turtleTurnL{90},
           r=\turtleTurnR{90}}
\end{tikzpicture}
\begin{tikzpicture*}
  \begin{luacode}
    function lind(start, rules, n, syntax)
       local tmp = start
       local ret = 
       for i=1,n do
          ret = 
          for c in tmp:gmatch(.) do
             if rules[c] == nil then
                 ret = ret .. c
             else
                 ret = ret .. rules[c]
             end
          end
          tmp = ret
       end
       ret = {}
       for c in tmp:gmatch(.) do
          if syntax[c] == nil then
    	      tex.error(Unknown syntax:  .. c)
          else
    	 table.insert(ret, syntax[c])
          end
       end
       return ret
    end
  \end{luacode}

  \begin{luacode}
    function createGensymCounter()
      local count = 0

      local inc = function()
        count = count + 1
        local ret = gensym .. count
        return ret
      end

      local curr = function()
        local ret = gensym .. count
        return ret
      end

      return inc, curr
    end

    function turtle(commands)
      local dir = 0
      local gensymInc, gensymCurr = createGensymCounter()
      for i, cmd in ipairs(commands) do
        if cmd.command == forward then
          tex.print([[\noexpand\draw (]] .. gensymCurr() .. ) -- coordinate[pos=1] ( .. gensymInc() .. ) +( .. dir .. :.. cmd.dist ..);)
        elseif cmd.command == turnL then
          dir = math.mod(dir + cmd.angle, 360)
        elseif cmd.command == turnR then
          dir = math.mod(dir - cmd.angle, 360)
        else
          tex.error(Unknown turtle command:  .. cmd)   
          print(Turtle command=  .. cmd.command)
        end
      end
    end
  \end{luacode}

  \newcommand\LSystem[4]{
    \luaexec{
      turtle(lind(#1, {#2}, #3, {#4}))
    }
  }

  \newcommand\turtleForward[1]{{command=forward, dist=#1}}
  \newcommand\turtleTurnL[1]{{command=turnL, angle=#1}}
  \newcommand\turtleTurnR[1]{{command=turnR, angle=#1}}
  \newcommand\LSInitial[1]{#1}
  \newcommand\LSExpansion[1]{#1}

  \LSystem{\LSInitial{FlFlFlF}}
          {F=\LSExpansion{FFlFllFlF}}
          {4}
          {F=\turtleForward{0.04},
           l=\turtleTurnL{90},
           r=\turtleTurnR{90}}
\end{tikzpicture*}

Example 3

\begin{tikzpicture}
  \LSystem{\LSInitial{L}}
          {L=\LSExpansion{LrRr},
           R=\LSExpansion{lLlR}}
          {10}
          {L=\turtleForward{0.1},
           R=\turtleForward{0.1},
           l=\turtleTurnL{90},
           r=\turtleTurnR{90}}
\end{tikzpicture}
\begin{tikzpicture*}
  \begin{luacode}
    function lind(start, rules, n, syntax)
       local tmp = start
       local ret = 
       for i=1,n do
          ret = 
          for c in tmp:gmatch(.) do
             if rules[c] == nil then
                 ret = ret .. c
             else
                 ret = ret .. rules[c]
             end
          end
          tmp = ret
       end
       ret = {}
       for c in tmp:gmatch(.) do
          if syntax[c] == nil then
    	      tex.error(Unknown syntax:  .. c)
          else
    	 table.insert(ret, syntax[c])
          end
       end
       return ret
    end
  \end{luacode}

  \begin{luacode}
    function createGensymCounter()
      local count = 0

      local inc = function()
        count = count + 1
        local ret = gensym .. count
        return ret
      end

      local curr = function()
        local ret = gensym .. count
        return ret
      end

      return inc, curr
    end

    function turtle(commands)
      local dir = 0
      local gensymInc, gensymCurr = createGensymCounter()
      for i, cmd in ipairs(commands) do
        if cmd.command == forward then
          tex.print([[\noexpand\draw (]] .. gensymCurr() .. ) -- coordinate[pos=1] ( .. gensymInc() .. ) +( .. dir .. :.. cmd.dist ..);)
        elseif cmd.command == turnL then
          dir = math.mod(dir + cmd.angle, 360)
        elseif cmd.command == turnR then
          dir = math.mod(dir - cmd.angle, 360)
        else
          tex.error(Unknown turtle command:  .. cmd)   
          print(Turtle command=  .. cmd.command)
        end
      end
    end
  \end{luacode}

  \newcommand\LSystem[4]{
    \luaexec{
      turtle(lind(#1, {#2}, #3, {#4}))
    }
  }

  \newcommand\turtleForward[1]{{command=forward, dist=#1}}
  \newcommand\turtleTurnL[1]{{command=turnL, angle=#1}}
  \newcommand\turtleTurnR[1]{{command=turnR, angle=#1}}
  \newcommand\LSInitial[1]{#1}
  \newcommand\LSExpansion[1]{#1}

  \LSystem{\LSInitial{L}}
          {L=\LSExpansion{LrRr},
           R=\LSExpansion{lLlR}}
          {10}
          {L=\turtleForward{0.1},
           R=\turtleForward{0.1},
           l=\turtleTurnL{90},
           r=\turtleTurnR{90}}
\end{tikzpicture*}

The full code

Here is the full code for generating L-Systems, should you ever want to play around with it.

%% filename.tex
% Compile with: lualatex -interaction=nonstopmode filename.tex
\documentclass{standalone}

\usepackage{luacode}
\usepackage{tikz}
\usetikzlibrary{shapes, arrows, positioning, decorations.pathmorphing, bending}

\begin{document}
  \begin{tikzpicture}
    \begin{luacode}
      function lind(start, rules, n, syntax)
         local tmp = start
         local ret = ""
         for i=1,n do
            ret = ""
            for c in tmp:gmatch(".") do
               if rules[c] == nil then
                   ret = ret .. c
               else
                   ret = ret .. rules[c]
               end
            end
            tmp = ret
         end
         ret = {}
         for c in tmp:gmatch(".") do
            if syntax[c] == nil then
              tex.error("Unknown syntax: " .. c)
            else
         table.insert(ret, syntax[c])
            end
         end
         return ret
      end
    \end{luacode}

    \begin{luacode}
      function createGensymCounter()
        local count = 0

        local inc = function()
          count = count + 1
          local ret = "gensym" .. count
          return ret
        end

        local curr = function()
          local ret = "gensym" .. count
          return ret
        end

        return inc, curr
      end

      function turtle(commands)
        local dir = 0
        local gensymInc, gensymCurr = createGensymCounter()
        for i, cmd in ipairs(commands) do
          if cmd.command == "forward" then
            tex.print([[\noexpand\draw (]] .. gensymCurr() .. ") -- coordinate[pos=1] (" .. gensymInc() .. ") +(" .. dir .. ":".. cmd.dist ..");")
          elseif cmd.command == "turnL" then
            dir = math.mod(dir + cmd.angle, 360)
          elseif cmd.command == "turnR" then
            dir = math.mod(dir - cmd.angle, 360)
          else
            tex.error("Unknown turtle command: " .. cmd)   
            print("Turtle command= " .. cmd.command)
          end
        end
      end
    \end{luacode}

    \newcommand\LSystem[4]{
      \luaexec{
        turtle(lind(#1, {#2}, #3, {#4}))
      }
    }

    \newcommand\turtleForward[1]{{command="forward", dist=#1}}
    \newcommand\turtleTurnL[1]{{command="turnL", angle=#1}}
    \newcommand\turtleTurnR[1]{{command="turnR", angle=#1}}
    \newcommand\LSInitial[1]{"#1"}
    \newcommand\LSExpansion[1]{"#1"}

    \LSystem{\LSInitial{FlFlFlF}}
            {F=\LSExpansion{FFlFlFlFlFlFrF}}
            {4}
            {F=\turtleForward{0.04},
             l=\turtleTurnL{90},
             r=\turtleTurnR{90}}
  \end{tikzpicture}
\end{document}