Skip to content

Pryzma Programming Language Reference

Pryzma is a simple, interpreted programming language written in Python. It features dynamic and static typing, list and dictionary operations, flow control, modules, references, structs, memory management, pragmas, and more.


Variables and Types

Pryzma has strings, integers, floats, booleans, lists, dictionaries, and tuples:

a = "a"
value = 1
value2 = 1.1
condition = True
condition2 = False
my_list = []
my_dict = {}
my_tuple = ()

Static Typing

You can define statically typed strings and integers:

int x          # default value is 0
int x = 6
int x = "7"    # "7" will be converted to integer

str a          # default value is ""
str a = "a"
str value = 7  # 7 will be converted to string

Local Variables

Define local variables inside functions:

/func{
    loc a = 10;
    print a // prints 10
}
print a // error, 'a' is not defined

List Operations

  • Indexing:
    print list[0] outputs first element.
  • Append:
    append a, "text" adds "text" to list a.
  • Copy:
    copy b, a appends elements from b to a.
  • Pop:
    pop a, 0 removes index 0 from list a. print pop a returns and removes the last element.
  • Remove:
    remove a, 5 removes value 5 from list a.
  • Move:
    move(0, 1, a) moves element at 0 to 1 in list a.
  • Swap:
    swap(0, 2, a) swaps elements at indexes 0 and 2 in list a.

Dictionary Operations

  • Push:
    push(dict_name,key,value) adds key/value.
  • Get:
    a = get(dict_name,key) retrieves value.
  • Pop:
    dpop(dict_name,key) removes key/value.

Type Conversion & Checking

  • Convert:
    variable = int(variable)
    variable = str(1)
    variable = list(a)
  • Type:
    type(a) returns the type as string.
  • Is Number:
    isanumber(a) returns True if number.
  • Length:
    len(a) returns length of string/list.
  • Index:
    index(a, 3) returns index of value in a given list.

Arithmetic Operations

result = 10 + 5
total = 2 * 3
difference = 8 - 4
quotient = 10 / 2

Input/Output

input age:"age:"
print "Your age is: ", age

without prompt:

input age
print "Your age is: ", age
  • \n for new line.

String Slicing

a = "text"
print a[1:-1]  # prints 'ex'

Flow Control

stop

Ends program execution.


Loops

For Loop

for(i, 1:4){
    print i
    print "\n"
    print "hello"
    print "\n"
}

output will be:

1
hello
2
hello
3
hello

Foreach Loop

a = ["h","e","l","l","o"]

foreach(element,a){
  print element
}

While Loop

while(a==b){
  print i
}

All loops support break.


Increment & Decrement

i = 0
i++
print i  # 1

i--
print i  # 0

x += 5
x -= 5

Importing Modules

use file_path

after import you can use functions from the imported file

if you add ./ before the file path it will look for the given file in the same folder as the file, and if you just give the module name it will look for it in the packages folder that is created while installing packages with ppm (under packages/package_name/package_name.pryzma). you can also import files over http and https like this:

use https://raw.githubusercontent.com/IgorCielniak/Pryzma-packages/main/net/net.pryzma

in imported files if there is a function "on_import" it will be called instantly when importing the file

you can invoke a pragma for the duration of the import with the 'with' keyword:

use ./module.pryzma with #nan

this will import the module.pryzma file without automatic namespacing, after the import automatic namespacing will be set back to the same value it was before the import (either True or False), any valid pragma can be used

you can import a particular function from a file using the following construction:

from file use function

you can also import functions from files called diffrently than the module like this:

use core::lib

this would import packages/core/lib.pryzma

it also supports submodules

use std::core::lib

this would import packages/std/core/lib.pryzma

you can also import .prz files the same way, but in a module the main file still must have the .pryzma extension, at least for now, you can go about it this way, make a main file with the .pryzma ext. that just has an insert directive pointing to a .prz file and this should work (not tested)

importing specific functions:

from ./module.pryzma use func, func2 with #nan

if functions are allready prefixed with module name and dot than you should pass the function names without the prefix and the #nan pragma

you can also import modules with aliases like this:

use math as m

it would import all functions from the module math with the prefix 'm' instead of 'math', aliases work with the 'with' keyword.


Comments

// This is a comment
print "Hello" // Inline comment

If Statements & Ternary Operators

if(variable==variable2){
  ...
}elif(variable>variable2){
  ...
}else{
  ...
}

