Overview

Pyr is a programming language based on Python.

Features

  • Static type checking
  • Can be interpreted
  • Can be compiled into a native object file (using LLVM)

Quick Start

Pyr is written in Rust. To compile Pyr you need to have a Rust compiler and LLVM version 14.0.x.

To compile Pyr:

cargo build --release

To interpret a Pyr program:

pyr run foo.pyr

To compile a Pyr program:

pyr compile foo.pyr
clang foo.o -o foo

Turing Completeness

We have proved a Turing Completeness for Pyr by implementing Rule 110 which you can see here.

License

Pyr is under the license of Apache-2.0.

Command Line Interface

pyr [OPTIONS] <SUBCOMMAND> [SUBCOMMAND_OPTIONS] <source_file>

Options

--help | -h

Prints the help message.

Links a dynamic/shared library.

Subcommand

Run

Run/Interprets the program line by line.

Compile

Compiles the program into an object file.

Options

--out | -o

Specifies the output file name.

--exe | -e

Links the object file into an executable.

Literals

Pyr currently supports the following literals:

Integer

Integers in Pyr are 64 bit signed integers.

123

Type: int

Boolean

Booleans in Pyr are secretly integers with true as 1 and false as 0.

true # returns 1
false # returns 0

Type: int

String

Strings in Pyr are delimited by double quotes.
For escaping things like double quotes, you can use a backslash.
Multiline strings are also supported.

"Hello, World!"
"Newlines\n"

To see what are the supported escape sequences are, look at Supported Escape Sequences

You can also index into strings.

"Hello, World!"[0] # returns "H"

Type: string

Arrays

Arrays in Pyr starts with the types of the elements, followed by the elements delimited with square brackets and separated by commas.

int[] # returns an empty array of integers
int[123, 456] # returns an array of integers with two elements
string["Hello, World!"] # returns an array of strings with one element

When initializing an array, you can specify an element then clone the element by the given length.

int[0; 30] # returns an array of 30 zeros
int[1, 2, 3; 10] # returns int[1, 2, 3, 1, 2, 3, 1, 2, 3, 1]

You can index into an array with the index.

int[1, 2, 3; 10][1] # returns 2

Type: [type; len] where type is the type of the elements and len is the length of the array.
Note: Currently, arrays cannot be converted to strings.

Keywords

Keywords in Pyr are as follows:

if

if evaluates its condition and if it's truthy, it executes its body.

if 34 < 56:
  print("34 is less than 56\n") # Executes this line

else

else executes its body if the previous if's condition is falsy.

if 100 < 50:
  print("100 is less than 50\n") # Skips this line
else:
  print("100 is greater than 50\n") # Executes this line

else can also be continued with another if:

if 100 < 50:
  print("100 is less than 50\n") # Skips this line
else if 100 > 50:
  print("100 is greater than 50\n") # Executes this line
else:
  print("100 is equal to 50\n") # Skips this line

while

while will execute its body until the condition is falsy.

i = 0
while i < 10: # Executes its body 10 times
  print(i + "\n")
  i += 1

and

and with the same type of operands will return the left value if either is falsy and will return the right value if both are truthy.
and with different types of operands will return false if either is falsy and will true if both are truthy.
and will short circuit if left hand side is falsy.

10 > 2 and 10 < 20 # result is true
"hello" and "world" # result is "world"
123 and "" # result is false

or

or with the same type of operands will return the left value if it is truthy otherwise it will return the right hand side.
or with different types of operands will return true if either is truthy and will return false if both are falsy.
or will short circuit if left hand side is truthy.

10 > 2 or 10 < 20 # result is true
"hello" or "world" # result is "hello"
123 or "" # result is true

func

func defines a function. For more information, see Functions.

func square(x: int):
  print(x * x)

ret

ret returns a value and breaks out of the function.

func square(x: int) -> int:
  ret x * x

print(square(2)) # prints 4

break

break breaks out of the current loop.

while true:
  print("hello")
  break

extern

extern brings an external function into the current scope.

extern func exit(status: int)

exit(0)

Arithmetic

Addition

+ with integers will add the numbers together.

1 + 2 # result is 3

+ with strings is concatenation.

"Hello, " + "World!" # result is "Hello, World!"

Subtraction

- will subtract the first number with the second.

1 - 2 # result is -1

Multiplication

* with integers will multiply the numbers together.

1 * 2 # result is 2

* between strings and integers is string repetition.

"Hello" * 3 # result is "HelloHelloHello"

Division

/ with integers will divide the first number by the second.

30 / 5 # result is 6

Negation

- will either make its right hand side negative or positive.
! will return true if its right hand side is falsy, and false if it is truthy.

-1 # result is -1
!true # result is 0

For more information about truthiness and falsiness, see Truthiness.

Exponent

^ will multiply its left hand side by itself the number of times on the right hand side.

2 ^ 3 # result is 8

Modulo / Remainder

