repl / scripting language / namespaced command line aliases

Overview

Adana

Toy project with the following goals in mind:

  • Making something concrete with rust
  • Learning more about parser combinator
  • Use the minimum amount of libraries
  • Making a scripting language
  • Making a REPL
  • No tutorials, best practices, design patterns, clean architecture, fancy frameworks

Table of Contents

  1. Features
  2. Installation
  3. Programming language
  4. Namespaced aliases

Features

  • alias commands in separate namespaces (dump, merge namespaces, backup, run,...)
  • calculator
  • simple scripting language

Installation

  1. if(age
    • From docker hub:
      • docker run -it nbittich/adana
    • Manually:
      • clone the repo
      • build the docker image: docker build -t adana .
      • docker run -it adana
  2. Cargo
    • From crate.io:
      • cargo install adana
      • adana
    • Manually:
      • cargo build --release
      • ./target/x86_64-unknown-linux-musl/release/adana

Programming language

Introduction

First we start with the traditional hello world:

>> println("hello world!") # prints hello world 

In the repl, you could also simply write:

>> "hello world!" # prints hello world 

Comments

Comments are defined like in python, starting with #. You can put them after the last statement or before any useful code, for example:

>> # to go to the next line in the repl, press CTRL+x

>> # this will be ignored by the repl

>> println("hello world!") # this is also ok

Multiline

Semicolons are not needed, if you need multiline statements, you can use a multiline block:

fancy_string = multiline {
    "this string" +
    "is " +
    "on several " +
    "lines"
}

Operators and constants

There are 14 operators & 3 constants:

operator description
+ add
- subtract
/ divide
* multiply
% modulo
^ pow
< less than
> greater than
<= less or equal
>= greater or equal
&& and
|| or
== equal
() parenthesis
π PI number
γ EULER number
τ TAU number
>> 5 + 5 # 10
>> 5 + 5.5 # 10.5
>> 5 / 5 # 1
>> 5 / 6 # 0
>> 5 / 6. # 0.8333333333333334 -- we force it to make a float division by adding "." 
>> 5 % 6 # 5 -- modulo on int 
>> 5 % 4.1 # 0.9000000000000004 modulo on double
>> 5 ^ 5 # 3125
>> 5 * 5 # 25
>> 5 * 5.1 # 25.5
>> 5 * (5+ 1/ (3.1 ^2) * 9) ^3. # 1046.084549281999

Variable definition

To define a variable, simply type the name of the variable followed by "=". Variable must always start by a letter and can have numerics or "_" in it. Add and assign(+=), subtract and assign (-=), etc are not supported.

>> vat = 1.21 # 1.21
>> sub_total1 = 10000
>> total = vat * sub_total1 # 12100
>> sub_total2 = 500 * vat # 605
>> sub_total1 = sub_total1 + sub_total2 # 10605

Loops

There's only one loop, the while loop. Just like in plain old C:

count = 0

while(count < 10) {
    println(count)
    count = count + 1
   }

You can break if you match a certain condition:

while(count < 10) {
     println(count)
     count = count + 1
     if(count % 3 ==0) {
        break
     }
   }

Conditions

Same as C:

if(age > 12) {
    println("age > 12")
} else if(age <9) {
    println("age < 9")
}else {
    println("dunno")
}

Types

There are no type checking in the language. You can add a string to an array, nothing will stop you!

In some cases though, you might get an error.

Below, a list of types and how you declare them. You cannot define (yet) your own structure.

type examples
null null
bool true / false
int 5
double 12. / 12.2
string "hello"
array [1,2,"3", true]
function () => "hello"
(name) => "hello" + name
(n) => {
"hello"
}

Manipulate arrays

Arrays are declared like in javascript but are "immutable". After declaration, you cannot (yet) push new data in them. in order to that, you have to concat them with another array using the "+" operator.

>> arr = [] # declare an empty array
>> arr[0] = "kl" # Err: index out of range
>> arr = arr + ["kl"] # arr now is ["kl"]

You can update a value in the array with the syntax above, as long as the array is greater than the index provided, e.g:

arr = ["this", "is", "ax", "array", true, 1, 2.3] 
arr[2] = "an" #fix the typo
print(arr[2]) # an

To get the length of an array, you can use the built-in function length

>> arr_len = length(arr) # 7

Characters within a string can be accessed & updated just like an array:

>> s = "this is a strink"
>> s[2] # i
>> length(s) # 16
>> s[15] = "g" # fix the typo
>> s # this is a string

Here are some other examples of what you can do with arrays:

count = 9
arr = null
# arr = [1, [2, [3, [4, [5, [6, [7, [8, [9, null]]]]]]]]]
while(count > 0) {
    arr = [count, arr]
    count = count -1
} 
# prints 1,2,3,4,5,6,7,8,9,done
while(arr != null) {
    print(arr[0] +",")
    arr=arr[1]
}
print("done")
   

Functions

Function can be declared inline or as a block, with the exception of anonymous function parameters that cannot be inlined (yet). In case of a function parameter, you either assign the function to a variable or you use an anymous function block.

Parameters cannot be modified within a function. if you want to update something, you have to return it and reassign.

# no parameters
hello = () => println("hello, world!")
hello()

# one parameter
hello_name = (name) => println("hello "+name)
hello_name("Bachir")

# takes an array and a function as a parameter
for_each = (arr, consumer) => {
    count = 0
    len = length(arr)
    while(count < len) {
        consumer(arr[count])
        count = count + 1
    }
    return ""  # do not print the count as the repl will print the latest statement
}

for_each(["Mohamed", "Hakim", "Sarah", "Yasmine", "Noah", "Sofia", "Sami"], hello_name)

Parameters cannot be modified within a function. if you want to update something, you have to return it and reassign. Everything that changes within the scope of a function won't have any effect on the outer scope.

Some other examples of what you can do with functions:

arr  = ["Mohamed", "Hakim", "Sarah", "Yasmine", "Noah", "Sofia", "Sami"]

acc  = (arr, v) => arr + [v] # arr is immutable, thus you have to reassign it if you call that function

arr = acc(arr, "Malika")

find_first = (arr, predicate) => {
    len = length(arr)
    count = 0
    while(count < len) {
        temp = arr[count]
        if(predicate(temp)) {
            return temp
        }
        count = count + 1
    }
    return null
}


find_first(arr, (v) => {
    v[0] == "S" || v[0] == "s"
})

# recursive
fact = (n) => {
   if(n>=1) {
    n * fact(n-1)
   }else {
     1
   }
}

fact(10)

Include a script file

You can dynamically load a script in the repl. Assuming you've cloned the repo and you use docker, here's an example of how to do it.

Note that the extension can be anything.

  • map the example directory as a docker volume:
docker run -v $PWD/file_tests:/scripts -it adana
include("scripts/test_fn.adana") # the built-in function to include
m = map()
m = push_v("nordine", 34, m)
get_v("nordine", m)

Builtin functions

There are several built-in functions available.

You already have seen length to find the length of an array or string, include to include a script inside the repl and println to print something.

Here are a list of built in functions available:

name description example
sqrt square root sqrt(2)
abs absolute value abs(-2)
log logarithm log(2)
ln natural logarithm ln(2)
length length of an array or string length("azert")
sin sine of a number sin(2)
cos cosine of a number cos(2)
tan tangent of a number tan(2.2)
print print without a newline print("hello")
println print with a newline println("hello")
include include a script include("scripts/test_fn.adana")
read_lines read a file and returns an array
of each lines
read_lines("scripts/name.txt")
to_int cast to int to_int("2")
to_int(2.2)
to_double cast to double to_double("2.2")
drop drop a variable from context drop("myvar")

Note that you can use the repl command script_ctx to see what variables are stored in the context.

Namespaced aliases

You can alias useful commands in a separate namespace (e.g: "work", "git", "docker").

You can then run that command through the repl. They will be save in disk so you can backup them, restore them etc.

You can also add any kind of values (e.g, ssh keys) to store them.

There is no possible interaction with the scripting language yet.

Demo

demo.mp4

Try it

docker run -it -v $PWD/sample.json:/adanadb.json adana --inmemory

restore

use misc

ds

printenv

Available commands

name alt description
put N/A Put a new value to current namespace. can have multiple aliases with option '-a'. e.g put -a drc -a drcomp docker-compose
describe ds List values within the current namespace.
listns lsns List available namespaces.
currentns currentns Print current namespace.
backup bckp Backup the database of namespaces to the current directory
restore N/A Restore the database from current directory
deletens delns Delete namespace or clear current namespace values.
mergens merge Merge current with a given namespace
delete del Remove value from namespace. Accept either a hashkey or an alias. e.g del drc
get Get value from namespace. Accept either a hashkey or an alias. e.g get drc
exec Run a value from the namespace as an OS command. Accept either a hashkey or an alias. It is completely optional, if you just write the alias, it will also works e.g exec drc or simply drc
cd Navigate to a directory in the filesystem
use Switch to another namespace. default ns is DEFAULT. e.g use linux
dump Dump namespace(s) as json. Take an optional parameter, the namespace name. e.g dump linux
clear cls Clear the terminal.
print_script_ctx script_ctx Print script context
help Display help.

Shortcuts

CTRL + x => new line in the repl
CTRL + d => quit
CTRL + c => undo
CTRL + l => clear screen
CTRL + r => history search

Environment variables

RUST_LOG=info adana

Arguments

# open an in memory db

adana --inmemory

# override db path & history path + fallback in memory in case of an error (default to false)
# path must exist! file doesn't have to.

adana --dbpath /tmp/mydb.db --historypath /tmp/myhistory.txt --fallback

You might also like...
cargo-add command to make dependencies into dylibs

cargo add-dynamic This cargo command allows to wrap dependencies as dylibs. For why you might want this see Speeding up incremental Rust compilation w

A tool that helps you to turn in one command a Rust crate into a Haskell Cabal library!
A tool that helps you to turn in one command a Rust crate into a Haskell Cabal library!

cabal-pack A tool that helps you to turn in one command a Rust crate into a Haskell Cabal library! To generate bindings, you need to annotate the Rust

The Computer Language Benchmarks Game: Rust implementations

The Computer Language Benchmarks Game: Rust implementations This is the version I propose to the The Computer Language Benchmarks Game. For regex-dna,

A community curated list of Rust Language streamers

Awesome Rust Streaming This is a community curated list of livestreams about the programming language Rust. Don't see a stream that you like? Feel fre

Core Temporal SDK that can be used as a base for language specific Temporal SDKs

Core SDK that can be used as a base for all other Temporal SDKs. Getting started See the Architecture doc for some high-level information. This repo u

Nixt is an interpreted programming language written in Rust

Nixt Nixt is an interpreted lisp inspired programming language written in Rust Index About Examples Installation Build About Nixt goal is to provide a

A programming language somewhat resembling cellular processes.

cytosol An embeddable programming language somewhat resembling cellular processes. State of the implementation tokenising parsing semantic analysis an

Orion lang is a lispy programming language that is strongly and statically typed.
Orion lang is a lispy programming language that is strongly and statically typed.

Orion Orion is a lisp inspired statically typed programming language written in Rust Install To install orion you can either: Download binary from the

Simple autoclicker written in Rust, to learn the Rust language.

RClicker is an autoclicker written in Rust, written to learn more about the Rust programming language. RClicker was was written by me to learn more ab

Releases(0.10.1)
  • 0.10.1(Sep 5, 2022)

  • 0.10.0(Jul 28, 2022)

    What's Changed

    • conditions, booleans by @nbittich in https://github.com/nbittich/adana/pull/8
    • while loop, if / else statement, comments, strings, arrays etc by @nbittich in https://github.com/nbittich/adana/pull/9
    • scripting phase 2 by @nbittich in https://github.com/nbittich/adana/pull/10
    • phase 3 by @nbittich in https://github.com/nbittich/adana/pull/11
    • Feat/phase 4 by @nbittich in https://github.com/nbittich/adana/pull/12

    Full Changelog: https://github.com/nbittich/adana/compare/0.9.0...0.10.0

    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Jul 13, 2022)

    What's Changed

    • override arguments & remove lazy static by @nbittich in https://github.com/nbittich/karsher/pull/5
    • modulo @nbittich in https://github.com/nbittich/karsher/pull/6
    • built in functions & constants by @nbittich in https://github.com/nbittich/karsher/pull/7

    Full Changelog: https://github.com/nbittich/karsher/compare/0.8.0...0.9.0

    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Jun 19, 2022)

    What's Changed

    • envs & bugfixes by @nbittich in https://github.com/nbittich/karsher/pull/2

    New Contributors

    • @nbittich made their first contribution in https://github.com/nbittich/karsher/pull/2

    Full Changelog: https://github.com/nbittich/karsher/compare/0.4.0...0.6.0

    Source code(tar.gz)
    Source code(zip)
  • 0.4.0(Jun 15, 2022)

