Renamed project to ucl

This commit is contained in:
Leon Mika 2024-04-27 10:11:22 +10:00
parent c7a4013641
commit 60147d8fdf
16 changed files with 158 additions and 108 deletions

View file

@ -2,7 +2,7 @@ clean:
-rm -r build
test:
go test ./cmdlang/...
go test ./ucl/...
site: clean
mkdir build

View file

@ -3,7 +3,7 @@ package main
import (
"context"
"github.com/chzyer/readline"
"github.com/lmika/cmdlang-proto/cmdlang"
"github.com/lmika/ucl/ucl"
"log"
)
@ -14,7 +14,7 @@ func main() {
}
defer rl.Close()
inst := cmdlang.New()
inst := ucl.New()
ctx := context.Background()
for {
@ -23,7 +23,7 @@ func main() {
break
}
if err := inst.EvalAndDisplay(ctx, line); err != nil {
if err := ucl.EvalAndDisplay(ctx, inst, line); err != nil {
log.Printf("%T: %v", err, err)
}
}

View file

@ -7,9 +7,8 @@ import (
"context"
"errors"
"github.com/alecthomas/participle/v2"
"github.com/lmika/cmdlang-proto/cmdlang"
"github.com/lmika/ucl/ucl"
"strings"
"syscall/js"
)
func invokeUCLCallback(name string, args ...any) {
@ -21,7 +20,7 @@ func invokeUCLCallback(name string, args ...any) {
func initJS(ctx context.Context) {
ucl := make(map[string]any)
inst := cmdlang.New(cmdlang.WithOut(&uclOut{
inst := ucl.New(ucl.WithOut(&uclOut{
lineBuffer: new(bytes.Buffer),
writeLine: func(line string) {
invokeUCLCallback("onOutLine", line)

View file

@ -1,7 +0,0 @@
package cmdlang
import "context"
func egLookup(ctx context.Context, args invocationArgs) (object, error) {
return nil, nil
}

2
go.mod
View file

@ -1,4 +1,4 @@
module github.com/lmika/cmdlang-proto
module github.com/lmika/ucl
go 1.21.1

View file

@ -1,4 +1,4 @@
package cmdlang
package ucl
import (
"io"
@ -64,12 +64,13 @@ type astStatements struct {
}
type astScript struct {
Statements *astStatements `parser:"NL* @@ NL*"`
Statements *astStatements `parser:"NL* (@@ NL*)?"`
}
var scanner = lexer.MustStateful(lexer.Rules{
"Root": {
{"Whitespace", `[ \t]+`, nil},
{"Comment", `[#].*`, nil},
{"String", `"(\\"|[^"])*"`, nil},
{"Int", `[-]?[0-9][0-9]*`, nil},
{"DOLLAR", `\$`, nil},
@ -86,7 +87,7 @@ var scanner = lexer.MustStateful(lexer.Rules{
},
})
var parser = participle.MustBuild[astScript](participle.Lexer(scanner),
participle.Elide("Whitespace"))
participle.Elide("Whitespace", "Comment"))
func parse(r io.Reader) (*astScript, error) {
return parser.Parse("test", r)

View file

@ -1,4 +1,4 @@
package cmdlang
package ucl
import (
"context"
@ -40,8 +40,7 @@ func setBuiltin(ctx context.Context, args invocationArgs) (object, error) {
newVal := args.args[1]
// TODO: if the value is a stream, consume the stream and save it as a list
args.ec.setVar(name, newVal)
args.ec.setOrDefineVar(name, newVal)
return newVal, nil
}
@ -383,9 +382,9 @@ func (b procObject) invoke(ctx context.Context, args invocationArgs) (object, er
for i, name := range b.block.Names {
if i < len(args.args) {
newEc.setVar(name, args.args[i])
newEc.setOrDefineVar(name, args.args[i])
} else {
newEc.setVar(name, nil)
newEc.setOrDefineVar(name, nil)
}
}

View file

@ -1,4 +1,4 @@
package cmdlang
package ucl
type evalCtx struct {
root *evalCtx
@ -34,7 +34,24 @@ func (ec *evalCtx) addMacro(name string, inv macroable) {
ec.root.macros[name] = inv
}
func (ec *evalCtx) setVar(name string, val object) {
func (ec *evalCtx) setVar(name string, val object) bool {
if ec == nil || ec.vars == nil {
return false
}
if _, ok := ec.vars[name]; ok {
ec.vars[name] = val
return true
}
return ec.parent.setVar(name, val)
}
func (ec *evalCtx) setOrDefineVar(name string, val object) {
if ec.setVar(name, val) {
return
}
if ec.vars == nil {
ec.vars = make(map[string]object)
}

View file

@ -1,4 +1,4 @@
package cmdlang
package ucl
import (
"context"
@ -27,6 +27,10 @@ func (e evaluator) evalScript(ctx context.Context, ec *evalCtx, n *astScript) (l
}
func (e evaluator) evalStatement(ctx context.Context, ec *evalCtx, n *astStatements) (object, error) {
if n == nil {
return nil, nil
}
res, err := e.evalPipeline(ctx, ec, n.First)
if err != nil {
return nil, err

29
ucl/evaldisplay.go Normal file
View file

@ -0,0 +1,29 @@
package ucl
import (
"context"
"fmt"
)
func EvalAndDisplay(ctx context.Context, inst *Inst, expr string) error {
res, err := inst.eval(ctx, expr)
if err != nil {
return err
}
return displayResult(ctx, inst, res)
}
func displayResult(ctx context.Context, inst *Inst, res object) (err error) {
switch v := res.(type) {
case nil:
if _, err = fmt.Fprintln(inst.out, "(nil)"); err != nil {
return err
}
default:
if _, err = fmt.Fprintln(inst.out, v.String()); err != nil {
return err
}
}
return nil
}

View file

@ -1,9 +1,8 @@
package cmdlang
package ucl
import (
"context"
"errors"
"fmt"
"io"
"os"
"strings"
@ -47,7 +46,7 @@ func New(opts ...InstOption) *Inst {
//rootEC.addCmd("testTimebomb", invokableStreamFunc(errorTestBuiltin))
rootEC.setVar("hello", strObject("world"))
rootEC.setOrDefineVar("hello", strObject("world"))
inst := &Inst{
out: os.Stdout,
@ -93,26 +92,3 @@ func (inst *Inst) eval(ctx context.Context, expr string) (object, error) {
// TODO: this should be a separate forkAndIsolate() session
return eval.evalScript(ctx, inst.rootEC, ast)
}
func (inst *Inst) EvalAndDisplay(ctx context.Context, expr string) error {
res, err := inst.eval(ctx, expr)
if err != nil {
return err
}
return inst.display(ctx, res)
}
func (inst *Inst) display(ctx context.Context, res object) (err error) {
switch v := res.(type) {
case nil:
if _, err = fmt.Fprintln(inst.out, "(nil)"); err != nil {
return err
}
default:
if _, err = fmt.Fprintln(inst.out, v.String()); err != nil {
return err
}
}
return nil
}

View file

@ -1,11 +1,11 @@
package cmdlang_test
package ucl_test
import (
"bytes"
"context"
"github.com/lmika/ucl/ucl"
"testing"
"github.com/lmika/cmdlang-proto/cmdlang"
"github.com/stretchr/testify/assert"
)
@ -67,7 +67,7 @@ func TestInst_Eval(t *testing.T) {
ctx := context.Background()
outW := bytes.NewBuffer(nil)
inst := cmdlang.New(cmdlang.WithOut(outW), cmdlang.WithTestBuiltin())
inst := ucl.New(ucl.WithOut(outW), ucl.WithTestBuiltin())
res, err := inst.Eval(ctx, tt.expr)
assert.NoError(t, err)

View file

@ -1,4 +1,4 @@
package cmdlang
package ucl
import (
"context"
@ -235,7 +235,7 @@ func (ma macroArgs) evalBlock(ctx context.Context, n int, args []object, pushSco
}
for i, n := range block.block.Names {
if i < len(args) {
ec.setVar(n, args[i])
ec.setOrDefineVar(n, args[i])
}
}
@ -341,7 +341,7 @@ func (bo blockObject) invoke(ctx context.Context, args invocationArgs) (object,
ec := args.ec.fork()
for i, n := range bo.block.Names {
if i < len(args.args) {
ec.setVar(n, args.args[i])
ec.setOrDefineVar(n, args.args[i])
}
}
@ -370,8 +370,7 @@ func (p proxyObject) String() string {
}
func (p proxyObject) Truthy() bool {
//TODO implement me
panic("implement me")
return p.p != nil
}
type listableProxyObject struct {
@ -383,7 +382,7 @@ func (p listableProxyObject) String() string {
}
func (p listableProxyObject) Truthy() bool {
panic("implement me")
return p.v.Len() > 0
}
func (p listableProxyObject) Len() int {

View file

@ -1,4 +1,4 @@
package cmdlang
package ucl
import (
"bytes"
@ -53,8 +53,8 @@ func WithTestBuiltin() InstOption {
return strObject(sb.String()), nil
}))
i.rootEC.setVar("a", strObject("alpha"))
i.rootEC.setVar("bee", strObject("buzz"))
i.rootEC.setOrDefineVar("a", strObject("alpha"))
i.rootEC.setOrDefineVar("bee", strObject("buzz"))
}
}
@ -84,6 +84,18 @@ func TestBuiltins_Echo(t *testing.T) {
;
echo "world"
;
`, want: "Hello\nworld\n"},
{desc: "multi-line 4", expr: `
# This is a comment
#
;;;
# This is another comment
echo "Hello"
;
echo "world" # command after this
;
`, want: "Hello\nworld\n"},
}
@ -167,7 +179,7 @@ func TestBuiltins_If(t *testing.T) {
outW := bytes.NewBuffer(nil)
inst := New(WithOut(outW), WithTestBuiltin())
err := inst.EvalAndDisplay(ctx, tt.expr)
err := EvalAndDisplay(ctx, inst, tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, outW.String())
@ -197,7 +209,7 @@ func TestBuiltins_ForEach(t *testing.T) {
outW := bytes.NewBuffer(nil)
inst := New(WithOut(outW), WithTestBuiltin())
err := inst.EvalAndDisplay(ctx, tt.expr)
err := EvalAndDisplay(ctx, inst, tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, outW.String())
@ -257,18 +269,18 @@ func TestBuiltins_Procs(t *testing.T) {
call (makeGreeter "Quick") "call me"
`, want: "Hello, world\nGoodbye cruel, world\nQuick, call me\n(nil)\n"},
//{desc: "modifying closed over variables", expr: `
// proc makeSetter {
// set bla "X"
// proc appendToBla { |x|
// set bla (cat $bla $x)
// }
// }
//
// set er (makeSetter)
// call $er "xxx"
// call $er "yyy"
// `, want: "Xxxx\nXxxxyyy(nil)\n"},
{desc: "modifying closed over variables", expr: `
proc makeSetter {
set bla "X"
proc appendToBla { |x|
set bla (cat $bla $x)
}
}
set er (makeSetter)
echo (call $er "xxx")
echo (call $er "yyy")
`, want: "Xxxx\nXxxxyyy\n(nil)\n"},
}
for _, tt := range tests {
@ -277,7 +289,7 @@ func TestBuiltins_Procs(t *testing.T) {
outW := bytes.NewBuffer(nil)
inst := New(WithOut(outW), WithTestBuiltin())
err := inst.EvalAndDisplay(ctx, tt.expr)
err := EvalAndDisplay(ctx, inst, tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, outW.String())
@ -309,12 +321,12 @@ func TestBuiltins_Map(t *testing.T) {
{desc: "map list with block", expr: `
map ["a" "b" "c"] { |x| toUpper $x }
`, want: "[A B C]\n"},
//{desc: "map list with stream", expr: `
// set makeUpper (proc { |x| $x | toUpper })
//
// set l (["a" "b" "c"] | map $makeUpper)
// echo $l
// `, want: "[A B C]\n"},
{desc: "map list with stream", expr: `
set makeUpper (proc { |x| toUpper $x })
set l (["a" "b" "c"] | map $makeUpper)
echo $l
`, want: "[A B C]\n(nil)\n"},
}
for _, tt := range tests {
@ -323,7 +335,7 @@ func TestBuiltins_Map(t *testing.T) {
outW := bytes.NewBuffer(nil)
inst := New(WithOut(outW), WithTestBuiltin())
err := inst.EvalAndDisplay(ctx, tt.expr)
err := EvalAndDisplay(ctx, inst, tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, outW.String())
@ -379,7 +391,7 @@ func TestBuiltins_Index(t *testing.T) {
Gamma: []int{22, 33},
}, nil
})
err := inst.EvalAndDisplay(ctx, tt.expr)
err := EvalAndDisplay(ctx, inst, tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, outW.String())
@ -438,7 +450,7 @@ func TestBuiltins_Len(t *testing.T) {
missing: "missing",
}, nil
})
err := inst.EvalAndDisplay(ctx, tt.expr)
err := EvalAndDisplay(ctx, inst, tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, outW.String())

View file

@ -1,4 +1,4 @@
package cmdlang
package ucl
import (
"context"
@ -10,8 +10,8 @@ type CallArgs struct {
args invocationArgs
}
func (ca CallArgs) Bind(vars ...interface{}) error {
if len(ca.args.args) != len(vars) {
func (ca *CallArgs) Bind(vars ...interface{}) error {
if len(ca.args.args) < len(vars) {
return errors.New("wrong number of arguments")
}
@ -20,6 +20,7 @@ func (ca CallArgs) Bind(vars ...interface{}) error {
return err
}
}
ca.args = ca.args.shift(len(vars))
return nil
}

View file

@ -1,19 +1,19 @@
package cmdlang_test
package ucl_test
import (
"bytes"
"context"
"github.com/lmika/ucl/ucl"
"strings"
"testing"
"github.com/lmika/cmdlang-proto/cmdlang"
"github.com/stretchr/testify/assert"
)
func TestInst_SetBuiltin(t *testing.T) {
t.Run("simple builtin accepting and returning strings", func(t *testing.T) {
inst := cmdlang.New()
inst.SetBuiltin("add2", func(ctx context.Context, args cmdlang.CallArgs) (any, error) {
inst := ucl.New()
inst.SetBuiltin("add2", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x, y string
if err := args.Bind(&x, &y); err != nil {
@ -28,9 +28,29 @@ func TestInst_SetBuiltin(t *testing.T) {
assert.Equal(t, "Hello, World", res)
})
t.Run("bind shift arguments", func(t *testing.T) {
inst := ucl.New()
inst.SetBuiltin("add2", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x, y string
if err := args.Bind(&x); err != nil {
return nil, err
}
if err := args.Bind(&y); err != nil {
return nil, err
}
return x + y, nil
})
res, err := inst.Eval(context.Background(), `add2 "Hello, " "World"`)
assert.NoError(t, err)
assert.Equal(t, "Hello, World", res)
})
t.Run("simple builtin with optional switches and strings", func(t *testing.T) {
inst := cmdlang.New()
inst.SetBuiltin("add2", func(ctx context.Context, args cmdlang.CallArgs) (any, error) {
inst := ucl.New()
inst.SetBuiltin("add2", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x, y, sep string
if err := args.BindSwitch("sep", &sep); err != nil {
@ -75,8 +95,8 @@ func TestInst_SetBuiltin(t *testing.T) {
x, y string
}
inst := cmdlang.New()
inst.SetBuiltin("add2", func(ctx context.Context, args cmdlang.CallArgs) (any, error) {
inst := ucl.New()
inst.SetBuiltin("add2", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x, y string
if err := args.Bind(&x, &y); err != nil {
@ -107,8 +127,8 @@ func TestInst_SetBuiltin(t *testing.T) {
for _, tt := range tests {
t.Run(tt.descr, func(t *testing.T) {
inst := cmdlang.New()
inst.SetBuiltin("add2", func(ctx context.Context, args cmdlang.CallArgs) (any, error) {
inst := ucl.New()
inst.SetBuiltin("add2", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x, y string
if err := args.Bind(&x, &y); err != nil {
@ -117,7 +137,7 @@ func TestInst_SetBuiltin(t *testing.T) {
return pair{x, y}, nil
})
inst.SetBuiltin("join", func(ctx context.Context, args cmdlang.CallArgs) (any, error) {
inst.SetBuiltin("join", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var x pair
if err := args.Bind(&x); err != nil {
@ -148,9 +168,9 @@ func TestInst_SetBuiltin(t *testing.T) {
for _, tt := range tests {
t.Run(tt.descr, func(t *testing.T) {
outW := bytes.NewBuffer(nil)
inst := cmdlang.New(cmdlang.WithOut(outW))
inst := ucl.New(ucl.WithOut(outW))
inst.SetBuiltin("countTo3", func(ctx context.Context, args cmdlang.CallArgs) (any, error) {
inst.SetBuiltin("countTo3", func(ctx context.Context, args ucl.CallArgs) (any, error) {
return []string{"1", "2", "3"}, nil
})
@ -167,14 +187,14 @@ func TestCallArgs_Bind(t *testing.T) {
t.Run("bind to an interface", func(t *testing.T) {
ctx := context.Background()
inst := cmdlang.New()
inst.SetBuiltin("sa", func(ctx context.Context, args cmdlang.CallArgs) (any, error) {
inst := ucl.New()
inst.SetBuiltin("sa", func(ctx context.Context, args ucl.CallArgs) (any, error) {
return doStringA{this: "a val"}, nil
})
inst.SetBuiltin("sb", func(ctx context.Context, args cmdlang.CallArgs) (any, error) {
inst.SetBuiltin("sb", func(ctx context.Context, args ucl.CallArgs) (any, error) {
return doStringB{left: "foo", right: "bar"}, nil
})
inst.SetBuiltin("dostr", func(ctx context.Context, args cmdlang.CallArgs) (any, error) {
inst.SetBuiltin("dostr", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var ds doStringable
if err := args.Bind(&ds); err != nil {