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 lista. - Copy:
copy b, aappends elements frombtoa. - Pop:
pop a, 0removes index0from lista.print pop areturns and removes the last element. - Remove:
remove a, 5removes value5from lista. - Move:
move(0, 1, a)moves element at0to1in lista. - Swap:
swap(0, 2, a)swaps elements at indexes0and2in lista.
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
\nfor 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.