Owner
Nordine Bittich
Nordine Bittich
Embeddable tree-walk interpreter for a "mostly lazy" Lisp-like scripting language.

ceceio Embeddable tree-walk interpreter for a "mostly lazy" Lisp-like scripting language. Just a work-in-progress testbed for now. Sample usage us

Vinícius Miguel 7 Aug 18, 2022
Rust command-line tool to encrypt and decrypt files or directories with age

Bottle A Rust command-line tool that can compress and encrypt (and decrypt and extract) files or directories using age, gzip, and tar. Bottle has no c

Sam Schlinkert 1 Aug 1, 2022
Utilities for converting Vega-Lite specs from the command line and Python

VlConvert VlConvert provides a Rust library, CLI utility, and Python library for converting Vega-Lite chart specifications into static images (SVG or

Vega 24 Feb 13, 2023
An open source WCH-Link library/command line tool written in Rust.

wlink - WCH-Link command line tool NOTE: This tool is still in development and not ready for production use. Known Issue: Only support binary firmware

WCH MCU for Rust 22 Mar 7, 2023
A repository for showcasing my knowledge of the Rust programming language, and continuing to learn the language.

Learning Rust I started learning the Rust programming language before using GitHub, but increased its usage afterwards. I have found it to be a fast a

Sean P. Myrick V19.1.7.2 2 Nov 8, 2022
Mewl, program in cats' language; A just-for-fun language

Mewl The programming language of cats' with the taste of lisp ?? What,Why? Well, 2 years ago in 2020, I created a esoteric programming language called

Palash Bauri 14 Oct 23, 2022
lelang programming language is a toy language based on LLVM.

lelang leang是一门使用Rust编写,基于LLVM(inkwell llvm safe binding library)实现的编程语言,起初作为课程实验项目,现在为个人长期维护项目。 Target Features 支持8至64位的整形类型和32/64位浮点 基本的函数定义,调用,声明外部

Aya0wind 5 Sep 4, 2022
The Devils' Programming Language (Quantum Programming Language)

devilslang has roots in Scheme and ML-flavored languages: it's the culmination of everything I expect from a programming language, including the desire to keep everything as minimalistic and concise as possible. At its core, devilslang is lambda-calculus with pattern-matching, structural types, fiber-based concurrency, and syntactic extension.

Devils' Language 2 Aug 26, 2022
Like wc, but unicode-aware, and with per-line mode

Like wc, but unicode-aware, and with per-line mode

Skyler Hawthorne 34 May 24, 2022
Salty and Sweet one-line Rust Runtime Optimization Library

SAS SAS (Salty-And-Sweet) is an one-line Rust runtime optimization library. Features NUMA-aware rayon: numa feature should be enabled If you have 1 NU

UlagBulag 3 Feb 21, 2024