Skip to content

Commit

Permalink
support UnsetByPath
Browse files Browse the repository at this point in the history
  • Loading branch information
AsterDY committed Jan 16, 2024
1 parent 0456fa0 commit e02c4f4
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 50 deletions.
2 changes: 1 addition & 1 deletion ast/api_compat.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (self *Node) encodeInterface(buf *[]byte) error {
func (self *Parser) getByPath(path ...interface{}) (int, types.ParsingError) {
for _, p := range path {
if idx, ok := p.(int); ok && idx >= 0 {
if err := self.searchIndex(idx); err != 0 {
if _, err := self.searchIndex(idx); err != 0 {
return self.p, err
}
} else if key, ok := p.(string); ok {
Expand Down
48 changes: 26 additions & 22 deletions ast/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,21 +391,22 @@ func (self *Parser) objectEnd() (bool, types.ParsingError) {

func (self *Parser) searchKey(match string) (int, types.ParsingError) {
ns := len(self.s)
keyPos := -1
comma := -1

if err := self.object(); err != 0 {
return keyPos, err
return comma, err
}

/* check for EOF */
if self.p = self.lspace(self.p); self.p >= ns {
return keyPos, types.ERR_EOF
return comma, types.ERR_EOF
}

/* check for empty object */
if self.s[self.p] == '}' {
return keyPos, _ERR_NOT_FOUND
return comma, _ERR_NOT_FOUND
}
comma = self.p

var njs types.JsonState
var err types.ParsingError
Expand All @@ -415,49 +416,49 @@ func (self *Parser) searchKey(match string) (int, types.ParsingError) {

/* decode the key */
if njs = self.decodeValue(); njs.Vt != types.V_STRING {
return keyPos, types.ERR_INVALID_CHAR
return comma, types.ERR_INVALID_CHAR
}

/* extract the key */
idx := self.p - 1
key := self.s[njs.Iv:idx]
keyPos = int(njs.Iv - 1)

/* check for escape sequence */
if njs.Ep != -1 {
if key, err = unquote(key); err != 0 {
return keyPos, err
return comma, err
}
}

/* expect a ':' delimiter */
if err = self.delim(); err != 0 {
return keyPos, err
return comma, err
}

/* skip value */
if key != match {
if _, err = self.skipFast(); err != 0 {
return keyPos, err
return comma, err
}
} else {
return keyPos, 0
return comma, 0
}

/* check for EOF */
self.p = self.lspace(self.p)
if self.p >= ns {
return keyPos, types.ERR_EOF
return comma, types.ERR_EOF
}

/* check for the next character */
switch self.s[self.p] {
case ',':
comma = self.p
self.p++
case '}':
return keyPos, _ERR_NOT_FOUND
return comma, _ERR_NOT_FOUND
default:
return keyPos, types.ERR_INVALID_CHAR
return comma, types.ERR_INVALID_CHAR
}
}
}
Expand Down Expand Up @@ -502,49 +503,52 @@ func (self *Parser) arrayEnd() (bool, types.ParsingError) {
}
}

func (self *Parser) searchIndex(idx int) types.ParsingError {
func (self *Parser) searchIndex(idx int) (int, types.ParsingError) {
comma := -1
ns := len(self.s)
if err := self.array(); err != 0 {
return err
return comma, err
}

/* check for EOF */
if self.p = self.lspace(self.p); self.p >= ns {
return types.ERR_EOF
return comma, types.ERR_EOF
}

/* check for empty array */
if self.s[self.p] == ']' {
return _ERR_NOT_FOUND
return comma, _ERR_NOT_FOUND
}
comma = self.p

var err types.ParsingError
/* allocate array space and parse every element */
for i := 0; i < idx; i++ {

/* decode the value */
if _, err = self.skipFast(); err != 0 {
return err
return comma, err
}

/* check for EOF */
self.p = self.lspace(self.p)
if self.p >= ns {
return types.ERR_EOF
return comma, types.ERR_EOF
}

/* check for the next character */
switch self.s[self.p] {
case ',':
comma = self.p
self.p++
case ']':
return _ERR_NOT_FOUND
return comma, _ERR_NOT_FOUND
default:
return types.ERR_INVALID_CHAR
return comma, types.ERR_INVALID_CHAR
}
}

return 0
return comma, 0
}

func (self *Node) skipNextNode() *Node {
Expand Down
92 changes: 77 additions & 15 deletions ast/raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,14 @@ func (self Value) GetByPath(path ...interface{}) Value {
return rawNode(self.js[s:p.p])
}

// GetByPath load given path on demands,
// which only ensure nodes before this path got parsed
func (self *Value) SetByPath(val Value, path ...interface{}) error {
// SetByPath set value on given path and create nodes on the json if not exist
func (self *Value) SetByPath(val Value, path ...interface{}) (bool, error) {
exist := false
if self.Check() != nil {
return self
return exist, self
}
if val.Check() != nil {
return val
return exist, val
}

p := NewParserObj(self.js)
Expand All @@ -170,17 +170,17 @@ func (self *Value) SetByPath(val Value, path ...interface{}) error {

for i, k := range path {
if id, ok := k.(int); ok && id >= 0 {
if err = p.searchIndex(id); err != 0 {
if _, err = p.searchIndex(id); err != 0 {
if err != _ERR_NOT_FOUND {
return errRawNode(p.ExportError(err))
return exist, errRawNode(p.ExportError(err))
}
idx = i
break
}
} else if key, ok := k.(string); ok {
if _, err = p.searchKey(key); err != 0 {
if err != _ERR_NOT_FOUND {
return errRawNode(p.ExportError(err))
return exist, errRawNode(p.ExportError(err))
}
idx = i
break
Expand All @@ -192,10 +192,11 @@ func (self *Value) SetByPath(val Value, path ...interface{}) error {

var b []byte
if err == 0 {
exist = true
// found, just skip and replace
s, err := p.skip()
s, err := p.skipFast()
if err != 0 {
return errRawNode(p.ExportError(err))
return exist, errRawNode(p.ExportError(err))
}
size := len(self.js) + len(val.js) - (p.p-s)
b = make([]byte, 0, size)
Expand All @@ -213,16 +214,17 @@ func (self *Value) SetByPath(val Value, path ...interface{}) error {
if !empty {
b = append(b, ","...)
}
// creat new nodes on path
b = appendPathValue(b, path[idx:], val)
}

b = append(b, self.js[p.p:]...)
self.js = rt.Mem2Str(b)
return nil
return exist, nil
}

// [2,"a"] - 1 => {"a":1}
// ["a",2] - 1 => "a":[1]
// [2,"a"],1 => {"a":1}
// ["a",2],1 => "a":[1]
func appendPathValue(b []byte, path []interface{}, val Value) []byte {
for i, k := range path {
if key, ok := k.(string); ok {
Expand Down Expand Up @@ -253,6 +255,64 @@ func appendPathValue(b []byte, path []interface{}, val Value) []byte {
return b
}

// UnsetByPath delete value on given path.
func (self *Value) UnsetByPath(path ...interface{}) (bool, error) {
if self.Check() != nil {
return false, self
}

p := NewParserObj(self.js)
var err types.ParsingError
var comma = -1

for _, k := range path {
if id, ok := k.(int); ok && id >= 0 {
if comma, err = p.searchIndex(id); err != 0 {
if err == _ERR_NOT_FOUND {
return false, nil
}
return false, p.ExportError(err)
}
} else if key, ok := k.(string); ok {
if comma, err = p.searchKey(key); err != 0 {
if err == _ERR_NOT_FOUND {
return false, nil
}
return false, p.ExportError(err)
}
} else {
panic("path must be either int(>=0) or string")
}
}

var b []byte
s, err := p.skipFast()
if err != 0 {
return true, errRawNode(p.ExportError(err))
}
if comma != -1 {
s = comma
}

e := p.p
println(string(self.js[s]))
if self.js[s] != ',' { // first elem
// check if trailling ','
p.p = p.lspace(p.p)
println(string(self.js[p.p]))
if p.p < len(self.js) && self.js[p.p] == ',' {
e = p.p+1
}
}

size := len(self.js) - (e-s)
b = make([]byte, 0, size)
b = append(b, self.js[:s]...)
b = append(b, self.js[e:]...)
self.js = rt.Mem2Str(b)
return true, nil
}

// Get loads given key of an object node on demands
func (self Value) Get(key string) Value {
if self.Check() != nil {
Expand Down Expand Up @@ -437,7 +497,8 @@ func (ps points) Len() int {
return len(ps)
}

// Set sets the node of given key under self, and reports if the key has existed.
// Set sets the node of given key under self, and insert new value if not exist.
// It reports if the key has existed.
func (self *Value) Set(key string, val Value) (bool, error) {
return self.SetMany([]string{key}, []Value{val})
}
Expand Down Expand Up @@ -534,8 +595,9 @@ func (self *Value) SetMany(keys []string, vals []Value) (bool, error) {
return exist, nil
}

// SetByIndex sets the node of given index, and reports if the key has existed.
// SetByIndex sets the node of given index and insert new value if not exist.
// If the index is out range of self's children, it will be ADD to the last
// and reports if the key has existed.
func (self *Value) SetByIndex(id int, val Value) (bool, error) {
return self.SetManyByIndex([]int{id}, []Value{val})
}
Expand Down
Loading

0 comments on commit e02c4f4

Please sign in to comment.