Introduction
Welcome to Ocypode programming language book, in this book you will learn how to use Ocypode programming language, and how to build your own Ocypode programs.
What is Ocypode?
Ocypode is a dynamic object-oriented programming language, and it's interpreted language, and its ownership memory management system. The interpreter is written in Rust. Ocypode is inspired by Rust, Python, and JavaScript. Lastly, Ocypode is a language for educational purposes, I made it to learn how to make a programming language, and it's the result.
Features
- Dynamic typing
- Object-oriented (WIP)
- Interpreted language
- Anonymous Functions
- Main-function entry point
- Pakcing parameters and unpacking arguments
- Owneship memory management system
Source code
You can find the source code of Ocypode in the GitHub repository.
License
Ocypode is licensed under the GPL-3.0 license. You can find the license in the LICENSE file
Getting Started
In this section, we will install the Ocypode interpreter and run a simple Ocypode program.
Installation
First, let's install the Ocypode interpreter using Cargo:
cargo install ocypode-lang --locked
You can also build Ocypode from the source by running these commands:
git clone https://github.com/TheAwiteb/ocypode-lang.git
cd ocypode
cargo build --release
Then you can find the binary in the target/release
directory.
You can also download the binary from the releases page and put it in your PATH
.
Basics
Each Ocypode program is a file with the .oy
extension. The file name is the name of the program. For example, if you have a file named hello.oy
, then the program name is hello
.
Main function
The main function is the entry point of the program, and it's the first function that will be executed. The main function is a function that has the name main
and it has argc
and argv
parameters. The argc
parameter is the number of arguments that are passed to the program, and the argv
parameter is an array of strings that contains the arguments that are passed to the program. And the main function returns an integer that will be the exit code of the program and it's optional (if you don't return anything, the exit code will be 0
).
Hello world
Note: The details of functions will be explained in the functions section. This just a simple program that prints
Hello world!
to the standard output.
~main<argc><argv>{<
println<"Hello world!">;
>}
Output
$ ocypode hello.oy
Hello world!
Comments
You can add comments to your Ocypode programs using /
for single-line comments, and /* */
for multi-line comments.
~main<argc><argv>{<
// This is a single line comment
/* This is
A multi line comment
*/
>}
Functions
In this section, we will learn how to use functions in Ocypode. We will learn how to define functions, how to call functions, and how to pass arguments to functions. We will also learn how to return values from functions, packing parameters and unpacking arguments.
Global Functions
Global functions are functions that are defined in the global scope. Global functions are defined with the ~
keyword and are useful when you want to use them in multiple places in your program.
Ownership
Global functions are owned by the global scope, and they are destroyed when the program ends.
Syntax
Global functions have a name, parameters, and body. before the name you need to add the ~
keyword, and after the name is optional to add the parameters, it will locks like this <param1><param2><param3>
, and after the parameters are the body of the function, and the body is the statements that will be executed when the function is called.
Function name
Each function has a name, and the is unique which means you can't have two functions with the same name. The function name can contain letters, numbers, and underscores, and it can't start with a number, also the function name is case-sensitive. And the function name must be snake case.
Examples
~foo{<
/*
function body
*/
>}
~foo<name>{<
/*
Anouther function body
*/
>}
~main<argc><argv>{< >}
The Error
Error(runtime::already_declared)
๐ฅ Identifier already declared
โญโ[test.oy:1:1]
1 โ ~foo{<
ยท โโฌโ
ยท โฐโโ Identifier `foo` already declared here
2 โ /*
3 โ function body
4 โ */
5 โ >}
6 โ
7 โ ~foo<name>{<
ยท โโฌโ
ยท โฐโโ And you tried to declare it again here
8 โ /*
9 โ Anouther function body
10 โ */
โฐโโโโ
help: Try renaming `foo` or removing the previous declaration.
Parameters
Each function can have parameters, and the parameters are optional. The parameters are the variables that you can use inside the function body. The parameters are defined after the function name, and before the body. The parameters are defined with the <param1><param2><param3>
syntax, each parameter is inside a pair of <
and >
.
Body
The body of the function is the statements that will be executed when the function is called. The body is defined after the parameters, starts with the {<
and ends with the >}
.
Return values
Global functions can return values, you can return a value using the return
keyword, and the value will be returned to the caller.
Examples
~foo{<
return 1;
>}
Call functions
To call a function, you need to add <>
after the function name if the function takes no arguments, or you need to add <arg1><arg2><arg3>
after the function name if the function takes arguments.
Examples
~foo<name><age>{<
println<format<"Hello {} you are {} years old"><name><age>>;
>}
~main<argc><argv>{<
foo<"Ahmed"><20>;
>}
Output:
Hello Ahmed you are 20 years old
Local Functions
Local functions are functions that are defined inside another function. Local functions are defined as global functions see here, but they are defined inside another function. Local functions are useful when you want to define a function that is only used inside another function.
Ownership
Local functions are owned by the function that they are defined in, and they are destroyed when the function ends.
Syntax
Same as global functions see here. The only difference is that local functions need to add a semi-colon ;
after the function body.
Examples
~main<argc><argv>{<
~foo<name>{<
println<format<"Hello {}"><name>>;
>};
foo<"Ahmed">;
>}
Output:
Hello Ahmed
Anonymous functions
Anonymous functions are functions that don't have a name, and they are defined inside another function. Anonymous functions are useful when you want to pass a function as an argument to another function, or when you want to return a function from another function.
Syntax
Anonymous functions have parameters and a body. The parameters are the same as local functions, and the body is the same as local functions. The only difference is that the function name is missing.
~main<argc><argv>{<
<param1><param2><param3>{</* The block */>};
>}
Take no arguments
There is a difference between local functions and anonymous functions if you want to make it take no arguments, you need to add <>
before the block.
Return values
Anonymous functions can return values, just like local functions, you can return a value by using the return
keyword.
~main<argc><argv>{<
<>{<return 1;>};
>}
Call anonymous functions
To call an anonymous function, you just need to call it like a [local function]. Add <>
to call it without arguments, or add <arg1><arg2><arg3>
to call it with arguments.
~main<argc><argv>{<
hello = <>{<return "Hello world!";>}<>;
println<hello>;
>}
Output:
Hello world!
Assign anonymous functions to variables
You can assign anonymous functions to variables, like the example below.
~main<argc><argv>{<
say_hello = <name>{<println<format<"Hello {}"><name>;>;>};
say_hello<"Ahmed">;
>}
Output:
Hello Ahmed
Packing Parameters
A pack parameter is a parameter that can be used to pack the rest of the parameters into a list. The packing parameter must be the last, and it has a star *
before the parameter name.
Examples
~foo<name><*childs>{<
println<format<"{} has {} childs"><name><len<childs>>>;
>}
~main<argc><argv>{<
foo<"Ahmad"><"Mohammed"><"Ali"><"Khalid">;
>}
Output:
Ahmad has 3 childs
A function with a packing parameter can be called with any number of arguments, the packing parameter will pack all the arguments into a list.
With anonymous functions
Packing parameters can be used with anonymous functions, the same as other functions.
~main<argc><argv>{<
<name><*childs>{<println<format<"{} has {} childs"><name><len<childs>>>;>}
<"Ahmad"><"Mohammed"><"Ali"><"Khalid">;
>}
Output:
Ahmad has 3 childs
Unpacking Arguments
A unpack argument is an argument that can be used to unpack a list into multiple arguments. The unpack argument must be an array, and it has a 3-dots ...
before the argument name.
Examples
~foo<name><*childs>{<
println<format<"{} has {} childs"><name><len<childs>>>;
>}
~main<argc><argv>{<
childs = ["Mohammed", "Ali", "Khalid"];
// The childs list will be unpacked into 3 arguments
foo<"Ahmad"><...childs>;
>}
Output:
Ahmad has 3 childs
With anonymous functions
Unpacking arguments can be used with anonymous functions, the same as other functions.
~main<argc><argv>{<
<name><*childs>{<println<format<"{} has {} childs"><name><len<childs>>>;>}
<"Ahmad"><...["Mohammed", "Ali", "Khalid"]>;
>}
Output:
Ahmad has 3 childs
Variables
A variable is a name that is used to store a value. The value can't be changed later, but you can change the value of the variable after is owned by another variable/parameter. see ownership.
Ownership
A variable is owned by the function that it is defined in, and it is destroyed when the function ends. After using a variable, you can't use it again because the value has been moved to another variable/parameter.
Syntax
A variable is defined by its name and its value. The name of the variable can contain letters, numbers, and underscores, and it can't start with a number, also the variable name is case-sensitive. And the variable name must be [snake case].
Examples
~main<argc><argv>{<
name = "Ahmed";
age = 20;
is_student = true;
height = 1.75;
grades = [100, 90, 80, 70, 60];
println<
format<"{} is {} years old, and he is {} a student, and his height is {}. His grades are {}">
<name><age><is_student><height><grades>
>;
>}
Output:
Ahmed is 20 years old, and he is true a student, and his height is 1.75. His grades are [100, 90, 80, 70, 60]
Data Types
A data type is a type of data that can be stored in a variable, parameter, or returned from a function. The data types in Ocypode are:
Strings
A string is a data type that can be used to store text, and it is used to represent a sequence of characters.
Syntax
A string is defined by a sequence of characters surrounded by double quotes ""
, and the characters can be any character except the double quote "
, and the backslash \
.
Escape Sequences
The backslash \
can be used to escape a character, and it can be used to escape the following characters:
"
: Escape the double quote.\
: Escape the backslash.n
: Escape the new line character.t
: Escape the tab character.r
: Escape the carriage return character.
Examples
~main<argc><argv>{<
name = "Ahmed";
address = "Cairo, Egypt";
println<format<"His name is {}\nHe resides in in \"{}\""><name><address>>;
>}
Output:
His name is Ahmed
He resides in "Cairo, Egypt"
Muilti-line Strings
A string can be defined in multiple lines, just like this:
~main<argc><argv>{<
name = "Ahmed";
address = "Cairo, Egypt";
println<format<"
His name is {}
He resides in in \"{}\"
"><name><address>>;
>}
Output:
His name is Ahmed
He resides in "Cairo, Egypt"
Or you can use \n
to define a new line, like the example above.
Integer
An integer is a data type that can be used to represent an integer number.
Syntax
An integer is defined as a sequence of digits. The digits can be any digit from 0
to 9
.
Examples
~main<argc><argv>{<
age = 20;
println<format<"Ahmed is {} years old"><age>>;
>}
Output:
Ahmed is 20 years old
Float
A float is a data type that can be used to represent a floating point number.
Syntax
A float is defined as a sequence of digits separated by a decimal point .
. The digits can be any digit from 0
to 9
.
Examples
~main<argc><argv>{<
pi = 3.14;
e = 2.718;
println<format<"pi = {}, e = {}"><pi><e>>;
>}
Output:
pi = 3.14, e = 2.718
Booleans
A boolean is a data type that can be either true
or false
, and it is used to represent a logical value.
Syntax
A boolean is defined by the keywords true
or false
.
Examples
~main<argc><argv>{<
is_student = true;
is_teacher = false;
println<format<"{} is a student, and {} is a teacher"><is_student><is_teacher>>;
>}
Output:
true is a student, and false is a teacher
Arrays
An array is a data type that can store multiple values in a single variable, and it is used to represent a list of values, values can be of any data type.
Syntax
An array is defined by a sequence of values surrounded by square brackets []
, and the values can be any data type, and they are separated by a comma ,
.
Examples
~main<argc><argv>{<
names = ["Ahmed", "Mohammed", "Ali"];
ages = [20, 21, 22];
persons = [
["Ahmed", 20],
["Mohammed", 21],
["Ali", 22]
];
println<format<"{}, {}, {}"><names><ages><persons>>;
>}
Indexing
Soon
Slicing
Soon
Nil
A nil
is a data type that can be used to represent a null value.
Syntax
A nil
is defined by the keyword nil
.
Examples
~main<argc><argv>{<
name = nil;
println<format<"name = {}"><name>>;
>}
Output:
name = nil
Built-in Functions
Built-in functions are functions that are built into the language. They are available to use without importing any modules. They are also available to use without having to declare them. They are just there. They are built-in.
print
built-in function
print
is a built-in function to print a value to the stdout. It takes one argument and prints it to the stdout. It does not add a newline character at the end of the output.
What can it print?
print
can print any data type
Examples
~main<argc><argv>{<
print<"Hello, World!">;
>}
Output:
Hello, World!โ
~main<argc><argv>{<
print<1>;
>}
Output:
1โ
println
built-in function
println
is a built-in function to print a value to the stdout. It takes one argument and prints it to the stdout. It adds a newline character at the end of the output.
What can it print?
println
can print any data type
Differences between print
and println
print
and println
are very similar. The only difference is that println
adds a newline character at the end of the output.
Examples
~main<argc><argv>{<
println<"Hello, World!">;
>}
Output:
Hello, World!
~main<argc><argv>{<
println<[1,2,3,[4,5,6]]>;
>}
Output:
[1, 2, 3, [4, 5, 6]]
format
built-in function
format
is a built-in function to format a string. It takes first argument as format string and the rest of the arguments as values to format. It returns a formatted string.
What can it format?
format
can format any data type
Format string
The format string is a string that contains a placeholders. and the placeholders are replaced with the values passed as arguments.
Placeholders
Placeholders are curly brackets like {}
and this curly bracket will be replaced with the values passed as arguments. Also the placeholders can contain a number to specify the index of the argument to use. The index starts from 0.
Examples
In this example, the first placeholder {}
will be replaced with the first argument passed to the format
function, and the second placeholder {0}
contains a number 0
which is the index of the first argument passed to the format
function, so the second placeholder will be replaced with the first argument passed to the format
function.
~main<argc><argv>{<
formatted_string = format<"{} {0}"><"Hello, World!">;
println<formatted_string>;
>}
Output:
Hello, World! Hello, World!
In this example, the first placeholder {}
will be replaced with the first argument passed to the format
function, and the second placeholder {}
will be replaced with the second argument passed to the format
function.
~main<argc><argv>{<
formatted_string = format<"Hi {} you are from {}"><"Jhon"><"USA">;
println<formatted_string>;
>}
Output:
Hi Jhon you are from USA
Errors
format
function will throw an error if the format string contains a placeholder with a number that is greater than the number of arguments passed to the format
function. Also it will throw an error if the format string contains a placeholder with invalid index. For example, if the format string contains a placeholder with a number that is less than 0.
len
built-in function
len
is a built-in function to get the length of a object. It takes one argument and returns the length of the object.
What can it get the length of?
len
can get the length of strings and arrays.
Errors
len
will throw an error if its argument is not a string or array.
Examples
~main<argc><argv>{<
len_of_string = len<"Hello, World!">;
println<len_of_string>;
>}
Output:
13
~main<argc><argv>{<
len_of_array = len<[1,2,3,[4,5,6]]>;
println<len_of_array>;
>}
Output:
4
input
built-in function
input
is a built-in function to get input from the user. It takes one argument, the prompt to display to the user. It returns the input from the user as a string.
Errors
input
will throw an error if its cannot get a input from stdin.
Examples
~main<argc><argv>{<
user_name = input<"Enter your name: ">;
user_age = input<"Enter your age: ">;
println<
format<"Hello {}! you are {} years old"><user_name><user_age>
>;
>}
Output:
Enter your name: John
Enter your age: 20
Hello John! you are 20 years old
push
built-in function
push
is a built-in function to add an element to the end of an array. It takes two arguments, the array and the element to add. It returns the array with the element added to the end.
Examples
~main<argc><argv>{<
array = [1,2,3];
array = push<array, 4>;
println<array>;
>}
Output:
[1, 2, 3, 4]
~main<argc><argv>{<
array = [1,2,3];
array = push<array, 4>;
array = push<array, 5>;
array = push<array, 6>;
println<array>;
>}
Output:
[1, 2, 3, 4, 5, 6]
pop
built-in function
pop
is a built-in function to remove the last element from an array. It takes one argument and returns the array without the last element.
Examples
~main<argc><argv>{<
array = [1,2,3];
array = pop<array>;
println<array>;
>}
Output:
[1, 2]
~main<argc><argv>{<
array = [1,2,3];
array = pop<array>;
array = pop<array>;
array = pop<array>;
println<array>;
>}
Output:
[]
~main<argc><argv>{<
array = [1,2,3];
array = pop<array>;
array = pop<array>;
array = pop<array>;
array = pop<array>;
println<array>;
>}
Output:
[]