-
Notifications
You must be signed in to change notification settings - Fork 0
Stackup
An introduction to the Stackup language
Stackup is a procedural, stack-oriented, reverse Polish notation (RPN) scripting language designed specifically for the quick creation and prototyping of patterns. Since Stackup is designed for a specific use case, it lacks most features of typical languages. There is only one type: numbers, which behave exactly like Lua numbers. There are no functions and no variables except for a few predefined globals.
Before starting, here are some basic concepts needed to understand Stackup.
A stack is a list with two basic operations: push and pop. Push adds a value to the list and pop removes the latest value from the list. This can be thought of as a stack of trays in a restaurant, where trays are deposited on the top but also removed from the top.
Stacks can also have a third operation called "peek" which reads the value on top without modifying the stack. This operation is optional but can make some explainations and code easier to grasp. Stackup stacks only contain numbers.
All instructions are delimited by any whitespace; the compiler does not care about whitespace. However, proper indentation is highly recommended as Stackup can look exceptionally confusing when not organized. RPN completely eliminates the need for parentheses so they are reserved for use elsewhere.
There is one exception to the whitespace rule which is the Lua function calling instructions. These statements use parentheses: (...) and #(...), which don't need to be delimited by whitespace on either side.
Writing a number will push it to the stack. So the code 1 2 3 4 will push the numbers 1, 2, 3, 4 to the stack in that order.
All instructions will pop and/or push numbers from the stack to do their operations. For example, the code 1 2 - will pop 1 and 2 from the stack, perform the operation 1-2 (note the order), and push the result -1 to the top of the stack. There are many other helpful instructions that are listed in Instructions.
Comments begin with --. Same as Lua.
Expressions written in RPN can take a bit getting used to. Here are some examples of simple expressions written in RPN:
2 * 3 - 9 ==> 2 3 * 9 -5 * (4 - 2) ==> 5 4 2 - *floor(-2 / -9) ==> -2 -9 / floor
True and false values are also just numbers. Any number that is not 0 is considered true. Typical and, or, and not instructions are included.
-3 2 and ==> 1-3 0 and ==> 0-3 0 or ==> 10 0 or ==> 032 not ==> 00 not ==> 1
Note: The below examples assume there is at least one number already in the stack.
If statements pop a number only run if the number is true.
2 % if
-- Runs if the number on top of the stack is even
end
There are also else statements:
2 % if
-- Runs if the number on top of the stack is even
else
-- Runs if the number on top of the stack is odd
end
Large chains of if-else statements can all be closed at once using endif.
Note:
dupduplicates the number on top of the stack. It is used to duplicate the result of the modulo since it needs to be used in multiple comparisons.
3 %
dup 0 == if
-- Runs if the number on top of the stack is a multiple of 3
else dup 1 == if
-- Runs if the number on top of the stack is a multiple of 3 plus 1
else dup 2 == if
-- Runs if the number on top of the stack is a multiple of 3 plus 2
endif
Stackup has two types of loops, for and while.
A for loop will peek the value at the top of the stack and decrement it by 1 every time a loop begins until that values equals 0. It will drop that 0 before continuing.
Example
This code pushes the numbers 2, 1, and 0 to the stack.
3 for
dup
end
Walkthrough
-
3
Push 3- Stack: ( 3 )
-
for
Peek the top value and check if it's 0. It's not so decrement it by 1.- Stack: ( 2 )
-
dup
Duplicate the value on top of the stack.- Stack: ( 2 2 )
-
for
Peek the top value and check if it's 0. It's not so decrement it by 1.- Stack: ( 2 1 )
-
dup
Duplicate the value on top of the stack.- Stack: ( 2 1 1 )
-
for
Peek the top value and check if it's 0. It's not so decrement it by 1.- Stack: ( 2 1 0 )
-
dup
Duplicate the value on top of the stack.- Stack: ( 2 1 0 0 )
-
for
Peek the top value and check if it's 0. It is so drop the 0.- Stack: ( 2 1 0 )
WARNING: Loops are dumb. When each iteration is completed, they expect that their condition or index value has been replaced back to the top of the stack. Failing to do so will almost certainly cause unexpected behavior.
While loops will pop a number and only run if it is true. While loops will not push any numbers back to the stack so it is up to the programmer to replace any condition back to the top of the stack.
The below code is equivalent to a for loop:
dup while
1 -
-- Looped statements go here
dup
end
Stackup scripts can be compiled by passing a string containing the script to Patternizer.compile(str). This is a pure function, not a class method, so all input scripts that are the same will always output identical compiled programs. However, different instances of Patternizer may interpret the same compiled stackup script differently depending on how certain per-instance parameters are set.
Interpretation is done using Patternizer:interpret(program, ...). Any extra numbers passed to this function are pushed to the stack before the program is run. This can be used to give Stackup programs parameters. When the Stackup program is done running, any numbers remaining in the stack are returned by the interpret function. This can be used to get output from the Stackup program.
Note:
overduplicates the number below the top of the stack and pushes it to the top.
Example
Lua:
local program = Patternizer.compile([[
over 4 * over 3 *
]])
print(Patternizer:interpret(program, 1, 2))Output:
1 2 4 6
The 1 and 2 are the original parameters we passed in. The 4 and 6 are the numbers calculated by the Stackup program
The Patternizer class has two methods of including Lua functions into Stackup programs. Both types of functions can be called directly from stackup using ().
-
Patternizer:link(char, fn)- Includes a Lua function primarily for use with P-Strings. These functions are named with a single alphanumeric character, period, or underscore. They can appear in P-strings but can also be called directly using
().
- Includes a Lua function primarily for use with P-Strings. These functions are named with a single alphanumeric character, period, or underscore. They can appear in P-strings but can also be called directly using
-
Patternizer:include(name, fn)- Includes a Lua function for general use in Stackup. These functions can be named with more than one character. There are still restrictions: Names must consist of only alphanumeric characters (underscores are allowed), cannot start with a number, and cannot conflict with an already existing Stackup function.
Note:
Patternizer:send(str, ...)combines compiling and interpreting into one function.
Linked functions
Patternizer:link('q', function(side, thickness)
cWall(side, thickness)
return 1, 2, 3
end)
-- The q function being called from a P-string.
-- (The return values are discarded.)
print(Patternizer:send([[
100
0 40 T:q
]]))
-- The q function being called directly.
-- The returned numbers (1, 2, 3) are pushed to the stack.
print(Patternizer:send([[
200
(0 40)'q'
]]))Output
100
200 1 2 3
Included functions
Patternizer:include("foo", function(...)
print("hello world", ...)
return 10, 11, 12
end)
-- The foo function being called directly.
print(Patternizer:send([[
(1 2 3)foo
]]))Output
hello world 1 2 3
10 11 12
Functions can also be called as timeline events using #(). The syntax is identical but return values are always discarded.
#(0 40)'q'#(1 2 3)foo
The information presented in this wiki is not guaranteed to be accurate. The information presented may be a few versions old.