| .. | ||
| README.md | ||
| srandard_library.md | ||
Reid Language
This is where the documentation for this language will go, describing the features, syntax and standard library of the language as best as I have time and motivation to write.
Documentation is presented in a formal grammar and an example, syntax-highlighted with Rust, because it's close enough.
Syntax
Syntax for Reid is very much inspired by rust, and examples of the language can be found in the examples-folder.
In Reid modules (or files) on the top-level are comprised of imports, type definitions, binop-definitions and functions.
In formal grammar
<module> :: (<import> | <type-definition> | <binop-definition> | <function>)*
Table of Contents:
- Common tokens
- Imports
- Type definitions
- Binary operation Definitions
- Function definitions
- Statement
- Expression
Common tokens
Common token used throughout this document to express parts of grammar include:
<ident> :: [a-Z]([_a-Z0-9])*
<literal> :: <integer> | <real> | <char> | <string> | <bool>
<integer> :: <decimal> | <hexadecimal> | <octal> | <binary>
<real> :: [0-9]+ "." [0-9]+
<char> :: "'" <any-character> "'"
<string> :: "\"" <any-character>* "\""
<bool> :: "true" | "false"
// Any character (except "), or any character escaped
<any-character> :: [.] | "\" [.]
<decimal> :: [0-9]+
<hexadecimal> :: "0x" [0-9a-f]+
<octal> :: "0o" [0-7]+
<binary> :: "0b" [01]+
<type> :: <primitive-type>
    | "[" <type> ";" <integer>] "]"
    | "*" <type>
    | "&" [ "mut" ] <type>
<primitive-type> :: 
    "char" | "bool" |
    "u8" | "u16" | "u32" | "u64" | "u128" |
    "i8" | "i16" | "i32" | "i64" | "i128" |
    "f16" | "f32" | "f32b" | "f64" | "f80" | "f128" | "f128ppc"
<binop> :: "+" | "-" | "*" | "/" | "%" | "&&" | <cmp>
<cmp> :: "<" | "<=" | "==" | "!=" | ">=" | >"
<unary> :: "+" | "-" | "!"
Imports
Imports are used to import functions and types from other modules. In formal grammar the syntax for imports is
<import> :: "import" <ident> "::" <ident> ";"
An example importing the print-function from std would be
import std::print;
Type Definitions
Type definitions are used to define new types. Currently this only supports struct-types. In formal grammar:
<type-definition> :: <struct-definition>
Struct types
Struct (or Structure) types are aggregate types containing other types within struct fields. In formal grammar:
<struct-definition> :: "struct" <ident> "{" [ <field-def> ( "," <field-def> )* [ "," ] ] "}"
<field-def> :: <ident> ":" <type>
An example of a struct Test containing two fields first and second of
integer types.
struct Test {
    first: u32,
    second: u64,
}
Binary Operation Definitions
Reid has a feature where custom binary operations can be defined with a specialized syntas. Only pre-defined operators are allowed however. In formal grammar:
<binop-definition>: "impl" "binop" <param> <binop> <param> "-> " <type> <block>
<param> :: "(" <ident> ":" <type> ")"
(Block-syntax is defined formally with functions)
An example of a custom binary operator + between type u16 and u32 could be:
impl binop (lhs: u16) + (rhs: u32) -> u32 {
    return (lhs as u32) + rhs;
}
Function Definition
Rust syntax for defining functions is similar to rust. There are two types of functions:
- externfunctions which are defined in another module, used to define functions from outside modules such as- libc.
- localfunctions which are defined locally in the module in Reid. Their definition is contained within a- blockwhich contains a list of- statements.
In formal grammar:
<function-definition> :: <extern-function> | <local-function>
<extern-function> :: "extern" "fn" <signature> ";"
<local-function> :: [ "pub" ] "fn" <signature> <block>
<signature> :: <ident> "(" [ <params> ] ")" [ "->" <type> ]
<params> <param> ( "," <param> )*
<param> :: <ident> ":" <type>
<block> :: "{" <statement>* "}"
An example of a simple extern and local function definition would be:
extern fn puts(message: *char) -> i32;
fn main() -> u8 {
    return 7;
}
Statement
Statements in Reid is how you tell the program to do anything. Currently supported statements include:
- Let-statement to declare new variables
- Let-statements declare immutable variables by-default, but can declare
mutable variables with the mutkeyword similar to Rust.
 