if(a){ ... } // if a is boolean

print a if 1 else b

Functions

  • Declare:
/func_name{ ... }
  • Call:
@func_name("text")
@func_name(a)
@func_name //no args
  • Return:
/func{return "hello"}
a = @func
print a //outputs hello

Defer Blocks

/func{
    print "start\n"
    defer{
        print "deferred\n"
    }
    print "end\n"
}
@func

output:

start
end
deffered

Methods


a = [1, 2, 3, 4, 5]

/add{
    x = *args[0]
    c = args[1]
    l = args[2]
    for(i, 0:l){
        x[i] += c
    }
}

a.@add(4, len(a))

this calls the function 'add' as a method on the list 'a', the reference to 'a' is passed as the first argument and 4 as well as len(a) are respectively as the second and third arg


print a //outputs [5, 6, 7, 8, 9]

System Calls

sys("os command")
command = "echo world"
sys(command)

output:

world

File Read/Write

content = file_read("path/to/file.txt")
file_write("path/to/file.txt", mode, content)

Membership Test

a = [1,2,3]
b = in(a, 2) // True

Deleting Variables & Functions

delvar("var_name")
delfunc("func_name")

Disable/Enable Keywords

disablekeyword("print", "use")
enablekeyword("print", "use")
print 5 //error: deleted keyword 'print'
use math //error: deleted keyword 'use'

Exec & Eval

exec("a = 10", "print a")
result = exec("@func")
eval("expression")
result = eval("expression")

CLI Arguments

Run with:

python Pryzma.py file_path d df

Access as argv, __file__, interpreter_path.


FFI: Calling Python & C Functions

call(file_path, func_name, args)

for example:

call("/home/user/test2.py", "func2", "hello from python")

and file "/home/igor/test2.py" content being:

def func2(self, arg):
        print(arg)

It will print "hello from python" self is passed as well so you can access the variables and basicly all of the interpreter's thinks so you can define variables etc. it needs to accept self even if you don't need it

There also is ccall() that allows you to call c functions the same way, you just pass the .so file path so for example:

ccall("./so.so", "add", 2, 2)
extern:

extern "libc.so.6" malloc
extern "libc.so.6" free
or

extern "libc.so.6" {
    malloc
    free
}
than you can call them like any other function

ptr = @malloc(1)

@free(ptr)

Time Operations

start_time = timenow
wait(5)
end_time = timenow
print end_time - start_time

Custom Keywords

Python extension example:

def start(line):
  if line.startswith("customfunc"):
    text = line[10:].strip()
    print(text)
    return True #indicate that the line was handeled
  return False #indicate that the line wasn't handeled
load("path/to/ext.py")
customfunc hello

This code will load the module and print "hello" the arg for the load() function can also be a variable the funcion in the python module code needs to be named "start"


Try/Catch Blocks

try{
  print a
}catch(38){
  print "a is not defined"
}
err # contains error code

The variable 'err' will hold a value of 38 after executing this code becouse the print instruction tries to acces variable a that is not created so it fails with status code 38 the err variable is reset to zero only after a next try block is successfully executed or when changed manually

all error codes with their exploanation are avaliable in the interpreter


