Stacker: An RPN Calculator and Extensible Programming Language
Stacker is a powerful Reverse Polish Notation (RPN) calculator built with Python, featuring basic mathematical operations and extensibility through plugins.
The user manual lives in docs/ (Japanese; it covers the syntax, every built-in word, scripts, the REPL and the plugin API).
Table of Contents
- Installation
- Dependencies
- Feedback and Contributions
- Usage
- Running Scripts
- VSCode Syntax Highlighting
- Command Line Execution
- Settings
- Configuration File
- Creating Plugins
- Running on Python
- Supported Operations
- Constants
Installation
git clone git@github.com:remokasu/stacker.git
cd stacker
pip install .
Optional: VSCode Syntax Highlighting
For syntax highlighting support in VSCode:
mkdir -p ~/.vscode/extensions/stacker-language-0.1.0
cp -r .vscode-extension/* ~/.vscode/extensions/stacker-language-0.1.0/
Reload VSCode (Ctrl+Shift+P → "Developer: Reload Window") and .stk files will be highlighted.
Dependencies
Python Prompt Toolkit is required for Stacker. Install it using the following command:
pip install prompt_toolkit
Feedback and Contributions
Feedback and contributions are welcome. Please submit issues or suggestions on the Issues page.
Usage
Run Stacker:
stacker
Or:
python -m stacker
Stacker supports standard arithmetic operations (+, -, *, /) and advanced functions (sin, cos, tan, etc.). Users can input commands in RPN format and extend functionality using custom plugins.
Input Examples
Stacker allows for straightforward RPN input. For example:
-
Single-line input:
stacker:0> 3 4 + [7]
-
Multi-line input:
stacker:0> 3 [3] stacker:1> 4 [3 4] stacker:2> + [7]
-
Numbers:
The Stacker command allows you to directly push integers, floating-point numbers, and complex numbers onto the stack. This facilitates easy management of various types of numerical data.
-
Integers:
stacker:0> 3 [3]
In this example, the integer 3 is added to the stack.
-
Floating-Point Numbers:
stacker:1> 3.14 [3.14]
Here, the floating-point number 3.14 is added to the stack.
-
Complex Numbers:
stacker:2> 1+2j [(1+2j)]
In this case, the complex number 1+2j (with a real part of 1 and an imaginary part of 2) is added to the stack. Complex numbers are denoted by combining the real and imaginary parts with a +, and the imaginary part is indicated using j.
-
-
Strings:
- syntax:
"Hello, World!" - example:
stacker:0> "Hello, World!" ['Hello, World!']
In this example, the string "Hello, World!" is added to the stack. Strings are written with double quotes and shown on the stack with single quotes. A backslash starts an escape:\n\t\r\0\\\"\'\xNN\uNNNN.stacker:0> "say \"hi\"\tnow" echo say "hi" now
- syntax:
-
Variables:
-
syntax:
value :name set # or value :name =
-
example:
stacker:0> 3 :x set stacker:1> x [3] # Using = operator (equivalent to set) stacker:0> 5 :y = stacker:1> y [5]
In this example, we assign
3toxusingset, and5toyusing=. Both operators work identically.Note: The
=operator is an alias forsetand can be used interchangeably. Use whichever feels more natural for your coding style.Three ways to write a name. A name can be written bare (
x), as a symbol (:x) or quoted ({x}). The rule is short: a bare name is a reference,:xand{x}are the name itself, and"x"is a string, never a name.- A bare name is looked up: as a macro, then a variable, then a function or operator. A macro is expanded,
a variable's value is pushed (a lambda too; call it with
eval), a function or operator is run. A name that is none of these is an error right there, so a typo stops where it is written. :xpushes the symbolxitself as a value, whatever a variablexholds. A symbol is a type of its own::x "x" ==is false,symbol?tells it from a string,strgives its text andsymbolmakes one from text ("x" symbol).:must be followed by a name (:1and:{x}are errors). Symbols are how a name is handed to any word that takes one (set,defun,alias, a macro, ...), and[:a :b]is a list of names.{x}(a block holding only the name) means "this name" everywhere a name is read, and as code it means the same as:x::x evaland{x} evalboth do what writingxdoes.- Tokens are read one at a time, left to right, and nothing looks ahead: a bare name is a reference
wherever it stands, even right before
set. So the name a word binds is always written:nameor{name}:3 :x set,0 10 :i {i echo} do,{a b} {a b +} :add defun,[1 2 3] 0 :acc :x {acc x +} reduce. Written bare,3 x setlooksxup and stops with "xis not defined", and3 dup setrunsdupand thensetcomplains that it got3instead of a name. The same spelling rebinds any name, a built-in word's included (3 :dup set), and redefining a prelude word such asaliaslogs a warning, as does adefunthat redefines a word of your own from another file. A bare name that holds a name binds that name: after:y :x set,7 x setbindsy. A literal is never a name (5 3 setand5 true setare errors) and neither is a string (5 "x" setis an error too).
- A bare name is looked up: as a macro, then a variable, then a function or operator. A macro is expanded,
a variable's value is pushed (a lambda too; call it with
-
-
Lists:
-
Single-line list:
stacker:0> [1 2 3; 4 5 6] [[[1 2 3] [4 5 6]]]
-
Multi-line list:
stacker:0> [1 2 3; ... > 4 5 6] [[[1 2 3] [4 5 6]]]
-
-
Code Blocks
Code blocks are fundamental structures in Stacker that enable deferred evaluation and control flow management. They are enclosed in curly braces
{}.Syntax:
{code_elements}
Key Characteristics:
- Deferred Evaluation: Code blocks are not executed immediately when encountered
- Stack Interaction: Pushed onto the stack as single units in their raw form
- Delayed Execution: Can be executed later when needed (via
eval,if,times, etc.)
Common Use Cases:
- Conditional statements (
if,ifelse) - Loop controls (
times,do,dolist) - Function definitions (
defun,lambda) - Lazy evaluation patterns
Examples:
# Create a code block stacker:0> {1 2 +} [{1 2 +}] # Execute with eval stacker:0> {1 2 +} eval [3] # Use in function definitions stacker:2> {x y} {x y *} :multiply defun
Note: Code blocks are stored but not executed until explicitly triggered. This allows for flexible program control, lazy evaluation, and higher-order programming patterns.
Quoted names: a block holding a single name, such as
{double}, is the way to pass a name as a value. Every word that takes a name (set,=,global,defun,defmacro,alias) accepts{name}as well as:name. Macros that define things take their names in this form. This is also how a variable name is passed to a macro:{n} incr!and:n incr!both hand over the namen, whereasn incr!hands over the variable's current value. -
Arrays and Dictionaries:
A list never changes. When you need to change elements in place, make an array or a dictionary. Words that change them end in
!.stacker:0> [1 2 3] array :a set [] stacker:1> 9 a 0 at! [] stacker:2> 4 a append! [] stacker:3> a [[9 2 3 4]] stacker:4> dict :d set [[9 2 3 4]] stacker:5> 1 d "x" put! [[9 2 3 4]] stacker:6> d "x" get [[9 2 3 4] 1]
An array is shown like a list (the REPL gives it a color of its own;
array?tells for sure) and a dictionary as{'x' 1}. An array or a dictionary is one thing wherever it goes:dup, a variable, a function argument or a list element refers to the same one, so a change made through one is seen through all.a b same?tells whether two are the same one;==compares contents. Words that only read take an array as they take a list, and the ones that make a new sequence (map,sorted,cdr, ...) make a list. -
Control Structures in Stacker
Stacker provides two main types of control structures: conditionals and loops. These allow for dynamic program flow based on conditions and repetitive execution of code blocks.
-
Conditionals
Conditionals in Stacker enable execution of code based on specified conditions.
-
if Statement
The
ifstatement executes a code block if a condition is true.Syntax:
condition <true-expr> if
The condition is a value, or a block that runs on the current stack and leaves the value to test on top (so
7 {0 >} {...} iftests7 0 >, and an empty block leaves the top as it is). A block that leaves nothing is an error.Example:
stacker:0> 0 :x set stacker:1> x 0 == {3 4 +} if [7]
Result: Pushes
7onto the stack asxequals0. -
ifelse Statement
The
ifelsestatement provides branching based on a condition, executing one of two code blocks.Syntax:
condition <true-expr> <false-expr> ifelse
Example:
stacker:0> 0 :x set stacker:1> x 0 == {3 4 +} {3 4 -} ifelse [7]
Result: Pushes
7onto the stack asxequals0. -
cond Statement
The
condstatement evaluates condition-result pairs in order and executes the first matching branch.Syntax:
{condition1} {result1} {condition2} {result2} ... n cond
nis the number of condition-result pairs.Example:
stacker:0> 5 :x set stacker:1> {x 0 >} {"positive"} {x 0 <} {"negative"} {true} {"zero"} 3 cond ['positive']
Result: Pushes
"positive"onto the stack asxis greater than0. Use{true}as the last condition to provide a default branch. -
Loops
Loops in Stacker allow for repeated execution of code blocks.
-
do Loop
The
doloop iterates over a range of values.Syntax:
start_value end_value :name {body} do
Example:
stacker:0> 1 10 :i {i echo} do 1 2 3 4 5 6 7 8 9 10
Result: Prints numbers from 1 to 10.
-
dolist
The
dolistloop iterates over a list of values.Syntax:
[value1 value2 ... valueN] :name {body} dolist
Example:
stacker:0> [1 2 3 4 5] :i {i echo} dolist 1 2 3 4 5
Result: Prints numbers 1 through 5.
Note: When expressing a list of consecutive values, the concise notation value1 valueN
seqcan be used instead of[value1 value2 ... valueN]to efficiently describe a sequence with a constant step size. -
times
The
timesloop repeats a code block a specified number of times.Syntax:
{body} n times
Example:
stacker:0> 1 {dup ++} 10 times [1 2 3 4 5 6 7 8 9 10 11]
Result: Pushes numbers 1 through 11 onto the stack by repeatedly duplicating and incrementing.
-
while Loop
The
whileloop repeats a code block while a condition is true.Syntax:
{condition} {body} while
Example:
stacker:0> 1 :i set 0 :s set stacker:2> {i 6 <=} {s i + :s set i ++ :i set} while stacker:2> s [21]
Result: Sums numbers from 1 to 6.
Note: Use
{condition}as a code block that leaves a boolean on the stack. The loop terminates when the condition evaluates to false. -
loop
looprepeats a code block forever untilbreakis executed. It is the primitive every other loop can be built from; the body runs on the current stack, in the current scope.Syntax:
{body} loop
Example:
stacker:0> 0 {1 + dup 10 > {break} if} loop [11]
-
break
breakimmediately exits the innermost loop (do,dolist,times,while,loop).Syntax:
{break}
Example:
stacker:0> 0 :s set 1 10 :i {i 5 == {break} if s i + :s set} do s [10]
Result: Sums 1 through 4, then exits when
ireaches 5. Note thatbreakinsideifimmediately stops execution of the current block —s i + :s setis not executed wheniequals 5.
-
-
Define a function:
- syntax:
{arg1 arg2 ... argN} {body} :name defun
- example:
stacker:0> {x y} {x y *} :multiply defun stacker:1> 10 20 multiply [200]
This defines a function namedmultiplythat takes two argumentsxandyand multiplies them together. The function pushes every value its body leaves, in order; a body that leaves nothing pushes nothing:stacker:0> {a b} {b a} :swap2 defun stacker:1> 1 2 swap2 [2 1]
- syntax:
-
Define an alias:
- syntax:
{body} {name} alias
- example:
stacker:0> {2 ^ 3 * 5 +} {calculatePowerAndAdd} alias stacker:1> 5 calculatePowerAndAdd [80]
An alias names a code snippet. Calling the name runs the body right there, on the caller's stack and in the caller's scope, exactly as if the body had been written in place. (Before 1.15.0 this was whatdefmacrodid.)
- syntax:
-
Define a macro:
-
syntax:
{args} {body} :name defmacro
-
example:
stacker:0> {cond body} { {cond eval not {body eval} if} } {unless} defmacro stacker:1> -3 {0 >} {"not positive" echo} unless not positive
A macro receives its arguments as code (blocks are not evaluated) and runs its body when it is called to build the code that will actually run. Inside a block literal of the body, an argument name is replaced by the argument passed in, so the body above produces
{{0 >} eval not {{"not positive" echo} eval} if}, which is then evaluated on the caller's stack. This lets you define new control structures in Stacker itself.The rules:
- The body runs on its own stack, in a scope of its own where the arguments are bound. It must leave exactly one value: a code block is evaluated in the caller's context, any other value is pushed as is.
- Every name bound in that scope (the arguments, and variables assigned with
=/setinside the body) is substituted into the body's block literals at the moment each literal is pushed: a symbol (:name, agensymname) becomes a bare name in the generated code, a string becomes a string literal, a block stays a block and any other value is spliced as a value. A:namewritten in the body is replaced by the symbol of the name thatnameholds (:tfor agensymname held int,:doublefor the quoted name{double}), which is how a generated temporary is bound:{ x eval :t set t t + }. The substituted values are not scanned again; string literals, list literals and unbound:names are left untouched. - Assignments inside the body are local to the macro; they never change the caller's variables.
- Argument names may be anything, including built-in names; every occurrence in the block literals is replaced, so choose them with care.
gensymreturns a fresh unique name (prefixed__g; do not use that prefix yourself) for temporaries in generated code:stacker:0> {x} { gensym :t = { x eval :t set t t + } } {double!} defmacro stacker:1> {3 4 +} double! [14]
{name} macroexpandpushes the expansion without running it, which is the easiest way to debug a macro.
-
-
Lambda Functions
Lambda functions are anonymous functions that can be defined and executed on the fly. They are useful for creating temporary functions without the need for a formal definition.
-
syntax:
{arg1 arg2 ... argN} {body} lambda
-
example:
stacker:0> {x y} {x y *} lambda [λxλy.{x y *}]
-
example:
stacker:0> {x y} {x y *} lambda :multiply set stacker:1> 3 4 multiply [3 4 λxλy.{x y *}] stacker:3> eval [12]
This example defines a lambda function that multiplies two numbers and assigns it to the variable
multiply. A lambda held in a variable is a value: writingmultiplypushes it, andevalcalls it with the arguments3and4(3 4 multiply eval). To call a function by its bare name, define it withdefuninstead. Like a function, a lambda pushes every value its body leaves when it is called.
-
-
Include Scripts
"file.stk" includeruns another script right here, in the interpreter and the scope it is written in, as if its text stood in place: the variables, functions and macros it defines are the ones you see afterwards, and its functions can call yours. It runs every time it is included. What the file leaves on its stack is dropped.stacker:0> "my_script.stk" include
"file.stk" requiredoes the same the first time, and nothing when the same file is required again. Read a library that keeps state withrequire, so that reading it from a second file does not set it up again.Relative paths are looked up next to the including file first, then in the current directory. An error inside an included file is reported at its own line, followed by the lines that included it (
included from ...). -
The prelude
Some words are written in Stacker itself and loaded into every interpreter at start-up (
aliasandmeanfor now). They behave like any other word;macrosandfuncsmark them with(prelude).
Running Scripts
Stacker scripts can be created in .stk files. To run a script, simply execute it with Stacker. For example:
-
my_script.stk:
0 :p set 0 100000 :k { -1 k ^ 2 k * 1 + / p + :p set } do 4 p * :p set p echo
Running the script:
stacker my_script.stk
VSCode Syntax Highlighting
Stacker provides syntax highlighting support for .stk files in Visual Studio Code, making it easier to read and write Stacker code.
Installation
Install the syntax highlighting extension by copying it to your VSCode extensions directory:
# For VSCode Server (Remote SSH)
mkdir -p ~/.vscode-server/extensions/stacker-language-0.1.0
cp -r .vscode-extension/* ~/.vscode-server/extensions/stacker-language-0.1.0/
# For local VSCode
mkdir -p ~/.vscode/extensions/stacker-language-0.1.0
cp -r .vscode-extension/* ~/.vscode/extensions/stacker-language-0.1.0/
Then reload VSCode (Ctrl+Shift+P → "Developer: Reload Window").
Features
- Comments (
#and#| ... |#), strings ("...",'...', triple quotes), numbers (42,3.14,1.5e3,0xFF,0b1010,2+3j) - Symbols (
:x) - Every word of the language, by kind: control flow (
if,do,while, ...), definitions (defun,defmacro,lambda,alias), assignment (set,=,global), code words (eval,parse,macroexpand, ...), arithmetic, comparison, logic, bit operations, math, stack words, list words, string words, type words, I/O and files, system words, prelude words and constants - Auto-closing brackets (
{,[,(,",') and code folding
The grammar is generated from the interpreter's own word lists by scripts/generate_vscode_grammar.py, so it lists exactly the words the interpreter knows. The colors come from your VSCode theme.
Command Line Execution
You can directly execute a specified RPN expression from the command line.
stacker -e "3 4 + echo"
Settings
-
disable_plugin Disable a specified plugin:
stacker:0> "hoge" disable_plugin
This command deactivates the
hogeoperator added as a plugin. Note that it cannot be used on non-plugin operators. -
disable_all_plugins Disable all plugins at once.
stacker:0> disable_all_plugins -
enable_disp_ans Enables the display of the last result (ans) at the end of the stack.
stacker:0> enable_disp_ans stacker:1> 3 4 + 7 [7]
-
disable_disp_ans Disables the display of the last result (ans) at the end of the stack.
stacker:0> disable_disp_ans stacker:1> 3 4 + [7]
-
enable_disp_stack Enables the setting to display the stack contents each time. By default, this setting is already active.
stacker:0> enable_disp_stack -
disable_disp_stack Sets the display of stack contents to be disabled. When this setting is enabled, only the latest element of the stack is displayed.
stacker:0> disable_disp_stack -
disable_disp_logo Disables the display of the logo at startup.
stacker:0> disable_disp_logo
Configuration File
You can automatically load settings at startup. The configuration file should be placed in ~/.stackerrc. For example, if you write the following contents in ~/.stackerrc, the disable_disp_logo and disable_disp_stack will be automatically activated at startup.
disable_disp_logo
disable_disp_stack
enable_disp_ans
Creating Plugins
Create custom plugins for Stacker using Python:
-
Creating the Plugin: In the
pluginsdirectory, create a new Python file for your plugin (e.g.,my_plugin.py).stacker/ │ ├── stacker/ │ ├── plugins/ │ │ ├── my_plugin.py │ │ └── ... │ │ │ ├── data/ │ ├── stacker.py │ └── ... │ └── ...Adding your plugin here and reinstalling Stacker will apply the plugin permanently.
-
Defining Functions and Classes: Define the necessary functions and classes in
my_plugin.py. -
Defining the
setupFunction: Inmy_plugin.py, define asetupfunction that takesstackeras its only argument. -
Registering Custom Commands and Parameters:
Within the
setupfunction, use theregister_pluginmethod ofstackerto register custom commands. Additionally, you can also register custom parameters using theregister_parametermethod. This allows for greater flexibility and customization in your plugin's behavior.Here's an example that registers a custom command:
Example:
from stacker.stacker import Stacker def function(a, b): return a + b def setup(stacker: Stacker): stacker.register_plugin("command", function, desc="Add two numbers")
You can specify the command description for the help command using desc. This field is optional.
A plugin word takes precedence over a built-in word of the same name: registering
+replaces the built-in+. This is how a plugin can give an operator a new meaning, for example for matrices.The register_parameter method is used to add a custom parameter to the Stacker environment, allowing for additional customization and control within your plugin.
-
Reinstalling Stacker: Run the following command to reinstall Stacker:
> pip install .Note: If you want to apply the plugin only temporarily, create a
pluginsdirectory in the directory where Stacker is executed and add your plugin there. The method for creating it is the same as described above. This method does not require reinstalling Stacker. -
Using the Plugin: When Stacker is launched, the plugin will automatically be loaded, and the custom commands will be available for use.
-
Disabling Plugins: Use operatorName disable_plugin to disable a specific plugin.
Use disable_all_plugins to disable all plugins.
Running on Python
You can also run Stacker as a Python module. For example:
from stacker import Stacker
stacker = Stacker()
print(stacker.eval("3 4 +"))
Stacker() loads the prelude like the command line does. Pass Stacker(prelude=False) for a bare interpreter.
Supported Operations
Basic Operators
| Operator | Description | Example |
|---|---|---|
| + | Add | 3 5 + |
| - | Subtract | 10 3 - |
| * | Multiply | 4 6 * |
| / | Divide | 12 4 / |
| // | Integer divide | 7 2 // |
| % | Modulus | 9 2 % |
| ^ | Power | 3 2 ^ |
| == | Equal | 1 1 == |
| != | Not equal | 1 0 != |
| < | Less than | 1 2 < |
| <= | Less than or equal to | 3 3 <= |
| > | Greater than | 2 1 > |
| >= | Greater than or equal to | 3 3 >= |
| neg | Negate | 5 neg |
| and | Logical and | true false and |
| or | Logical or | true false or |
| not | Logical not | true not |
| band | Bitwise and | 3 2 band |
| bor | Bitwise or | 3 2 bor |
| bxor | Bitwise xor | 3 2 bxor |
| >> | Right bit shift | 8 2 >> |
| << | Left bit shift | 2 2 << |
| bit-length | The bits needed to write an integer | 255 bit-length |
| popcount | The number of one bits in an integer | 255 popcount |
| trailing-zeros | Zero bits below the lowest one bit | 40 trailing-zeros |
| ~ | Bitwise not | 5 ~ |
| bin | Binary representation (result is a string) | 5 bin |
| oct | Octal representation (result is a string) | 10 oct |
| dec | Decimal representation (result is an integer) | 0b101010 dec |
| hex | Hexadecimal representation (result is a string) | 255 hex |
Math Operator
| Operator | Description | Example |
|---|---|---|
| abs | Absolute value | -3 abs |
| exp | Exponential | 3 exp |
| real | Real part (a real number is its own) | 3+4j real |
| imag | Imaginary part (0 for a real number) | 3+4j imag |
| conj | Complex conjugate | 3+4j conj |
| arg | Angle of a number, from -pi to pi | 1j arg |
| polar | Complex number of length r and angle t: r t polar |
1 pi 2 / polar |
| log | Natural logarithm | 2 log |
| log10 | Common logarithm (base 10) | 4 log10 |
| log2 | Logarithm base 2 | 4 log2 |
| sin | Sine (argument in radians) | pi 2 / sin |
| cos | Cosine (argument in radians) | pi cos |
| tan | Tangent (argument in radians) | pi 4 / tan |
| asin | Arcsine (result in radians) | 0.5 asin |
| acos | Arccosine (result in radians) | 0.5 acos |
| atan | Arctangent (result in radians) | 1 atan |
| sinh | Hyperbolic sine | 1 sinh |
| cosh | Hyperbolic cosine | 1 cosh |
| tanh | Hyperbolic tangent | 1 tanh |
| asinh | Inverse hyperbolic sine | 1 asinh |
| acosh | Inverse hyperbolic cosine | 2 acosh |
| atanh | Inverse hyperbolic tangent | 0.5 atanh |
| sqrt | Square root | 9 sqrt |
| ceil | Ceiling | 3.2 ceil |
| floor | Floor | 3.8 floor |
| round | Round | 3.5 round |
| roundn | Round to specified decimal places | 3.51 1 roundn |
| gcd | Greatest common divisor | 4 2 gcd |
| ! | Factorial | 4 ! |
| radians | Convert degrees to radians | 180 radians |
| rand | Generate a random floating-point number between 0 and 1 | rand |
| randint | Generate a random integer within a specified range | 1 6 randint |
| uniform | Generate a random floating-point number within a specified range | 1 2 uniform |
| frac | Fraction | 3 6 frac |
| dice | Roll dice (e.g., 3d6) | 3 6 dice |
Stack Operators
| Operator | Description | Example |
|---|---|---|
| drop | Drops the top element of the stack. | drop |
| drop2 | Drops the top two elements of the stack. | drop2 |
| dropn | Drops the top n elements of the stack. | 2 dropn |
| dup | Duplicate the top element of the stack. | dup |
| dup2 | Duplicate the top two elements of the stack. | dup2 |
| dupn | Duplicate the top n elements of the stack. | 2 dupn |
| swap | Swap the top two elements of the stack. | swap |
| rev | Reverse the stack. | rev |
| rot | Move the third element to the top of the stack. | rot |
| unrot | Move the top element to the third position of the stack. | unrot |
| roll | Moves the nth element to the top of the stack. | 2 roll |
| over | Copy the second element from the top of the stack. | over |
| pick | Copies the nth element to the top of the stack. | 2 pick |
| nip | Remove the second element from the top of the stack. | nip |
| depth | Returns the depth of the stack. | depth |
| ins | Insert a value below the top n elements: n value ins |
1 2 3 2 99 ins |
| count | Pop a value and count its occurrences in the rest of the stack. | 5 5 7 5 count |
| clear | Clear the stack. | clear |
| disp | Display the stack. | disp |
Control Operators
| Operator | Description | Example |
|---|---|---|
| if | Conditional statement | true {...} if |
| ifelse | Conditional statement with an else block | true {"yes"} {"no"} ifelse |
| iferror | Run the catch block if the first block raises an error, with the stack put back as it was | {1 0 /} {"caught"} iferror |
| try | Like iferror, with the error pushed as a dictionary ('kind', 'message', 'stack') before the handler runs | {1 0 /} {"kind" get} try |
| throw | Raise your own error: kind message throw |
{"E" "boom" throw} {"message" get} try |
| cond | Multi-branch conditional (first matching pair wins) | 5 :x set {x 0 >} {"pos"} {true} {"other"} 2 cond |
| do | Loop | 0 10 :i {i echo} do |
| dolist | Loop over the elements of a list | [1 2 3] :x {x echo} dolist |
| times | Loop a specified number of times | {dup ++} 10 times |
| while | Loop while a condition is true | 0 :i set {i 10 <} {i ++ :i set} while |
| loop | Repeat a block until break |
0 {1 + dup 10 > {break} if} loop |
| break | Break out of a loop | break |
Function, Macro, Lambda, and Variable Operators
| Operator | Description | Example |
|---|---|---|
| defun | Define a function | {x y} {x y *} :multiply defun |
| alias | Name a code snippet (prelude) | {2 ^ 3 * 5 +} {calculatePowerAndAdd} alias |
| defmacro | Define a macro | {c b} {{c eval not {b eval} if}} {unless} defmacro |
| macroexpand | Push a macro's expansion without running it | {0 >} {"neg" echo} {unless} macroexpand |
| code-text | The text of a block, without running it (parse eval reads it back) |
{1 2 +} code-text |
| code= | Whether two blocks are the same code, token by token, without running them | {1 2 +} {3} code= |
| gensym | Push a fresh unique name for macro temporaries | gensym :t = |
| symbol | Make a symbol from a string or a quoted name | "x" symbol |
| lambda | Create a lambda function | {x y} {x y *} lambda |
| set | Assign a value to a variable | 3 :x set |
| = | Assign a value to a variable (alias for set) |
3 :x = |
| global | Assign a value to a global variable | 42 :answer global |
List Operators
| Operator | Description | Example |
|---|---|---|
| map | Apply a function to each element of a list | [1 2 3] {2 *} map |
| zip | Pair up the elements of two lists: [[1 4] [2 5] [3 6]] |
[1 2 3] [4 5 6] zip |
| filter | Filter a list based on a condition | [1 2 3 4 5] {2 % 0 ==} filter |
| at | Push element i, taking the container off (nth leaves it) |
[10 20 30] 1 at |
| slice | Elements a up to b as a new value of the same kind (list, array or string) | [10 20 30 40] 1 3 slice |
| all | Check if all elements of a list are true | [true true false] all |
| any | Check if any element of a list is true | [true false false] any |
| car | Return the first element of a list | [1 2 3] car |
| cdr | Return the list without its first element | [1 2 3] cdr |
| cons | Prepend an element to a list | 1 [2 3] cons |
| pair? | Return true if the value is a non-empty list | [1 2 3] pair? |
| apply | Expand list elements onto the stack and apply a function | [3 4] {+} apply |
| collect | Run a block on an empty stack and push what it left as one list | {1 2 3} collect |
| expand | Push the elements of a list, or each token of a code block as a one-token block | [1 2 3] expand, {a b} expand |
| concat-block | Join two code blocks | {a b} {c} concat-block |
Array and Dictionary Operators
| Operator | Description | Example |
|---|---|---|
| array | Make an array (changeable in place) from a list, a string or a block | [1 2 3] array |
| array-of | Make an array of n slots that all hold x | 5 0 array-of |
| at! | Store v at index i of an array: v a i at! |
9 [1 2 3] array 0 at! |
| append! | Add v at the end of an array: v a append! |
4 [1 2 3] array append! |
| pop! | Take the last element off an array and push it | [1 2 3] array pop! |
| dict | Make an empty dictionary | dict |
| get | Value of key k, or null when k is not there: d k get |
dict "k" get |
| put! | Store v under key k: v d k put! |
1 dict "k" put! |
| has? | True if the dictionary has key k | dict "k" has? |
| del! | Remove key k (an error when it is not there) | dict :d set 1 d "k" put! d "k" del! |
| keys | The keys as a list, in the order they were added | dict keys |
| values | The values as a list | dict values |
| same? | True if two arrays or dictionaries are the same one | dict dup same? |
Note: Blocks, lambdas and functions passed to map and filter must leave exactly one value for each element, and the block of reduce / fold exactly one value per step (collect several with listn). For side-effect-only iteration (e.g. printing each element), use dolist instead.
Type Operators
| Operator | Description | Example |
|---|---|---|
| int | Convert to int | 3.7 int |
| float | Convert to float | 3 float |
| str | Convert to string | 42 str |
| bool | Convert to bool | 1 bool |
| complex | Convert to complex number | 3 complex |
| type | The kind of a value as a symbol (:int, :str, :array, :null, ...) |
42 type |
| int? | Returns true if the value is an int (not bool) | 42 int? |
| float? | Returns true if the value is a float | 3.14 float? |
| str? | Returns true if the value is a string | "hello" str? |
| symbol? | Returns true if the value is a symbol | :x symbol? |
| bool? | Returns true if the value is a bool | true bool? |
| complex? | Returns true if the value is a complex number | 1+2j complex? |
| list? | Returns true if the value is a list (an array is not) | [1 2 3] list? |
| array? | Returns true if the value is an array | [1 2 3] array array? |
| dict? | Returns true if the value is a dictionary | dict dict? |
| code? | Returns true if the value is a code block, without running it (a lambda is not) | {1 2 +} code? |
| null? | Returns true if the value is null (an empty list is not) | null null? |
| empty? | Returns true if a list, an array, a string or a dictionary has no elements | [] empty? |
| number? | Returns true if the value is a number (int, float, or complex) | 42 number? |
Other Operators
| Operator | Description | Example |
|---|---|---|
| sub | Wrap the top value in a one-token code block | :x sub |
| subn | Wrap the top n values in one code block | 1 2 2 subn |
| ans | Push again the result of the last input that left one | ans |
| include | Run another script here, in this scope, every time | "file.stk" include |
| require | Run another script once per interpreter | "file.stk" require |
| eval | Run a code block, a symbol or a lambda (text: parse first) |
{3 5 +} eval, '3 5 +' parse eval |
| echo | Print the specified value to stdout without adding it to the stack | 3 4 + echo |
| echo-err | Print the specified value to standard error, shown as echo shows it |
"warning" echo-err |
| print-err | The same as echo-err |
"warning" print-err |
| printc-err | Print the specified value to standard error without a newline | "." printc-err |
| read | Read one line from the console and push it as a code block | read |
| read-line | Read one line of standard input as a string (null at the end) | read-line |
| read-stdin | Read the rest of standard input as one string | read-stdin |
| args | The command-line arguments after the script name, as strings | args |
| script-path | The absolute path of the running file (null in the REPL) | script-path |
| script-dir | Its directory | script-dir |
| main-script-path | The absolute path of the script the command line ran (null in the REPL) | main-script-path |
| main? | Whether the running code is that script (false inside an included file) | main? |
| read-stdin-bytes | The rest of standard input as a list of bytes (0 to 255) | read-stdin-bytes |
| write-stdout-bytes | Write a list of bytes to standard output | [72 105 10] write-stdout-bytes |
| write-stderr-bytes | Write a list of bytes to standard error | [72 105 10] write-stderr-bytes |
| write-to-file | Write the top element of the stack to a file | 3 "output.txt" write-to-file |
| append-to-file | Append the top element of the stack to a file | 3 "output.txt" append-to-file |
| read-from-file | Read the contents of a file and push it to the stack | "input.txt" read-from-file |
| file-exists | Check if a file exists | "file.txt" file-exists |
| read-bytes | Read a whole file as a list of bytes (0 to 255) | "image.png" read-bytes |
| write-bytes | Write a list of bytes to a file | [72 105] "out.bin" write-bytes |
| path-join | Join two parts of a path (the right one may be a list) | "data" ["in" "x.txt"] path-join |
| list-dir | The names inside a directory, in name order | "." list-dir |
| path-base | The last part of a path | "a/b/c.txt" path-base |
| path-dir | The directory part of a path | "a/b/c.txt" path-dir |
| encode | The bytes of a string in an encoding | "hi" "utf-8" encode |
| decode | The string a list of bytes spells in an encoding | [104 105] "utf-8" decode |
Constants
| Constants | Description |
|---|---|
| e | Euler's number |
| pi | Pi |
| phi | Golden ratio |
| tau | Tau |
| nan | Not a number |
| inf | Infinity |
true, false and null are literals rather than constants: they are values, never names, and cannot be redefined.
Metadata
Release files for pystacker 1.25.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pystacker-1.25.1.tar.gz | 269.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pystacker-1.25.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 420.7 kB
Release files / pystacker-1.25.1.tar.gz
| Download URL | pystacker-1.25.1.tar.gz |
|---|---|
| Size | 269.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fcad2fc7beb057c9000f1cc3fc3650d3c3b74f0b105ef2a8ba58c6dbea081d8f
|
|
BLAKE2b-256 checksum How to use checksums |
229383d669e0f803c09406b36e4cb2639ba742276bc4b9fdc34ba3dfc07cac1a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|
Release files / pystacker-1.25.1-py3-none-any.whl
| Download URL | pystacker-1.25.1-py3-none-any.whl |
|---|---|
| Size | 150.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a37086d38017ada31aaa17463ca682a120ada38c71fb6e1b049c1b952e92b69b
|
|
BLAKE2b-256 checksum How to use checksums |
32f9af6d6a728817ed22e94fb3a373c8a908c67cbcfa801c0e8531936bd4b062
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|
Security for pystacker 1.25.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.