Tau is a dynamically-typed open-source concurrent programming language designed to be minimal, fast and efficient.
In order to install Tau, you'll need Go and GCC.
Clone the repo with
git clone --recurse-submodules https://github.com/NicoNex/tau
cd tau
sudo make installYou can try it out in the terminal by simply running tau.
For additional info run tau --help.
We all start from here...
println("Hello World")As every interpreter Tau supports files either by passing the path to the interpreter or by using the shebang.
#!/path/to/tau
println("hello world")$ tau helloworld.tau
hello world
myVar = 10
if myVar > 10 {
println("more than 10")
} else if myVar == 10 {
println("it's exactly 10")
} else {
println(myVar)
}fib = fn(n) {
if n < 2 {
return n
}
fib(n-1) + fib(n-2)
}
println(fib(40))The return value can be implicit:
add = fn(x, y) { x + y }
sum = add(9, 1)
println(sum)>>> 10
Also you can inline the if expressions:
a = 0
b = 1
minimum = if a < b { a } else { b }The semicolon character ; is implicit on a newline but can be used to separate multiple expressions on a single line.
printData = fn(a, b, c) { println(a); println(b); println(c) }Functions are first-class and treated as any other data type.
min = fn(a, b) { if a < b { a } else { b } }
var1 = 1
var2 = 2
m = min(var1, var2)
println(m)>>> 1
# errtest.tau
div = fn(n, d) {
if d == 0 {
return error("zero division error")
}
n / d
}
if failed(result1 = div(16, 2)) {
exit(result1)
}
println("the result of 16 / 2 is {result1}")
if failed(result2 = div(32, 0)) {
exit(result2)
}
println("the result of 32 / 0 is {result2}")$ tau errtest.tau
the result of 16 / 2 is 8
error: zero division error
$
# errtest.tau
increment = fn(n) {
return n + 1
}
increment("this will raise a runtime error")error in file errtest.tau at line 4:
return n + 1
^
unsupported operator '+' for types string and int
Tau supports go-style concurrency.
This is obtained by the use of four builtins pipe, send, recv close.
pipecreates a new FIFO pipe and optionally you can pass an integer to it to create a buffered pipe.sendis used to send values to the pipe.recvis used to receive values from the pipe.closecloses the pipe.
Pipes can be buffered or unbuffered. Buffered pipes make the tau-routine sleep once send is called until at least one value is read from the pipe.
Once recv is called on an empty pipe it will cause the tau-routine to sleep until a new value is sent to the pipe.
send is used to send values to the pipe.
close closes the pipe thus allowing it to be garbage collected.
Calling recv on a closed pipe will return null.
listen = fn(p) {
for val = recv(p) {
println(val)
}
println("bye bye...")
}
p = pipe()
tau listen(p)
send(p, "hello")
send(p, "world")
send(p, 123)
send(p, "this is a test")
close(p)Tau also comes with a multiline REPL:
Tau v2.0.0 on Linux
>>> repeat = fn(n, func) {
... for i = 0; i < n; ++i {
... func(i)
... }
... }
...
>>> repeat(5, fn(i) {
... println("Hello #{i}")
... })
...
Hello #0
Hello #1
Hello #2
Hello #3
Hello #4
>>>
Tau is a dynamically-typed programming language and it supports the following primitive types:
myVar = 10myVar = 2.5myString = "My string here"Tau also supports strings interpolation.
temp = 25
myString = "The temperature is { if temp > 20 { \"hot\" } else { \"cold\" } }"
println(myString)>>> The temperature is hot
For raw strings use the backtick instead of double quotes.
s = `this is a raw string\n {}`
println(s)>>> this is a raw string\n {}
t = true
f = falsepow = fn(base, exponent) {
if exponent > 0 {
return base * pow(base, exponent-1)
}
1 # You could optionally write 'return 1', but in this case the return is implicit.
}Tau has an assortment of useful builtin functions that operate on many data types:
len(x)-- Returns the length of the given objectxwhich could be a String, List, Map or Bytes.println(s)-- Prints the Stringsto the terminal (standard out) along with a new-line.print(s)-- Same asprintln()but without a new-line.input(prompt)-- Asks for input from the user by reading from the terminal (standard in) with an optional prompt.string(x)-- Converts the objectxto a String.error(s)-- Constructs a new error with the contents of the Strings.type(x)-- Returns the type of the objectx.int(x)-- Converts the objectxto an Integer.float(x)-- Converts the objectxto a Float.exit([code | message, code])-- Terminates the program with the optional exit code and/or message.append(xs, x)-- Appends the objectxto the Listxsand returns the new List.new-- Constructs a new empty object.failed(f)-- Calls the Functionfand returns true if an error occurred.plugin(path)-- Loads the Plugin at the given path.pipe-- Creates a new pipe for sending/receiving messages to/from coroutines.send(p, x)-- Sends the objectxto the pipep.recv(p)-- Reads from the pipepand returns the next object sent to it.close(p)-- Closes the pipep.hex(x)-- Returns a hexadecimal representation ofx.oct(x)-- Returns an octal representation ofx.bin(x)-- Returns a binary representation ofx.slice(x, start, end)-- Returns a slice ofxfromstarttoendwhich could be a String, List or Bytes.keys(x)-- Returns a List of keys of the Mapx.delete(xs, x)-- Deletes the keyxfrom the Mapxs.bytes(x)-- Converts the Stringxto Bytes.
empty = []
stuff = ["Hello World", 1, 2, 3, true]You can append to a list with the append() builtin:
xs =[]
xs = append(xs, 1)Lists can be indexed using the indexing operator [n]:
xs = [1, 2, 3]
xs[1]empty = {}
stuff = {"Hello": "World", 123: true}Keys can be added using the set operator [key] = value:
kv = {}
k["foo"] = "bar"Keys can be accessed using the get operator [key]:
kv = ["foo": "bar"}
kv["foo"]for i = 0; i < 10; ++i {
println("hello world", i)
}
lst = [0, 1, 2, 3, 4]
println(lst)
for len(lst) > 0 {
println(lst = slice(lst, 1, len(lst)))
}When you invoke the new() builtin function, it creates a fresh, empty object. You can then add properties to this object using the dot notation.
The constructor is essentially a standard function that fills up this empty object with properties and values before it is returned.
Dog = fn(name, age) {
dog = new()
dog.name = name
dog.age = age
dog.humanage = fn() {
dog.age * 7
}
return dog
}
snuffles = Dog("Snuffles", 8)
println(snuffles.humanage())>>> 56
When importing a module only the fields whose name start with an upper-case character will be exported.
Same thing applies for exported objects, in the example Snuffles is exported but the field id won't be visible ouside the module.
# import_test.tau
data = 123
printData = fn() {
println(data)
}
printText = fn() {
println("example text")
}
TestPrint = fn() {
printData()
printText()
}
dog = fn(name, age) {
d = new()
d.Name = name
d.Age = age
d.id = 123
d.ID = fn() {
d.id
}
return d
}
Snuffles = dog("Mr Snuffles", 5)it = import("import_test")
it.TestPrint()
println(it.Snuffles.Name)
println(it.Snuffles.Age)
println(it.Snuffles.ID())>>> 123
>>> example text
>>> Mr Snuffles
>>> 5
>>> 456
Tau plugin system makes it possible to import and use C shared libraries in Tau seamlessly. To run your C code in Tau just compile it with:
gcc -shared -o mylib.so -fPIC mylib.cthen you can import it in Tau with the plugin builtin function.
myplugin = plugin("path/to/myplugin.so")C code:
#include <stdio.h>
void hello() {
puts("Hello World!");
}
int add(int a, int b) {
return a + b;
}
int sub(int a, int b) {
return a - b;
}Tau code:
myplugin = plugin("mylib.so")
myplugin.hello()
println("The sum is", int(myplugin.add(3, 2)))
println("The difference is", int(myplugin.sub(3, 2)))Output:
>>> Hello World!
>>> The sum is 5
>>> The difference is 1