Using FunL as embedded language - anssihalmeaho/funl GitHub Wiki
Usage as embedded language
FunL can be used is Go application also so that application defines its own main package and main function in Go.
First funl needs to be fetched as Go package:
go get github.com/anssihalmeaho/funl
FunL interpreted is executed from application code by calling funl.FunlMainWithArgs:
func FunlMainWithArgs(content string, argsItems []*Item, name, srcFileName string, initSTD func(*Interpreter) error) (retValue Value, err error)
In this call initSTD function is called by FunL interpreter to initialize built-in libraries/modules. If just normal standard libraries are used std.InitSTD can be given.
Note. application can add own external module implementations as built-in libraries by defining own initSTD function which is given to funl.FunlMainWithArgs.
Example: application using FunL in embedded way (embedded_appl.go):
package main
import (
"fmt"
"github.com/anssihalmeaho/funl/funl"
"github.com/anssihalmeaho/funl/std"
)
func main() {
body := "import stdfu call(stdfu.filter list(1 2 3) func(x) true end)"
program := fmt.Sprintf("ns main main = proc() %s end endns", body)
retv, err := funl.FunlMainWithArgs(program, []*funl.Item{}, "main", "dummy.fnl", std.InitSTD)
if err != nil {
fmt.Println("Error: ", err)
return
}
fmt.Println(fmt.Sprintf("Result is %v", retv))
}
Create go.mod:
go mod init embedded_appl
go mod tidy
Running example application prints:
go run embedded_appl.go
-> Result is list(1, 2, 3)
Embedding FunL source to Go program by using Go 1.16 file embedding
As Go 1.16 supports embedding static files to executable it's possible to make Go program "shell" for FunL code.
Go program main just initializes modules and goes to execute FunL interpreter.
Here's example of simple program storing some food items to valuez storage. There's separate FunL-module for reading storage (just demo for having FunL module there).
There are two FunL source files (example.fnl and food_reader_module.fnl) and one Go source file (app.go).
app.go:
package main
import (
_ "embed"
"fmt"
"github.com/anssihalmeaho/funl/funl"
"github.com/anssihalmeaho/funl/std"
"github.com/anssihalmeaho/fuvaluez/fuvaluez"
)
//go:embed example.fnl
var program string
//go:embed food_reader_module.fnl
var mymodule string
func addMyOwnModule(interpreter *funl.Interpreter) (err error) {
return funl.AddFunModToNamespace("food_reader_module", []byte(mymodule), interpreter)
}
func init() {
funl.AddExtensionInitializer(initMyExt)
funl.AddExtensionInitializer(addMyOwnModule)
}
func convGetter(inGetter func(string) fuvaluez.FZProc) func(string) std.StdFuncType {
return func(name string) std.StdFuncType {
return std.StdFuncType(inGetter(name))
}
}
func initMyExt(interpreter *funl.Interpreter) (err error) {
stdModuleName := "valuez"
topFrame := funl.NewTopFrameWithInterpreter(interpreter)
stdFuncs := []std.StdFuncInfo{
{
Name: "open",
Getter: convGetter(fuvaluez.GetVZOpen),
},
{
Name: "new-col",
Getter: convGetter(fuvaluez.GetVZNewCol),
},
{
Name: "get-col",
Getter: convGetter(fuvaluez.GetVZGetCol),
},
{
Name: "get-col-names",
Getter: convGetter(fuvaluez.GetVZGetColNames),
},
{
Name: "put-value",
Getter: convGetter(fuvaluez.GetVZPutValue),
},
{
Name: "get-values",
Getter: convGetter(fuvaluez.GetVZGetValues),
},
{
Name: "take-values",
Getter: convGetter(fuvaluez.GetVZTakeValues),
},
{
Name: "update",
Getter: convGetter(fuvaluez.GetVZUpdate),
},
{
Name: "trans",
Getter: convGetter(fuvaluez.GetVZTrans),
},
{
Name: "view",
Getter: convGetter(fuvaluez.GetVZView),
},
{
Name: "del-col",
Getter: convGetter(fuvaluez.GetVZDelCol),
},
{
Name: "close",
Getter: convGetter(fuvaluez.GetVZClose),
},
}
err = std.SetSTDFunctions(topFrame, stdModuleName, stdFuncs, interpreter)
return
}
func main() {
retv, err := funl.FunlMainWithArgs(program, []*funl.Item{}, "main", "example.fnl", std.InitSTD)
if err != nil {
fmt.Println("Error: ", err)
return
}
fmt.Println(fmt.Sprintf("Result is %v", retv))
}
example.fnl:
ns main
import valuez
import stddbc
import food_reader_module
main = proc()
open-ok open-err db = call(valuez.open 'foods'):
_ = call(stddbc.assert open-ok open-err)
col-ok col-err col = call(valuez.new-col db 'food-col'):
_ = call(stddbc.assert col-ok col-err)
_ = call(valuez.put-value col 'Pizza')
_ = call(valuez.put-value col 'Pasta')
_ = call(valuez.put-value col 'Meatballs')
all-foods = call(food_reader_module.get-all-foods col)
_ = call(valuez.close db)
all-foods
end
endns
food_reader_module.fnl:
ns food_reader_module
import valuez
get-all-foods = proc(col)
call(valuez.get-values col func(x) true end)
end
endns
Build and run executable:
go mod init app
go mod tidy
go build app.go
./app.exe
Result is list('Pizza', 'Pasta', 'Meatballs')