From 7d759b8137dc5947bb372e3de1dd079ba5de80e3 Mon Sep 17 00:00:00 2001 From: Laurent Demailly Date: Sat, 3 Aug 2024 14:47:09 -0700 Subject: [PATCH] adding Unwrap on all object types so we can also add sprintf and pass the variadic any args to it --- eval/eval_test.go | 10 ++++++++++ extensions/extension.go | 16 +++++++++++++++ object/interp.go | 8 ++++++++ object/object.go | 44 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/eval/eval_test.go b/eval/eval_test.go index 01f7023b..c390f10d 100644 --- a/eval/eval_test.go +++ b/eval/eval_test.go @@ -770,4 +770,14 @@ func TestExtension(t *testing.T) { input = `round(2.7)` evaluated = testEval(t, input) testFloatObject(t, evaluated, 3) + input = `sprintf("%d %s %g", 42, "ab\ncd", pow(2, 43))` + evaluated = testEval(t, input) + expected = "42 ab\ncd 8.796093022208e+12" // might be brittle the %g output of float64. + actual, ok := evaluated.(object.String) + if !ok { + t.Errorf("object is not string. got=%T (%+v)", evaluated, evaluated) + } + if actual.Value != expected { + t.Errorf("object has wrong value. got=%q, want=%q", actual, expected) + } } diff --git a/extensions/extension.go b/extensions/extension.go index a5c85853..b0fa4251 100644 --- a/extensions/extension.go +++ b/extensions/extension.go @@ -3,6 +3,7 @@ package extensions import ( + "fmt" "math" "grol.io/grol/object" @@ -36,6 +37,16 @@ func initInternal() error { if err != nil { return err } + err = object.CreateFunction(object.Extension{ + Name: "sprintf", + MinArgs: 1, + MaxArgs: -1, + ArgTypes: []object.Type{object.STRING}, + Callback: sprintf, + }) + if err != nil { + return err + } oneFloat := object.Extension{ MinArgs: 1, MaxArgs: 1, @@ -79,3 +90,8 @@ func pow(args []object.Object) object.Object { result := math.Pow(base, exp) return object.Float{Value: result} } + +func sprintf(args []object.Object) object.Object { + res := fmt.Sprintf(args[0].(object.String).Value, object.Unwrap(args[1:])...) + return object.String{Value: res} +} diff --git a/object/interp.go b/object/interp.go index e86ced23..9ef0c62f 100644 --- a/object/interp.go +++ b/object/interp.go @@ -36,3 +36,11 @@ func CreateFunction(cmd Extension) error { func ExtraFunctions() map[string]Extension { return extraFunctions } + +func Unwrap(objs []Object) []any { + res := make([]any, len(objs)) + for i, o := range objs { + res[i] = o.Unwrap() + } + return res +} diff --git a/object/object.go b/object/object.go index 8eeb9b01..78364695 100644 --- a/object/object.go +++ b/object/object.go @@ -15,6 +15,7 @@ type Type uint8 type Object interface { Type() Type Inspect() string + Unwrap() any } const ( @@ -120,6 +121,10 @@ func (i Integer) Inspect() string { return strconv.FormatInt(i.Value, 10) } +func (i Integer) Unwrap() any { + return i.Value +} + func (i Integer) Type() Type { return INTEGER } @@ -128,6 +133,10 @@ type Float struct { Value float64 } +func (f Float) Unwrap() any { + return f.Value +} + func (f Float) Type() Type { return FLOAT } @@ -140,6 +149,10 @@ type Boolean struct { Value bool } +func (b Boolean) Unwrap() any { + return b.Value +} + func (b Boolean) Type() Type { return BOOLEAN } @@ -152,6 +165,10 @@ type String struct { Value string } +func (s String) Unwrap() any { + return s.Value +} + func (s String) Type() Type { return STRING } @@ -162,6 +179,7 @@ func (s String) Inspect() string { type Null struct{} +func (n Null) Unwrap() any { return nil } func (n Null) Type() Type { return NIL } func (n Null) Inspect() string { return "nil" } @@ -169,6 +187,8 @@ type Error struct { Value string // message } +func (e Error) Unwrap() any { return e } +func (e Error) Error() string { return e.Value } func (e Error) Type() Type { return ERROR } func (e Error) Inspect() string { return "" } @@ -176,6 +196,7 @@ type ReturnValue struct { Value Object } +func (rv ReturnValue) Unwrap() any { return rv.Value } func (rv ReturnValue) Type() Type { return RETURN } func (rv ReturnValue) Inspect() string { return rv.Value.Inspect() } @@ -198,7 +219,8 @@ func WriteStrings(out *strings.Builder, list []Object, before, sep, after string out.WriteString(after) } -func (f Function) Type() Type { return FUNC } +func (f Function) Unwrap() any { return f } +func (f Function) Type() Type { return FUNC } // Must be called after the function is fully initialized. // Whether a function result should be cached doesn't depend on the Name, @@ -235,7 +257,8 @@ type Array struct { Elements []Object } -func (ao Array) Type() Type { return ARRAY } +func (ao Array) Unwrap() any { return Unwrap(ao.Elements) } +func (ao Array) Type() Type { return ARRAY } func (ao Array) Inspect() string { out := strings.Builder{} WriteStrings(&out, ao.Elements, "[", ",", "]") @@ -290,6 +313,14 @@ func (mk MapKeys) Swap(i, j int) { mk[i], mk[j] = mk[j], mk[i] } +func (m Map) Unwrap() any { + res := make(map[any]any, len(m)) + for k, v := range m { + res[k.Unwrap()] = v.Unwrap() + } + return res +} + func (m Map) Type() Type { return MAP } func (m Map) Inspect() string { @@ -318,7 +349,8 @@ type Quote struct { Node ast.Node } -func (q Quote) Type() Type { return QUOTE } +func (q Quote) Unwrap() any { return q.Node } +func (q Quote) Type() Type { return QUOTE } func (q Quote) Inspect() string { out := strings.Builder{} out.WriteString("quote(") @@ -333,7 +365,8 @@ type Macro struct { Env *Environment } -func (m Macro) Type() Type { return MACRO } +func (m Macro) Unwrap() any { return m } +func (m Macro) Type() Type { return MACRO } func (m Macro) Inspect() string { out := strings.Builder{} out.WriteString("macro(") @@ -374,7 +407,8 @@ func (e *Extension) Usage(out *strings.Builder) { } } -func (e Extension) Type() Type { return EXTENSION } +func (e Extension) Unwrap() any { return e } +func (e Extension) Type() Type { return EXTENSION } func (e Extension) Inspect() string { out := strings.Builder{} out.WriteString(e.Name)