Dune combines the simplicity, reliability and efficiency of Go with the flexibility of Typescript.
It is evolving so expect frequent breaking changes.
Try it online, download the latest release or get it from source:
go get github.com/dunelang/dune/cmd/dune
REPL:
Dune v0.95
> 1 + 2
3
Create a hello.ts:
fmt.println("Hello world")
Run it:
$ dune hello.ts
Hello world
Arrays have multiple built in functions:
let items = [1, 2, 3, 4, 5]
let r = items.where(t => t > 2).select(t => t * t).sum()
console.log(r)
A web server:
let s = http.newServer()
s.address = ":8080"
s.handler = (w, r) => w.write("Hello world")
s.start()
Working with databases:
let db = sql.open("sqlite3", ":memory:")
db.exec("CREATE TABLE people (id KEY, name TEXT)")
db.exec("INSERT INTO people (name) VALUES (?)", "Bob")
for (let r of db.query("SELECT id, name FROM people")) {
console.log(r.id, r.name)
}
By default a virtual machine has no access to the external world. The file system is virtual.
This code throws: unauthorized
let p = bytecode.compileStr(`http.get("http://google.com")`)
let vm = runtime.newVM(p)
vm.run()
Programs can request permissions with directives:
// [permissions networking]
http.get("http://google.com")
This code throws: Step limit reached: 100
let p = bytecode.compileStr(`while(true) { }`)
let vm = runtime.newVM(p)
vm.maxSteps = 100
vm.run()
This code throws: Max stack frames reached: 5
let p = bytecode.compileStr(`function main() { main() }`)
let vm = runtime.newVM(p)
vm.maxFrames = 5
vm.run()
MaxAllocations counts the number of variables set and in the case of strings their size so it is not very useful yet. This code throws: Max allocations reached: 10
let p = bytecode.compileStr(`
let v = "*"
while(true) {
v += v
}
`)
let vm = runtime.newVM(p)
vm.maxAllocations = 10
vm.run()
Checkout more examples.
Since everything is virtual, including the file system, programs are easily testable. There is an assert package in the standard library.
The syntax is a subset of Typescript to get type checking, autocompletion and refactoring tools from editors like VS Code.
$ dune --init
$ code rand.ts
Write a basic program:
export function main(len?: string) {
let n = convert.toInt(len || "15")
let v = crypto.randomAlphanumeric(n)
fmt.println(v)
}
$ dune rand
1RyXuMFKwmxPbTa6bpXk
To decompile a program:
$ dune -d /tmp/rand.ts
=============================
Functions
=============================
0F @global
-----------------------------
0 return -- -- -- ; @global
1F main
-----------------------------
0 MoveAndTest 2L 0L 3L ; /tmp/rand.ts:8
1 TestJump 3L 1D 0D ; /tmp/rand.ts:8
2 Move 2L 0K -- ; /tmp/rand.ts:8
3 CallSingleArg 81N 1L 2L ; /tmp/rand.ts:8
4 CallSingleArg 107N 4L 1L ; /tmp/rand.ts:9
5 CallSingleArg 131N -- 4L ; /tmp/rand.ts:10
6 Return -- -- -- ; /tmp/rand.ts:10
0L len 0-6
1L n 0-6
2L @ 0-6
3L @ 0-6
4L v 4-6
=============================
Constants
=============================
0K string 15
package main
import (
"fmt"
"github.com/dunelang/dune"
)
func main() {
v, err := dune.RunStr("return 3 * 2")
fmt.Println(v, err)
}
To call Go functions:
package main
import (
"fmt"
"log"
"github.com/dunelang/dune"
)
func main() {
dune.AddNativeFunc(dune.NativeFunction{
Name: "math.square",
Arguments: 1,
Function: func(this dune.Value, args []dune.Value, vm *dune.VM) (dune.Value, error) {
v := args[0].ToInt()
return dune.NewInt64(v * v), nil
},
})
p, err := dune.CompileStr("return math.square(5)")
if err != nil {
log.Fatal(err)
}
v, err := dune.NewVM(p).Run()
if err != nil {
log.Fatal(err)
}
fmt.Println(v)
}
To import the standard library:
package main
import (
"log"
"github.com/dunelang/dune"
_ "github.com/dunelang/dune/lib"
)
func main() {
_, err := dune.RunStr(`
let v = { foo: 33 }
console.log(v)
`)
if err != nil {
log.Fatal(err)
}
}