String Functions

  • Split:
    splitby("M", a)
    resplit("\d+", e)
  • Replace:
    replace(a,"d","f")
  • Strip:
    strip(a)
  • Join:
    join("", a)
  • Char Conversion:
    char(123) outputs {
  • Starts/Ends With: startswith("A", a) endswith("Z", a)

Random Numbers

a = randint(1,100)

self explainatory


Python Evaluation/Execution

result = pyeval("1==1", stack) //stack is optional
pyexec("print('hi')", stack) //stack is optional

a = "world"

py{
    "print('hello')"
    "print(a)"
}

Defined Check

a = defined(b) //returns True if b is defined else returns False

Pragmas

#preproc = np,fd,nan
#nan, #an, #fd, #np, #rs, #rds
#esc, #desc
#fail, #df
#replace "a" -> "b"
#replace "[0-9]+" -> "X"
#insert "file.pryzma"
#shell
fd - forward declare functions
np - don't preprocess
nan - no automatic namespacing
an - enable automatic namespacing
rs - make retrun statements stop the function execution
rds - make retrun statements don't stop the function execution (default)
esc - allow for escaping of locals (explained bellow)
desc - don't allow for escaping of locals (explained below)
fail - explained below
df - explained below
gc - enable garbage colection (default)
ngc - disable garbage colection

#nan, #an, #fd, #np, #rs and #rds etc. are also supported

#esc and #desc pragmas control if local variables can escape their scope, and here is what i mean by that:

#esc

/func{
    loc a = 9
    return &a
}

b = @func

print *b //outputs 9, value of the local a

*b = 7 //sets the local a to 7

print *b //outputs 7 the new value of the local a

in the following example the #esc pragma is set and as you can see the function returns a reference to its local variable, in this case when the #esc pragma is set the variable would be still accesable via the reference and you can read from it and write to it, if you don't set, the #esc pragma you will get an error saying that the referenced variable a no longer exists, and thats true becouse it doesn't just go out of scope, if #esc isn't set the locals are deleted by the garbage collector after the function finishes execution, the garbage colector also removes the locals that had a valid reference but no longer do becouse the variable holding the reference got deleted etc. so basicly as of my understanding pryzma now has normal garbage collection. I hope that everythink is understandable after this long but i think needed exploanation

there also is #fail and #df

by default pryzma programs don't terminate after an error

when #fail is set any error will terminate the program execution when #df is set errors will not terminate the program execution

#replace a -> b

replaces all instances of a with b, a and b can be strings, numbers, variables etc. no limits, replaces in all source code no matter what, example:

#replace "a" -> "b"

print "aabbaa" //prints bbbbbb

replace also supports regural expressions:

#replace "[0-9]+" -> "X"

a = 123
print 45

changes to:

a = X
print X

#shell pragma:

not a lot to say, it opens the repl during the program execution allowing for inspection of vars, funcs and structs as well as executing arbitrary pryzma code

insert:

#insert "file.pryzma"

the #insert pragma inserts code from another file in to the source code, and thats basicly all it does, it supports both regular .pryzma files as well as .prz files. (btw under the hood it doesn't insert anythink it just gets the file content and interprets it, same result easier to implement but i wanted a cool name (compleatly not inspired by jai))

more pragmas will be added in the future


Inline Assembly

a = 10
b = 20
c = 0
asm{
    "mov rax, a"
    "add rax, b"
    "mov c, rax"
}
print c  # 30

for now it only supports integers


File & Directory Operations

mkdir(path)
makedirs(path)
rmdir(path)
removedirs(path)
copy(src, dst)
copyfile(src, dst)
move(src, dst)
rename(src, dst)
remove_path(path)
symlink(src, dst)
unlink(path)
dirname(path)
is_file(path)
is_dir(path)
exists(path)
file_size(path)
join_path(path1, path2 ...)
abs_path(path)
basename(path)
split_ext(path)
list_dir(path)
walk(path)
is_link(path)
read_link(path)

Match Statement

x = 4
match(x){
    case(1){
        print "x is 1"
    }
    case(2){
        print "x is 2"
    }
    case(_){
        print "default case"
    }
}

Assert Statement

x = @func()
assert x==0, "func return value wasn't 0"

Structs

simple example should say everythink:

struct Person {
    name
    age = 30
}
p = Person {"Some, name", 30}
print p.name, "\n"
print p.age, "\n"

p2 = Person {} //fields without a default value will be initialized to None
p2.name = "Alice"
print p2

print fields(p2) //['name', 'age']

btw. it works on dictionaries and just returns the keys (its not that structs are basicly dictionaries, noooo, not at all)

  • Nested Structs:
struct Address {
    street
    city
}
struct Person {
    name
    address
}
p = Person {
    "Alice"
    Address {
        "123 Main St"
        "Paris"
    }
}
print p.address.city
  • Destructuring:
struct Person {name, age}
p = Person {}
p.name = "some name"
p.age = 7
{name, age} = p
print name, "\n", age

Using Statement

struct Person {
    age = 20
    name = "Alex"
}
a = Person {}
using a
print age  //20
print name //Alex
  • To export to global: using global args[0]
  • Locals only: using args[0]

using exports all fields of the struct to the local namespace by default

struct t {val = 0}
i = t {}
i.val = 45
/func{
    using args[0]
    print val //prints 45
}
@func(&i)

print val //error, variable not found

in this form of the example:

struct t {val = 0}
i = t {}
i.val = 45
/func{
    using global args[0]
    print val //prints 45
}
@func(&i)

print val //prints 45

the fields are exported to the global namespace and later avaliable to the print


References & Dereferencing

Pryzma supports references, which allow you to create variables that point other va

Creating a reference (&):

You can create a reference to a variable using the & operator. For example:

x = 8
p = &x

In this example, p is a reference to x.

Dereferencing (*):

You can get the value of a variable that a reference points to using the * operator

x = 8
p = &x
print *p //prints 8

Assigning a new value to a referenced variable:

You can change the value of the original variable by assigning a new value to the d

x = 8
p = &x
*p = 10
print x //prints 10

Assigning a new reference:

You can also assign a new reference to a variable. For example:

x = 8
y = 12
p = &x
p = &y
*p = 14
print y //prints 14

Automatic dereferencing:

When you access a field of a reference to a struct, the interpreter will automatically dereference the variable for you. For example:

struct Person {
    name
    age
}

p = Person{"Alice", 30}
p_ref = &p
p_ref.name = "Bob"
print p.name //prints "Bob"

Function References & Lambdas

function references:

/func{ return 2+2 }

a = &func

print @a //prints 4

a in this case can be passed as an argument to another function etc. its just a variable from the point of the backend its automaticly dereferenced on call, @*a is not supported but b = *a; @b is becouse in pryzma you can dereference all values and if the value isn't a reference it will just be unaffected i think its all self explanatory

you can check if somethink is a function reference with is_func()

/func{return 2+2}
a = &func
b = @func
print is_func(a) //True
print is_func("func") //True
print is_func("b") //False

lambdas:

a = /{return 2+2}

print @a //outputs 4

in this case a is actually a function pointer and it points to a function that gets created that is named _lambda+random id

/func{a = args[0]; @a}

@func(/{print "a"})

output:

`a`

Lazy Expressions

a = 7
b = ~a
a = 6
print b //6

Memory Management

By default pryzma provides 4kb of memory and you can use two functions to manipulate it:

write(0x0000, ascii("g"))
print char(read(0x0000)) //g

for now those are all of the memory manipulation functions, for more you can download the mem library using ppm


Hot Patching Functions

/func{
    try{ print a }
    if(err!=0){
        patch("func", "func2")
        @func
    }
}
/func2{print "gg"}
@func

As you can see the variable a is not defined anywhere so print a would fail and the err would not be equal to 0 anymore so this would run the patch() that would swap the list of instructions for func to the list of instructions of func2 and than call func once more and the output of all of this code would be 'gg', so this is basicly hot swapping function bodies at runtime might prove usefull, or compleatly useless, we will see


Isolates

In pryzma there is a concept of isolates that are basicly isolated instances of the interpreter, isolates allow for execution of pryzma code without modifying the main program interpreter's stack. This is how to use isolates: to run some code in an isolate you use the isolate() function, pretty obvious isn't it?, the function accepts pryzma code and optionally you can pass an allready existing isolate so you can reuse it later, if you don't pass an existing isolate a new one will be created automaticly. Now you may ask 'how to get an isolate?', and here comes to play the new_isolate() function, it creates and returns a new isolate, this is how to use it:


isolate = new_isolate()

and than calling the isolate function itself:


isolate("a = 1;print a", isolate=isolate)

this would run the code using our existing isolate, if you want to run the code in a new isolate you can just do this:


isolate("a=1;print a")

this would automaticly create a new isolate for you you can also use the isolate function as an rvalue:


value = isolate("a=5;b=6;return a+b")

value would be equal to 11 but both variables a and b wouldn't be created on the main stack but only on the inner stack of the new automaticly created isolate if you create an isolate your self, than you can reuse it and acces its inner stack:


isolate = new_isolate()

isolate(isolate=isolate,"a=1")

b = isolate(isolate=isolate, "return a")

print b

the output would be 1 with some pyeval() and pyexec() magick you can pass the main programs stack to the isolate and other interesting stuff, but i will leave it to be explored by you ;)


JSON Operations

json_data = '{"name": "John", "age": 30}'
data = json_decode(json_data)
print data.name //John
data = {"name": "John", "age": 30}
json_data = json_encode(data)
print json_data //'{"name": "John", "age": 30}'

Running Programs

run("file.pryzma")

License & Author

Pryzma is licensed under the MIT License. Created by Igor Cielniak.


Support

For support, please open an issue on the GitHub repository.

https://github.com/IgorCielniak/Pryzma