Skip to content

Sample codes

RandomMaerks edited this page Feb 27, 2026 · 21 revisions

Demonstrating functions and arguments

Hello, World!

Make a simple Hello, World! program.

Input code:

OUTPUT interpreter

PRINT "Hello, World!"

Output:

Hello, World!

A simple program to print out the good old "Hello, World!" message which also illustrates how the OUTPUT and PRINT functions work!

Order of operations

Calculate 9 + 3^2 * 4 – (6 % 3).

Input code:

OUTPUT interpreter

DO sub(sum(9, pro(pow(3, 2), 4)), mod(6, 3)); var1
RETURN var1

Output:

var1 = 45

In math, the convention is: (1) parentheses, (2) exponentiation, (3) multiplication and divsion, (4) addition and subtraction. When following the conventional order of operations:

  1. Parentheses: (6 % 3) gets carried out first to get 0.
  2. Exponentiation: 3^2 gets carried out to get 9.
  3. Multiplication and division: 3^2 * 4 or 9 * 4 gets carried out to get 36.
  4. Addition and subtraction: 9 + 3^2 * 4 or 9 + 36 gets carried out to get 45. Then, 9 + 3^2 * 4 – (6 % 3) or 45 - 0 gets carried out to get 45.

However, in Piège, the order of operations starts from the most deeply-nested operator to the least. Consequently, the order of operations might look something like this:

1   sub( |  |   |   |  |  |   |     |  |  | )
2       sum(|   |   |  |  |   | ), mod(|  | )
3           9, pro( |  |  |   |)       6, 3
4                  pow(|  |), 4
5                      3, 2

The values inside each operator have been shifted down to the next line. By the order of operations in Piège, the operator whose values are in the lowest line will be carried out first, then the second lowest will be carried out.

The process might look like this:

    # Step 1
1   sub( |  |   |  |  |    |  |  | )
2       sum(|   |  |  |), mod(|  | )
3           9, pro(|  |)       6, 3
4                  9, 4

    # Step 2
1   sub( |  |  |     |  |  | )
2       sum(|  | ), mod(|  | )
3           9, 36       6, 3

    # Step 3
1   sub(|   |)
2       45, 0

    # Step 4
1   45

It is easily seen that pow(3, 2) was calculated a whole 2 steps before mod(6, 3) was, even though the modulo operation was technically inside the parentheses. Though, the original expression using regular math syntaxes can be rewritten as ((9 + ((3^2) * 4)) – (6 % 3)), and the logic would be the same as in Piège.

Assign value to variable

Calculate the circumference and area of a circle of radius 5, given pi = 3.14159.

Input code:

OUTPUT interpreter

DO 3.14159; pi
DO 5; radius

DO pro(2, pi, radius); circumference
DO pro(pi, pow(radius, 2)); area
RETURN circumference
RETURN area

Output:

circumference = 31.41590
area = 78.53975

The DO function has two sections: operation and output. When the operation is a single float value, the function basically acts as an assignment statement, with the output variable being assigned the value in the operation section.

In this example, the variable pi gets assigned a value of 3.14159, and the variable radius gets assigned a value of 5. These variables are then used in the following operations.

Assign variable to another

Swap the values of variable a = 5 and b = 6.

Input code:

OUTPUT interpreter

DO 5; a
DO 6; b
RETURN a
RETURN b

DO a; c
DO b; a
DO c; b
RETURN a
RETURN b

Output:

a = 5
b = 6
a = 6
b = 5

In addition to assigning values to a variable, you can assign the value of one variable to another just by calling the variable name.

If necessary, include DELETE c at the end to remove the temporary variable c.

Self-assign new values

Increase an index i from 1 to 5 step 2 and printing out the index after each increment.

Input code:

OUTPUT interpreter

DO 1; i
RETURN i

DO sum(i, 2); i
RETURN i

DO sum(i, 2); i
RETURN i

Output:

i = 1
i = 3
i = 5

In addition to assigning one variable to another, you can assign a variable to the value of itself. Well, that wouldn’t be of much use unless you use that variable in a math operator like in Example 5, where the variable i is assigned the value of the sum of the value of itself and 2. Or, in programming syntax, i = i + 2 or i += 2.

