Skip to content

Commit 9da4d4d

Browse files
committed
add json module with decode method
1 parent f857740 commit 9da4d4d

File tree

5 files changed

+123
-8
lines changed

5 files changed

+123
-8
lines changed

examples/hello.ghost

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
foo = 'hello'
1+
test = '{ "name": "Kai", "age": 34, "is_admin": true, "hobbies": ["programming", "gaming", "reading"], "address": { "street": "1234 Main St", "city": "Anytown", "state": "CA", "zip": 12345 } }'
22

3-
bar = {
4-
foo: 'bar'
5-
}
3+
foo = json.parse(test)
64

7-
print(bar.foo)
8-
print(foo)
9-
print(bar)
5+
console.log(foo.name)
6+
console.log(foo.age)
7+
console.log(foo.is_admin)
8+
console.log(foo.hobbies)
9+
console.log(foo.address)
10+
console.log(foo.address.street)
11+
console.log(foo.address.city)
12+
console.log(foo.address.state)
13+
console.log(foo.address.zip)
14+
console.log(foo.hobbies[0])
15+
console.log(foo.hobbies[1])
16+
console.log(foo.hobbies[2])

library/library.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func init() {
1818
RegisterModule("os", modules.OsMethods, modules.OsProperties)
1919
RegisterModule("random", modules.RandomMethods, modules.RandomProperties)
2020
RegisterModule("time", modules.TimeMethods, modules.TimeProperties)
21+
RegisterModule("json", modules.JsonMethods, modules.JsonProperties)
2122

2223
RegisterFunction("print", functions.Print)
2324
RegisterFunction("type", functions.Type)

library/modules/json.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package modules
2+
3+
import (
4+
"encoding/json"
5+
6+
"ghostlang.org/x/ghost/object"
7+
"ghostlang.org/x/ghost/token"
8+
)
9+
10+
var JsonMethods = map[string]*object.LibraryFunction{}
11+
var JsonProperties = map[string]*object.LibraryProperty{}
12+
13+
func init() {
14+
RegisterMethod(JsonMethods, "decode", jsonDecode)
15+
// RegisterMethod(JsonMethods, "encode", jsonEncode)
16+
}
17+
18+
// jsonDecode decodes the JSON-encoded data and returns a new list or map object.
19+
func jsonDecode(scope *object.Scope, tok token.Token, args ...object.Object) object.Object {
20+
if len(args) != 1 {
21+
return object.NewError("wrong number of arguments. got=%d, want=1", len(args))
22+
}
23+
24+
str, ok := args[0].(*object.String)
25+
26+
if !ok {
27+
return object.NewError("argument to `decode` must be STRING, got %s", args[0].Type())
28+
}
29+
30+
var data interface{}
31+
32+
err := json.Unmarshal([]byte(str.Value), &data)
33+
34+
if err != nil {
35+
return object.NewError("failed to decode JSON: %s", err.Error())
36+
}
37+
38+
switch v := data.(type) {
39+
case []interface{}:
40+
var elements []object.Object
41+
42+
for _, val := range v {
43+
elements = append(elements, object.AnyValueToObject(val))
44+
}
45+
46+
return &object.List{Elements: elements}
47+
case map[string]interface{}:
48+
pairs := make(map[object.MapKey]object.MapPair)
49+
50+
for key, val := range v {
51+
pairKey := &object.String{Value: key}
52+
pairValue := object.AnyValueToObject(val)
53+
54+
pairs[pairKey.MapKey()] = object.MapPair{Key: pairKey, Value: pairValue}
55+
}
56+
57+
return &object.Map{Pairs: pairs}
58+
}
59+
60+
return object.NewError("failed to decode JSON: %s", err.Error())
61+
}

object/error.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type Error struct {
1313

1414
// String represents the error object's value as a string.
1515
func (err *Error) String() string {
16-
return "error"
16+
return "error: " + err.Message
1717
}
1818

1919
// Type returns the error object type.

object/object.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package object
33
import (
44
"ghostlang.org/x/ghost/ast"
55
"ghostlang.org/x/ghost/token"
6+
"github.com/shopspring/decimal"
67
)
78

89
var evaluator func(node ast.Node, scope *Scope) Object
@@ -37,3 +38,48 @@ type ObjectMethod func(value interface{}, args ...Object) (Object, bool)
3738
func RegisterEvaluator(e func(node ast.Node, scope *Scope) Object) {
3839
evaluator = e
3940
}
41+
42+
func AnyValueToObject(val any) Object {
43+
switch v := val.(type) {
44+
case bool:
45+
if v {
46+
return &Boolean{Value: true}
47+
}
48+
49+
return &Boolean{Value: false}
50+
case string:
51+
return &String{Value: v}
52+
case int:
53+
return &Number{Value: decimal.NewFromInt(int64(v))}
54+
case int64:
55+
return &Number{Value: decimal.NewFromInt(int64(v))}
56+
case float64:
57+
return &Number{Value: decimal.NewFromFloat(v)}
58+
case nil:
59+
return &Null{}
60+
case []any:
61+
elements := make([]Object, len(v))
62+
63+
for index, item := range v {
64+
elements[index] = AnyValueToObject(item)
65+
}
66+
67+
return &List{Elements: elements}
68+
case map[string]any:
69+
pairs := make(map[MapKey]MapPair)
70+
71+
for key, val := range v {
72+
pairKey := &String{Value: key}
73+
var pairValue Object
74+
hashed := pairKey.MapKey()
75+
76+
pairValue = AnyValueToObject(val)
77+
78+
pairs[hashed] = MapPair{Key: pairKey, Value: pairValue}
79+
}
80+
81+
return &Map{Pairs: pairs}
82+
}
83+
84+
return nil
85+
}

0 commit comments

Comments
 (0)