---
You can build the binary using Makefile.
$ make # Build binary
$ ./kamby # Run REPL
$ ./kamby script.ka # Run script
$ ./kamby --to-c script.ka > script.c # Transpile to C
$ ./kamby --help # Show help
Variables can contain any type of data, including number, strings, lists or even blocks of code.
message := 'Hello, World!' # Define a new variable
message = 'Bye, bye' # Edit variable value
message := 'Hello again! ' # Append new 'message' in stack
message = () # Remove last 'message' in stack
print(message) # Message has old value 'Bye, bye'
/* or */
def message 'Hello, World!'
set message 'Bye, bye'
def message 'Hello again! '
del message
Variables are not unique. You can declare multiple values for the same variable name using “:=” and append into the stack. If you want to edit the last declaration, just use a simple “=” operator. To remove the last variable with specific name in stack, use “del varname” or set it to “()”.
You can return the value of some stack item using index instead of var name
num1 = 12 // Change num1 value or define if inexistent
num2 = 23
print $0 // Return last item value from stack. 23
print $1 // Return the second item from stack. 12
Atoms like numbers and strings can be named and not saved in the stack. This is useful to name a value in a list item.
list = [firstName: 'John', lastName: 'Doe', age: 25]
print(list.firstName) // John
print(list.age) // 25
print(list.$1) // Doe
Basically those three types of lists has the same structure but act different when executed.
// Expressions will be evaluated an return the value
expression = (1 + 2)
// Binary operators wraps itself in an expression
expression = 1 + 2 # Same as (1 + 2)
/*
List items are separated by spaces, comma or semicolon.
Expressions return will be attribuited to item
*/
list = ['first item' (1 + 2)] # Same as ['first item', 5]
// Blocks are lists of expressions that can be called after declaration
def block {
print(message)
}
For source code or blocks, each line is considered an expression. To execute a block, just call it as first item of an expression with any arguments. The arguments are be appended to the internal block variables stack. If there’s no argument, use an empty expression “()” as argument.
def sum { $0 + $1 }
print(sum 2 3)
/* or */
sum := { first + second }
print(sum(first: 2, second: 3))
Lists can be send as a context appended to global variables context.
name := 'Global name' // Declare a new variable in current stack
person = [
name: 'Local name'
age: 20
]
print(name) // Return 'Global name'
print(person.name) // "person" is set as context
// "name" returns list's "name" item
person.{ // Context blocks uses list as context
name = 'New name' // Block of code that change "person" values
age = 30
}
print(person.name) // Return 'New name'
Statements are represented by pairs of condition and execution blocks. The last block is the else block.
// Simple condition. Block executed
1 == 1 ? { print('TRUE') }
/* or */
if 1 == 1 { print('TRUE') }
// False condition. Else block is executed
1 != 1 ? { print('FALSE') } { print('ELSE') }
/* or */
if 1 != 1 { print('FALSE') } else { print('ELSE') }
// First is false. Second condition is true. Second block is executed
1 != 1 ? { print('FALSE') } 2 == 2 { print('TRUE') }
/* or */
if 1 != 1 { print('FALSE') } 2 == 2 { print('TRUE') }
// Two false conditions. Else block is executed
1 != 1 ? { print('FALSE') } 2 != 2 { print('FALSE') } else { print('ELSE') }
/* or */
if 1 != 1 { print('FALSE') } 2 != 2 { print('FALSE') } { print('ELSE') }
The “else” keyword means “true” and it can be omitted. Different from other languages, the “:” symbol can’t be used as “else”.
While condition block is true, run execution block.
i := 0
while ((i += 1) <= 10) { print i }
For each item in list, run execution block.
list := [1, 2, 3] * { $0 * 2 }
list * { print($0) } // 2 4 6
/* or */
for list { print($0) }
For with range is used to iterate a range of numbers or a list.
for 0..2 { print(i) } // 0 1 2
split "Hello World!" " " // ["Hello", "World!"]
join ['Hello', 'World!'] ' ' // "Hello World!"
length "John Doe" // 8
upper "John Doe" // "JOHN DOE"
lower "John Doe" // "john doe"
Input and output functions.
print('Enter your name:') // Print to console
name = (input) // Read user input
write 'file.txt' name // Write name to file
content = (read 'file.txt') // Read file content
You can load other scripts or dynamic libraries using the “load” function.
load 'script.ka' // Read and evaluate scripts
load 'library.so' // Load a dynamic library
Dynamic libraries should have a function named “void ka_extend(Kamby **ctx)” that will be called to extend the context with new functions.
Some operators are overloaded to perform different actions based on argument types.
1 + 2 // 3
"John " + "Doe" // "John Doe"
[1, 2] + [3, 4] // [1 2 3 4]
[1, 2] + 3 // [1 2 3]
1 + [2, 3] // [1 2 3]
4 / 2 // 2
"John Doe" / " " // ["John" "Doe"]
"Doe" / "" // ["D" "o" "e"]
3 * 2 // 6
[1 2] * { $0 * 2 } // [2 4]
["a" "b" "c"] * "-" // "a-b-c"
true false
get def set del $ : := = .
&& || ! == != > < >= <=
+ - * / % += -= *= /= %=
if else while for ? ..
split join length upper lower
print input read write load
MIT