openbirch written in rust
Find a file
2025-03-20 14:56:09 +01:00
src fixed closures not wroking 2025-03-20 14:25:08 +01:00
.gitignore initial commit 2025-02-11 19:27:56 +01:00
adamina.ttf the ui is finally here 2025-02-14 00:10:39 +01:00
adamina.woff the ui is finally here 2025-02-14 00:10:39 +01:00
Cargo.lock cleaned up some code 2025-02-24 02:37:50 +01:00
Cargo.toml cleaned up some code 2025-02-24 02:37:50 +01:00
fibonacci fixed closures not wroking 2025-03-20 14:25:08 +01:00
flake.lock parsing 2025-02-20 02:08:16 +01:00
flake.nix cleaned up some code 2025-02-24 02:37:50 +01:00
mononoki.ttf the ui is finally here 2025-02-14 00:10:39 +01:00
README.md updated readme 2025-03-20 14:56:09 +01:00
test.txt did some grinding off camera 2025-03-20 12:27:27 +01:00
xits-italic.ttf AUGHR 2025-02-13 16:50:36 +01:00

Openbirch Rust

Openbirch rewritten in rust.

Very messy and bad, im gonna start the umpteenth rewrite soon.

Features

  • Constants (numbers)
    • Addition
    • Subtraction
    • Multiplication
    • Division
    • Arbitrary float precision
  • Sets (or lists, arrays, etc)
    • Definition
    • Multiplication with constants
    • Concattenation
    • Index operator
  • Functions
    • Native functions
    • User defined functions
    • Currying
    • Closures
    • Calling
  • Scopes
    • Shadowing variables
  • Ranges
    • Defining ranges (1..5 is a range of [1..5))
    • Iterating ranges
    • Evaluating ranges (very hacky, but defining a Set of length 1 with a range expands said range into the set, eg [1..3] becomes [1,2])
  • Loops
    • Infinite loops (loop ... end block)
    • For loops (for x in y ... end block)
    • Break keyword
  • Match expression
  • If expression
    • If expression
    • If-else expression

Wanted

  • More concise syntax, i dont like how it is right now
  • Fix stack overflow with recursive functions (internally convert to loops)
    • Tail call optimization
  • Generally implement more
  • Implicit multiplication (may not be possible to implement non-ambiguously)
  • Differentiation
  • Integration
  • Better error handling (i was lazy and skimped on errors, bad move)

Syntax

Every Openbirch program is made up of 0 or more expressions.

An expression is anything from a binary operation like 2+2 to function calls f(x) and even assignments x := 5.

All expressions return a value. For expressions that "dont" they return an Empty, which cant be used for anything and will error if used in other operations (eg. 2+{Empty}).

Definition

Variables can be defined with a define expression.

x := 5 # define x as 5

print(x+x) # prints 10

As openbirch is a symbolic language variables can be used before assignment

y := x^2

print(y) # prints x^2

x := 5

print(y) # prints 25

Scopes

Some expressions define a scope, where all defined variables inside will be deleted once the scope ends.

Expressions that create a scope include, but are not limited to,

  • If-else
  • Loop
  • Functions
  • Closures

If a variable is defined outside a scope it will be shadowed inside the scope. What this means is that inside the scope the variable will have the shadowed value, while outside the scope it will have the previous value.

x := 5 # Global scope

print(x) # prints 5

if true then    # Define a new scope
    x := 5000   # Shadow the value of x
    print(x)    # prints 5000
end             # Scope ends here

print(x) # prints 5

Assignment

Since you may want to change the value of a variable outside the current scope you can use an assignment rather than a definition.

x := 5

if true then
    x <- 500 # Assign to x rather than defining it and shadowing it
end

print(x) # prints 500

Set assignment

You can unpack a set by assigning it to another set, eg

[x,y] := [5,6]

print(x) # 5
print(y) # 6

The sets must have the same length, otherwise the expression will error.

This is useful for functions such as head

If-else

If expressions are defined as such

if {condition} then
    # 0 or more statements
end

and if-else as

if {condition} then
    # 0 or more statements
else 
    {expression}
end

Since if is an expression the body of the else branch can be another if expression, in which case only 1 end keyword is needed.

if {condition} then
    # 0 or more statements
else if {condition} then
    # 0 or more statements
end

Since all expressions evaluate to a value the value of the if expression is the resulting value of the last statement in the chosen branch, eg.

x := if true then
    # Evaluate some expressions
    2+2
    f(x)
    y := f(f(x))
    # Last expression is returned
    5
else
    0
end

print(x) # prints 5

if a branch is empty then Empty is returned.

Loops

Currently the only type of loop is an infinite loop.

loop
    # This will repeat forever
end

In order to avoid the system hanging you can use the break keyword to stop a loop.

x := 5

loop
    if x = 0 then
        break
    end

    x <- x-1
end

Functions

Functions in openbirch allow you to define operations on a set of inputs. Functions are a first class type and can be defined as a set of arguments and a body.

args -> body

Heres a few examples:

# A function that takes 1 argument and returns the argument multiplied by 2
f := x -> x*2

f(4) # 8

If you want to use multiple arguments then supply a set as the argument

# A function that takes 2 arguments and adds them together
f := [x,y] -> x+y

f(2,3) # 5

Assigning a function to a name is the most common way of using them. This can be done in 2 ways

# Define f with a value that is a function
f := x -> x*2

# This does the same, but is more intuitive for people from a math background
f(x) := x*2

Currying

Functions can return other functions. This creates a closure, that captures the variables passed to it.

add := x -> y -> x+y

add(2)(3) # this evaluates to 5

add2 := add(2) # This returns a closure where x is defined as 2

print(add2) # this prints ( (x: 2) => ([y] -> x+y))
            # What this shows is a closure that captures "x: 2",
            # where inside is another function that defines y

add2(5) # this is the same as add(2)(5) and returns 7.

Built-in functions

A few functions are defined by default. They provide additional functionality that has yet to be implemented into the language, such as syntax for indexing sets and getting their length.

  • length(Set) Returns the length of the given set
  • get(Set,Constant) Returns the value at the given index in the set
  • head(Set) Returns another Set consisting of [head, [tail]] where head is the first element in the set, and tail is all elements except the first element.
  • map(Function, Set) Returns a new set where every value has had the function applied to it.
  • unset(String) Undefines the given variable
  • set_float_precision(Constant) Sets the number of bits used for numbers. Default is 128bits
  • get_float_precision(<optional Constant>) Returns the current float precision. If a constant is given as the argument it will return the number of bits used for that constant.

Running

Linux

$ cargo run

Nixos

$ nix develop
$ cargo run

Windows

idfk, havent used that shit in years, but probably just cargo run.

MacOS

never used, but again probably cargo run