From 4b8bbba04a7c6bc417645768bab5243eef1100a2 Mon Sep 17 00:00:00 2001 From: Flipez Date: Mon, 14 Mar 2022 23:59:50 +0100 Subject: [PATCH 1/4] add .to_json() Signed-off-by: Flipez --- object/array.go | 6 ++++++ object/array_test.go | 2 ++ object/boolean.go | 5 +++++ object/boolean_test.go | 3 +++ object/float.go | 5 +++++ object/float_test.go | 2 ++ object/hash.go | 23 +++++++++++++++++++++++ object/hash_test.go | 3 +++ object/integer.go | 5 +++++ object/integer_test.go | 1 + object/object.go | 22 ++++++++++++++++++++++ object/string.go | 5 +++++ object/string_test.go | 1 + 13 files changed, 83 insertions(+) diff --git a/object/array.go b/object/array.go index b36dcf9..860cc1f 100644 --- a/object/array.go +++ b/object/array.go @@ -2,6 +2,7 @@ package object import ( "bytes" + "encoding/json" "hash/fnv" "strings" ) @@ -214,3 +215,8 @@ func (ao *Array) Next() (Object, Object, bool) { return nil, NewInteger(0), false } + +func (ao *Array) MarshalJSON() ([]byte, error) { + + return json.Marshal(ao.Elements) +} diff --git a/object/array_test.go b/object/array_test.go index 56d22a0..be152f4 100644 --- a/object/array_test.go +++ b/object/array_test.go @@ -50,6 +50,8 @@ func TestArrayObjectMethods(t *testing.T) { {"[1,2,3].first()", 1}, {"[].last()", "NULL"}, {"[1,2,3].last()", 3}, + {"[1,2,3].to_json()", "[1,2,3]"}, + {`["test",true,3].to_json()`, `["test",true,3]`}, } testInput(t, tests) diff --git a/object/boolean.go b/object/boolean.go index ef99041..38649a6 100644 --- a/object/boolean.go +++ b/object/boolean.go @@ -1,6 +1,7 @@ package object import ( + "encoding/json" "fmt" "strconv" ) @@ -49,3 +50,7 @@ func (b *Boolean) InvokeMethod(method string, env Environment, args ...Object) O return objectMethodLookup(b, method, env, args) } + +func (b *Boolean) MarshalJSON() ([]byte, error) { + return json.Marshal(b.Value) +} diff --git a/object/boolean_test.go b/object/boolean_test.go index b7a8887..8f8f7c9 100644 --- a/object/boolean_test.go +++ b/object/boolean_test.go @@ -36,6 +36,9 @@ func TestBooleanObjectMethods(t *testing.T) { {"(true.wat().lines().size() == true.methods().size() + 1).plz_s()", "true"}, {"true == true", true}, {"true == false", false}, + + {"true.to_json()", "true"}, + {"false.to_json()", "false"}, } testInput(t, tests) diff --git a/object/float.go b/object/float.go index fe11fac..3bb7241 100644 --- a/object/float.go +++ b/object/float.go @@ -1,6 +1,7 @@ package object import ( + "encoding/json" "fmt" "hash/fnv" "strconv" @@ -82,3 +83,7 @@ func (f *Float) toString() string { } return strconv.FormatFloat(f.Value, 'f', -1, 64) } + +func (f *Float) MarshalJSON() ([]byte, error) { + return json.Marshal(f.Value) +} diff --git a/object/float_test.go b/object/float_test.go index 857df35..a5ca49d 100644 --- a/object/float_test.go +++ b/object/float_test.go @@ -28,6 +28,8 @@ func TestFloatObjectMethods(t *testing.T) { {`10.0.type()`, "FLOAT"}, {`2.2.nope()`, "undefined method `.nope()` for FLOAT"}, {`(2.0.wat().lines().size() == 2.0.methods().size() + 1).plz_s()`, "true"}, + {"1.1.to_json()", "1.1"}, + {"3.123456.to_json()", "3.123456"}, } testInput(t, tests) diff --git a/object/hash.go b/object/hash.go index a0524f1..7819a8d 100644 --- a/object/hash.go +++ b/object/hash.go @@ -2,6 +2,7 @@ package object import ( "bytes" + "encoding/json" "fmt" "hash/fnv" "strings" @@ -126,3 +127,25 @@ func (h *Hash) Next() (Object, Object, bool) { return nil, NewInteger(0), false } + +func (h *Hash) MarshalJSON() ([]byte, error) { + tempHash := make(map[string]Serializable) + for _, pair := range h.Pairs { + _, ok := pair.Key.(Serializable) + if !ok { + return nil, fmt.Errorf("unable to serialize key: %s", pair.Key.Inspect()) + } + serializableValue, ok := pair.Value.(Serializable) + if !ok { + return nil, fmt.Errorf("unable to serialize value: %s", pair.Key.Inspect()) + } + + if str, ok := pair.Key.(*String); ok { + tempHash[str.Value] = serializableValue + } else { + tempHash[pair.Key.Inspect()] = serializableValue + } + } + + return json.Marshal(tempHash) +} diff --git a/object/hash_test.go b/object/hash_test.go index e3419f0..88cc219 100644 --- a/object/hash_test.go +++ b/object/hash_test.go @@ -28,6 +28,9 @@ func TestHashObjectMethods(t *testing.T) { {`{"a": 1, "b": 2}["a"]`, 1}, {`{"a": 1, "b": 2}.keys().size()`, 2}, {`{"a": 1, "b": 2}.values().size()`, 2}, + {`{"a": "b"}.to_json()`, `{"a":"b"}`}, + {`{1: "b"}.to_json()`, `{"1":"b"}`}, + {`{true: "b"}.to_json()`, `{"true":"b"}`}, } testInput(t, tests) diff --git a/object/integer.go b/object/integer.go index f979db4..3e9e457 100644 --- a/object/integer.go +++ b/object/integer.go @@ -1,6 +1,7 @@ package object import ( + "encoding/json" "fmt" "strconv" ) @@ -102,3 +103,7 @@ func (i *Integer) Next() (Object, Object, bool) { } return nil, NewInteger(0), false } + +func (i *Integer) MarshalJSON() ([]byte, error) { + return json.Marshal(i.Value) +} diff --git a/object/integer_test.go b/object/integer_test.go index 8a43c20..41adc38 100644 --- a/object/integer_test.go +++ b/object/integer_test.go @@ -29,6 +29,7 @@ func TestIntegerObjectMethods(t *testing.T) { {`10.type()`, "INTEGER"}, {`2.nope()`, "undefined method `.nope()` for INTEGER"}, {`(2.wat().lines().size() == 2.methods().size() + 1).plz_s()`, "true"}, + {"1.to_json()", "1"}, } testInput(t, tests) diff --git a/object/object.go b/object/object.go index b5de00c..2ee142b 100644 --- a/object/object.go +++ b/object/object.go @@ -1,6 +1,7 @@ package object import ( + "encoding/json" "fmt" "strings" @@ -26,6 +27,10 @@ type Hashable interface { HashKey() HashKey } +type Serializable interface { + MarshalJSON() ([]byte, error) +} + const ( INTEGER_OBJ = "INTEGER" FLOAT_OBJ = "FLOAT" @@ -147,6 +152,23 @@ func ListObjectMethods() map[ObjectType]map[string]ObjectMethod { func init() { objectMethods["*"] = map[string]ObjectMethod{ + "to_json": ObjectMethod{ + description: "Returns the object as json notation.", + returnPattern: [][]string{ + []string{STRING_OBJ, ERROR_OBJ}, + }, + method: func(o Object, _ []Object, _ Environment) Object { + if serializeableObject, ok := o.(Serializable); ok { + j, err := json.Marshal(serializeableObject) + if err != nil { + return NewErrorFormat("Error while marshal value: %s", err.Error()) + } + return NewString(string(j)) + } + + return NewErrorFormat("%s is not serializable", o.Type()) + }, + }, "methods": ObjectMethod{ description: "Returns an array of all supported methods names.", example: `🚀 > "test".methods() diff --git a/object/string.go b/object/string.go index db3617a..4a973bc 100644 --- a/object/string.go +++ b/object/string.go @@ -1,6 +1,7 @@ package object import ( + "encoding/json" "hash/fnv" "strconv" "strings" @@ -344,3 +345,7 @@ func (s *String) Next() (Object, Object, bool) { return nil, NewInteger(0), false } + +func (s *String) MarshalJSON() ([]byte, error) { + return json.Marshal(s.Value) +} diff --git a/object/string_test.go b/object/string_test.go index fa4f09b..8a53874 100644 --- a/object/string_test.go +++ b/object/string_test.go @@ -86,6 +86,7 @@ func TestStringObjectMethods(t *testing.T) { {`a = "test"; b = []; foreach char in a { b.yoink(char) }; b.size()`, 4}, {`"test" * 2`, "testtest"}, {`2 * "test"`, "testtest"}, + {`"test".to_json()`, `"test"`}, } testInput(t, tests) From 0ef2a2b18a28211e9c2021f96f5966dffe4c89a7 Mon Sep 17 00:00:00 2001 From: Flipez Date: Tue, 15 Mar 2022 00:04:51 +0100 Subject: [PATCH 2/4] add marshal error test Signed-off-by: Flipez --- object/string_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/object/string_test.go b/object/string_test.go index 8a53874..4de65c2 100644 --- a/object/string_test.go +++ b/object/string_test.go @@ -87,6 +87,7 @@ func TestStringObjectMethods(t *testing.T) { {`"test" * 2`, "testtest"}, {`2 * "test"`, "testtest"}, {`"test".to_json()`, `"test"`}, + {`{"test": HTTP.new()}.to_json()`, `Error while marshal value: json: error calling MarshalJSON for type *object.Hash: unable to serialize value: "test"`}, } testInput(t, tests) From 6e6694f2f8e02825c57ccaafa307db5da7639aa0 Mon Sep 17 00:00:00 2001 From: Flipez Date: Tue, 15 Mar 2022 00:09:59 +0100 Subject: [PATCH 3/4] add not serializable test Signed-off-by: Flipez --- object/http_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/object/http_test.go b/object/http_test.go index 2843ac5..bea2086 100644 --- a/object/http_test.go +++ b/object/http_test.go @@ -20,6 +20,7 @@ func TestHTTPObjectMethods(t *testing.T) { {`def test(){};HTTP.handle("/", test)`, "Invalid handler. Call only supported on instance."}, {`a = HTTP.new(); a.listen(-1)`, "listening on port -1: listen tcp: address -1: invalid port"}, {`a = HTTP.new(); a.listen(80)`, "listening on port 80: listen tcp :80: bind: permission denied"}, + {"HTTP.new().to_json()", "HTTP is not serializable"}, } testInput(t, tests) From 17199eb26d244202e6fb1d36aecd08f1c3d96261 Mon Sep 17 00:00:00 2001 From: Flipez Date: Tue, 15 Mar 2022 00:21:15 +0100 Subject: [PATCH 4/4] remove empty line Signed-off-by: Flipez --- object/array.go | 1 - 1 file changed, 1 deletion(-) diff --git a/object/array.go b/object/array.go index 860cc1f..33bc87c 100644 --- a/object/array.go +++ b/object/array.go @@ -217,6 +217,5 @@ func (ao *Array) Next() (Object, Object, bool) { } func (ao *Array) MarshalJSON() ([]byte, error) { - return json.Marshal(ao.Elements) }