Skip to content

Commit

Permalink
add json module with decode method
Browse files Browse the repository at this point in the history
  • Loading branch information
kaidesu committed Oct 27, 2023
1 parent f857740 commit 9da4d4d
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 8 deletions.
21 changes: 14 additions & 7 deletions examples/hello.ghost
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
foo = 'hello'
test = '{ "name": "Kai", "age": 34, "is_admin": true, "hobbies": ["programming", "gaming", "reading"], "address": { "street": "1234 Main St", "city": "Anytown", "state": "CA", "zip": 12345 } }'

bar = {
foo: 'bar'
}
foo = json.parse(test)

print(bar.foo)
print(foo)
print(bar)
console.log(foo.name)
console.log(foo.age)
console.log(foo.is_admin)
console.log(foo.hobbies)
console.log(foo.address)
console.log(foo.address.street)
console.log(foo.address.city)
console.log(foo.address.state)
console.log(foo.address.zip)
console.log(foo.hobbies[0])
console.log(foo.hobbies[1])
console.log(foo.hobbies[2])
1 change: 1 addition & 0 deletions library/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func init() {
RegisterModule("os", modules.OsMethods, modules.OsProperties)
RegisterModule("random", modules.RandomMethods, modules.RandomProperties)
RegisterModule("time", modules.TimeMethods, modules.TimeProperties)
RegisterModule("json", modules.JsonMethods, modules.JsonProperties)

RegisterFunction("print", functions.Print)
RegisterFunction("type", functions.Type)
Expand Down
61 changes: 61 additions & 0 deletions library/modules/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package modules

import (
"encoding/json"

"ghostlang.org/x/ghost/object"
"ghostlang.org/x/ghost/token"
)

var JsonMethods = map[string]*object.LibraryFunction{}
var JsonProperties = map[string]*object.LibraryProperty{}

func init() {
RegisterMethod(JsonMethods, "decode", jsonDecode)
// RegisterMethod(JsonMethods, "encode", jsonEncode)
}

// jsonDecode decodes the JSON-encoded data and returns a new list or map object.
func jsonDecode(scope *object.Scope, tok token.Token, args ...object.Object) object.Object {
if len(args) != 1 {
return object.NewError("wrong number of arguments. got=%d, want=1", len(args))
}

str, ok := args[0].(*object.String)

if !ok {
return object.NewError("argument to `decode` must be STRING, got %s", args[0].Type())
}

var data interface{}

err := json.Unmarshal([]byte(str.Value), &data)

if err != nil {
return object.NewError("failed to decode JSON: %s", err.Error())
}

switch v := data.(type) {
case []interface{}:
var elements []object.Object

for _, val := range v {
elements = append(elements, object.AnyValueToObject(val))
}

return &object.List{Elements: elements}
case map[string]interface{}:
pairs := make(map[object.MapKey]object.MapPair)

for key, val := range v {
pairKey := &object.String{Value: key}
pairValue := object.AnyValueToObject(val)

pairs[pairKey.MapKey()] = object.MapPair{Key: pairKey, Value: pairValue}
}

return &object.Map{Pairs: pairs}
}

return object.NewError("failed to decode JSON: %s", err.Error())
}
2 changes: 1 addition & 1 deletion object/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Error struct {

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

// Type returns the error object type.
Expand Down
46 changes: 46 additions & 0 deletions object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package object
import (
"ghostlang.org/x/ghost/ast"
"ghostlang.org/x/ghost/token"
"github.com/shopspring/decimal"
)

var evaluator func(node ast.Node, scope *Scope) Object
Expand Down Expand Up @@ -37,3 +38,48 @@ type ObjectMethod func(value interface{}, args ...Object) (Object, bool)
func RegisterEvaluator(e func(node ast.Node, scope *Scope) Object) {
evaluator = e
}

func AnyValueToObject(val any) Object {
switch v := val.(type) {
case bool:
if v {
return &Boolean{Value: true}
}

return &Boolean{Value: false}
case string:
return &String{Value: v}
case int:
return &Number{Value: decimal.NewFromInt(int64(v))}
case int64:
return &Number{Value: decimal.NewFromInt(int64(v))}
case float64:
return &Number{Value: decimal.NewFromFloat(v)}
case nil:
return &Null{}
case []any:
elements := make([]Object, len(v))

for index, item := range v {
elements[index] = AnyValueToObject(item)
}

return &List{Elements: elements}
case map[string]any:
pairs := make(map[MapKey]MapPair)

for key, val := range v {
pairKey := &String{Value: key}
var pairValue Object
hashed := pairKey.MapKey()

pairValue = AnyValueToObject(val)

pairs[hashed] = MapPair{Key: pairKey, Value: pairValue}
}

return &Map{Pairs: pairs}
}

return nil
}

0 comments on commit 9da4d4d

Please sign in to comment.