avp-rag-system / PseudocodeSyntax.md
BeefStewBibi's picture
Update grammar of AVPlang and README.md
37e2dba

[^1]: Last updated: Feb. 20, 2026

Purpose

This page[^1] describes the syntax of our custom pseudocode language, AVP. It's designed to be intuitive for students who already have experience in a common language such as Python or JavaScript. Therefore, you may be familiar with many parts of AVP, however there are still significant differences.

If you were looking for details on the AVP metadata syntax or information on the interpreter, they are covered here and here respectively.

Formatting

By convention, variables and functions are written in snake_case. Other formats such as camelCase are acceptable, but for the purposes of these reference materials, snake_case is used. Also by convention, bagel and toast are used in place of foo and bar. When more names are required for a demonstration, more bread types are used as variable names.

To write a comment inside of your program, use the familiar // to denote the start of your comment. This disregards anything else written after it for that line of code.. Remember this if word wrapping is enabled.

Like CPython, indentations in AVP are strictly 4-spaced. Tab characters are not acceptable under any circumstances; otherwise, this will lead to parse errors.

AVP files are saved in ASCII format with the extention .avp. The interpreter can work with UTF-8, but will only recognize ASCII characters; Non-ASCII characters will cause a ParseError (TODO: VERIFY).

Variables

Variables are used to store and manage data in your script. In AVP, variables are created dynamically, so you don't have to assign types manually.

At a minimum, variable names must contain at least one English letter (A-z). Once this requirement is satisfied, an acceptable name can include arabic numerals (0-9) and underscores (_). Symbols other than these are not accepted.

Assignment

To assign a variable to a value, use the familiar = operator. In the example below, the variable bagel is set to a value of 8

bagel = 8

As detailed below, assignments are performed dynamically and can occur several times for the same variable. This way, the value of any variable reflects its most recent assignment:

bagel = 8        // set value of bagel to 8
bagel = "pizza"  // set value of bagel to "pizza"

If you are unsure or do not need to assign a variable to anything, variables can be assigned to Null. This is particularly useful in scoping, but can also be used in other ways. Null variables indicate the absence of any data for that reference.

Types

In AVP, variables are one of three types at any given time: Number, Boolean, or String.

  • Number: considered to be an integer (e.g., 19) or a floating-point number (e.g, 2.71)
  • Boolean: A binary value, which must be either False or True.
  • String: a sequence of characters enclosed in double quotes (ex: "hello")

While AVP is intended to be flexible, it will not automatically convert types for you. When performing operations (such as comparisons), variables must be of the same type.

bagel = 1.9
toast = True

if (bagel <= toast):  // Invalid comparison!
    ...
end if

To make a string, simply assign a variable to your desired string of characters encased in quotes:

bagel = "abc"
toast = "abc123"

Arrays

There is another special kind of a variable called an array. This allows you to have one name that stores multiple values that can be retrieved later. Think of them how you might a List in Python:

bagel = arr[2, 5, 3, 9, 10]  // assign 'bagel' to an array of numbers

Like variables, arrays are flexible with their types. What this means is that not every entry in an array is required to be of the same type:

toast = arr[42, 2.71, True, "brot"]
bagel = arr[64, True]

For every entry entered into an array, a copy of what it represents is created at the specified location. So if you were to insert a variable bagel into an array toast:

toast = 10
bagel = arr[9, toast, 11]  // set bagel to [9, 10, 11]
toast = True            // bagel[1] would still be equal to 10, whereas toast is now of boolean value True

Creating large arrays of sequential numbers can be quite tedious. This can be simplified by referencing the sequential set of whole Numbers between two Numbers. In AVP this is performed using to:

toast = arr[1, 2, 3, 4, 5]
bagel = arr[1 to 5]            // bagel = [1, 2, 3, 4, 5]

You can also use a variable to do this:

bagel = 6
toast = arr[3 to bagel]    // toast = [3, 4, 5, 6]

An important detail to note though: Floating point numbers not allowed this syntax, algorithms containing them will give an error:

biscuit = arr[2.27 to 4]       // Invalid!

TODO: Creating a array by specifing the size of it. How it's filled isn't specified well! If a specific array size is needed, but you don't want anything inside it yet, you can initialize an empty array.

bagel = arr(5)    // bagel = [Null, Null, Null, Null, Null]

You can also specify what value to fill it with:

toast = arr(4, "jam")    // toast = ["jam", "jam", "jam", "jam"]
bagel = arr(2, False)    // bagel = [False, False]

Operations

Operations are the fundamental steps taken to compare variables or modify their data.

There are two kinds of operations in AVP: arithmetic and comparative.

Arithmetic

These are your familiar +, -, *, / operators, use them as you would in Python/JS.

There's also **, which may look strange if you're unfamiliar with Python. This is the "power of" operator, that will produce the exponent of a base to a given power. Sort of like the Math.Pow() method you may be familiar with but as a primitive operator.

bagel = 5 ** 2  // set bagel to 25

Arithmetic operations are performed using the standard Order of Operations TODO: verify

Comparative

To compare data with each other, you can use one of the following: ==, <, <=, >, >=, and, or. As expected, these can be used in the format a x b where x is any operator previously listed. Also notice how both the and and or operators are spelled out instead of a c-style syntax you might be used to.

To compare pieces of data (i.e. variables) with each other, they must be of the same type. So performing operations like below are invalid:

bagel = True
toast = "butter"
if (bagel or toast):
    ...
end if

Each comparison will return a boolean value of True or False, so operations like the following are invalid:

