2019-01-09 10:17:42 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/d5/tengo/ast"
|
|
|
|
"github.com/d5/tengo/compiler"
|
|
|
|
"github.com/d5/tengo/objects"
|
|
|
|
"github.com/d5/tengo/parser"
|
2019-01-11 12:16:34 +03:00
|
|
|
"github.com/d5/tengo/source"
|
2019-01-09 10:17:42 +03:00
|
|
|
"github.com/d5/tengo/vm"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
runFib(35)
|
2019-01-11 09:34:28 +03:00
|
|
|
runFibTailCall(35)
|
|
|
|
}
|
|
|
|
|
|
|
|
func runFibTailCall(n int) {
|
|
|
|
start := time.Now()
|
|
|
|
nativeResult := fibTC(n, 0)
|
|
|
|
nativeTime := time.Since(start)
|
|
|
|
|
|
|
|
input := `
|
|
|
|
fib := func(x, s) {
|
|
|
|
if x == 0 {
|
|
|
|
return s
|
|
|
|
} else if x == 1 {
|
|
|
|
return 1 + s
|
|
|
|
} else {
|
|
|
|
return fib(x-1, fib(x-2, s))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
` + fmt.Sprintf("out = fib(%d, 0)", n)
|
|
|
|
|
|
|
|
parseTime, compileTime, runTime, result, err := runBench([]byte(input))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if nativeResult != int(result.(*objects.Int).Value) {
|
|
|
|
panic(fmt.Errorf("wrong result: %d != %d", nativeResult, int(result.(*objects.Int).Value)))
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("-------------------------------------")
|
2019-01-11 12:22:21 +03:00
|
|
|
fmt.Printf("fibonacci(%d) (tail-call)\n", n)
|
2019-01-11 09:34:28 +03:00
|
|
|
fmt.Println("-------------------------------------")
|
2019-01-11 12:22:21 +03:00
|
|
|
fmt.Printf("Result: %d\n", nativeResult)
|
2019-01-11 09:34:28 +03:00
|
|
|
fmt.Printf("Go: %s\n", nativeTime)
|
|
|
|
fmt.Printf("Parser: %s\n", parseTime)
|
|
|
|
fmt.Printf("Compile: %s\n", compileTime)
|
|
|
|
fmt.Printf("VM: %s\n", runTime)
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func runFib(n int) {
|
|
|
|
start := time.Now()
|
|
|
|
nativeResult := fib(n)
|
|
|
|
nativeTime := time.Since(start)
|
|
|
|
|
|
|
|
input := `
|
|
|
|
fib := func(x) {
|
2019-01-11 01:51:20 +03:00
|
|
|
if x == 0 {
|
2019-01-09 10:17:42 +03:00
|
|
|
return 0
|
2019-01-11 01:51:20 +03:00
|
|
|
} else if x == 1 {
|
2019-01-09 10:17:42 +03:00
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
return fib(x-1) + fib(x-2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
` + fmt.Sprintf("out = fib(%d)", n)
|
|
|
|
|
|
|
|
parseTime, compileTime, runTime, result, err := runBench([]byte(input))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if nativeResult != int(result.(*objects.Int).Value) {
|
|
|
|
panic(fmt.Errorf("wrong result: %d != %d", nativeResult, int(result.(*objects.Int).Value)))
|
|
|
|
}
|
|
|
|
|
2019-01-11 09:34:28 +03:00
|
|
|
fmt.Println("-------------------------------------")
|
2019-01-11 12:22:21 +03:00
|
|
|
fmt.Printf("fibonacci(%d)\n", n)
|
2019-01-11 01:51:20 +03:00
|
|
|
fmt.Println("-------------------------------------")
|
2019-01-11 12:22:21 +03:00
|
|
|
fmt.Printf("Result: %d\n", nativeResult)
|
2019-01-09 10:17:42 +03:00
|
|
|
fmt.Printf("Go: %s\n", nativeTime)
|
|
|
|
fmt.Printf("Parser: %s\n", parseTime)
|
|
|
|
fmt.Printf("Compile: %s\n", compileTime)
|
|
|
|
fmt.Printf("VM: %s\n", runTime)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fib(n int) int {
|
|
|
|
if n == 0 {
|
|
|
|
return 0
|
|
|
|
} else if n == 1 {
|
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
return fib(n-1) + fib(n-2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-11 09:34:28 +03:00
|
|
|
func fibTC(n, s int) int {
|
|
|
|
if n == 0 {
|
|
|
|
return s
|
|
|
|
} else if n == 1 {
|
|
|
|
return 1 + s
|
|
|
|
} else {
|
|
|
|
return fibTC(n-1, fibTC(n-2, s))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
func runBench(input []byte) (parseTime time.Duration, compileTime time.Duration, runTime time.Duration, result objects.Object, err error) {
|
|
|
|
var astFile *ast.File
|
|
|
|
parseTime, astFile, err = parse(input)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var bytecode *compiler.Bytecode
|
|
|
|
compileTime, bytecode, err = compileFile(astFile)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
runTime, result, err = runVM(bytecode)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func parse(input []byte) (time.Duration, *ast.File, error) {
|
2019-01-11 12:16:34 +03:00
|
|
|
fileSet := source.NewFileSet()
|
2019-01-09 10:17:42 +03:00
|
|
|
inputFile := fileSet.AddFile("test", -1, len(input))
|
|
|
|
|
|
|
|
start := time.Now()
|
|
|
|
|
|
|
|
file, err := parser.ParseFile(inputFile, input, nil)
|
|
|
|
if err != nil {
|
|
|
|
return time.Since(start), nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return time.Since(start), file, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func compileFile(file *ast.File) (time.Duration, *compiler.Bytecode, error) {
|
|
|
|
symTable := compiler.NewSymbolTable()
|
|
|
|
symTable.Define("out")
|
|
|
|
|
|
|
|
start := time.Now()
|
|
|
|
|
|
|
|
c := compiler.NewCompiler(symTable, nil)
|
|
|
|
if err := c.Compile(file); err != nil {
|
|
|
|
return time.Since(start), nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return time.Since(start), c.Bytecode(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func runVM(bytecode *compiler.Bytecode) (time.Duration, objects.Object, error) {
|
|
|
|
globals := make([]*objects.Object, vm.GlobalsSize)
|
|
|
|
|
|
|
|
start := time.Now()
|
|
|
|
|
|
|
|
v := vm.NewVM(bytecode, globals)
|
|
|
|
if err := v.Run(); err != nil {
|
|
|
|
return time.Since(start), nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return time.Since(start), *globals[0], nil
|
|
|
|
}
|