Skip to main content

Stacker: An RPN Calculator and Extensible Programming Language

Python License: MIT

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

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
      
  • 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 3 to x using set, and 5 to y using =. Both operators work identically.

      Note: The = operator is an alias for set and 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, :x and {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.
      • :x pushes the symbol x itself as a value, whatever a variable x holds. A symbol is a type of its own: :x "x" == is false, symbol? tells it from a string, str gives its text and symbol makes one from text ("x" symbol). : must be followed by a name (:1 and :{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 eval and {x} eval both do what writing x does.
      • 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 :name or {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 set looks x up and stops with "x is not defined", and 3 dup set runs dup and then set complains that it got 3 instead of a name. The same spelling rebinds any name, a built-in word's included (3 :dup set), and redefining a prelude word such as alias logs a warning, as does a defun that 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 set binds y. A literal is never a name (5 3 set and 5 true set are errors) and neither is a string (5 "x" set is an error too).
  • 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:

    1. Deferred Evaluation: Code blocks are not executed immediately when encountered
    2. Stack Interaction: Pushed onto the stack as single units in their raw form
    3. 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 name n, whereas n 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 if statement 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 >} {...} if tests 7 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 7 onto the stack as x equals 0.

    • ifelse Statement

      The ifelse statement 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 7 onto the stack as x equals 0.

    • cond Statement

      The cond statement evaluates condition-result pairs in order and executes the first matching branch.

      Syntax:

      {condition1} {result1} {condition2} {result2} ... n cond
      

      n is 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 as x is greater than 0. 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 do loop 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 dolist loop 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 seq can be used instead of [value1 value2 ... valueN] to efficiently describe a sequence with a constant step size.

    • times

      The times loop 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 while loop 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

      loop repeats a code block forever until break is 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

      break immediately 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 i reaches 5. Note that break inside if immediately stops execution of the current block — s i + :s set is not executed when i equals 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 named multiply that takes two arguments x and y and 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]
      
  • 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 what defmacro did.)
  • 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 = / set inside the body) is substituted into the body's block literals at the moment each literal is pushed: a symbol (:name, a gensym name) 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 :name written in the body is replaced by the symbol of the name that name holds (:t for a gensym name held in t, :double for 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.
      • gensym returns 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} macroexpand pushes 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: writing multiply pushes it, and eval calls it with the arguments 3 and 4 (3 4 multiply eval). To call a function by its bare name, define it with defun instead. Like a function, a lambda pushes every value its body leaves when it is called.

  • Include Scripts

    "file.stk" include runs 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" require does the same the first time, and nothing when the same file is required again. Read a library that keeps state with require, 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 (alias and mean for now). They behave like any other word; macros and funcs mark 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 hoge operator 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:

  1. Creating the Plugin: In the plugins directory, 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.

  2. Defining Functions and Classes: Define the necessary functions and classes in my_plugin.py.

  3. Defining the setup Function: In my_plugin.py, define a setup function that takes stacker as its only argument.

  4. Registering Custom Commands and Parameters:

    Within the setup function, use the register_plugin method of stacker to register custom commands. Additionally, you can also register custom parameters using the register_parameter method. 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.

  5. Reinstalling Stacker: Run the following command to reinstall Stacker:

    > pip install .
    

    Note: If you want to apply the plugin only temporarily, create a plugins directory 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.

  6. Using the Plugin: When Stacker is launched, the plugin will automatically be loaded, and the custom commands will be available for use.

  7. 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)

Source distribution for pystacker 1.25.1
File Size Uploaded Provenance
pystacker-1.25.1.tar.gz 269.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pystacker 1.25.1
File Interpreter ABI Platform
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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page