[Question]
  [
*Prindeal* (pronounced [prin-dee-al](http://www.pronunciationgenerator.com/v/1mJVa/prindeal.mp4)) is a new [esoteric](http://esolangs.org/wiki/esoteric_programming_language) programming language that only has four commands: ***pr***int, ***in***crement, ***de***crement, and ***al***ias. Despite its minimalism, complex mathematical operations can be done in Prindeal by cleverly combining the four commands.
Your task in this [code golf](https://codegolf.stackexchange.com/tags/code-golf/info) challenge is to write the shortest program that can run Prindeal code.
The spec is long but I've tried to make it as clear as possible and I believe that if you make the effort to learn Prindeal you'll find it to be quite elegant!
---
# Intrepreting Prindeal
## Preprocessing
Before a Prindeal program can be interpreted these things need to be removed from it in this order:
1. Anything after a `#` sign to the end of the line it's on, plus the `#` itself. (These are comments.)
2. Trailing whitespace on any line.
3. Completely empty lines.
For example, the Prindeal program
```
p cat #The next line has 7 trailing spaces.
p dog       
#p mouse
```
would be preprocessed into
```
p cat
p dog
```
From here on we'll assume this preprocessing step has been done.
## Variables
We quickly need to define variables before showing how they are used.
Variables (and references to variables) are what are passed into the arguments of Prindeal commands. **Variables are always global**, so modifications to a variable, no matter where they occur, are reflected everywhere.
Each variable holds a **non-negative [arbitrary-precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) integer** (0, 1, 2, 3, ...). Variables do not need to be pre-initialized - **they always start with the value 0** the first time they are used or called for.
A variable name may be any non-empty string of alphanumerics and underscores that does not start with a digit - `[a-zA-Z_][0-9a-zA-Z_]*` in [regex](https://en.wikipedia.org/wiki/Regular_expression). They are case sensitive, so `spiny_lumpsuck3r` and `Spiny_lumpsuck3r` are different variables.
## Execution
Prindeal is an [imperative](https://en.wikipedia.org/wiki/Imperative_programming) programming language. When a Prindeal program is run its [statements](https://en.wikipedia.org/wiki/Statement_(computer_science)) are executed from top to bottom in order and then the program ends.
Every **non-indented** line in a Prindeal program is a statement that involves the execution of a single command which may or may not take arguments.
Indented lines only occur after *alias* commands. Specifically, exactly **three lines indented with single spaces occur after every *alias* command** and are considered a part of it. So *alias* statements are really four lines long. (They could be one line, four is merely more readable.)
### Non-*alias* Statements
With the exception of *alias*, every statement in a Prindeal program has the form:
```
[command name] [argument 1] [argument 2] [argument 3] ...
```
There may be an arbitrary number of arguments (including none at all). Each argument is always a **variable** or (as we'll see when discussing *alias*) a **reference to a variable**.
Once done executing, **each statement is flagged as either a *failure* or *success*** depending on if errors were encountered or not. (This only really matters when we get around to using *alias*.)
The built-ins *print*, *increment*, and *decrement* are statements with the above form. Here is what they do:
1. *print* has command name `p` and takes one argument. It prints the name of the variable passed in and its value (in decimal) separated by " = ", then a newline. It is always flagged as a *success*.
For example, the Prindeal program
```
p _MyVariable_321
p screaming_hairy_armadillo
```
would output
```
_MyVariable_321 = 0
screaming_hairy_armadillo = 0
```
because all variables start at 0. (The spaces before and after the equals sign are required.)
2. *increment* has command name `i` and takes one argument. It increments the value of the variable passed in by 1. It is always flagged as a *success*..
For example, the program
```
i alpaca
p alpaca
i alpaca
p alpaca
```
would output
```
alpaca = 1
alpaca = 2
```
Note how `alpaca` was incremented from 0 to 1 even though it had never been accessed before.
3. *decrement* has command name `d` and takes one argument. If the variable passed in is nonzero its value is decremented by 1 and the statement is flagged as a *success*. If the variable passed in is 0 then nothing is done and the statement is flagged as a *failure*.
For example, the program
```
i malamute
p malamute
d malamute    #success
p malamute
d malamute    #failure
p malamute
d akita       #failure
p akita
```
would output
```
malamute = 1
malamute = 0
malamute = 0
akita = 0
```
Notice that decrementing a variable with value 0 is the only way to produce a *failure*.
### The *alias* Statement and Aliased Commands
The *alias* command has a special syntax and is the most powerful because it can be used to define new commands. The *alias* command name is `a` and an *alias* statement has the form:
```
a [name of new command]
 [statement A]
 [statement B]
 [statement C]
```
Where each `[statement X]` represents any non-*alias* statement, i.e. something with the form `[command name] [argument 1] [argument 2] [argument 3] ...`.
The aliased command's name `[name of new command]` may be any non-empty string of alphanumerics and underscores that does not start with a digit - `[a-zA-Z_][0-9a-zA-Z_]*` in regex.
(This is the same set of names as variables but aliased **commands and variables are different things used in different places**. A variable could be named the same as a command with no ill consequences.)
When an *alias* statement is executed, a new command is added alongside the original four `p` `i` `d` `a` commands. The new command can be used as the `[command name]` in statements and called with arguments just like any other non-*alias* command.
When a statement with an aliased command name is executed, exactly **two more statements from its original *alias* statement are run:**
* `[statement A]` is always run
* `[statement B]` is run if `[statement A]` was a *success*
* `[statement C]` is run if `[statement A]` was a *failure*
Statements A, B, and C are **always run [lazily](https://en.wikipedia.org/wiki/Lazy_evaluation)**, i.e. they are evaluated on the fly at the time they are run.
When done executing, **the aliased command is flagged with the same *success* or *failure* flag as statement B or C, whichever one was executed**. (*alias* statements themselves do not need to be flagged since they cannot occur within themselves.)
> 
> **Alias Example 1**
> 
> 
> Say we want a new command that increments the variable `frog` twice. This alias statement achieves it:
> 
> 
> 
> ```
> a increment_frog_twice
>  i frog
>  i frog
>  d frog
> 
> ```
> 
> Statement A (`i frog`) is always run and always flagged as a *success*
>  so statement B (`i frog`) is also always run and the variable `frog`
>  is thus incremented by 2. The `increment_frog_twice` command is always
>  flagged as a *success* because statement B is always run and B is
>  always a *success*. Statement C (`d frog`) is never run.
> 
> 
> So the output to
> 
> 
> 
> ```
> a increment_frog_twice
>  i frog
>  i frog
>  d frog
> p frog
> increment_frog_twice
> p frog
> 
> ```
> 
> would be
> 
> 
> 
> ```
> frog = 0
> frog = 2
> 
> ```
> 
> 
We can generalize this example so that *any* variable can be incremented twice by giving the aliased command an argument.
**Within an *alias* statement the positive integers 1, 2, 3, etc. represent the 1st, 2nd, 3rd, etc. arguments passed into the aliased command.** (These arguments might be plain variables or references to variables themselves.) These numbers can only appear within the arguments of statement A, B, and C in an *alias* statement. It doesn't make sense for them to appear elsewhere.
> 
> **Alias Example 2**
> 
> 
> This generalizes the last example - any variable passed into `increment_twice` will be incremented by 2 because `1` is a reference to the first argument passed in:
> 
> 
> 
> ```
> a increment_twice
>  i 1
>  i 1
>  d 1 #never reached
> p toad
> increment_twice toad
> p toad
> 
> ```
> 
> The output of this program would be
> 
> 
> 
> ```
> toad = 0
> toad = 2
> 
> ```
> 
> We could then alias another command that takes in two arguments and calls `increment_twice` on both of them:
> 
> 
> 
> ```
> a increment_twice
>  i 1
>  i 1
>  d 1 #never reached
> a increment_both_twice
>  increment_twice 1
>  increment_twice 2
>  d 1 #never reached
> increment_both_twice platypus duck
> p platypus
> p duck
> 
> ```
> 
> The output here would be
> 
> 
> 
> ```
> platypus = 2
> duck = 2
> 
> ```
> 
> 
It's important to realize that aliased commands can be recursive, as this is where their true power lies. For example, we can make a command that sets any variable passed in to 0:
> 
> **Alias Example 3**
> 
> 
> The `set_to_zero` command takes one argument and sets its variable to 0 and is flagged as a *success* when done:
> 
> 
> 
> ```
> a set_to_zero
>  d 1
>  set_to_zero 1
>  i _dummy_
> i oryx
> i oryx
> i oryx
> p oryx
> set_to_zero oryx
> p oryx
> 
> ```
> 
> The output of this program would be
> 
> 
> 
> ```
> oryx = 3
> oryx = 0
> 
> ```
> 
> What's happening is that when `set_to_zero oryx` is run, `d 1` successfully decrements `oryx` from from 3 to 2, then `set_to_zero 1` is called, which is the same as calling `set_to_zero oryx` again. So the process repeats until `d 1` is a *failure*, stopping the recursion and increment the `_dummy_` variable so a *success* is produced.
> 
> 
> 
---
# Challenge
Write a program that can execute Prindeal code exactly as described above. Take the Prindeal code in via stdin, the command line, or as a text file. Print the Prindeal program's output to stdout or your language's closest alternative.
Alternatively, you can write a function that takes in the code as a string and prints or returns the output string.
Additionally, you can assume that:
* The input Prindeal code will only contain newlines and [printable ASCII](https://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters) and (optionally) that it ends with an empty line.
* The input code will be valid Prindeal - well formed and syntactically correct.
* Running the code will not produce any infinite loops nor any invalid references to commands that have not been defined or arguments that have not been given.
* The command names `p`, `i`, `d`, and `a` will never be aliased over. (You may ***not*** assume that variables will not have these names.)
Also, it doesn't matter if your variable values aren't truly arbitrary-precision integers as only numbers less than about 1000 will be tested. It's also alright if you language has recursion limits (such as [Python](https://docs.python.org/2/library/sys.html#sys.setrecursionlimit)) that more complex Prindeal programs might encounter as long as the test program below works.
# Test Program
Here is a large Prindeal program that builds up the operations of addition, multiplication, and exponentiation through the use of dummy variables (starting with `_` by convention) and many helper aliases:
```
#Command Definitions:
a s             #flag as a success
 i _
 d _
 d _
a f             #flag as a failure
 d _
 d _
 d _
a z             #1 = zero
 d 1
 z 1
 s
a n             #1 = one
 z 1
 i 1
 s
a move          #2 += 1, 1 = zero
 moveH 1 2
 move 1 2
 s
a moveH         #move helper
 d 1
 i 2
 f
a dupe          #2 += 1, 3 += 1, 1 = zero
 dupeH1 1 2 3
 dupe 1 2 3
 s
a dupeH1        #dupe helper
 d 1
 dupeH2 2 3
 f
a dupeH2        #dupe helper
 i 1
 i 2
 s
a copy          #2 = 1
 z 2
 copyH 1 2
 s
a copyH         #copy helper
 dupe 1 2 _copy
 move _copy 1
 s
a addTo         #1 += 2
 copy 2 _add
 #testing comments #
 move _add 1#in weird places # just because #
 s
#it's a g##d idea
###
a add           #1 = 2 + 3
 #its a good idea
 z 1
 addH 1 2 3
 s
##
#
a addH          #add helper
#this is a comment
 addTo 1 2 #as is this
 addTo 1 3
 s
a mul           #1 = 2 * 3
 mulH1 1 2
 mulH2 1 3
 s
a mulH1         #mul helper
 z 1
 copy 2 _mul
 s
a mulH2         #mul helper
 mulH3 1 2
 mulH2 1 2
 s
a mulH3         #mul helper
 d _mul
 addTo 1 2
 f
a mulBy         #1 *= 2
 mul _mulBy 1 2
 copy _mulBy 1
 s
a pow           #1 = 2^3
 powH1 1 3
 powH2 1 2
 s
a powH1         #pow helper
 n 1
 copy 2 _pow
 s
a powH2         #pow helper
 powH3 1 2
 powH2 1 2
 s
a powH3         #pow helper
 d _pow
 mulBy 1 2
 f
#Running Tests:
p A
p B
p C
n A         #A = 1
n B         #B = 1
add C A B   #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C   #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C   #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B   #d = d * B = 6 * 3 = 18
p ____
p d
d A         #A = A - 1 = 1 - 1 = 0
mulBy d A   #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B   #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C   #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B   #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C
```
(If you're playing around with this code be aware that many of the commands will fail if the same variable is given multiple times as an argument. This can easily be fixed but the resulting code is longer.)
Your Prindeal interpreter should be able to produce the exact output:
```
A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729
```
# Scoring
The shortest code in bytes wins. Tiebreaker goes to earlier submission.
*Brownie Bonus: Write a cool program in Prindeal. I implemented addition and multiplication, can you do subtraction or division?*
  
[Answer]
# Pyth, ~~162~~ 136 bytes
```
JfTmchcd\#).zKHW<ZlJI!e=T@J~+Z1=@Tk)=k0 .x=J+]h=Nm.xL@Tskd@K=NhT+]+tN0>J~Z0,=Y.x@H=eT0?qN\pps[Td\=dYb)?xGN?qN\iXHThY?YXTH_1=k1XKT:JZ=+Z3
```
[Demonstration.](https://pyth.herokuapp.com/?code=JfTmchcd%5C%23).zKHW%3CZlJ%3DG%40JZI!eG%3D%40GT)%3DT0%3DNhGI.x%7DNK0%3Dkm.xL%40Gskd%40KN%3DJ%2B%5Dhk%2B%5D%2Btk0%3EJhZ%3DZ0)E%3DeG%3DY.x%40HG0%3D%2BZ1IqN%5Ca%20XKG%3AJZ%2BZ3%3D%2BZ3)IqN%5Cpjd%5BG%5C%3DY))IqN%5Ci%20XHGhY)IqN%5CdIY%20XGH_1)E%3DT1&input=%23Command%20Definitions%3A%0Aa%20s%20%20%20%20%20%20%20%20%20%20%20%20%20%23flag%20as%20a%20success%0A%20i%20_%0A%20d%20_%0A%20d%20_%0Aa%20f%20%20%20%20%20%20%20%20%20%20%20%20%20%23flag%20as%20a%20failure%0A%20d%20_%0A%20d%20_%0A%20d%20_%0Aa%20z%20%20%20%20%20%20%20%20%20%20%20%20%20%231%20%3D%20zero%0A%20d%201%0A%20z%201%0A%20s%0Aa%20n%20%20%20%20%20%20%20%20%20%20%20%20%20%231%20%3D%20one%0A%20z%201%0A%20i%201%0A%20s%0Aa%20move%20%20%20%20%20%20%20%20%20%20%232%20%2B%3D%201%2C%201%20%3D%20zero%0A%20moveH%201%202%0A%20move%201%202%0A%20s%0Aa%20moveH%20%20%20%20%20%20%20%20%20%23move%20helper%0A%20d%201%0A%20i%202%0A%20f%0Aa%20dupe%20%20%20%20%20%20%20%20%20%20%232%20%2B%3D%201%2C%203%20%2B%3D%201%2C%201%20%3D%20zero%0A%20dupeH1%201%202%203%0A%20dupe%201%202%203%0A%20s%0Aa%20dupeH1%20%20%20%20%20%20%20%20%23dupe%20helper%0A%20d%201%0A%20dupeH2%202%203%0A%20f%0Aa%20dupeH2%20%20%20%20%20%20%20%20%23dupe%20helper%0A%20i%201%0A%20i%202%0A%20s%0Aa%20copy%20%20%20%20%20%20%20%20%20%20%232%20%3D%201%0A%20z%202%0A%20copyH%201%202%0A%20s%0Aa%20copyH%20%20%20%20%20%20%20%20%20%23copy%20helper%0A%20dupe%201%202%20_copy%0A%20move%20_copy%201%0A%20s%0Aa%20addTo%20%20%20%20%20%20%20%20%20%231%20%2B%3D%202%0A%20copy%202%20_add%0A%20move%20_add%201%0A%20s%0Aa%20add%20%20%20%20%20%20%20%20%20%20%20%231%20%3D%202%20%2B%203%0A%20z%201%0A%20addH%201%202%203%0A%20s%0Aa%20addH%20%20%20%20%20%20%20%20%20%20%23add%20helper%0A%20addTo%201%202%0A%20addTo%201%203%0A%20s%0Aa%20mul%20%20%20%20%20%20%20%20%20%20%20%231%20%3D%202%20*%203%0A%20mulH1%201%202%0A%20mulH2%201%203%0A%20s%0Aa%20mulH1%20%20%20%20%20%20%20%20%20%23mul%20helper%0A%20z%201%0A%20copy%202%20_mul%0A%20s%0Aa%20mulH2%20%20%20%20%20%20%20%20%20%23mul%20helper%0A%20mulH3%201%202%0A%20mulH2%201%202%0A%20s%0Aa%20mulH3%20%20%20%20%20%20%20%20%20%23mul%20helper%0A%20d%20_mul%0A%20addTo%201%202%0A%20f%0Aa%20mulBy%20%20%20%20%20%20%20%20%20%231%20*%3D%202%0A%20mul%20_mulBy%201%202%0A%20copy%20_mulBy%201%0A%20s%0Aa%20pow%20%20%20%20%20%20%20%20%20%20%20%231%20%3D%202%5E3%0A%20powH1%201%203%0A%20powH2%201%202%0A%20s%0Aa%20powH1%20%20%20%20%20%20%20%20%20%23pow%20helper%0A%20n%201%0A%20copy%202%20_pow%0A%20s%0Aa%20powH2%20%20%20%20%20%20%20%20%20%23pow%20helper%0A%20powH3%201%202%0A%20powH2%201%202%0A%20s%0Aa%20powH3%20%20%20%20%20%20%20%20%20%23pow%20helper%0A%20d%20_pow%0A%20mulBy%201%202%0A%20f%0A%0A%23Running%20Tests%3A%0Ap%20A%0Ap%20B%0Ap%20C%0An%20A%20%20%20%20%20%20%20%20%20%23A%20%3D%201%0An%20B%20%20%20%20%20%20%20%20%20%23B%20%3D%201%0Aadd%20C%20A%20B%20%20%20%23C%20%3D%20A%20%2B%20B%20%3D%201%20%2B%201%20%3D%202%0Ap%20____%0Ap%20A%0Ap%20B%0Ap%20C%0Aadd%20B%20A%20C%20%20%20%23B%20%3D%20A%20%2B%20C%20%3D%201%20%2B%202%20%3D%203%0Ap%20____%0Ap%20A%0Ap%20B%0Ap%20C%0Amul%20d%20B%20C%20%20%20%23d%20%3D%20B%20*%20C%20%3D%203%20*%202%20%3D%206%0Ap%20____%0Ap%20d%0AmulBy%20d%20B%20%20%20%23d%20%3D%20d%20*%20B%20%3D%206%20*%203%20%3D%2018%0Ap%20____%0Ap%20d%0Ad%20A%20%20%20%20%20%20%20%20%20%23A%20%3D%20A%20-%201%20%3D%201%20-%201%20%3D%200%0AmulBy%20d%20A%20%20%20%23d%20%3D%20d%20*%20A%20%3D%2018%20*%200%20%3D%200%0Ap%20____%0Ap%20d%0Apow%20A%20C%20B%20%20%20%23A%20%3D%20C%20%5E%20B%20%3D%202%20%5E%203%20%3D%208%0Ap%20____%0Ap%20A%0Ap%20B%0Ap%20C%0Apow%20A%20B%20C%20%20%20%23A%20%3D%20B%20%5E%20C%20%3D%203%20%5E%202%20%3D%209%0Ap%20____%0Ap%20A%0Ap%20B%0Ap%20C%0Apow%20C%20A%20B%20%20%20%23C%20%3D%20A%20%5E%20B%20%3D%209%20%5E%203%20%3D%20729%0Ap%20____%0Ap%20A%0Ap%20B%0Ap%20C&debug=0)
Golfed out 26 characters by inlining variables and changing from `I` and `E` based control flow to `?` and `.x` based control flow.
For the first time ever, I ran out of variables in Pyth. Every single variable in Pyth (`bdkGHNTY` and `JK`) was in use, and I wanted to use `b` as a newline. Fortunaely, I was able to use `N` to mean two completely different things in different parts of the program, and so it still works.
### Ungolfed (run with -m):
```
JfTmchcd\#).z
KH
W<ZlJ
  I!e=T@J~+Z1
    =@Tk)
  =k0
     .x
      =J+]h=Nm.xL@Tskd@K=NhT+]+tN0>J~Z0
      ,
        =Y.x@H=eT0
        ?qN\p
          ps[Td\=dYb)
          ?xGN
            ?qN\i
              XHThY
              ?Y
                XTH_1
                =k1
            XKT:JZ=+Z3
```
[Answer]
# Python 2, ~~600~~ ~~584~~ ~~397~~ 373 bytes
This is my own golfed reference solution. Anyone is welcome to improve it or follow its logic in their own answer as long as attribution is given.
The neat part about it is that no recursion is done, so it will never have problems with Python's recursion limit. For example, [Sp's](https://codegolf.stackexchange.com/a/54559/26997) Countup Prindeal program can run indefinitely.
```
p=filter(len,[l.split('#')[0].split()for l in input().split('\n')]);m={};v={};i=0
while i<len(p):
 s=p[i]
 if'('in`s`:s=s[f]
 n,f=s[0],0
 if n in m:a,b,c=([s[int(g)]if g.isdigit()else g for g in t]for t in m[n]);p=[a,(b,c)]+p[i+1:];i=0;continue
 s=s[1]
 q=v.get(s,0)
 if'd'>n:m[s]=p[i+1:i+4];i+=3
 elif'i'<n:print s,'=',q
 elif'd'<n:v[s]=q+1
 elif q:v[s]-=1
 else:f=1
 i+=1
```
It's a program that takes in the quoted program string with newlines escaped, e.g.  
`'p _MyVariable_321\np screaming_hairy_armadillo'`.
I took various golfing cues from [Sp's](https://codegolf.stackexchange.com/a/54559/26997) and [Pietu's](https://codegolf.stackexchange.com/a/54547/26997) answers. Thanks guys :)
[Answer]
# Python 3, ~~345~~ ~~336~~ ~~335~~ 328 bytes
```
a=0
A={}
V={}
def f(l):
 if l[0]in"d p i":c,u=l;U=V[u]=V.get(u,0)+"pi".find(c);S=U<0;V[u]+=S;c<"p"or print(u,"=",U)
 else:d=lambda q:[w.isdigit()and l[int(w)]or w for w in A[l[0]][q]];S=f(d(1+f(d(0))))
 return S
for z in open("P"):
 l=z.split("#")[0].split()
 if"a "==z[:2]:a,s,*x=3,l[1]
 elif l*a:x+=l,;a-=1;A[s]=x
 elif l:f(l)
```
*(-6 bytes thanks to @orlp)*
Still golfing. Assumes the program is stored in a file named `P`.
Putting the calls to `f` inside the lambda `d` would save a few bytes, but it would make the last test case hit max recursion depth.
## Some Prindeal programs
### The useless subtraction program
Here is [a useless subtraction program](http://pastebin.com/8u4hXDdd). It's useless because, even though it subtracts properly, it doesn't return success/failure accordingly.
The output should be:
```
a = 15
b = 6
__________ = 0
a = 9
b = 6
```
### Countup
```
a helper
 p 1
 countup 1
 i success
 
a countup
 i 1
 helper 1
 d failure
 
countup n
```
Counts upwards and prints `n` forever. Could possibly work as a test for interpreter speed (beware the long tracebacks on keyboard interrupt).
[Answer]
# JavaScript (ES6), 273 ~~258~~
**Edit** Fixed bugs and added a real test suite.
Not counting leading spaces and newlines.
Surely can be golfed a little more.
Too tired to write an explanation now, I think it's a good example of using closures to keep alive temporary values (parameters).
Test running the snippet on any EcmaScript 6 compliant browser (notably not Chrome not MSIE. I tested on Firefox, Safari 9 could go)
```
F=p=>(
  p=p.match(/^[^#\n]+/gm).filter(r=>r.trim(o='',v=[])),
  s={
    '':_=>1,
    p:a=>o+=a+` = ${v[a]||0}\n`,
    i:a=>v[a]=-~v[a],
    d:a=>v[a]&&v[a]--,
    a:(n,j)=>s[n]=(u,t,a)=>x(p[!x(p[j+1],0,a,1)+j+2],0,a,1)
  },
  p.map(x=(r,i,w,l,a=r.split(/ +/).slice(l).map(x=>-x?w[x]:x))=>s[a[0]](a[1],i,a)),
  o
)
// TEST
$('#O tr').each(function() {
  var $cells = $(this).find('td')
  var prg = $cells.eq(0).text()
  console.log(prg)
  var output = F(prg)
  $cells.eq(1).text(output)
})
```
```
#O td { vertical-align:top; white-space: pre; border: 1px solid #888; font-family:monospace }
```
```
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table>
<tr><th>Program</th><th>Outpout</th></tr>
<tbody id=O>  
<tr><td>p _MyVariable_321
p screaming_hairy_armadillo</td><td></td></tr>
<tr><td>i alpaca
p alpaca
i alpaca
p alpaca</td><td></td></tr>
<tr><td>i malamute
p malamute
d malamute    #success
p malamute
d malamute    #failure
p malamute
d akita       #failure
p akita</td><td></td></tr>
<tr><td>a increment_frog_twice
 i frog
 i frog
 d frog
p frog
increment_frog_twice
p frog</td><td></td></tr>
<tr><td>a increment_twice
 i 1
 i 1
 d 1 #never reached
a increment_both_twice
 increment_twice 1
 increment_twice 2
 d 1 #never reached
increment_both_twice platypus duck
p platypus
p duck</td><td></td></tr>
<tr><td>a set_to_zero
 d 1
 set_to_zero 1
 i _dummy_
i oryx
i oryx
i oryx
p oryx
set_to_zero oryx
p oryx</td><td></td></tr>
<tr><td>#Command Definitions:
a s             #flag as a success
 i _
 d _
 d _
a f             #flag as a failure
 d _
 d _
 d _
a z             #1 = zero
 d 1
 z 1
 s
a n             #1 = one
 z 1
 i 1
 s
a move          #2 += 1, 1 = zero
 moveH 1 2
 move 1 2
 s
a moveH         #move helper
 d 1
 i 2
 f
a dupe          #2 += 1, 3 += 1, 1 = zero
 dupeH1 1 2 3
 dupe 1 2 3
 s
a dupeH1        #dupe helper
 d 1
 dupeH2 2 3
 f
a dupeH2        #dupe helper
 i 1
 i 2
 s
a copy          #2 = 1
 z 2
 copyH 1 2
 s
a copyH         #copy helper
 dupe 1 2 _copy
 move _copy 1
 s
a addTo         #1 += 2
 copy 2 _add
 #testing comments #
 move _add 1#in weird places # just because #
 s
#it's a g##d idea
###
a add           #1 = 2 + 3
 #its a good idea
 z 1
 addH 1 2 3
 s
##
#
a addH          #add helper
#this is a comment
 addTo 1 2 #as is this
 addTo 1 3
 s
a mul           #1 = 2 * 3
 mulH1 1 2
 mulH2 1 3
 s
a mulH1         #mul helper
 z 1
 copy 2 _mul
 s
a mulH2         #mul helper
 mulH3 1 2
 mulH2 1 2
 s
a mulH3         #mul helper
 d _mul
 addTo 1 2
 f
a mulBy         #1 *= 2
 mul _mulBy 1 2
 copy _mulBy 1
 s
a pow           #1 = 2^3
 powH1 1 3
 powH2 1 2
 s
a powH1         #pow helper
 n 1
 copy 2 _pow
 s
a powH2         #pow helper
 powH3 1 2
 powH2 1 2
 s
a powH3         #pow helper
 d _pow
 mulBy 1 2
 f
#Running Tests:
p A
p B
p C
n A         #A = 1
n B         #B = 1
add C A B   #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C   #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C   #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B   #d = d * B = 6 * 3 = 18
p ____
p d
d A         #A = A - 1 = 1 - 1 = 0
mulBy d A   #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B   #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C   #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B   #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C  
</td><td></td></tr>
</tbody>
</table>
```
[Answer]
# C#6, 653 bytes
Here's my entry, amidst a sea of Python...
```
class P{string[]l;string r="";Dictionary<string,int>v=new Dictionary<string,int>();Dictionary<string,int>s=new Dictionary<string,int>();public string R(string t){l=t.Split('\n');for(int i=0;i<l.Length;i++){var z=l[i].Split(' ');if(z[0]=="a"){s.Add(z[1],i);i+=3;}else E(i, null);}return r;}bool E(int n,string[]p){var z=l[n].Split(' ');var a=z.Skip(1).Select(x=>Char.IsDigit(x[0])?p[int.Parse(x)-1]:x).ToArray();if(a.Length>0&&!v.ContainsKey(a[0]))v[a[0]]=0;if (z[0]=="p")r+=$"{a[0]} = {v[a[0]]}\n";else if(z[0]=="i")v[a[0]]++;else if(z[0]=="d")if(v[a[0]]>0)v[a[0]]--;else return false;else{var y=s[z[0]];return E(y+1,a)?E(y+2,a):E(y+3,a);}return true;}}
```
Expanded and commented:
```
class Prindeal
{
    string[] lines;
    string result = "";
    Dictionary<string, int> variables = new Dictionary<string, int>();
    Dictionary<string, int> statements = new Dictionary<string, int>();
    public string Run(string text)
    {
        lines = text.Split('\n');
        for (int i = 0; i < lines.Length; i++)
        {
            // Split on spaces to get the statement and any arguments
            var z = lines[i].Split(' ');
            // Are we defining a new statement?
            if (z[0] == "a")
            {
                // Add to the statements dictionary, step over definition statements
                statements.Add(z[1], i);
                i += 3;
            }
            else
            {
                // Execute the statement
                Execute(i, null);
            }
        }
        return result;
    }
    bool Execute(int lineNumber, string[] parameters)
    {
        // Split on spaces to get the statement and any arguments
        var z = lines[lineNumber].Split(' ');
        // Parse the arguments - if it's a number, get the corresponding 
        // parameter from the calling statement
        var arguments = z.Skip(1).Select(
            x => Char.IsDigit(x[0]) ? 
            parameters[int.Parse(x) - 1] : 
            x)
            .ToArray();
        // If the first argument isn't already in the variables dict, add it
        if (arguments.Length > 0 && !variables.ContainsKey(arguments[0])) variables[arguments[0]] = 0;
        // Print statement, using string interpolation
        if (z[0] == "p")
            result += $"{arguments[0]} = {variables[arguments[0]]}\n";
        // Increment statement
        else if (z[0] == "i")
            variables[arguments[0]]++;
        // Decrement statement
        else if (z[0] == "d")
            if (variables[arguments[0]] > 0)
                variables[arguments[0]]--;
            else
                return false;
        else
        {
            // Get the line number to jump to
            var y = statements[z[0]];
            // Execute A ? B : C
            return Execute(y + 1, arguments) ? Execute(y + 2, arguments) : Execute(y + 3, arguments);
        }
        // If we reach this point, it's from a 'p', 'i' or 'd' statement which has succeeded
        return true;
    }
}
```
To use it, simply instantiate the class and call the `R()` method, for example:
```
string prindealText = new StreamReader("prindeal.txt").ReadToEnd();
Console.WriteLine(new P().R(prindealText));
```
[Answer]
# Common Lisp, ~~758~~ ~~646~~ 619
```
(progn(set-macro-character #\#(get-macro-character #\;))(setf(readtable-case *readtable*):invert)(#3=defun v(s)(if(boundp s)(eval s)0))(#3# i(s)(set s(1+ (v s))))(#3# d(s)(and(plusp(v s))(set s(1-(v s)))))(#3# p(s)(format t"~A = ~A~%"s(v s)))(defmacro a(n . p)`(#3#,(cadr n)(&rest g)(if,@p)))(#3# k(s)(typecase s(integer`(nth,(1- s)g))(symbol `',s)(t(list*(car s)(mapcar 'k(cdr s))))))(#3# r()(prog(l p q)$(setf p()l(make-string-input-stream(or(read-line()()())(return))))@(when(setf p(read l()()))(push p q)(go @))(if q(return(k(reverse q)))(go $))))(do ((x(r)(r)))((not x))(eval(if(eq(car x)'a)`(,@x,(r),(r),(r))x))))
```
Put this in `file.lisp` and call for example `sbcl --script file.lisp`; input is read from the standard input stream. 
This version parses a *superset* of Prindeal: without not much difficulties, you can access all Common Lisp from a Prindeal source. I consider this a *feature* of the intepreter.
### Commented version
```
;; copy-readtable is only used during development, so that I do not 
;; mess with my running environment. The real code starts with the
;; progn below, which is superfluous of course inside a let.
(let ((*readtable* (copy-readtable)))
  ;; I use PROGN in the golfed version so that I can have the whole
  ;; program as a unique tree. This allows me to define reader 
  ;; variables like #3=defun in order to gain a few bytes by writing
  ;; #3# instead of defun. Reader variables are removed in
  ;; this human-friendly version.
  (progn
    ;; Let # point to the same reader function as ;
    ;; Of course, ; is still usable as a comment delimiter
    (set-macro-character #\#
                         (get-macro-character #\;))
    ;; :invert does what is necessary to enable case-sensitive reading
    ;; and printing of symbols
    (setf (readtable-case *readtable*) :invert)
    ;; value of symbol, or zero
    (defun v(s)(if(boundp s)(eval s)0))
    ;; increment
    (defun i(s)(set s(1+ (v s))))
    ;; decrement
    (defun d(s)(and(plusp(v s))(set s(1-(v s)))))
    ;; print
    (defun p(s)(format t"~A = ~A~%"s(v s)))
    ;; alias: wrap an "if" inside a "defun".
    ;; YES, that means you can redefine ANY lisp function with "a" !
    ;; A safer version would properly intern symbols in a dedicated package.
    ;;
    ;; Notice the G variable.  We take advantage of the "unhygienic"
    ;; (what a bad adjective) nature of macros to create a context
    ;; where G is bound to the argument list. The same G is referenced
    ;; implicitely later.
    (defmacro a(n . p)`(defun,(cadr n)(&rest g)(if,@p)))
    ;; Canonicalize expressions:
    ;;
    ;; - if s is a symbol, return s quoted. All functions manipulate
    ;; symbols in order to allow the undeclared use of variables. With
    ;; symbols, we can check for boundness.
    ;;
    ;; - if s is an integer, then we are inside an alias definition. The
    ;; integer is replaced by an access to the s'th element of the
    ;; implicit argument list G using (nth (1- s) g). G will be bound
    ;; when the expressions is injected in the defun corresponding to
    ;; the alias, or else an error will be signaled: either because G
    ;; is unbound, or because you defined a variable named G which is
    ;; by construction not a list. Since we do not sanitize properly
    ;; the input, you could bind G globally to a list, but that would be
    ;; nasty.
    ;; 
    ;; - Finally, if s is a list, apply k to all but the first
    ;; elements of s.  The first element is a symbol but we do not
    ;; need to quote it because we want to call the function
    ;; associated with the symbol. Due to the Lisp-2-ness
    ;; of Common Lisp, functions and variables can coexist
    ;; with the same name.
    ;;
    (defun k(s)(typecase s
                 (integer`(nth,(1- s)g))
                 (symbol`',s)
                 (t(list*(car s)(mapcar #'k(cdr s))))))
    ;; Reader function
    (defun r()
      (prog (l ; current line, as an input-stream reading a string
             p ; current read form
             q ; whole line and return value, as a list
             )
         ;; PROG includes an implicit TAGBODY. Below, $ and @ are
         ;; labels for GO statements (gotos).
       $ (setf
          ;; emtpy p
          p ()
          ;; Read a whole line and if we do not fail, build an input
          ;; stream to read from it.
          l (make-string-input-stream
             (or (read-line()()()) ;; try to read a line,
                 (return)          ;; but return from prog if we reach
                                   ;; the end of file.
                 )))
       @ (when (setf p (read l()()))
           ;; Read a lisp expression, put it in p and if p is not nil
           ;; push it into q.  A nil could happen at the end of the
           ;; line or if someone (you know who) inserted an empty list
           ;; in the file being read.
           ;; 
           ;; Thanks to the readtable which now handles comments
           ;; and spaces for us, nothing needs to be done here to
           ;; preprocess the input.
           (push p q) (go @))
         ;; If we read an empty line, q can be nil. In this case, go
         ;; back to $ and read another line. If q is not nil, reverse
         ;; it (we pushed, remember), canonicalize it and return the
         ;; result.
         (if q (return(k(reverse q))) (go $)))
      )
    ;; Read/eval loop.  When reading "(a name)", we read the three
    ;; next lines and append them to the first so that it builds a
    ;; call the the alias definition macro a. Otherwise, just eval x.
    (do((x(r)(r))((not x))
      (eval (if (eq(car x'a))
                `(,@x,(r),(r),(r))
                x)))))
```
# Example
```
~$ sbcl --script file.lisp < testfile
A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729
```
If we replace `eval` by `print` in the read/eval loop, then we can see what is being evaluated:
```
(a 's (i '_) (d '_) (d '_)) 
(a 'f (d '_) (d '_) (d '_)) 
(a 'z (d (nth 0 g)) (z (nth 0 g)) (s)) 
(a 'n (z (nth 0 g)) (i (nth 0 g)) (s)) 
(a 'move (moveH (nth 0 g) (nth 1 g)) (move (nth 0 g) (nth 1 g)) (s)) 
(a 'moveH (d (nth 0 g)) (i (nth 1 g)) (f)) 
(a 'dupe (dupeH1 (nth 0 g) (nth 1 g) (nth 2 g))
   (dupe (nth 0 g) (nth 1 g) (nth 2 g)) (s)) 
(a 'dupeH1 (d (nth 0 g)) (dupeH2 (nth 1 g) (nth 2 g)) (f)) 
(a 'dupeH2 (i (nth 0 g)) (i (nth 1 g)) (s)) 
(a 'copy (z (nth 1 g)) (copyH (nth 0 g) (nth 1 g)) (s)) 
(a 'copyH (dupe (nth 0 g) (nth 1 g) '_copy) (move '_copy (nth 0 g)) (s)) 
(a 'addTo (copy (nth 1 g) '_add) (move '_add (nth 0 g)) (s)) 
(a 'add (z (nth 0 g)) (addH (nth 0 g) (nth 1 g) (nth 2 g)) (s)) 
(a 'addH (addTo (nth 0 g) (nth 1 g)) (addTo (nth 0 g) (nth 2 g)) (s)) 
(a 'mul (mulH1 (nth 0 g) (nth 1 g)) (mulH2 (nth 0 g) (nth 2 g)) (s)) 
(a 'mulH1 (z (nth 0 g)) (copy (nth 1 g) '_mul) (s)) 
(a 'mulH2 (mulH3 (nth 0 g) (nth 1 g)) (mulH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'mulH3 (d '_mul) (addTo (nth 0 g) (nth 1 g)) (f)) 
(a 'mulBy (mul '_mulBy (nth 0 g) (nth 1 g)) (copy '_mulBy (nth 0 g)) (s)) 
(a 'pow (powH1 (nth 0 g) (nth 2 g)) (powH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'powH1 (n (nth 0 g)) (copy (nth 1 g) '_pow) (s)) 
(a 'powH2 (powH3 (nth 0 g) (nth 1 g)) (powH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'powH3 (d '_pow) (mulBy (nth 0 g) (nth 1 g)) (f)) 
(p 'A) 
(p 'B) 
(p 'C) 
(n 'A) 
(n 'B) 
(add 'C 'A 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(add 'B 'A 'C) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(mul 'd 'B 'C) 
(p '____) 
(p 'd) 
(mulBy 'd 'B) 
(p '____) 
(p 'd) 
(d 'A) 
(mulBy 'd 'A) 
(p '____) 
(p 'd) 
(pow 'A 'C 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(pow 'A 'B 'C) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(pow 'C 'A 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C)
```
### Macroexpansion
If we pick the following alias definition:
```
(a 'powH2 (powH3 (nth 0 g) (nth 1 g)) (powH2 (nth 0 g) (nth 1 g)) (s))
```
... we can see references to a variable named `g` which is nowhere to be found in the lexical scope. But after macroexpansion, here is the actual code being evaluated: 
```
(defun powH2 (&rest g)
  (if (powH3 (nth 0 g) (nth 1 g))
      (powH2 (nth 0 g) (nth 1 g))
      (s))) 
```
Now, `g` refers to the argument list of the function being defined.
[Answer]
# Python 2, 486 bytes
**This is the reference solution which I golfed more (currently -98 bytes).**
```
import sys;sys.setrecursionlimit(2000)
def r(s):
 n=s[0]
 if n in A:f=lambda i:r([s[int(t)]if'0'<t[0]<':'else t for t in A[n][i]]);return f(1+(f(0)or 0))
 k=s[1]
 if'i'<n:print k,'=',V.get(k,0)
 elif'd'<n:V[k]=-~V[k]if k in V else 1
 elif'a'<n:
    if~-(k in V)or V[k]<1:return 1
    V[k]-=1
 else:A[k]=s[2:]
A={};V={};c=filter(bool,([l,l[:l.find('#')]]['#'in l]for l in input().split('\n')))
while c:
 s=c[0].split();c=c[1:]
 if'a'!=s[0]:r(s)
 else:r(['a',s[1]]+map(str.split,c[:3]));c=c[3:]
```
Changes (that I recall):
* automatic boolean-integer conversion (`[l,l[:l.find('#')]]['#'in l]`).
* set or increment in one statement (`V[k]=-~V[k]if k in V else 1`)
* more aliases to longer expressions (`k=s[1]`)
* no counter in the main loop, clearing the input list instead
* `print` automatically adding spaces (`print k,'=',V.get(k,0)`)
* checking digits 1-9 (`'0'<t[0]<':'`)
* flipping the return values of `r` around to save `return`s
* removing repetition of slicing and splitting (`map(str.split,c[:3]))`)
[Answer]
# C++, 752 bytes
**Thanks to @ceilingcat for some very nice pieces of golfing - now even shorter (yet again!)**
This one is C++ - as idiomatic as I could make it.  
That means making it more C++-ish and less C-ish.  
That also means it is bigger than the equivalent C program.  
I think C++ rivals Java for the most verbose standard library.  
```
#import<bits/stdc++.h>
#define a first
#define c(s);else if(B==#s)
#define e(n)(n=regex_replace(n,regex{"^ *(.*?) *(#.*)?$"},"$1")).size()
#define f(n)for(;getline(cin,n),!e(n););
#define i p.top().a
#define j p.size()
#define k n[o[0]]
#define U;using
U namespace std;U g=string;U h=istream_iterator<g>U s=vector<g>;map<g,s>m;map<g,int>n;stack<pair<pair<int,s>,s>>p;main(int v){for(g X,Y,Z;getline(cin,X);)if(e(X))for(p.push({{0,{X,"",""}},{}});bool z=j;){g B;s C,o;copy(h(stringstream(i.second[i.a])>>B),h(),back_inserter(C));for(g x:C)o.push_back((v=atoi(&x[0]))>0?p.top().second[v-1]:x);if(0)c()c(p)cout<<o[0]+" = "<<k<<'\n'c(i)k++c(d)k-=z=k c(a){f(X)f(Y)f(Z)m[o[0]]={X,Y,Z};}else{p.push({{0,m[B]},o});continue;}while(j&&i.a)p.pop();j?i.a-=~!z:0;}}
```
[Try it online!](https://tio.run/##fVLRctowEHzPVygmk@iCcMlrZJsZ8g3MkFDKCCEbYVvWWDKleNxfpwdOSZqHas723WrndLuWtHaUSXk6DXRpq9pHa@3dN@c3cjgMt8nNYKNSbRQRJNW189daUgdcFU4RndJpHA8cXPcUNUBNXKtMHVa1soWQCLFL3QY/yCMNHyeAn0H4CJO7oGPB3VMAEDp9VPSjT4p90qqmPFO@QIBKbZgBdns@gAO/EjWxoa8shVBcsR1iX/rlxCyqxXi5vCIz3jhtspsZMaJUzuKgBLXzGcli52vcwnQba8yVKFfaq1r4qo6yZEZcvFeyL3gpbJQxl5TvmTY@Mdx5IfPICl33L0SRg5FY5GlDESB7aM8aMzJnr@ztH6lzFInuKjqHiw82tI3b0rYds3bOggCj61jbdcDXVVWQY7zj0GZkyh15YRWXlf1Ft7QX0kugOnRKVmaz0KFYQpJMgW0psDVOutLGqRol0hcA3g91eH6B6nLs6kyhdB@jAZreH9BHgGQ8@ev8e9v96Gn5fACOc49BUgwLsmp8FJ2dHwYkJkEU5VH08N08SKohHw4l3UA@io9xjtdKoB8oOKWv@LxB2f@xuL3Y0/HufOfaT06Ui@myYxV6gOd7bRrFu59bXSi6u79HkYDc84B8N8FqFP@@PT6PededTpqUohBl49WN/Ug315TgGrhGSuXcfxip0EVTf@khcu0F6dcnxgX@Aw "C++ (gcc) – Try It Online")
Below is the original. If anyone can think of a way to make it more idiomatic and shorter at the same time please let me know.
```
#include <array>
#include <iostream>
#include <map>
#include <regex>
#include <sstream>
#include <stack>
typedef std::vector<std::string> List;
typedef std::pair<std::string, List> Statement;
typedef std::array<std::string, 3> Alias;
typedef std::pair<long, Alias> IndexedAlias;
typedef std::pair<IndexedAlias, List> Item;
std::map<std::string, Alias> aliases;
std::map<std::string, long> variables;
std::stack<Item> stack;
std::regex re("^ *(.*?) *(#.*)?$");
int main()
{
    std::string line, line1, line2, line3;
    while (std::getline(std::cin, line)) // control-Z to exit
    {
        line = std::regex_replace(line, re, "$1");
        if (line.empty()) continue;
        stack.push(Item{ { 0, { { line, "", "" } } }, {} });
        bool flag;
        while (!stack.empty())
        {
            Statement statement;
            std::istringstream ss(stack.top().first.second[stack.top().first.first]);
            ss >> statement.first;
            std::copy(std::istream_iterator<std::string>(ss), std::istream_iterator<std::string>(), std::back_inserter(statement.second));
            List arguments;
            std::transform(std::begin(statement.second), std::end(statement.second), std::back_inserter(arguments),
                [](std::string arg){ int i = atoi(arg.c_str()); return i > 0 ? stack.top().second[i - 1] : arg; });
            flag = true;
            if (statement.first == "")
                ;
            else if (statement.first == "p")
                std::cout << arguments[0] << " = " << variables[arguments[0]] << std::endl;
            else if (statement.first == "i")
                variables[arguments[0]]++;
            else if (statement.first == "d")
                variables[arguments[0]] -= (flag = variables[arguments[0]]);
            else if (statement.first == "a")
            {
                do { std::getline(std::cin, line1); line1 = std::regex_replace(line1, re, "$1"); } while (line1.empty());
                do { std::getline(std::cin, line2); line2 = std::regex_replace(line2, re, "$1"); } while (line2.empty());
                do { std::getline(std::cin, line3); line3 = std::regex_replace(line3, re, "$1"); } while (line3.empty());
                aliases.insert(std::make_pair(arguments[0], Alias{ { line1, line2, line3 } }));
            }
            else
            {
                stack.push(Item{ { 0, aliases[statement.first] }, arguments });
                continue;
            }
            while (!stack.empty() && stack.top().first.first) stack.pop();
            if (!stack.empty()) stack.top().first.first += 1 + !flag;
        }
    }
    std::cout << "-- Variables --" << std::endl;
    std::transform(std::begin(variables), std::end(variables), std::ostream_iterator<std::string>(std::cout, "\n"),
        [](std::map<std::string, long>::value_type pair){ std::ostringstream ss; ss << pair.first << " = " << pair.second; return ss.str(); });
    std::cout << "-- Aliases --" << std::endl;
    std::transform(std::begin(aliases), std::end(aliases), std::ostream_iterator<std::string>(std::cout, "\n"),
        [](std::map<std::string, Alias>::value_type pair){ std::ostringstream ss; ss << pair.first << " = [1]:" << pair.second[0] << " [2]:" << pair.second[1] << " [3]:" << pair.second[1]; return ss.str(); });
    std::cout << "---------------" << std::endl;
    return 0;
}
```
[Answer]
# [Uiua](https://uiua.org) (tentative), 468 characters
**Note that due to a bug in the interpreter, there is a spurious error preventing proper execution. Once the bug is fixed, I will update this answer to reflect that. Until then, I'm leaving the (tentative) in the heading.**
```
p←⊂↯→(⊟∶□0□,)¬/+≡(|2≅!)→(↯→∶⧻).≡⊢.→(.!⊡1)!→→(⊡2)⊡1,∶
;∧(|2!⊡∶(⊂□1⊟□&p/+∺(×≅→∶∷!⊢∶⊡1.),∶&pf⊂∶" = ",p)_(⊂□1⊟□∺([∶∷□]+→(∶→≅.)!⊡1∶!⊢.)p)_(⊂⊟→□→≡(⍜'⊡1⍜!'↥0)/×≡(≥0!⊡1).∺([∶∷□]-∶→(∶→≅.)!⊡1∶!⊢.)p)_(|2↬2∶□/(⊂⊂∶@ ∷!)⊡-∶2!⊢,∶↬2∶□/(⊂⊂∶@ ∷!)⊢,∶→→;∺(|2⍜'↘1∺(|2□⍣(!⊡parse)(→;;)!)⊜□≠,@ !)→,↘1⊡∶!⊡2,⊗∶≡⊢!⊡2,⊢,)⊗∶["p""i""d"]!⊢,∶⊜□≠@ .!∶)⊂{1[{"1"0}]}□→(▽¬∵(|1↥=@ ∶=@a.⊢!).)≡(⍜'↘1(∵⍜!(↘1))⍜⊢(⍜!(↘2)))⊜·▽∵(4;).+1⇡÷4⧻.▽∵(|1↥=@ ⊢∶≅"a ".↙2!)..▽↧1∵⧻.∵⍜!(⇌▽\↥×≠@\r∶≠@ ..⇌▽=0\+=@#.)⊜□≠@\n.&ru@\00
```
This is my first Uiua program of more than a couple dozen characters, so I'm sure there's lots of places I could golf it more effectively. It was a good opportunity to practice the language, though :)
## Non-minified version
```
# preexec state_alias_array command -> state_array command_arg alias_array
preexec ← (
  ⊡1,∶ # put state on top
  !→→(⊡2) # get aliases (for later)
  →(.!⊡1) # get variable name (put two copies on stack for later)
  ≡⊢. # get list of current variable names 
  ¬/+≡(|2 ≅!) →(↯→∶⧻). # test if we need to add a new variable
  ⊂↯→(⊟∶□0□,) # add variable if necessary
)
# exec<T> state_alias_array command -> state_alias_array
execp ← (
  preexec
  &pf⊂∶" = ", # print "varName = "
  &p/+∺(×≅→∶∷!⊢∶⊡1.),∶ # print "varValue\n"
  ⊂□1⊟□
)
execi ← (
  preexec
  ∺([∶∷□]+→(∶→≅.)!⊡1∶!⊢.) # increment the relevant variable
  ⊂□1⊟□ # reform state_alias_array
)
execd ← (
  preexec
  ∺([∶∷□]-∶→(∶→≅.)!⊡1∶!⊢.) # decrement the relevant variable
  /×≡(≥0!⊡1). # check if <0
  →≡(⍜'⊡1⍜!'↥0) # reset back up to 0
  ⊂⊟→□ # reform state_alias_array
)
execcall ← (|2
  ⊢, # get alias name
  ⊗∶≡⊢!⊡2, # get index of call
  ↘1⊡∶!⊡2, # get subcommands
  →,
  ∺(|2
    ⊜□≠,@ ! # split box"thing 1 2" into [box"thing" box"1" box"2"]
    ⍜'↘1∺(|2 □⍣(!⊡parse)(→;;)!) # turn the 1 and 2 into first and second arguments
  )
  →→; # original call no longer necessary
  □/(⊂⊂∶@ ∷!)⊢,∶ # join [box"i" box"dog"] into box"i dog"
  ↬2∶ # call first
  ⊡-∶2!⊢,∶ # select second or third based on success
  □/(⊂⊂∶@ ∷!) # join [box"i" box"dog"] into box"i dog"
  ↬2∶
)
execline ← (|2
  ∶⊜□≠@ .!∶ # split command
  !⊡∶execp_execi_execd_execcall⊗∶["p""i""d"]!⊢, # dispatch correct command
)
&ru@\00
⊜□≠@\n. # split lines
∵⍜!(
  ▽=0\+=@#. # remove comments
  ⇌▽\↥×≠@\r∶≠@ ..⇌ # remove trailing whitespace
)
▽↧1∵⧻. # remove blank lines
▽∵(|1 ↥=@ ⊢∶≅"a ".↙2!).. # get alias definitions
⊜· ▽∵(4;).+1⇡÷4⧻. # separate out each alias
≡(
  ⍜⊢(⍜!(↘2)) # remove alias decl
  ⍜'↘1(∵⍜!(↘1)) # remove indentation
)
→(▽¬∵(|1 ↥=@ ∶=@a.⊢!).) # remove alias definitions from command list
⊂{1 [{"123illegalvariable" 0}]}□ # group last success, state array and alias array
∧execline # exec all instructions
;
```
[Answer]
# Python 3, 1322 bytes
**Golfed:**
```
import re,sys;sys.setrecursionlimit(2000);F,L=filter,list
class P:
 N,O,F=0,{},{}
 def __init__(S,c):
  S.B,S.E={"p":S.P,"i":S.I,"d":S.D,"a":S.L},dict(enumerate(F(None,[i.split('#')[0].rstrip()for i in c.splitlines()])))
  while S.N in S.E:S.X(S.E[S.N])
 def V(S, v, y, z=0):
  if re.match("[\w_][\d\w_]*",v):
   if not v in y:
    if z is not None:y[v]=z
    else:return False
   return True
  return False
 def A(S):S.N+=1
 def P(S,v):
  if S.V(v,S.O):print("{0} = {1}".format(v, S.O[v]));return True
  return False
 def I(S,v):
  if S.V(v, S.O):S.O[v]+=1;return True
  return False
 def D(S,v):
  if S.V(v,S.O)and S.O[v]>0:S.O[v]-=1;return True
  return False
 def L(S,v):
  e=[]
  if S.V(v,S.F,e):
   for i in range(3):S.A();e.append(S.E[S.N].lstrip())
   return True
  return False
 def C(S,c,v):
  def R(Z,v):
   for i in re.findall("\s(\d+)", Z):Z=Z.replace(" %s"%i," %s"%v[int(i)-1])
   return Z
  Q,m,f=map(lambda l:R(l,v),S.F[c])
  if S.X(Q,False):return S.X(m,False)
  return S.X(f,False)
 def X(S,Z,C=True):
  u=re.match("\s?([\w_][\d\w_]*)\s?([\w_][\d\w ]*)?",Z)
  if u:
   c,v=map(lambda i:''if i is None else i,u.groups());v=L(F(None,v.split(' ')))
   if S.V(c,S.F,None):
    T=S.C(c, v)
    if C:S.A()
   elif S.V(c,S.B,None):
    T=S.B[c](*v)
    if C:S.A()
   else:return False
   return T
  return False
```
**Ungolfed:**
```
import re
class Prindeal:
    iline = 0
    local = {}
    udef = {}
    content  = {}
    def __init__(self, c):
        self.built = {
            "p": self.print,
            "i": self.increment,
            "d": self.decrement,
            "a": self.alias,
        }
        self.content = dict(enumerate(filter(None, [i.split('#')[0].rstrip()for i in c.splitlines()])))
        while self.iline in self.content:
            self.execute_line(self.content[self.iline])
    def validate_name(self, varname, stack, default=0):
        if re.match("[\w_][\d\w_]*", varname):
            if not varname in stack:
                if default is not None:
                    stack[varname] = default
                else:
                    return False
            return True
        return False
    def advance_stack(self):
        self.iline += 1
    def print(self, varname):
        if self.validate_name(varname, self.local):
            print("{0} = {1}".format(varname, self.local[varname]))
            return True
        return False
    def increment(self, varname):
        if self.validate_name(varname, self.local):
            self.local[varname] += 1
            return True
        return False
    def decrement(self, varname):
        if self.validate_name(varname, self.local) and self.local[varname] > 0:
            self.local[varname] -= 1
            return True
        return False
    def alias(self, aliasname):
        indexed_lines = []
        if self.validate_name(aliasname, self.udef, indexed_lines):
            for i in range(3):
                self.advance_stack()
                indexed_lines.append(self.content[self.iline].lstrip())
            return True
        return False
    def execute_alias(self, cmd, variables):
        def parse_args(line, variables):
            for i in re.findall("\s(\d+)", line):
                line = line.replace(" %s" % i, " %s" % variables[int(i) - 1])
            return line
        init, success, failure = map(lambda l: parse_args(l, variables), self.udef[cmd])
        if self.execute_line(init, False):
            return self.execute_line(success, False)
        return self.execute_line(failure, False)
    def execute_line(self, line, cont=True):
        valid_execution = re.match("\s?([\w_][\d\w_]*)\s?([\w_][\d\w ]*)?", line)
        if valid_execution:
            cmd, variables = map(lambda i: '' if i is None else i, valid_execution.groups())
            variables = list(filter(None, variables.split(' ')))
            if self.validate_name(cmd, self.udef, None):
                temp = self.execute_alias(cmd, variables)
                if cont:
                    self.advance_stack()
            elif self.validate_name(cmd, self.built, None):
                temp = self.built[cmd](*variables)
                if cont:
                    self.advance_stack()
            else:
                return False
            return temp
        return False
```
**Usage:**
```
P(c)
```
Where `c` is the text content.
**Examples:**
*Single-line strings are accepted:*
* `P("p cat")`
* `P("p dog\ni dog\np dog")`
*Multi-lined strings are also accepted:*
```
P("""
p dog
i dog
p dog
""")
```
Or:
```
P("""p dog
i dog
p dog""")
```
Etc.
**Notes:**
This works correctly for all test cases, but reaches the recursion limit on:
```
pow C A B   #C = A ^ B = 9 ^ 3 = 729
```
Hence the `sys.setrecursionlimit(2000)`.
[Answer]
# Haskell, 1009
I did my best to golf it; my ungolfed code consisted of over 3,000 characters. At this point I can't remember what all the functions are doing so golfing it more mean guessing what will break it and what won't.
```
import qualified Data.Map as M
import Control.Monad.State.Lazy
import Data.List
type A=M.Map String
data P=P(A Int)(A([String]->StateT P IO Int))
a f=evalStateT f(P M.empty$M.fromList[("i",\(b:_)->(+1)%b),("d",\(b:_)->pred%b),("p",\(b:_)->i b>>= \v->liftIO(putStrLn$b++"="++show v)>>q 1)])
e(k:l)=do{(P v a)<-get;put.P v$M.insert k(m l)a;q 1}
g t s f= \a->t a>>= \b->if b>0then s a else f a
f%k=f<$>i k>>= \v->if v<0then k#0>>q 0else k#v>>q 1
i k=get>>= \(P v _)->q$M.findWithDefault 0 k v
k#v=get>>= \(P b a)->put$P(M.insert k v b)a
l k=get>>= \(P _ a)->q$a M.!k
f s=let(f:a)=r s in($a)<$>l f>>=id
m(t:s:f:_)=g(k t)(k s)(k f)
k s=let(f:b)=r s in\a->($(map((\y z->if all(\c->c>'/'&&c<':')z then y!!(read z-1)else z)a)b))<$>l f>>=id
n=dropWhileEnd(==' ').takeWhile(not.(=='#')).dropWhile(==' ')
o[]=[]
o(l:ls)|(head.r$l)=="a"=(l:take 3 ls):(o$drop 3 ls)|1>0=[l]:o ls
p s|length s>1=e$(n.tail.head$s):tail s|1>0=f.head$s
q=return
main=join$a.(\s->mapM_ p(o.filter(not.null).map n.lines$s))<$>getContents
r=words
```
[Answer]
# Python - ~~695~~ 688 bytes
```
def p(v):print v,"=",w.get(v,0)
def i(v):w[v]=w.get(v,0)+1
def d(v):
 if v in w:
<TAB>w[v]-=1
<TAB>if not w[v]:del w[v]
 else:return 1
def a(n,b,d,h):
 def g(*a):
<TAB>i=1;f=b;s=d;t=h
<TAB>for v in a:v=q+v+q;k=q+j(i)+q;f=c(f,k,v);s=c(s,k,v);t=c(t,k,v);i+=1
<TAB>y=u(t,e)if u(f,e)else u(s,e);i=1;return y
 e[n]=g
q="'";w=x={};u=eval;e={'a':a,'d':d,'i':i,'p':p};import sys;l=sys.stdin.readlines();r="";j=str;c=j.replace;sys.setrecursionlimit(2000)
for h in l:
 h = h.strip()
 if not h:continue
 l = h.split();f=l[0];n=f+"("
 if "#" in f:continue
 for g in l[1:]:
<TAB>b=g.find("#")+1
<TAB>if b:g=g[:b-1]
<TAB>if g:n+="'%s',"%g
<TAB>if b:break
 if x:x-=1;d+='"%s)",'%n
 else:x=(f=="a")*3;d=n
 if not x:d+=")\n";r+=d
exec r in e
```
`<TAB>` is a literal tab character.
[Answer]
# APL (Dyalog Unicode), ~~449~~ ~~422~~ ~~388~~ 383 bytes
This is a somewhat optimized APL version of a program to run the Prindeal code. It assumes that the index origin is 0 (⎕IO←0). The Prindeal program to run is in an enclosed string array P.
```
z
L←0
V←N←F←M←⍬
r←{⍬≡h←⍸⎕D∊⍨⊃¨t←↑⍵:t⋄t[h]←⍺[⍎¨t[h]]⋄t}
R←{⍵/⍨∊0≠⍴∘∊¨⍵}{' '(≠⊆⊢)⍵/⍨~∨\'#'=⍵}∘∊¨P
k:e↑L⌷R
L+←1
→k/⍨L<⍴R
{y}←e c;x
y←1
→((⊃c)∘≡∘,¨'pida')/p i d a
y←e c r x⌷⍨2-e c r 0⌷x←↑F[M⍳0⌷c]
→0
p:N[x],’=‘,2⊥⌽↑V[x←g c]
→0
i:V[x]←⊂{⍬≡⍵:1⋄⊃⍵:0,∇1↓⍵⋄1,1↓⍵}↑V[x←g c]
→0
d:→0/⍨~y←(,0)≢m←↑V[x←g c]
V[x]←⊂m↓⍨-(1=⍴m)⍱⊃⌽m←{⍬≡⍵:⍬⋄~⊃⍵:1,∇1↓⍵⋄0,1↓⍵}m
→0
a:M←M,1⌷c
F←F,⊂R[L+1+⍳3]
L+←3
x←g c
x←N⍳1⌷c
→0/⍨x<⍴N
N←N,1⌷c
V←V,⊂,0
```
The code length was a bit less until I reread the specification that requires arbitrary precision integers. I added code to provide this functionality although I'd hate to wait around for a Prindel program that requires this feature to execute. Dialog APL does have some prewritten routines ('big') to provide this functionality, However, I felt it would be a bit of a cheat to use all that extra 'hidden' code and call it an inherent part of the language.
In any case, the program consists of eight basic functions as follows:
```
 z - this function initializes variables, strips comments, trailing blanks and empty lines; then loops through the Prindeal program code.
 e - this executes a Prindeal statement either an inherent statement (p,i,d,a) or a defined alias. Recursive.
 g - gets a reference to a variable value (defines if as-of-yet undefined)
 r - at execution time, resolves numerical reference to arguments in alias functions
```
Comments and improvements are welcomed. I first dabbled in APL in the late 60s and early 70s. Life took me in another direction, but apparently I never lost my interest in the language. I recently took a look at the state of the language (via Dialog) and found that what was a powerful language back in the day had only grown in complexity and capability. I'm using some of these code golf challenges to introduce me to the newer (to me) language features and have a bit of fun ... lifelong learning.
In any case, here is the output from the test program:
```
     z
 A = 0
 B = 0
 C = 0
 ____ = 0
 A = 1
 B = 1
 C = 2
 ____ = 0
 A = 1
 B = 3
 C = 2
 ____ = 0
 d = 6
 ____ = 0
 d = 18
 ____ = 0
 d = 0
 ____ = 0
 A = 8
 B = 3
 C = 2
 ____ = 0
 A = 9
 B = 3
 C = 2
 ____ = 0
 A = 9
 B = 3
 C = 729
```
```
]