- Let-statements declare immutable variables by-default, but can declare
mutable variables with the 
- Set-statement to re-set previously declared (mutable) variables to new values.
- Statements can also be simply expressions without intent to store the result.
- Return-statements to return values out of blocks or functions.
- For-loops to loop over a certain range of numbers
- While-loops to loop until a certain condition is no longer met.
In formal grammar:
<statement> :: <let> | <set> | <return> | <for> | <while> | <expr-statement>
<let> :: "let" [ "mut" ] <ident> "=" <expression> ";"
<set> :: <ident> "=" <expression> ";"
<expr-statement> :: <expression> ";"
<for> :: "for" <ident> "in" <expression> ".." <expression> <block>
<while> :: "while" <expression> <block>
<return> :: ( "return" <expression> ";" ) | <expression>
An example of each statement type would be:
let mut value = 5;
value = 6;
for i in 0..5 { }
while value < 5 { }
// "hard" return
return value; 
// "soft" return
value
Expression
Expressions in Reid are anything that can return a value, such as function calls, literals, or if-expressions. Types of supported expressions include:
- Variable name reference, to reference a value in a variable
- Borrow, which works similar to Rust but with a little less safety. In simple terms it creates a safer pointer-type than a regular pointer.
- Deref, which extracts the value out of a borrow.
- Literal, which is just some direct value, such as a number.
- Array-value, to declare a new array with a static length
- Struct-value, to declare a new value for a struct that has been defined earlier.
- Shorter way to declare arrays with a single initialized value.
- Indexing into an array-value
- Accessing a field in a struct-value
- Binary operations (such as add/sub/mult)
- Unary operations (such as !value or -value)
- Function calls, to invoke a predefined function with given parameters
- Block-expressions, which can return a value to the higher-level expression if they have a statement with a soft-return. Otherwise they return void.
- If-expressions, which can execute one of two expressions depending on the given condition.
- Casts to explicitly cast a value to another type.
Expressions can also always be surrounded by (paranthesis).
In formal grammar:
<expression> ::
    <variable> | <borrow> |
    <deref> | <literal> |
    <array> | <struct> |
    <indexing> | <accessing> |
    <binary-exp> | <unary-exp> |
    <function-call> | <block> |
    <if-expr> | <cast> |
    ( "(" <expression> ")" )
<variable> :: <ident>
<borrow> :: "&" [ "mut" ] <ident>
<deref> :: "*" <ident>
<array> :: "[" <expression>* "]"
<struct> :: <ident> "{" [ <field> ( "," <field> )* [ "," ] ]
<field> :: <ident> ":" <expression>
<indexing> :: <expression> "[" <integer> "]"
<accessing> :: <expression> "." <ident>
<binary-exp> :: <expression> <binop> <expression>
<unary-exp> :: <unary> <expression>
<function-call> :: <expression> "(" [ <expression> ( "," <expression> )* ] ")"
<if-expr> :: "if" <expression> <expression> [ "else" <expression> ]
<cast> :: <expression> "as" <type>
An example of each expression in-order is:
varname // Variable
&varname // Borrow
*borrowed_varname // Deref
[ varname, 5, 7 ] // Array
Test { first: 4, second: 15 } // Struct
array[0] // Indexing
test.first // Accessing
7 + value // Binop
!bool_value // Unary
func(value, 14) // Function call
if varname {} else {} // If-expression
value as u32 // cast
(value + 2) // Binop within parenthesis