Added a CSV module
This commit is contained in:
parent
ca95ac7008
commit
f119683b57
|
@ -18,10 +18,12 @@ func main() {
|
||||||
defer rl.Close()
|
defer rl.Close()
|
||||||
|
|
||||||
instRepl := repl.New(
|
instRepl := repl.New(
|
||||||
ucl.WithModule(builtins.OS()),
|
ucl.WithModule(builtins.CSV(nil)),
|
||||||
ucl.WithModule(builtins.FS(nil)),
|
ucl.WithModule(builtins.FS(nil)),
|
||||||
ucl.WithModule(builtins.Log(nil)),
|
ucl.WithModule(builtins.Log(nil)),
|
||||||
|
ucl.WithModule(builtins.OS()),
|
||||||
ucl.WithModule(builtins.Strs()),
|
ucl.WithModule(builtins.Strs()),
|
||||||
|
ucl.WithModule(builtins.Time()),
|
||||||
)
|
)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,11 @@ func initJS(ctx context.Context) {
|
||||||
replInst := repl.New(
|
replInst := repl.New(
|
||||||
ucl.WithModule(builtins.Log(nil)),
|
ucl.WithModule(builtins.Log(nil)),
|
||||||
ucl.WithModule(builtins.Strs()),
|
ucl.WithModule(builtins.Strs()),
|
||||||
|
ucl.WithModule(builtins.Time()),
|
||||||
ucl.WithOut(ucl.LineHandler(func(line string) {
|
ucl.WithOut(ucl.LineHandler(func(line string) {
|
||||||
invokeUCLCallback("onOutLine", line)
|
invokeUCLCallback("onOutLine", line)
|
||||||
})))
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
uclObj["eval"] = js.FuncOf(func(this js.Value, args []js.Value) any {
|
uclObj["eval"] = js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
|
|
|
@ -44,15 +44,15 @@ func echoBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
|
|
||||||
func addBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func addBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
if len(args.args) == 0 {
|
if len(args.args) == 0 {
|
||||||
return intObject(0), nil
|
return IntObject(0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
for i, a := range args.args {
|
for i, a := range args.args {
|
||||||
switch t := a.(type) {
|
switch t := a.(type) {
|
||||||
case intObject:
|
case IntObject:
|
||||||
n += int(t)
|
n += int(t)
|
||||||
case strObject:
|
case StringObject:
|
||||||
v, err := strconv.Atoi(string(t))
|
v, err := strconv.Atoi(string(t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("arg %v of 'add' not convertable to an int", i)
|
return nil, fmt.Errorf("arg %v of 'add' not convertable to an int", i)
|
||||||
|
@ -63,21 +63,21 @@ func addBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intObject(n), nil
|
return IntObject(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func subBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func subBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
if len(args.args) == 0 {
|
if len(args.args) == 0 {
|
||||||
return intObject(0), nil
|
return IntObject(0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
for i, a := range args.args {
|
for i, a := range args.args {
|
||||||
var p int
|
var p int
|
||||||
switch t := a.(type) {
|
switch t := a.(type) {
|
||||||
case intObject:
|
case IntObject:
|
||||||
p = int(t)
|
p = int(t)
|
||||||
case strObject:
|
case StringObject:
|
||||||
v, err := strconv.Atoi(string(t))
|
v, err := strconv.Atoi(string(t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("arg %v of 'sub' not convertable to an int", i)
|
return nil, fmt.Errorf("arg %v of 'sub' not convertable to an int", i)
|
||||||
|
@ -93,20 +93,20 @@ func subBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intObject(n), nil
|
return IntObject(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mupBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func mupBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
if len(args.args) == 0 {
|
if len(args.args) == 0 {
|
||||||
return intObject(1), nil
|
return IntObject(1), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 1
|
n := 1
|
||||||
for i, a := range args.args {
|
for i, a := range args.args {
|
||||||
switch t := a.(type) {
|
switch t := a.(type) {
|
||||||
case intObject:
|
case IntObject:
|
||||||
n *= int(t)
|
n *= int(t)
|
||||||
case strObject:
|
case StringObject:
|
||||||
v, err := strconv.Atoi(string(t))
|
v, err := strconv.Atoi(string(t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("arg %v of 'mup' not convertable to an int", i)
|
return nil, fmt.Errorf("arg %v of 'mup' not convertable to an int", i)
|
||||||
|
@ -117,21 +117,21 @@ func mupBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intObject(n), nil
|
return IntObject(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func divBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func divBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
if len(args.args) == 0 {
|
if len(args.args) == 0 {
|
||||||
return intObject(1), nil
|
return IntObject(1), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 1
|
n := 1
|
||||||
for i, a := range args.args {
|
for i, a := range args.args {
|
||||||
var p int
|
var p int
|
||||||
switch t := a.(type) {
|
switch t := a.(type) {
|
||||||
case intObject:
|
case IntObject:
|
||||||
p = int(t)
|
p = int(t)
|
||||||
case strObject:
|
case StringObject:
|
||||||
v, err := strconv.Atoi(string(t))
|
v, err := strconv.Atoi(string(t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("arg %v of 'div' not convertable to an int", i)
|
return nil, fmt.Errorf("arg %v of 'div' not convertable to an int", i)
|
||||||
|
@ -147,21 +147,21 @@ func divBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intObject(n), nil
|
return IntObject(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func modBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func modBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
if len(args.args) == 0 {
|
if len(args.args) == 0 {
|
||||||
return intObject(0), nil
|
return IntObject(0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
for i, a := range args.args {
|
for i, a := range args.args {
|
||||||
var p int
|
var p int
|
||||||
switch t := a.(type) {
|
switch t := a.(type) {
|
||||||
case intObject:
|
case IntObject:
|
||||||
p = int(t)
|
p = int(t)
|
||||||
case strObject:
|
case StringObject:
|
||||||
v, err := strconv.Atoi(string(t))
|
v, err := strconv.Atoi(string(t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("arg %v of 'mod' not convertable to an int", i)
|
return nil, fmt.Errorf("arg %v of 'mod' not convertable to an int", i)
|
||||||
|
@ -177,7 +177,7 @@ func modBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intObject(n), nil
|
return IntObject(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func setBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
|
@ -204,7 +204,7 @@ func toUpperBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return strObject(strings.ToUpper(sarg)), nil
|
return StringObject(strings.ToUpper(sarg)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func eqBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func eqBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
|
@ -319,12 +319,12 @@ func objectsEqual(l, r Object) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch lv := l.(type) {
|
switch lv := l.(type) {
|
||||||
case strObject:
|
case StringObject:
|
||||||
if rv, ok := r.(strObject); ok {
|
if rv, ok := r.(StringObject); ok {
|
||||||
return lv == rv
|
return lv == rv
|
||||||
}
|
}
|
||||||
case intObject:
|
case IntObject:
|
||||||
if rv, ok := r.(intObject); ok {
|
if rv, ok := r.(IntObject); ok {
|
||||||
return lv == rv
|
return lv == rv
|
||||||
}
|
}
|
||||||
case boolObject:
|
case boolObject:
|
||||||
|
@ -373,12 +373,12 @@ func objectsEqual(l, r Object) bool {
|
||||||
|
|
||||||
func objectsLessThan(l, r Object) (bool, error) {
|
func objectsLessThan(l, r Object) (bool, error) {
|
||||||
switch lv := l.(type) {
|
switch lv := l.(type) {
|
||||||
case strObject:
|
case StringObject:
|
||||||
if rv, ok := r.(strObject); ok {
|
if rv, ok := r.(StringObject); ok {
|
||||||
return lv < rv, nil
|
return lv < rv, nil
|
||||||
}
|
}
|
||||||
case intObject:
|
case IntObject:
|
||||||
if rv, ok := r.(intObject); ok {
|
if rv, ok := r.(IntObject); ok {
|
||||||
return lv < rv, nil
|
return lv < rv, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -391,10 +391,10 @@ func strBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.args[0] == nil {
|
if args.args[0] == nil {
|
||||||
return strObject(""), nil
|
return StringObject(""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return strObject(args.args[0].String()), nil
|
return StringObject(args.args[0].String()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func intBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func intBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
|
@ -403,23 +403,23 @@ func intBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.args[0] == nil {
|
if args.args[0] == nil {
|
||||||
return intObject(0), nil
|
return IntObject(0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := args.args[0].(type) {
|
switch v := args.args[0].(type) {
|
||||||
case intObject:
|
case IntObject:
|
||||||
return v, nil
|
return v, nil
|
||||||
case strObject:
|
case StringObject:
|
||||||
i, err := strconv.Atoi(string(v))
|
i, err := strconv.Atoi(string(v))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("cannot convert to int")
|
return nil, errors.New("cannot convert to int")
|
||||||
}
|
}
|
||||||
return intObject(i), nil
|
return IntObject(i), nil
|
||||||
case boolObject:
|
case boolObject:
|
||||||
if v {
|
if v {
|
||||||
return intObject(1), nil
|
return IntObject(1), nil
|
||||||
}
|
}
|
||||||
return intObject(0), nil
|
return IntObject(0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("cannot convert to int")
|
return nil, errors.New("cannot convert to int")
|
||||||
|
@ -435,7 +435,7 @@ func concatBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
sb.WriteString(a.String())
|
sb.WriteString(a.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return strObject(sb.String()), nil
|
return StringObject(sb.String()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func callBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func callBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
|
@ -457,21 +457,21 @@ func lenBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := args.args[0].(type) {
|
switch v := args.args[0].(type) {
|
||||||
case strObject:
|
case StringObject:
|
||||||
return intObject(len(string(v))), nil
|
return IntObject(len(string(v))), nil
|
||||||
case Listable:
|
case Listable:
|
||||||
return intObject(v.Len()), nil
|
return IntObject(v.Len()), nil
|
||||||
case hashable:
|
case hashable:
|
||||||
return intObject(v.Len()), nil
|
return IntObject(v.Len()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return intObject(0), nil
|
return IntObject(0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func indexLookup(ctx context.Context, obj, elem Object) (Object, error) {
|
func indexLookup(ctx context.Context, obj, elem Object) (Object, error) {
|
||||||
switch v := obj.(type) {
|
switch v := obj.(type) {
|
||||||
case Listable:
|
case Listable:
|
||||||
intIdx, ok := elem.(intObject)
|
intIdx, ok := elem.(IntObject)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -480,7 +480,7 @@ func indexLookup(ctx context.Context, obj, elem Object) (Object, error) {
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case hashable:
|
case hashable:
|
||||||
strIdx, ok := elem.(strObject)
|
strIdx, ok := elem.(StringObject)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("expected string for hashable")
|
return nil, errors.New("expected string for hashable")
|
||||||
}
|
}
|
||||||
|
@ -516,7 +516,7 @@ func keysBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
case hashable:
|
case hashable:
|
||||||
keys := make(listObject, 0, v.Len())
|
keys := make(listObject, 0, v.Len())
|
||||||
if err := v.Each(func(k string, _ Object) error {
|
if err := v.Each(func(k string, _ Object) error {
|
||||||
keys = append(keys, strObject(k))
|
keys = append(keys, StringObject(k))
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -602,9 +602,9 @@ func (s seqObject) Index(i int) Object {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if s.from > s.to {
|
if s.from > s.to {
|
||||||
return intObject(s.from - i)
|
return IntObject(s.from - i)
|
||||||
}
|
}
|
||||||
return intObject(s.from + i)
|
return IntObject(s.from + i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func seqBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
func seqBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||||
|
@ -721,7 +721,7 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
||||||
}
|
}
|
||||||
case hashable:
|
case hashable:
|
||||||
err := t.Each(func(k string, v Object) error {
|
err := t.Each(func(k string, v Object) error {
|
||||||
last, err = args.evalBlock(ctx, blockIdx, []Object{strObject(k), v}, true)
|
last, err = args.evalBlock(ctx, blockIdx, []Object{StringObject(k), v}, true)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if errors.As(err, &breakErr) {
|
if errors.As(err, &breakErr) {
|
||||||
|
|
104
ucl/builtins/csv.go
Normal file
104
ucl/builtins/csv.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"strings"
|
||||||
|
"ucl.lmika.dev/ucl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type csvHandlers struct {
|
||||||
|
fs fs.FS
|
||||||
|
}
|
||||||
|
|
||||||
|
func CSV(fs fs.FS) ucl.Module {
|
||||||
|
fsh := csvHandlers{fs: fs}
|
||||||
|
|
||||||
|
return ucl.Module{
|
||||||
|
Name: "csv",
|
||||||
|
Builtins: map[string]ucl.BuiltinHandler{
|
||||||
|
"each-record": fsh.eachRecord,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h csvHandlers) eachRecord(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
var (
|
||||||
|
filename string
|
||||||
|
closure ucl.Invokable
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := args.Bind(&filename, &closure); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := h.fs.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
cr := csv.NewReader(f)
|
||||||
|
|
||||||
|
header, err := cr.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hio := make(headerIndexObject)
|
||||||
|
for i, h := range header {
|
||||||
|
hio[h] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
record, err := cr.Read()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := closure.Invoke(ctx, stringSlice(record), hio); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type headerIndexObject map[string]int
|
||||||
|
|
||||||
|
func (hio headerIndexObject) String() string {
|
||||||
|
strs := make([]string, len(hio))
|
||||||
|
for h, i := range hio {
|
||||||
|
strs[i] = h
|
||||||
|
}
|
||||||
|
return strings.Join(strs, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hio headerIndexObject) Truthy() bool {
|
||||||
|
return len(hio) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hio headerIndexObject) Len() int {
|
||||||
|
return len(hio)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hio headerIndexObject) Value(k string) ucl.Object {
|
||||||
|
v, ok := hio[k]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ucl.IntObject(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hio headerIndexObject) Each(fn func(k string, v ucl.Object) error) error {
|
||||||
|
for k, v := range hio {
|
||||||
|
if err := fn(k, ucl.IntObject(v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
53
ucl/builtins/csv_test.go
Normal file
53
ucl/builtins/csv_test.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package builtins_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"testing/fstest"
|
||||||
|
"ucl.lmika.dev/ucl"
|
||||||
|
"ucl.lmika.dev/ucl/builtins"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testCsvFS = fstest.MapFS{
|
||||||
|
"test.csv": &fstest.MapFile{
|
||||||
|
Data: []byte(strings.Join([]string{
|
||||||
|
"wind,dir,bearing",
|
||||||
|
"north,N,0",
|
||||||
|
"south,S,180",
|
||||||
|
"east,E,90",
|
||||||
|
"west,W,270",
|
||||||
|
}, "\n")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCSV_ReadRecord(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
descr string
|
||||||
|
eval string
|
||||||
|
wantOut string
|
||||||
|
}{
|
||||||
|
{descr: "read csv 1", eval: `csv:each-record "test.csv" { |r h| echo $r.(0) }`, wantOut: "north\nsouth\neast\nwest\n"},
|
||||||
|
{descr: "read csv 2", eval: `csv:each-record "test.csv" { |r h| echo $r.($h.dir) }`, wantOut: "N\nS\nE\nW\n"},
|
||||||
|
{descr: "read csv 3", eval: `csv:each-record "test.csv" { |r h| echo $r.($h.bearing) "-" $r.($h.dir) }`, wantOut: "0-N\n180-S\n90-E\n270-W\n"},
|
||||||
|
{descr: "read csv 4", eval: `csv:each-record "test.csv" { |r h| echo $h.bearing }`, wantOut: "2\n2\n2\n2\n"},
|
||||||
|
{descr: "read csv 5", eval: `csv:each-record "test.csv" {}`, wantOut: ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.descr, func(t *testing.T) {
|
||||||
|
var bfr bytes.Buffer
|
||||||
|
|
||||||
|
inst := ucl.New(
|
||||||
|
ucl.WithModule(builtins.CSV(testCsvFS)),
|
||||||
|
ucl.WithOut(&bfr),
|
||||||
|
)
|
||||||
|
|
||||||
|
_, err := inst.Eval(context.Background(), tt.eval)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.wantOut, bfr.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -259,7 +259,7 @@ func (e evaluator) evalLiteral(ctx context.Context, ec *evalCtx, n *astLiteral)
|
||||||
}
|
}
|
||||||
return sval, nil
|
return sval, nil
|
||||||
case n.Int != nil:
|
case n.Int != nil:
|
||||||
return intObject(*n.Int), nil
|
return IntObject(*n.Int), nil
|
||||||
}
|
}
|
||||||
return nil, errors.New("unhandled literal type")
|
return nil, errors.New("unhandled literal type")
|
||||||
}
|
}
|
||||||
|
|
12
ucl/objs.go
12
ucl/objs.go
|
@ -102,13 +102,13 @@ func (s StringObject) Truthy() bool {
|
||||||
return string(s) != ""
|
return string(s) != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
type intObject int
|
type IntObject int
|
||||||
|
|
||||||
func (i intObject) String() string {
|
func (i IntObject) String() string {
|
||||||
return strconv.Itoa(int(i))
|
return strconv.Itoa(int(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i intObject) Truthy() bool {
|
func (i IntObject) Truthy() bool {
|
||||||
return i != 0
|
return i != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ func toGoValue(obj Object) (interface{}, bool) {
|
||||||
return nil, true
|
return nil, true
|
||||||
case StringObject:
|
case StringObject:
|
||||||
return string(v), true
|
return string(v), true
|
||||||
case intObject:
|
case IntObject:
|
||||||
return int(v), true
|
return int(v), true
|
||||||
case boolObject:
|
case boolObject:
|
||||||
return bool(v), true
|
return bool(v), true
|
||||||
|
@ -187,7 +187,7 @@ func fromGoValue(v any) (Object, error) {
|
||||||
case string:
|
case string:
|
||||||
return StringObject(t), nil
|
return StringObject(t), nil
|
||||||
case int:
|
case int:
|
||||||
return intObject(t), nil
|
return IntObject(t), nil
|
||||||
case bool:
|
case bool:
|
||||||
return boolObject(t), nil
|
return boolObject(t), nil
|
||||||
case time.Time:
|
case time.Time:
|
||||||
|
@ -358,7 +358,7 @@ func (ia invocationArgs) intArg(i int) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := ia.args[i].(type) {
|
switch v := ia.args[i].(type) {
|
||||||
case intObject:
|
case IntObject:
|
||||||
return int(v), nil
|
return int(v), nil
|
||||||
default:
|
default:
|
||||||
return 0, errors.New("expected an int arg")
|
return 0, errors.New("expected an int arg")
|
||||||
|
|
|
@ -126,7 +126,7 @@ func bindArg(v interface{}, arg object) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case *int:
|
case *int:
|
||||||
if iArg, ok := arg.(intObject); ok {
|
if iArg, ok := arg.(IntObject); ok {
|
||||||
*t = int(iArg)
|
*t = int(iArg)
|
||||||
} else {
|
} else {
|
||||||
return errors.New("invalid arg")
|
return errors.New("invalid arg")
|
||||||
|
@ -151,7 +151,7 @@ func canBindArg(v interface{}, arg object) bool {
|
||||||
case *string:
|
case *string:
|
||||||
return true
|
return true
|
||||||
case *int:
|
case *int:
|
||||||
_, ok := arg.(intObject)
|
_, ok := arg.(IntObject)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue