-
Notifications
You must be signed in to change notification settings - Fork 7
Olivine
Olivine is a command line programming language inspired by bash and developed for profanOS.
Although Olivine is developed for the profan environment, it can be used on a lot of systems (see Cross Platform Build).
Olivine uses substitution to execute lines of code, for example in the code
echo Hello !user
, !user
will be literally replaced by the value of the
variable !user. And the process is the same for function calls and pseudos.
olivine [options] [file] [arg1 arg2 ...]
-
-c
execute argument as code line -
-i
start a shell after executing -
-h
show this help message and exit -
-n
don't execute the init program -
-p
show file with syntax highlighting -
-v
display program's version number
Without any arguments, Olivine will start in interactive mode.
Olivine supports quoting using single quotes '
. Inside quotes, the string
can contain spaces and will not be split into multiple arguments.
Note: Even words are quoted, variables and subfunctions will still be substituted
Indentation will be ignored by Olivine. Each line of code must be separated
by a line break or a semicolon ;
.
echo first line
echo second line
echo first line; echo second line
Comments are defined using the //
syntax. Everything after the //
will be
ignored by Olivine. Comments are disabled between quotes.
// This is a comment
echo Hello // This is also a comment
If a line does not start with a keyword, it is considered as a function call. The function name is the first word of the line and the arguments are the following words.
function arg1 arg2 arg3
function 'arg1 with spaces' arg2
Two types of function exist, the internal functions which are in the source code in C as well as the classic functions which are coded in Olivine. (see Internal Functions and Classic Functions)
If a function returns a non-empty string, and it is not in a subfunction, it will be printed.
Variables are set using the set
internal function which takes two arguments,
the first one being the name of the variable (without the !
prefix) and the
second one being the value of the variable.
Variable names (fonctions and pseudos too) can contain letters, numbers and underscores.
set var 42
Variables can be accessed using the !
prefix.
echo !var
Variables can be deleted using the del
internal function which takes one
argument, the name of the variable to delete.
del var
Variables on several layers are supported like in php or bash.
set a 42
set b a
echo !!b
If the variable was not found among the local variables, Olivine will also search in the enviroment (can be deactivated during build).
A environment variable can be set using the export
internal function which
takes two arguments, the first one being the name of the variable and the
second one being the value of the variable.
export var 42
A variable defined outside of a function or declared using the global
/ export
keyword will be accessible from all the functions (see Classic Functions).
Subfunctions are functions that are called inside a line of code. They are
called using the !(...)
syntax. Arguments are passed in the same way
as classic function calls. Like variables, subfunctions also work in strings.
echo !(subfunction arg1 arg2)
Pseudos (aka Nicknames) are replaced between all the substitution steps and the final call of the function. They have been created in order to simplify the function calls.
Pseudos are defined with the internal function pseudo
which takes two
arguments, the first one being the name of the pseudo and the second one
being the value of the pseudo.
pseudo pseudo_name value
pseudo ll 'ls -l'
Pseudos are accessible without a prefix like a function call.
ll user
// Will be automatically replaced by
ls -l user
The .
internal function can be used to execute a binary file. The first
argument is the path to the binary file and the following arguments are the
arguments passed to the binary.
If BIN_AS_PSEUDO
is set to 1 and the binary exists in a directory listed
in the PATH
environment variable, the binary can be executed without .
.
// Using dot
. /bin/ls -l
// Without dot
ls -l
If the last argument of .
is the symbol &
, the file will be launched in
the background, and the PID of the process will be stored in the variable !spi
.
// Launches the cube in the background
. /bin/games/cube.elf &
To redirect stdin and stdout of a binary, the >
and <
symbols can be used.
// Redirects the output of ls to a file
ls -l > file.txt
// Redirects the input of more from a file
more < file.txt
// Redirects the output and input of lua
lua < program.lua > output.txt
See Pipes section for information about pipes.
The exec
internal function can be used to execute an Olivine file in the
current instance of Olivine. The file will be executed as if it was a
function and the arguments will be passed to the file.
A variable named filename
will be set to the name of the file.
exec /path/to/file.olv arg1 arg2
Note: The
.
will have to same effect asexec
if the file ends with the.olv
extension and contains//olivine:exec
at the beginning of the first line.
Olivine will always execute in the following order even if two elements have exactly the same name.
KEYWORD > PSEUDO > FUNCTION > INTERNAL FUNCTION > BINARY
Defore execution, the characters '
(quote), !
(variable access), *
(wildcard) will be replaced by another character, to actually write
'
, !
or *
it is possible to add a backslash just before.
// Will print 'Hello World!'
echo \'Hello World\!\'
To transform a character !
into its representative so that it can be used as access
to a variable, it is possible to use the inter
function (same for '
and *
) which
takes a string as an argument and returns the string with the characters transformed.
set var Hi
// Will print Hi
echo !var
// Will print !var
echo \!var
// Will print Hi
echo !(inter \!var)
If during execution one of the arguments is |
(without quotes), the command
will be split into two parts and the pipe processor will be used to redirect
the output of the first command to the input of the second. Unlike POSIX-style
shells, the pipe processor will not create a new process for each command but
will execute all the commands one by one in the same process.
echo Hello | rev
ls | rev | cat -e
The pipe processor can be used in a subfunction call, if there is a pipe in the last argument, the contents of the pipe will be returned as a string.
echo arg1 !(echo 2gra | rev |) arg3
The *
character can be used as a wildcard to match multiple files. The
wildcard will be replaced by the list of files that match the pattern.
Wildcards can be mixed with other characters to create more complex patterns.
echo *
cc /path/to/*/*.c
If no file or directory matches the pattern, the wildcard will raise an error.
The eval
internal function can be used to evaluate an expression.
Number operators:
-
+
Addition -
-
Substraction -
x
Multiplication -
/
Division -
^
Modulo -
<
Less than -
>
Greater than -
=
Equal -
~
Not equal
String operators:
-
+
Concatenation -
.
Concatenation (with number) -
x
Repeat -
@
Get character -
<
String shift (left) -
>
String shift (right) -
=
Equal -
~
Not equal
Operators priority (from lowest to highest): = ~ < > @ . + - * / ^
Parenthesis can be used to change the order of the operations.
Numbers, strings and operators must be separated by spaces (even parentheses), arguments which are not convertible into integers and which are not operators either, must be enclosed in double quotes.
eval 1 + 2 x ( 3 - 1 )
eval 42 = 41 + 1
eval "Hi" x 3
eval "Hello" @ 1 = "e"
eval "!var" = "/path/to/file"
The <
and >
operators for strings allow you to cut the start or end.
// Will print He
eval "Hello" < 3
// Will print llo
eval "Hello" > 2
Keywords allow you to create conditions, loops and functions to extend the
possibilities of Olivine.
Capital letters don't matter, ELSE
, else
and eLsE
are the same.
Conditionals are defined using the IF
keyword followed by the condition.
The END
keyword must be used to close the conditional.
IF !(eval 1 = 1)
echo 1 is equal to 1
END
The ELSE
keyword can be used after the IF .. END
block and will be executed
if the condition is false.
IF !(eval 1 = 2)
echo 1 is equal to 2
END; ELSE
echo 1 is not equal to 2
END
Note: The
ELSE
keyword does not have to follow the END of the condition but can be separated by code 🫠
For loops are defined using the FOR
keyword followed by the variable name
and the list of values. The END
keyword must be used to close the loop.
FOR i hi hello hey
echo !i
END
Note: For loops can be slow if the list of values is too long. In this case, it is recommended to use a while loop.
While loops are defined using the WHILE
keyword followed by the condition.
The END
keyword must be used to close the loop.
set i 0
WHILE !(eval !i < 5)
echo !i
set i !(eval !i + 1)
END
The BREAK
keyword can be used to exit a loop.
// Will print 1 2
FOR i 1 2 3 4 5
IF !(eval !i = 3)
BREAK
END
echo !i
END
The CONTINUE
keyword can be used to skip the current iteration of a loop.
// Will print 1 2 4 5
FOR i 1 2 3 4 5
IF !(eval !i = 3)
CONTINUE
END
echo !i
END
Classic functions are functions that are coded in Olivine. They are defined
using the FUNC
keyword followed by the name of the function.
A variable named !#
is automatically set to the number of arguments passed
to the function, and the arguments are accessible using the !<number>
syntax.
FUNC my_function
echo !# arguments passed
echo first argument: !0
echo second argument: !1
END
The function can return a value using the RETURN
keyword.
FUNC double
RETURN !(eval !0 * 2)
END
We can note an important difference between bash (like all /bin/sh compatible shells) and Olivine, in Olivine the stdout is not used in functions as an output value. In bash there is no keyword to return a value, it is the stdout content. In Olivine, the stdout is always printed (except in pipes).
All the variables defined inside a function are local to the function and cannot be accessed from outside.
FUNC func1
set var 42
END
FUNC func2
set var 21
func1
RETURN !var
END
// Will print 21
echo !(func2)
The global
keyword can be used to set a value of a variable globally,
global variables should always be assigned with global
keyword, otherwise
they will be considered as local variables (to avoid collisions). If a
variable is declared as global in a function, it will be accessible from
all the code.
FUNC func3
global var 42
END
func3
// Will print 42
echo !var
Global variables override local variables, if a variable is declared as global and local, the global variable will be used.
FUNC func4
global var 2
set var 3
echo !var // Will print 3
END
set var 1
func4
echo !var // Will print 3
Note: This section is of no importance if you only use Olivine as your shell language.
Internal functions are functions that are coded in C and are compiled with the Olivine binary.
char *if_demo(char **input) {
// input[0] is the first argument
// input[1] is the second...
// The function must return a freeable string.
// if the function returns NULL, the output
// will be treated as an empty string.
// return ERROR_CODE; can be used to raise an
// error and stop the execution of the line.
return strdup("Hello World");
}
// The function must be registered using the
// internal_functions array:
internal_function_t internal_functions[] = {
...
{"demo", if_demo},
...
{NULL, NULL}
};
Name | Arguments | Description |
---|---|---|
. |
file [args] |
Executes the binary file |
cd |
dir |
Changes the current directory |
debug |
[arg] |
Prints all variables, pseudos and functions |
del |
name [arg] |
Deletes variables, pseudos or functions |
eval |
... |
Evaluates an expression |
exec |
file |
Executes the file as Olivine code |
exit |
[code] |
Exits the program with the code |
export |
name var |
Sets the env variable name to var
|
fsize |
file |
Returns the size or null if not found |
global |
name var |
Sets the variable name to var globally |
inter |
str |
Returns the string with special characters |
print |
... |
Prints the arguments withouth separators |
pseudo |
name val |
Creates a pseudo |
range |
[start] end |
Returns a list of numbers |
rep |
str ic [oc] |
Replaces characters in a string |
set |
name var |
Sets the variable name to var
|
split |
str [sep] [n] |
Splits a string into a list of strings |
sprintf |
fmt [...] |
Returns a formatted string |
strlen |
str |
Returns the length of a string |
ticks |
Returns the ms since the OS boot |
Name | Description |
---|---|
version |
The version of Olivine |
profan |
1 if build for profan, 0 otherwise |
path |
The current directory |
exit |
The exit code of the last command |
spi |
The PID of the the last . command |
prompt |
The prompt of the shell |
The Olivine source code is available on the profanOS repository.
At the top of the file we find defined to facilitate the build on different platforms.
Name | Description |
---|---|
PROFANBUILD |
enable profan features |
UNIXBUILD |
enable unix features (recommended for linux) |
USE_READLINE |
readline library for input (recommended) |
BIN_AS_PSEUDO |
check for binaries in path |
USE_ENVVARS |
enable environment variables |
ENABLE_DEBUG |
print function calls in stderr |
STOP_ON_ERROR |
stop execution at the first error |
ENABLE_WILDC |
enable wildcard expansion |
To build Olivine on linux, it is recommended to set PROFANBUILD
to 0
and
UNIXBUILD
to 1
. The USE_READLINE
option is also recommended to use the
readline library for input.
# Install readline library (if not already installed)
sudo apt-get install libreadline-dev
# Download the olivine source code
wget https://raw.githubusercontent.com/elydre/profanOS/main/zapps/fatpath/olivine.c
# Compile olivine (Use -DolvUnix to enable unix features)
# The -O3 option is recommended for better performance
gcc -DolvUnix olivine.c -O3 -lreadline -o olivine
# [optional] Move the olivine binary to /usr/bin
sudo mv olivine /usr/bin
It is possible to build Olivine by deactivating PROFANBUILD
and UNIXBUILD
to have a minimal version of Olivine. Only the programming language will be
available, without the shell features.
Only the headers
stdarg.h
,stdlib.h
,string.h
,stdio.h
are required to compile Olivine in minimal configuration.
# Download the olivine source code
wget https://raw.githubusercontent.com/elydre/profanOS/main/zapps/fatpath/olivine.c
# Edit the olivine.c file and set PROFANBUILD and UNIXBUILD to 0
# vim olivine.c / nano olivine.c / code olivine.c
# Compile olivine
gcc olivine.c -o olivine
Updated for Olivine 1.5 rev 6
-
Getting Started
- Compiling profanOS
- Running profanOS
-
User Guide
-
Development Guide
-
Library Documentation