Introduction
rigz is a dynamic scripting language written in Rust, it's heavily inspired by Ruby, Kotlin, and Rust; the best way to start using it is by installing it with cargo or you can try it using the online repl.
cargo install rigz
For now there are no pre-built binaries and rigz must be built from source, if you need how to install rust you can follow this guide.
Getting Started
Rigz is a mix of expressions and statements, programs outside of the REPL must end with an expression. Currently only one file is supported but this will change in future versions as imports are improved.
Hello World
There is no main method in Rigz, the default scope is the main method.
puts "Hello, World" # prints "Hello, World", returns none
Expressions
Expressions include everything that isn't a statement; values, do/end blocks, if/else, unless, variables usages, function calls, casting as Type
, binary expressions, and unary expressions.
The most important difference in Rigz compared to most modern programming languages is that there is no operator precedence, expressions are evaluated from left to right so 1 + 2 * 3
is not equal to 3 * 2 + 1
. Parenthesis should be used if that is not the desired behavior. Future versions may allow you to select an option to change how the parser works (Operator precedence or right to left expression evaluation), but that is not currently planned.
Variables vs Function calls
Variables are looked up through the current call frame, or scope, if they are not present here the parent is checked. The exception to this is if a function exists with the same name, a match will be attempted. If a match is not found this will result in a parse error, future versions will fall back correctly.
Binary Operators
+
- Add-
- Subtract*
- Multiply/
- Divide%
- Modulo, remainder of two values^
- Xor, same behavior as Or, unless both sides are truthy or falsy then it returns none- This may change to a bitwise xor in future versions
|
- bitwise or||
- Or, returns the first truthy value (errors are false)&
- bitwise and&&
- And, returns the first falsy value>>
- Bit Shift right<<
- Bit shift left<=
- Less than or equal<
- Less than>
- Greater than>=
- Greater than or equal?:
- Elvis operator, if value is none or an error use the right hand side
To see how values of types interact with different types check the definitions here, documentation will be updated once this is finalized.
Unary Operators
-
- Negative, for ranges and numbers this makes the value negative, booleans are inverted, and all other types remain the same.!
- Not, converts the value to a boolean and inverts it (the only exception are errors which continue to be errors and evaluate to false,Any.is_err
should be used here)
Statements
Statements include assignments, function definitions, type definitions, trait definitions, imports and exports.
Assignments
There are three ways to assign a variable in rigz:
- In-place assign, this is an immutable variable definition or a mutable re-assignment:
a = 2
let
assign, this is also an immutable variable:let a = 2
. In future versions this will allow shadowing.mut
assign, this is meant for mutable variables.mut a = "abc"
If you attempt to overwrite an immutable variable the program will exit with an error, this is one of the few terminal errors and in future versions will be caught at parse time.
Function Definitions
Function definitions use the fn
keyword to define a function, argument types and return types are optional, here are some examples:
By default the return type of standard functions is Any?!, it may be none or an error.
fn hello
"hi there"
end
fn hello = 'hi there'
hello # "hi there"
Rigz also supports extension functions, they can be mutable or immutable.
fn mut Number.foo -> mut Self
self *= 3
self
end
mut a = 2
a.foo.foo.foo # a = 54
fn foo(number: Number) -> Number
number * 2
end
a = 3
1.to_s + foo a # "1" + 6 = 7
Immutable extension functions have the same default return type as standard functions, however if not included the default return type of mutable extensions is mut Self. In future versions the return type will match the type of the last expression.
Built In Functionality
There are two built-in functions, puts
and log
.
Puts
puts
works the same as the ruby counterpart and accepts a list of arguments to print out, each separated by a comma and followed by a new line.
Log
log
is a top level function used by the Log module that takes in an argument for the level to log at (either a symbol or string), a string template, followed by the arguments to log.
For example: log :info, "Hello {}", :world
Modules
Modules are written in Rust to provide extra functionality through a trait
(interface).
There are five built in modules:
- Std - the standard library module which is automatically imported
- File - Functions to interact with files
- JSON - Read and write to json
- Log - Write log messages
- VM - Interact with the VM directly, these methods may change the VM so use with caution.
Upcoming Changes
In future versions you will be able to overwrite the puts
and log
methods, then the most appropriate function will be used when called.
It's extremely likely that some functionality in the Std module will be moved to an object type in future versions but the design has not been decided yet.
The following modules are planned:
- Http - an http client with a function similar to Javascript's fetch as well as dedicated methods for each of the HTTP methods.
- Query - A superset module for interacting with data (these are the most likely to become objects)
- Html - a module to interact with html, mainly for web scraping
- JSON - jq like functionality to query object data
- Migrations - Initially this will be part of the migrations CLI tool built on top of db_dsl, over time this is expected to become part of the core library.
@test
The @test
lifecycle is built-in to supply an easy way to write unit tests for your code and can be run with rigz test foo.rg
or the online REPL using the test button.
Consider the following snippet:
fn foo = 42
@test
fn test_foo
assert_eq foo, 43
end
Upcoming features
To support parameterized testing for a function you'll be able to use @test.assert* lifecycle methods like so:
@test.assert_eq([], 42)
fn foo = 42
Currently only one lifecycle is supported at a time, this will be changing in future versions to support functionality so that they can be combined where appropriate.