feature/issue-1: built out the core libraries #3
114
ucl/builtins.go
114
ucl/builtins.go
|
@ -53,6 +53,120 @@ 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) {
|
||||||
|
if len(args.args) == 0 {
|
||||||
|
return intObject(0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 0
|
||||||
|
for i, a := range args.args {
|
||||||
|
var p int
|
||||||
|
switch t := a.(type) {
|
||||||
|
case intObject:
|
||||||
|
p = int(t)
|
||||||
|
case strObject:
|
||||||
|
v, err := strconv.Atoi(string(t))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("arg %v of 'sub' not convertable to an int", i)
|
||||||
|
}
|
||||||
|
p = v
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("arg %v of 'sub' not convertable to an int", i)
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
n = p
|
||||||
|
} else {
|
||||||
|
n -= p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return intObject(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mupBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||||
|
if len(args.args) == 0 {
|
||||||
|
return intObject(1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 1
|
||||||
|
for i, a := range args.args {
|
||||||
|
switch t := a.(type) {
|
||||||
|
case intObject:
|
||||||
|
n *= int(t)
|
||||||
|
case strObject:
|
||||||
|
v, err := strconv.Atoi(string(t))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("arg %v of 'mup' not convertable to an int", i)
|
||||||
|
}
|
||||||
|
n *= v
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("arg %v of 'mup' not convertable to an int", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return intObject(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func divBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||||
|
if len(args.args) == 0 {
|
||||||
|
return intObject(1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 1
|
||||||
|
for i, a := range args.args {
|
||||||
|
var p int
|
||||||
|
switch t := a.(type) {
|
||||||
|
case intObject:
|
||||||
|
p = int(t)
|
||||||
|
case strObject:
|
||||||
|
v, err := strconv.Atoi(string(t))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("arg %v of 'div' not convertable to an int", i)
|
||||||
|
}
|
||||||
|
p = v
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("arg %v of 'div' not convertable to an int", i)
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
n = p
|
||||||
|
} else {
|
||||||
|
n /= p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return intObject(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func modBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||||
|
if len(args.args) == 0 {
|
||||||
|
return intObject(0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 0
|
||||||
|
for i, a := range args.args {
|
||||||
|
var p int
|
||||||
|
switch t := a.(type) {
|
||||||
|
case intObject:
|
||||||
|
p = int(t)
|
||||||
|
case strObject:
|
||||||
|
v, err := strconv.Atoi(string(t))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("arg %v of 'mod' not convertable to an int", i)
|
||||||
|
}
|
||||||
|
p = v
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("arg %v of 'mod' not convertable to an int", i)
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
n = p
|
||||||
|
} else {
|
||||||
|
n %= p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return intObject(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
func setBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
func setBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
||||||
if err := args.expectArgn(2); err != nil {
|
if err := args.expectArgn(2); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -71,6 +71,10 @@ func New(opts ...InstOption) *Inst {
|
||||||
rootEC.addCmd("int", invokableFunc(intBuiltin))
|
rootEC.addCmd("int", invokableFunc(intBuiltin))
|
||||||
|
|
||||||
rootEC.addCmd("add", invokableFunc(addBuiltin))
|
rootEC.addCmd("add", invokableFunc(addBuiltin))
|
||||||
|
rootEC.addCmd("sub", invokableFunc(subBuiltin))
|
||||||
|
rootEC.addCmd("mup", invokableFunc(mupBuiltin))
|
||||||
|
rootEC.addCmd("div", invokableFunc(divBuiltin))
|
||||||
|
rootEC.addCmd("mod", invokableFunc(modBuiltin))
|
||||||
|
|
||||||
rootEC.addCmd("cat", invokableFunc(concatBuiltin))
|
rootEC.addCmd("cat", invokableFunc(concatBuiltin))
|
||||||
rootEC.addCmd("break", invokableFunc(breakBuiltin))
|
rootEC.addCmd("break", invokableFunc(breakBuiltin))
|
||||||
|
|
|
@ -1093,3 +1093,59 @@ func TestBuiltins_Int(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuiltins_AddSubMupDivMod(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
expr string
|
||||||
|
want int
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{desc: "add 1", expr: `add 1 2`, want: 3},
|
||||||
|
{desc: "add 2", expr: `add "3" 5`, want: 8},
|
||||||
|
{desc: "add 3", expr: `add 1 "2" 8`, want: 11},
|
||||||
|
{desc: "add 4", expr: `add 1`, want: 1},
|
||||||
|
{desc: "add 5", expr: `add`, want: 0},
|
||||||
|
{desc: "sub 1", expr: `sub 9 3`, want: 6},
|
||||||
|
{desc: "sub 2", expr: `sub 2 "5"`, want: -3},
|
||||||
|
{desc: "sub 3", expr: `sub 8 1 8`, want: -1},
|
||||||
|
{desc: "sub 4", expr: `sub 4`, want: 4},
|
||||||
|
{desc: "sub 5", expr: `sub`, want: 0},
|
||||||
|
{desc: "mup 1", expr: `mup 2 4`, want: 8},
|
||||||
|
{desc: "mup 2", expr: `mup 3 "4" 5`, want: 60},
|
||||||
|
{desc: "mup 3", expr: `mup 7`, want: 7},
|
||||||
|
{desc: "mup 4", expr: `mup`, want: 1},
|
||||||
|
{desc: "div 1", expr: `div 8 4`, want: 2},
|
||||||
|
{desc: "div 2", expr: `div "7" 4`, want: 1},
|
||||||
|
{desc: "div 3", expr: `div 7`, want: 7},
|
||||||
|
{desc: "div 4", expr: `div`, want: 1},
|
||||||
|
{desc: "mod 1", expr: `mod 2 3`, want: 2},
|
||||||
|
{desc: "mod 2", expr: `mod "7" 4`, want: 3},
|
||||||
|
{desc: "mod 3", expr: `mod 8 4`, want: 0},
|
||||||
|
{desc: "mod 4", expr: `mod 3`, want: 3},
|
||||||
|
{desc: "mod 5", expr: `mod`, want: 0},
|
||||||
|
|
||||||
|
{desc: "add err", expr: `add [] [:]`, wantErr: true},
|
||||||
|
{desc: "sub err", expr: `sub [] [:]`, wantErr: true},
|
||||||
|
{desc: "mup err", expr: `mup [] [:]`, wantErr: true},
|
||||||
|
{desc: "div err", expr: `div [] [:]`, wantErr: true},
|
||||||
|
{desc: "mod err", expr: `mod [] [:]`, wantErr: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
outW := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
inst := New(WithOut(outW), WithTestBuiltin())
|
||||||
|
|
||||||
|
eqRes, err := inst.Eval(ctx, tt.expr)
|
||||||
|
if tt.wantErr {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.want, eqRes)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue