diff --git a/cmd/cmsh/main.go b/cmd/cmsh/main.go index 8793777..8296224 100644 --- a/cmd/cmsh/main.go +++ b/cmd/cmsh/main.go @@ -20,6 +20,8 @@ func main() { instRepl := repl.New( ucl.WithModule(builtins.OS()), ucl.WithModule(builtins.FS(nil)), + ucl.WithModule(builtins.Log(nil)), + ucl.WithModule(builtins.Strs()), ) ctx := context.Background() diff --git a/cmd/playwasm/jsiter.go b/cmd/playwasm/jsiter.go index 1e62e6d..e653f4b 100644 --- a/cmd/playwasm/jsiter.go +++ b/cmd/playwasm/jsiter.go @@ -21,9 +21,12 @@ func invokeUCLCallback(name string, args ...any) { func initJS(ctx context.Context) { uclObj := make(map[string]any) - replInst := repl.New(ucl.WithOut(ucl.LineHandler(func(line string) { - invokeUCLCallback("onOutLine", line) - }))) + replInst := repl.New( + ucl.WithModule(builtins.Log(nil)), + ucl.WithModule(builtins.Strs()), + ucl.WithOut(ucl.LineHandler(func(line string) { + invokeUCLCallback("onOutLine", line) + }))) uclObj["eval"] = js.FuncOf(func(this js.Value, args []js.Value) any { if len(args) != 2 { diff --git a/ucl/builtins/log.go b/ucl/builtins/log.go new file mode 100644 index 0000000..c336833 --- /dev/null +++ b/ucl/builtins/log.go @@ -0,0 +1,44 @@ +package builtins + +import ( + "context" + "log" + "strings" + "ucl.lmika.dev/ucl" +) + +type LogMessageHandler func(s string) error + +func Log(handler LogMessageHandler) ucl.Module { + if handler == nil { + handler = func(s string) error { + log.Println(s) + return nil + } + } + + lh := logHandler{handler: handler} + + return ucl.Module{ + Name: "log", + Builtins: map[string]ucl.BuiltinHandler{ + "puts": lh.logMessage, + }, + } +} + +type logHandler struct { + handler LogMessageHandler +} + +func (lh logHandler) logMessage(ctx context.Context, args ucl.CallArgs) (any, error) { + var sb strings.Builder + var s ucl.Object + + for args.NArgs() > 0 { + if err := args.Bind(&s); err == nil { + sb.WriteString(s.String()) + } + } + return nil, lh.handler(sb.String()) +} diff --git a/ucl/builtins/log_test.go b/ucl/builtins/log_test.go new file mode 100644 index 0000000..9071c02 --- /dev/null +++ b/ucl/builtins/log_test.go @@ -0,0 +1,47 @@ +package builtins_test + +import ( + "bytes" + "context" + "github.com/stretchr/testify/assert" + "testing" + "ucl.lmika.dev/ucl" + "ucl.lmika.dev/ucl/builtins" +) + +func TestLog_Puts(t *testing.T) { + tests := []struct { + desc string + eval string + want any + wantErr bool + wantLog string + }{ + {desc: "log 1", eval: `log:puts "bad stuff here"`, wantLog: "bad stuff here\n"}, + {desc: "log 2", eval: `log:puts "bad " 123 " here " [1 2 3]`, wantLog: "bad 123 here [1 2 3]\n"}, + {desc: "log 3", eval: `log:puts`, wantLog: "\n"}, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var logMessage bytes.Buffer + + inst := ucl.New( + ucl.WithModule(builtins.Log(func(s string) error { + logMessage.WriteString(s) + logMessage.WriteByte('\n') + return nil + })), + ) + + res, err := inst.Eval(context.Background(), tt.eval) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.want, res) + } + assert.Equal(t, tt.wantLog, logMessage.String()) + }) + } +} diff --git a/ucl/userbuiltin.go b/ucl/userbuiltin.go index 04021c3..78f3d47 100644 --- a/ucl/userbuiltin.go +++ b/ucl/userbuiltin.go @@ -96,8 +96,12 @@ func (u userBuiltin) invoke(ctx context.Context, args invocationArgs) (Object, e func (ca CallArgs) bindArg(v interface{}, arg Object) error { switch t := v.(type) { + case *Object: + *t = arg + return nil case *interface{}: *t, _ = toGoValue(arg) + return nil case *Invokable: i, ok := arg.(invokable) if !ok { @@ -123,12 +127,14 @@ func (ca CallArgs) bindArg(v interface{}, arg Object) error { } else { *t = "" } + return nil case *int: if iArg, ok := arg.(intObject); ok { *t = int(iArg) } else { return errors.New("invalid arg") } + return nil } switch t := arg.(type) {