% will return the remainder of its left hand side subtracted by its right hand side multiple times until it is less than its original value.

30 % 5 # result is 0
4 % 3 # result is 1
12 % 5 # result is 2

Comparison

Less Than

< will return true if its left hand side is less than its right hand side.

1 < 2 # result is true

Greater Than

> will return true if its left hand side is greater than its right hand side.

1 > 2 # result is false

Less Than or Equal

<= will return true if its left hand side is less than or equal to its right hand side.

2 <= 2 # result is true

Greater Than or Equal

>= will return true if its left hand side is greater than or equal to its right hand side.

2 >= 2 # result is true

Equal

== will return true if its left hand side is equal to its right hand side.

1 == 2 # result is false
"Hello" == "Hello" # result is true
"Hello" == "World" # result is false

Not Equal

!= will return true if its left hand side is not equal to its right hand side.

1 != 2 # result is true
"Hello" != "Hello" # result is false
"Hello" != "World" # result is true

And and Or

Look at Keywords

Bitwise

<<

<< shifts the bits of the left operand to the left by the number of bits specified by the right operand.

a = 2 # 00000010
b = 3
a << b # 00010000 or 16

>>

>> shifts the bits of the left operand to the right by the number of bits specified by the right operand.

a = 2 # 00000010
b = 1
a >> b # 00000001 or 1

&

& performs a bitwise AND operation on the two operands.

a = 2 # 00000100
b = 3 # 00000011
a & b # 00000000 or 0

|

| performs a bitwise OR operation on the two operands.

a = 2 # 00000100
b = 3 # 00000011
a | b # 00000111 or 7

Truthiness

Integers

An integer is truthy if it is equal to 1.

!!0 # result is false
!!1 # result is true
!!2 # result is false

String

A string is truthy if it is not empty.

!!"" # result is false
!!"Hello" # result is true

Operator Precedence

The following are the precedence levels and associativity of the operators in the Pyr language. The higher the precedence, the faster the operation will be performed.

Operator Description Associativity Precedence
(), [] Function Call, Indexing Left-to-right 11
-, ! Negation Right-to-left 10
^ Exponent 9
*, /, % Multiply, Division, Modulo Left-to-right 8
+, - Addition, Subtraction 7
<<, >> Bitwise Shift Left, Bitwise Shift Right 6
<, <=, >, >= Less Than, Less Than or Equal, Greater Than, Greater Than or Equal Not chainable 5
==, != Equal, Not Equal 4
& Bitwise AND Left-to-right 3
| Bitwise OR 2
and, or Logical And, Logical Or 1
= Variable Assignment Right-to-left 0

For more information about each of the operators, see Arithmetic, Comparison and Bitwise.

Variable

Declaration and assignment in Pyr use the same syntax.

foo = "bar"

To access a variable, use the following syntax.

print(var_name) # prints "bar"

Functions

Functions in Pyr are defined with the func keyword. Function arguments are explicitly typed, and are separated by commas.

The maximum count of arguments/parameters for function declaring/calling is 255.

func foo(a: int, b: int) -> int:
  print(a + b + "\n")

To call a function, simply use the function name and parentheses with the arguments.

foo(1, 2) # prints 3

Functions and variables have different scopes. So you can have both a variable named foo and a function named foo.

foo = "bar"
func foo():
  print(foo + "\n")

foo() # prints "bar"
foo = "baz"
foo() # prints "baz"

Functions can also return a value.

func sum_of_squares(a: int, b: int) -> int:
  return a ^ 2 + b ^ 2

print(sum_of_squares(2, 3)) # prints 9

To overload a function, you can define a function with the same name and different arguments.

func foo(a: int) -> string:
  return "int"

func foo(a: string) -> string:
  return "string"

foo(1) # returns "int"
foo("foo") # returns "string"

Comments

Comments in Pyr starts with a # and go to the end of the line.

# This is a comment

# print("Hello World")
# ^ Skipped

Multiline comments are currently not supported but are planned.

Supported Escape Sequences

Below are the supported escape sequences in Pyr.

Escape SequenceDescription
\ then newlinea trailing slash for escaping newline at the end
\'single quote
\"double quote
\\backslash
\aaudible bell
\bbackspace
\fform feed
\nnewline
\rcarriage return
\ttab
\vvertical tab
\ then 3 octal numbersoctal value in ascii
\x then 2 hex numbershexadecimal value in ascii
\u then 4..=6 hex numbersunicode char

Functions

print

print prints the v to the console without appendding a newline.

Signature:

def print(v: int | str):
  ...

Example:

print("Hello, World!")
print(123)

sqrt

sqrt returns the square root of the n

Signature:

def sqrt(n: int) -> int:
  ...

Example:

sqrt(64) == 8
sqrt(16) == 4

to_int

to_int converts s into an integer then returns it

Signature:

def to_int(s: str) -> int:
  ...

Example:

to_int("123") == 123