A small language deserves a small interpreter
Ruby, 182 bytes

$h=Hash.new 0
def r(c)c.scan(/(([^!?^&lt;&gt;]*)(&lt;(\g&lt;1&gt;*)&gt;|[!?^]))/){$4?($1=~/(.*?)&lt;(.*)&gt;/
($h[$1]-=1;r$2)while$h[$1]&gt;0):$3&lt;?"?p($h[$2]):$h[$2]+=$3&lt;?@?STDIN.gets.to_i:
1}end
r IO.read *$*


Try it like this:

$ cat code
a?b&lt;&gt;c&lt;&gt;a&lt;c^c^c&lt;b^&gt;&gt;b!

$ ruby lynn.rb code
3                           &lt;-- input
6                           &lt;-- output


How it works

The r function tokenizes an input string and executes each token:

def r(c)
    c.scan(/(([^!?^&lt;&gt;]*)(&lt;(\g&lt;1&gt;*)&gt;|[!?^]))/){
        ...
    }
end


We look for some variable name $2 matching [^!?^&lt;&gt;]*, followed by either


&lt;...&gt; where ... matches zero or more programs (\g is recursion), in which case $4 isn't nil
A !, ?, or ^ character, captured by $3, in which case $4 is nil.


Then the logic for executing a token is quite simple when indenting it a bit:

$4 ? (                                    # If it's a loop:
    $1 =~ /(.*?)&lt;(.*)&gt;/                   #   Re-match token*
    ($h[$1]-=1; r $2) while $h[$1] &gt; 0    #   Recurse to run loop
) :                                       # Else:
    $3 &lt; ?"                               #   If it's an !:
      ? p($h[$2])                         #     Print the var
      : $h[$2] +=                         #   Else, increment it by:
          $3 &lt; ?@                         #     If it's a ?:
              ? STDIN.gets.to_i           #       User input
              : 1                         #     Else: 1

* There's an oniguruma bug, I think, that keeps me from simply using $3 here.