Looping methods

Return the value of 2^i for each i in range from 0 to 5 (including 5) step 1.

In this example, we’ll try to execute this using 3 methods:

  • Method 1: Use SETCUR with type = char
  • Method 2: Use SETCUR with type = line
  • Method 3: Use MOVECUR with type = char

Method 1

Input code:

OUTPUT interpreter

DO 0; i

DO pow(2, i); a
RETURN a

DO sum(i, 1); i
SETCUR char = 29; notmore(i, 5)

Output:

a = 1
a = 2
a = 4
a = 8
a = 16
a = 32

In a way, the function works similarly to the do–while statement in many programming languages, except it doesn’t loop back to the start of the code block but instead loops to (a) a specific character index, or (b) a specific line in the whole code.

In this case, the function set the cursor at char = 29, which is where our DO pow(2, i); a line starts. And, when we run the code, the loop will work! Every function from line 7 to line 11 will run 6 times in total, and the output should have 6 return values for the variable a.

In versions prior to 2.0, there used to be a function called getIndex[] (or GETINDEX if it was still here) that would have taken the starting index of the next line. However, this function was deprecated in version 2.0, thus making this method a lot less useful and intuitive.

Method 2

Input code:

OUTPUT interpreter

DO 0; i

DO pow(2, i); a
RETURN a

DO sum(i, 1); i
SETCUR line = 5; notmore(i, 5)

Output:

a = 1
a = 2
a = 4
a = 8
a = 16
a = 32

Instead of using type = char, this now uses the much more intuitive type = line, which sets the cursor to the start of the exact line in the whole code.

Method 3

Input code:

OUTPUT interpreter

DO 0; i

DO pow(2, i); a
RETURN a

DO sum(i, 1); i
MOVECUR line = -4; notmore(i, 5)

Output:

a = 1
a = 2
a = 4
a = 8
a = 16
a = 32

The SETCUR line = index method can break if you add more code before the referenced line of code. Instead, you can use the more errorproof MOVECUR line = amount method.

In the example code above, the MOVECUR function checks if i ≤ 5 is True and moves 4 lines back if it is. Since i starts at 0 and only increases by 1 at line 8 before MOVECUR, MOVECUR will be activated precisely 5 times before i surpasses 5 and the condition returns False.

General math calculations

The following examples will also include:

  • Runtime from the Piègeur interpreter
  • Ratio of that runtime to 1 second, or ratio of 1 to # of times the same program can be executed back-to-back in 1 second

Fibonacci sequence

Calculate the first 20 Fibonacci numbers.

Input code:

OUTPUT interpreter

DO 20; numberToReturn
DO 1; index
DO 0; a
DO 1; b
    
DO sum(a, b); result
RETURN result
DO b; a
DO result; b
    
DO sum(index, 1); index
MOVECUR line = -6; notmore(index, numberToReturn)

Output:

result = 1
result = 2
result = 3
result = 5
result = 8
result = 13
result = 21
result = 34
result = 55
result = 89
result = 144
result = 233
result = 377
result = 610
result = 987
result = 1597
result = 2584
result = 4181
result = 6765
result = 10946

Runtime: 0.00377s
Ratio: 0.00377 : 1 or 1 : 265.25199

Prime numbers

Output prime numbers between 1 and 100.

Input code:

OUTPUT interpreter

DO 2; n
RETURN n

DO 3; n
DO 100; maxToCheck
    
DO 2; i
DO mod(n, i); remainder
MOVECUR line = 6; equal(remainder, 0)

DO sum(i, 1); i
MOVECUR line = -4; less(i, n)

RETURN n
DO sum(n, 1); n
MOVECUR line = -9; notmore(n, maxToCheck)

Output:

n = 2
n = 3
n = 5
n = 7
n = 11
n = 13
n = 17
n = 19
n = 23
n = 29
n = 31
n = 37
n = 41
n = 43
n = 47
n = 53
n = 59
n = 61
n = 67
n = 71
n = 73
n = 79
n = 83
n = 89
n = 97

Runtime: 0.48984s
Ratio: 0.48984 : 1 or 1 : 2.04148

This one might be hard to follow, so let me explain.

DO 2; n and RETURN n simply outputs 2, which we can consider to be a prime number.

After that, the cycle starts from the number n = 3 and should end at maxToCheck = 100. For each n, an index i is set at value 2, and then we calculate n % i and assign to remainder.

If remainder == 0, then the first MOVECUR moves the cursor 6 lines forward to DO sum(n, 1); n to increase n by 1. If, however, the condition is incorrect, then that MOVECUR will not be activated, and we move on to DO sum(i, 1); i to increase index i by 1.

The second MOVECUR function checks if i < n. If True, then it moves the cursor back 4 lines to executing the modulo operation. Otherwise, it continues to RETURN n, which will give the supposed prime number.

Afterwards, n increases by 1, and the third MOVECUR checks if n ≤ maxToCheck. If True, it moves to the line that sets i back to 2, and the cycle continues. If not, the cursor will move to the end of the math operator.

Basically, if any number n is divisible by any number i from 2 to itself - 1, it is not a prime number, and we skip to the next n to process. Otherwise, we consider that n to be a prime. When we haven't reached the maxToCheck value, we reset i back to 1, and we do all that checking once again. When we eventually reach the max value, we do nothing and let the program run to the end.

Derivative

Calculate the derivative of f(x) = x^2 at x from range -4 to 4 step 1.

Input code:

OUTPUT interpreter

DO -4; x0
DO sum(x0, pow(10, -15)); x

DO pow(x0, 2); y0
DO pow(x, 2); y

DO round(rat(sub(y, y0), sub(x, x0)), 4); derivative
DO sum(x0, 1); x0
DO sum(x0, pow(10, -15)); x
RETURN derivative
MOVECUR line = -7; notmore(x0, 4)

Output:

derivative = -8
derivative = -6
derivative = -4
derivative = -2
derivative = 0
derivative = 2
derivative = 4
derivative = 6
derivative = 8

Runtime: 0.00227s
Ratio: 0.00227 : 1 or 1 : 440.52863

Definite integral

Calculate the definite integral of f(x) = x^2 from 0 to 2.

Input code:

OUTPUT interpreter

DO 0; a
DO 2; b
DO a; x
DO 0.0001; step
DO 0; defIntg

DO pow(x, 2); y
DO sum(pro(y, step), defIntg); defIntg
    
DO sum(x, step); x
MOVECUR line = -4; notmore(x, b)
    
DO defIntg; result
RETURN result

Output:

result = 2.666866670000

Runtime: 3.21193s
Ratio: 3.21193 : 1 or 1 : 0.31134

For your information, the runtime of this program has massively improved through different versions of Piègeur (from 41s (v0.1.5) to 27s (v0.2.0), to now 3s (v0.3.0)). Not that it's anything important, just a fun thing to know.

Approximate analytic functions

Calculate e^0.25 using Maclaurin series to degree 10.

Input code:

OUTPUT interpreter

DO 0; n
DO 0.25; x
DO 0; result
    
DO sum(result, rat(pow(x, n), fact(n))); result
    
DO sum(n, 1); n
MOVECUR line = -3; notmore(n, 10)
    
RETURN result

Output:

result = 1.284025416687735384313728023

Runtime: 0.00213s
Ratio: 0.00213 : 1 or 1 : 469.48357

Pi approximation

Approximate pi by counting points inside the circle.

Input code:

OUTPUT interpreter

DO 0; points
DO 1; radius
DO 0.01; step

DO sub(0, radius); x
DO sub(0, radius); y

DO sum(pow(x, 2), pow(y, 2)); value1
MOVECUR line = 2; more(value1, 1)
DO sum(points, 1); points

DO sum(y, step); y
MOVECUR line = -5; notmore(y, radius)

DO sum(x, step); x
MOVECUR line = -10; notmore(x, radius)

DO rat(rat(points, pow(radius, 2)), 10000); piApprox
RETURN piApprox

Output:

piApprox = 3.1417

Runtime: 16.8628s
Ratio: 16.8628 : 1 or 1 : 0.0593

Similarly to the definite integral example, this program used to run very slowly (100+s (v0.1.5) to 52s (v0.2.0).

Visualising with visual[]

The following examples are done using the Piègeur interpreter which has its own data formatting. Other interpreters might have different visual interpretation and formatting.

Magnitude / quantity

Input code:

OUTPUT interpreter

DO 12; a
VISUAL quantity = |; 15
VISUAL magnitude = <>, before; a

Output:

||||||||||||||| 15
12 <><><><><><><><><><><><>

In Piègeur, the value will either go before or after the string of characters that represents the magnitude or quantity of that value, depending on your second option of before or after.

Notice how in the second VISUAL, there can be two characters.

Matrix (brackets)

Input code:

OUTPUT interpreter

VISUAL matrix = brackets, 2; 1, 4, 2, 6, 4, 7
VISUAL matrix = brackets, 2; 1, 4, 2, 6, 4

Output:

⎡ 1  4  2 ⎤
⎣ 6  4  7 ⎦
⎡ 1  4 ⎤
⎣ 2  6 ⎦

The first VISUAL correctly displays a 2×3 matrix with 6 entries.

The second only has 5 input entries. Consequently, only 4 entries are accepted to make a 2×2 matrix.

Matrix (parentheses)

Input code:

OUTPUT interpreter

VISUAL matrix = parentheses, 3; 5, -2, 7, 0, 0.4, 7, 4, 6, -5, 8, -26, 1.7

Output:

⎛ 5    -2   7    0   ⎞
⎢ 0.4  7    4    6   ⎥
⎝ -5   8    -26  1.7 ⎠

The matrix is scaled based on the length of the value with the longest string representation.

Bar graph & quantity graph

Input code:

OUTPUT interpreter

VISUAL graph = bar, scaled; "2023", 70, "2024", 48, "2025", 102
VISUAL graph = bar, literal, noBetween; "2023", 70, "2024", 48, "2025", 102

Output:

    ▕
2023▕███████████████████████████▍ 70
    ▕
2024▕██████████████████▊ 48
    ▕
2025▕████████████████████████████████████████ 102
    ▕
    ▕
2023▕▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌ 70
2024▕▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌ 48
2025▕▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌ 102
    ▕

In graph type bar of scale type scaled, all values are scaled based on the biggest value. In Piègeur, the largest value by default has 40 block characters, and the other values are scaled in relation to it. You can change this in option 4.

In graph type quantity of scale type scaled, all values show the full quantity representation, even if it's a million. You can also set the padding in option 3 to change the gap in between bars.

Table

Input code:

OUTPUT interpreter

VISUAL table = row, 3; "x", 1, 2, 3, 4, "x^2", 1, 4, 9, 16, "x^3", 1, 8, 27, 64
VISUAL table = column, 3; "x", 1, 2, 3, 4, "x^2", 1, 4, 9, 16, "x^3", 1, 8, 27, 64

Output:

┍━━━━━┯━━━━┯━━━━┯━━━━┯━━━━┑
│ x   │ 1  │ 2  │ 3  │ 4  │
┝━━━━━┿━━━━┿━━━━┿━━━━┿━━━━┥
│ x^2 │ 1  │ 4  │ 9  │ 16 │
┝━━━━━┿━━━━┿━━━━┿━━━━┿━━━━┥
│ x^3 │ 1  │ 8  │ 27 │ 64 │
┕━━━━━┷━━━━┷━━━━┷━━━━┷━━━━┙
┍━━━━━┯━━━━━┯━━━━━┑
│ x   │ x^2 │ x^3 │
┝━━━━━┿━━━━━┿━━━━━┥
│ 1   │ 1   │ 1   │
┝━━━━━┿━━━━━┿━━━━━┥
│ 2   │ 4   │ 8   │
┝━━━━━┿━━━━━┿━━━━━┥
│ 3   │ 9   │ 27  │
┝━━━━━┿━━━━━┿━━━━━┥
│ 4   │ 16  │ 64  │
┕━━━━━┷━━━━━┷━━━━━┙

Object parameters should always go in the same order: name / label -> variable / value, then the next name / label -> variable / value, and so on. This way, if you change the orientation from row to column, the table will adjust accordingly.

Additionally, if the number of rows / columns chosen is different from the intended number of names / labels, the table might create an extra row / column to fit the extra values, and the formatting might break.

Disobeying limitations

This section is dedicated to scripts that do things outside of what Piège should really be used for. Things that are not (yet) directly supported by Piège, but can be achieved by ridiculously convoluted methods.

I know this is really solvable by just implementing more features, but I don't want to stray away from the main focus (calculator disguised as a programming language). Also, figuring these things out is infinitely more fun, and it makes learning about Piège more easily.

List creation and reader

Input code:

OUTPUT interpreter

# Initial start and end indices
DO 0; startIndex
DO 5; endIndex

# Initialise index
DO startIndex; index

# Read list
DO 0; list
DO sum(pro(dif(sign(dif(index, 0)), 1),  6), list); list
DO sum(pro(dif(sign(dif(index, 1)), 1), -1), list); list
DO sum(pro(dif(sign(dif(index, 2)), 1),  3), list); list
DO sum(pro(dif(sign(dif(index, 4)), 1),  1), list); list
DO sum(pro(dif(sign(dif(index, 5)), 1), -9), list); list

# Print list
PRINT "list[", index, "] = ", list

# Add index & continue looping to list reader if index <= endIndex
DO sum(index, 1); index
SETCUR line = 11; notmore(index, endIndex)

Output:

list[0] = 6
list[1] = -1
list[2] = 3
list[3] = 0
list[4] = 1
list[5] = -9

Alright, what do I mean by a "list"? A list is an ordered and changeable set of numbers, with each element of the set having assigned an index, usually starting from 0 for the leftmost element.

There is no concept of a list in Piège, at least not in any versions of it.

What I'm doing here is simulating a list by making a chain of DO functions, with each one acting as a "signal" that takes in an outside index and returns a value when that index matches its own assigned index. When the chain finishes, the final output of list should only be the value corresponding to that outside index.

To understand this better, I'll disect one of the lines of code:

DO sum(pro(dif(sign(dif(index, 0)), 1), 6), list); list

DO  |   |   |    |   |    |    |    |   |    |   ; list
   sum( |   |    |   |    |    |    |   |    |  )
       pro( |    |   |    |    |    |   |), list
           dif(  |   |    |    |    |), 6
               sign( |    |    | ), 1
                    dif(  |    |)
                        index, 0

Let's look at the pro(df(sign(dif(index, 0)), 1), 6) first. This one contains two key components: the index detector (left part of the pro( operator), and the set value (right part).

The index detector is dif(sign(dif(index, 0)), 1), which returns 0 if index == 0 is True, and 1 if not. How?

  • The sign( operator is an operator that takes in one value and returns 1 if that value is positive, -1 if it's negative, and 0 if it's zero. This is the only operator that returns only set values. When you combine with the abs( operator, it only returns exactly 2 set values: 1 and 0.
  • The dif( operator inside sign( acts as a comparator between the outside index index and the set index 0. When index == 0 is True, dif( returns 0. Otherwise, it just returns the difference, which is always positive. However, since this operator is inside of sign(, we can convert all non-zero difference values into 1. This turns the whole sign(dif( chunk into a binary operator.
  • However, the sign( operator actually returns 0 if True and 1 if False. I needed them to switch (you'll see why later), so I simply took the difference between the sign( and 1.

All of this equate to: If index = set index, the detector returns 1. If not, the detector returns 0.

This is the binary operation stripped to its minimum if you need to use it:

DO 1; value1
DO 2; value2
DO dif(sign(dif(value1, value2)), 1); returnValue
RETURN returnValue

What I can do next is take the result of that index detector and multiply it by the value I want to store, which in the line of code above I've put 6. When the index detector returns 1, I can retrieve the value 6, and when it returns 0, I get 0.

So, that one piece of code is done; why so many lines of the same code? Since each has a set index and a corresponding value, you can actually have multiple indices, with each one saving its own value. And since only one index can make the condition of one index detector be True, when the interpreter go through all the sum( operators, what you ultimately get is the precise value at the exact index that you requested. When I set the index to 1, it looks through all of the codes, and returns what the value is set for the index 1.

Alright, that was crazy. But the rest of the code is actually very normal. The first three DO functions just put preset values in variables to reference in the following code, the PRINT function prints... and the last few increases the index and moves to the chain if its own condition is True (index between startIndex and endIndex).