bagel = 2
toast = 1
if (bagel or toast):
    ...
end if

Since the comparison operators can only compare two data points at once, you can chain these operations together using parentheses, which will evaluate from the inside out.

// using the same defs from last example
bagel = 2
toast = 1
biscuit = 2
if ((bagel < toast) or (bagel <= biscuit)):
    ...
end if

In the above example: bagel will be compared to toast before biscuit, then the results of that will be compared.

Blocks

In order to perform complex branching or iteration, it's helpful to utilize AVP's Block structure. Conceptually, "blocks" are a set of predefined instructions that allow a variety of operations given specific conditions and settings. You can create branches through if and else statments, and repeat code using loops and functions, all of which are covered soon.

Structure

Fundamentally, blocks consist of three parts: declaration, body, terminator.

Blocks are created through a declaration, which identifies the operation and its arguments. Declarations[^2] begin with a statement, then parameters/condition encased in parentheses, finished by a colon. The body contains the set of instructions that are performed, denoted by indented lines directly under the declaration. Lastly, the block is terminated with an end, which just signifies the end.

some_declaration (argument_1):
    ...
end some_declaration

[^2]: For illustrative purposes, assume "some_declaration ()" is a valid AVP block declaration; it does not exist otherwise.

Using this structure, you can see how it can be turned into valid AVP:

while (bagel < 5):
    bagel = bagel + 1
end while

What is scope?

Like many languages, a significant quantity of AVP relies on the concept of "scoping". Simply, scope is the area where your data's identifier (i.e. variable name) can be recognized. (TODO: EXPAND). Each block has its own scope where variables can be assigned and modified, be it inside that one or in a child scope.

Data assigned outside of any block (i.e. global) are considered to be in the main scope, and can be accessed from anywhere in the program. In the example below, bagel can be accessed from anywhere, but toast cannot as it was declared in a different block:

bagel = 10

if (bagel == 10):
    toast = 4
    if (bagel < toast):
        toast = toast + 10  // toast was defined in a parent scope, so this is valid
    end if
end fun

if (bagel < 15):
    if (bagel < toast):  // toast isn't defined in this scope, so an error will occur
        ... 
    end if
end fun

Exiting a Scope

If needed, you can forcefully exit a scope by using break. When used, the process wil "break out" of whatever function or loop it's inside of, returning to the previous position in the parent scope. Using this will skip the remainder of that loop or function.

Exiting a function via return is detailed separately below.

Declarations

Now that you have learned the structure of blocks and declarations, this section provides detail on some that we've built into the language.

if

Simplest condition operator. When the boolean value is satisfied, the block is entered and executed.

if (<condition>):
    ...
end if

if-blocks can also be "chained" together via else if to act on different conditions accordingly:

if (bagel < toast):
    ...
else if (bagel == toast):
    ...
else:
    ...
end if      // only a single 'end if' is needed

while

These loops will repeat until their condition is no longer true.

while (bagel):
    ...
end while

for

In many cases when a for-loop is necessary (such as iterating through an array), you would want to use the "for-each" style:

toast = arr[1, 2, 3, 4, 5]
for bagel in toast: 
    ...
end for

In this example, we are initializing bagel to represent the index at the start of the array, for every iteration, bagel increases by one.

There is also a "regular" for-loop for extended algorithms:

for (bagel = 1, bagel < 10, bagel += 1):
    ...
end for

Functions

The backbone of AVP! Such as in other languages, functions allow you to re-use parts of your code with a simple call:

fun some_fn():        // declare a function some_function()
    bagel = 1
end fun

some_fn()

By default, functions return Null. That is, unless a return <some value> is explicitly used, a function will implicitly return Null . To illustrate, consider the example below:

fun some_fn(bagel):
    if (bagel < 3):
        return True
    end if

    // if bagel isn't less than three return Null implicitly
end fun 

Functions can only be defined in the Main scope.

fun some_fn():          // valid function declaration
    fun invalid_fn():   // invalid!
        ...
    end fun
end fun

It should also be mentioned that there is no "overloading" feature in AVP; No two functions can have the same name.

Glossary of Reserved Words

AVP has a set of "reserved words" such that they cannot be used in any capacity other than as determined by the language design. In other words: functions and variables cannot--under any circumstances--use these words alone as names. For your convenience, these words and their meanings are detailed below.

Word Definition
Null A special value that marks an absense of usable data.
True Constant Boolean value.
False Constant Boolean value.
or Logic operator that combines two Boolean expressions. Results to True if at least one expression is true, otherwise it resvolves as False.
and Logic operator which combines two Boolean expressions. Results to True if both expressions are true, False otherwise.
if Evaluates a boolean condition, executes the provided block when True, otherwise the block is skipped.
else Branches from a prior if-statement when the condition provided is False.
while Loop which is executed given a single boolean condition. Will continue looping until that condition is false.
for Loop that directly iterates over an array. Or set a start variable with a condition, then update that variable every iteration.
in used for mark a shared variable when utilizing for-each loops TODO: DEFINE THIS PRIOR// UPDATE: I think I have?
to Iteratively select whole numbers between values provided. These can either be constants or the value of a Number variable.
fun Function declaration. Allows you to write reusable code and call through a provided name and arguments.
break Terminate the process of a function or loop and return to the parent scope, skipping the remainder of that iteration or call. TODO: DEFINE THIS PRIOR BETTER // UPDATE: I think I have?
return Return a specified variable or constant as the result of a function, which is then terminated.
end Marks the end of a block. Must be followed by the type of declaration which it is ending at the same indentation level as that declaration