Overview
This update introduces environments (previously called “scopes”) to Clojush in an effort to offer encapsulation of functionality. Many times, it is nice to perform instructions and return a value but not affect the rest of the stack space, which environments allow you to do.
New Stacks
There are two new stacks, :environment and :return.
:environment stack
The :environment stack holds environments, which are essentially Push interpreter states that can be saved and later returned to. In fact, since the Push state is stored as a map, this entire map is pushed onto the stack, and later can replace the current state when popping it off the :environment stack.
An item on the :environment stack can be popped in two ways. The first is by the instruction environment_end. The second automatically happens when the :exec stack is empty and there is something on the :environment stack. When the :environment stack is popped, that popped state replaces the current Push interpreter state. Then, the instructions remaining on the old :exec stack (if any) are placed at the beginning of the new :exec stack. Finally, everything on the :return stack is pushed onto the :exec stack, with the top item of the :return stack pushed first (so that the bottom item of the :return stack will be the top item of the :exec stack).
:return stack
The :return stack allows both literals and instructions to be “returned” to the pushed environment when it is popped (see above). There are instructions for moving the top item of any stack to the :return stack, including code from the :exec stack. Additionally, there are specialized instructions that can be used to “consume” arguments in the pushed environment by popping them, and for tagging literals in the pushed environment (see those instructions below).
New Instructions
environment instructions
- environment_new – Saves the next instruction or parenthetical grouping on the :exec stack and pushes the state onto the :environment stack, with the saved instruction/grouping popped from the :exec stack of the pushed state. The :exec stack is replaced by this saved instruction/grouping, the :return stack is emptied, and the rest of the state stays the same as it was before this instruction.
- environment_begin – Pushes the state onto the :environment stack (with an empty :exec stack). The :return stack is emptied in the current state, but all other stacks (including the :exec stack) are left full.
- environment_end – If the :environment stack is empty, no-ops. Otherwise, does the same as what happens when there is an empty :exec stack with something on the :environment stack, which is described above.
return stack instructions
- return_from<type> – For <type> equivalent to the name of one of the stacks :boolean, :integer, :float, :string, :zip, and :exec, these instructions pop the next item off of the <type> stack and push that item onto the :return stack.
- return_fromcode – Returning something from the :code stack is different, since normal instructions would just be performed on the :exec stack. So, return_fromcode pushes (code_quote top_code_stack_item) onto the :return stack, which will result in the top item of the current :code stack being quoted (and thus pushed to the :code stack) in the popped environment.
- return_<type>_pop – For <type> including each of the stacks above (including :code), this instruction places the instruction <type>_pop onto the bottom of the :return stack. The effect is that in the popped environment, these popping instructions will be the first thing that is executed, and will effectively “consume” the “arguments” given on that parent environment’s stacks.
- return_tagspace – This instruction immediately copies the tagspace of the current environment to the tagspace of the first environment on the :environment stack. This allows children environments to give their tagspace to their parent.
- return_tag_<type>_<index> – There is a new erc, the return-tag-instruction-erc, that returns instructions of this form. These instructions, when executed, push (<literal> tag_<type>_<index>) onto the :return stack, where <literal> is the top item on the <type> stack (and is popped from that stack). These instructions allow an environment to add tags to their parent environment.
Original Proposal
The original proposal, which is somewhat accurate still, is kept here for reference:
- Use environment_new (previously scope_enter) to enter a new scope. This instruction pushes a copy of the stacks onto the :environment stack, clears the :exec and :return stacks, and pushes the next item on the old :exec stack onto the new :exec stack, popping it from the old one.
- Whenever the :exec stack is empty during execution, Push will first check if there is anything on the :environment stack. If so, it is popped off (replacing all the stacks with the ones stored in the top environment), and then everything on the original :return stack is pushed onto the :exec stack.
- No instructions fetch or push literals between environments on the :environment stack. As of now, the only way to alter the environment stack is either through the instruction environment_push or by having an empty :exec stack.
- Return instructions will allow environments (scopes) to pass return values, consume arguments, and tag things in other environments.
- Instructions like return_integer will move the top item of the :integer stack onto the :return stack.
- Instructions like return_integer_pop will push the instruction integer_pop onto the :return stack, so that the “parent” environment will pop the top integer before executing further. To avoid popping instructions from just popping literals that have been pushed by instructions like return_frominteger, the popping instructions will be inserted at the bottom of the :return stack, and thus executed first in the state once the environment has ended. Since ordering of pop instructions is commutative, it is inconsequential that return_pop_stack instructions that are executed later will result in pop_stack instructions that are executed sooner.
- Instructions like return_tag_integer_123 (say 2999 is on top of the :integer stack) will push the code ‘(2999 tag_integer_123) onto the :return stack and pop 2999 from the :integer stack. This will cause the tag 123 to be associated with 2999 in the “parent” environment.
- The instructions return_exec and return_code are legal and pull the top code item off the respective stack to the :return stack. return_code will put a code_quote before the code, so that it will be moved to the :code stack in the “parent” environment. For example, the code ‘(return_code (2 integer_add)) will put (code_quote (2 integer_add)) on the :return stack. return_exec will just pull the next instruction grouping itself, meaning that the code will be executed in the “parent” environment. Note: Since this leads to permeability of the scoping of environments, it may often be best to leave return_exec out of the instruction set to avoid undesirable messing of stacks between environments.