Custom formatter based on go's fmt package. (#185)
* Custom formatter based on go's fmt package. * Cleanup * Cleanup * Added tengo.MaxStringLen check * Cleanup
This commit is contained in:
parent
bb07fa15b7
commit
b2df4f579c
5 changed files with 1262 additions and 23 deletions
27
objects/builtin_format.go
Normal file
27
objects/builtin_format.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package objects
|
||||
|
||||
func builtinFormat(args ...Object) (Object, error) {
|
||||
numArgs := len(args)
|
||||
if numArgs == 0 {
|
||||
return nil, ErrWrongNumArguments
|
||||
}
|
||||
|
||||
format, ok := args[0].(*String)
|
||||
if !ok {
|
||||
return nil, ErrInvalidArgumentType{
|
||||
Name: "format",
|
||||
Expected: "string",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
if numArgs == 1 {
|
||||
return format, nil // okay to return 'format' directly as String is immutable
|
||||
}
|
||||
|
||||
s, err := Format(format.Value, args[1:]...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &String{Value: s}, nil
|
||||
}
|
|
@ -111,4 +111,8 @@ var Builtins = []*BuiltinFunction{
|
|||
Name: "type_name",
|
||||
Value: builtinTypeName,
|
||||
},
|
||||
{
|
||||
Name: "format",
|
||||
Value: builtinFormat,
|
||||
},
|
||||
}
|
||||
|
|
1212
objects/formatter.go
Normal file
1212
objects/formatter.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -157,6 +157,18 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `a := func(x) { return func() { return x } }; out = is_callable(a)`, nil, true) // function
|
||||
expect(t, `a := func(x) { return func() { return x } }; out = is_callable(a(5))`, nil, true) // closure
|
||||
expect(t, `out = is_callable(x)`, Opts().Symbol("x", &StringArray{Value: []string{"foo", "bar"}}).Skip2ndPass(), true) // user object
|
||||
|
||||
expect(t, `out = format("")`, nil, "")
|
||||
expect(t, `out = format("foo")`, nil, "foo")
|
||||
expect(t, `out = format("foo %d %v %s", 1, 2, "bar")`, nil, "foo 1 2 bar")
|
||||
expect(t, `out = format("foo %v", [1, "bar", true])`, nil, `foo [1, "bar", true]`)
|
||||
expect(t, `out = format("foo %v %d", [1, "bar", true], 19)`, nil, `foo [1, "bar", true] 19`)
|
||||
expect(t, `out = format("foo %v", {"a": {"b": {"c": [1, 2, 3]}}})`, nil, `foo {a: {b: {c: [1, 2, 3]}}}`)
|
||||
expect(t, `out = format("%v", [1, [2, [3, 4]]])`, nil, `[1, [2, [3, 4]]]`)
|
||||
|
||||
tengo.MaxStringLen = 9
|
||||
expectError(t, `format("%s", "1234567890")`, nil, "exceeding string size limit")
|
||||
tengo.MaxStringLen = 2147483647
|
||||
}
|
||||
|
||||
func TestBytesN(t *testing.T) {
|
||||
|
|
|
@ -44,17 +44,12 @@ func fmtPrintf(args ...objects.Object) (ret objects.Object, err error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
formatArgs := make([]interface{}, numArgs-1)
|
||||
for idx, arg := range args[1:] {
|
||||
switch arg := arg.(type) {
|
||||
case *objects.Int, *objects.Float, *objects.Bool, *objects.Char, *objects.String, *objects.Bytes:
|
||||
formatArgs[idx] = objects.ToInterface(arg)
|
||||
default:
|
||||
formatArgs[idx] = arg
|
||||
}
|
||||
s, err := objects.Format(format.Value, args[1:]...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf(format.Value, formatArgs...)
|
||||
fmt.Print(s)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -89,20 +84,9 @@ func fmtSprintf(args ...objects.Object) (ret objects.Object, err error) {
|
|||
return format, nil // okay to return 'format' directly as String is immutable
|
||||
}
|
||||
|
||||
formatArgs := make([]interface{}, numArgs-1)
|
||||
for idx, arg := range args[1:] {
|
||||
switch arg := arg.(type) {
|
||||
case *objects.Int, *objects.Float, *objects.Bool, *objects.Char, *objects.String, *objects.Bytes:
|
||||
formatArgs[idx] = objects.ToInterface(arg)
|
||||
default:
|
||||
formatArgs[idx] = arg
|
||||
}
|
||||
}
|
||||
|
||||
s := fmt.Sprintf(format.Value, formatArgs...)
|
||||
|
||||
if len(s) > tengo.MaxStringLen {
|
||||
return nil, objects.ErrStringLimit
|
||||
s, err := objects.Format(format.Value, args[1:]...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &objects.String{Value: s}, nil
|
||||
|
|
Loading…
Reference in a new issue