6.6 Lexicals and Globals
So far, we've been treating Parrot registers like the variables of a high-level language. This is fine, as far as it goes, but it isn't the full picture. The dynamic nature and introspective features of languages like Perl make it desirable to manipulate variables by name, instead of just by register or stack location. These languages also have global variables, which are visible throughout the entire program. Storing a global variable in a register would either tie up that register for the life of the program or require unwieldy manipulation of the user stack.
Parrot provides structures for storing both global and lexically scoped named variables. Lexical and global variables must be PMC values. PASM provides instructions for storing and retrieving variables from these structures so the PASM opcodes can operate on their values.
new P10, .PerlInt set P10, 42 store_global "$foo", P10 # ... find_global P0, "$foo" print P0 # prints 42 end
The first two statements create a PerlInt in the PMC register P10 and give it the value 42.
The store_global opcode only stores a reference to the object. If we add an increment statement:
after the store_global it increments the stored global printing 43. If that's not what you want, you can clone the PMC before you store it. It does have advantages, though. If you retrieve a stored global into a register and modify it as follows:
find_global P0, "varname" inc P0
the value of the stored global is directly modified, so you don't need to call store_global again.
Lexical variables are stored in a lexical scratchpad. There's one pad for each lexical scope. Every pad has both a hash and an array, so elements can be stored either by name or by numeric index. Parrot stores the scratchpads for nested lexical scopes in a pad stack.
188.8.131.52 Basic instructions
The instructions for manipulating lexical scratchpads are new_pad to create a new pad, store_lex to store a variable in a pad, find_lex to retrieve a variable from a pad, push_pad to push a pad onto the pad stack, and pop_pad to remove a pad from the stack:
new_pad 0 # create and push a pad with depth 0 new P0, .PerlInt # create a variable set P0, 10 # assign value to it store_lex 0, "$foo", P0 # store the var at depth 0 by name # ... find_lex P1, 0, "$foo" # get the var into P1 print P1 print "\n" # prints 10 pop_pad # remove pad end
The first statement creates a new scratchpad and pushes it onto the pad stack. It's created with depth 0, which is the outermost lexical scope. The next two statements create a new PMC object in P0, and give it a value. The store_lex opcode stores the object in P0 as the named variable $foo in the scratchpad at depth 0. At some later point in the program, the find_lex opcode retrieves the value of $foo in the pad at depth 0 and stores it in the register P1 so it can be printed. At the very end, pop_pad removes the pad from the pad stack.
The new_pad opcode has two forms, one that creates a new scratchpad and stores it in a PMC, and another that creates a new scratchpad and immediately pushes it onto the pad stack. If the pad were stored in a PMC, you would have to push it onto the pad stack before you could use it:
new_pad P10, 0 # create a new pad in P10 push_pad P10 # push it onto the pad stack
In a simple case like this, it really doesn't make sense to separate out the two instructions, but you'll see later in Section 6.7 why it's valuable to have both.
store_lex 0, 0, P0 # store by index # ... find_lex P1, 0 # retrieve by index
184.108.40.206 Nested scratchpads
To create a nested scope, you create another scratchpad with a higher depth number and push it onto the pad stack. The outermost scope is always depth 0, and each nested scope is one higher. The pad stack won't allow you to push on a scratchpad that's more than one level higher than the current depth of the top of the stack:
new_pad 0 # outer scope new_pad 1 # inner scope new P0, .PerlInt set P0, 10 store_lex -1, "$foo", P0 # store in top pad new P1, .PerlInt set P1, 20 store_lex -2, "$foo", P1 # store in next outer scope find_lex P2, "$foo" # find in all scopes print P2 # prints 10 print "\n" find_lex P2, -1, "$foo" # find in top pad print P2 # prints 10 print "\n" find_lex P2, -2, "$foo" # find in next outer scope print P2 # prints 20 print "\n" pop_pad pop_pad end
The first two statements create two new scratchpads, one at depth 0 and one at depth 1, and push them onto the pad stack. When store_lex and find_lex have a negative number for the depth specifier, they count backward from the top pad on the stack. -1 is the top pad, and -2 is the second pad back. In this case, the pad at depth 1 is the top pad, and the pad at depth 0 is the second pad. So:
store_lex -1, "$foo", P0 # store in top pad
stores the object in P0 as the named variable $foo in the pad at depth 1. Then:
store_lex -2, "$foo", P1 # store in next outer scope
stores the object in P1 as the named variable $foo in the pad at depth 0.
A find_lex statement with no depth specified searches every scratchpad in the stack from the top of the stack to the bottom:
find_lex P2, "$foo" # find in all scopes
Both pad 0 and pad 1 have variables named $foo, but only the value from the top pad is returned. store_lex also has a version with no depth specified, but it only works if the named lexical has already been created at a particular depth. It searches the stack from top to bottom and stores the object in the first lexical it finds with the right name.