package asld import ( "bytes" "fmt" ) // Walker.... texas ranger. // Except he's a cringe conservative. // // This is also cringe but not for cringe reasons. type symbol byte const ( symbolOpenParen symbol = iota symbolClosedParen symbolOpenArray symbolClosedArray symbolString symbolColon symbolNullStart symbolEOB // End-Of-Buffer ) const ( statusOK status = iota statusError statusWalkerNotAffected ) var ( stoppable = []SymbolInfo{ symbolOpenParen: { self: '{', closer: '}', }, symbolClosedParen: { self: '}', }, symbolOpenArray: { self: '[', closer: ']', }, symbolClosedArray: { self: ']', }, symbolString: { self: '"', closer: '"', }, symbolColon: { self: ':', }, } ) type status int type walkerStatus struct { walker status } func (w walker) Reset() walker { w.position = 0 return w } func (w walker) Debug() { for index, b := range w.content { out := string(b) if w.position == index { out = "<[_" + out + "_]>" } print(out) } print("\n") print("globPos", w.globPos) print("\n") } func (w walker) SliceInner() (walker, bool) { // the !ok scenario here is only if the code is bad s, ok := stoppableByByte[w.Current()] // Debug if !ok { panic(w) } var height uint for pos := w.position + 1; pos < w.len; pos++ { curr := w.content[pos] if curr == s.self && s.self != s.closer { height++ continue } if curr == s.closer { if height == 0 { return w.Between(w.position+1, pos), ok } height-- } } return w, false } type walker struct { content []byte len int position int globPos int } func newWalker(data []byte) walker { return walker{ content: data, len: len(data), } } func (w walker) Between(lower, upper int) walker { // As lower, and upper, are offsets from the beginning // of this walker's buffer, we can diff lower and w.position // for an offset to set global position at w.content = w.content[lower:upper] offset := lower - w.position w.position = 0 w.globPos += offset return w } // Sub returns a subwalker from the current position func (w walker) Sub() walker { w.content = w.content[w.position:] w.len = len(w.content) w.position = 0 return w } // Until returns a subwalker from position 0 to the current position func (w walker) Until() walker { w.content = w.content[:w.position] w.len = len(w.content) return w } func (w walker) Pos(v int) walker { offset := v - w.position w.position = v w.globPos += offset return w } // ToOrStay will stay at where the walker is if b is the // same as the current walker position. // // Otherwise, calls To func (w walker) ToOrStay(b byte) (walker, bool) { if w.Current() == b { return w, true } return w.To(b) } func (w walker) To(b byte) (walker, bool) { for pos := w.position + 1; pos < w.len; pos++ { if w.content[pos] == b { return w.Pos(pos), true } } return w, false } func (w walker) WalkThroughSpaces() walker { for pos := w.position; pos < w.len; pos++ { b := w.content[pos] if _, ok := stoppableByByte[b]; ok || (b >= '0' && b <= '9') || b == '-' || w.IsNullAt(pos) { return w.Pos(pos) } } return w } func (w walker) StayOrNext() (walker, symbol) { if sym, ok := stoppableByByte[w.content[w.position]]; ok { return w, sym.enum } return w.Next() } func (w walker) Next() (walker, symbol) { w, s := w.ToNext() return w.Sub(), s } func (w walker) IsNullAt(pos int) bool { return w.content[pos] == 'n' && w.len-pos >= 4 && bytes.Equal(w.content[pos:pos+4], null) } func (w walker) ToNext() (walker, symbol) { for pos := w.position + 1; pos < w.len; pos++ { if w.IsNullAt(pos) { return w.Pos(pos), symbolNullStart } if s, ok := stoppableByByte[w.content[pos]]; ok { return w.Pos(pos), s.enum } } return w, symbolEOB } // CommaOrEnd walks to the next comma, or, if none // is present in the current walker scope, returns // itself with status statusWalkerNotAffected. func (w walker) CommaOrEnd() (walkerStatus, error) { if w.Current() == ',' { w = w.Pos(w.position + 1) } for pos := w.position; pos < w.len; pos++ { if in(openRaw, w.content[pos]) { sb, ok := stoppableByByte[w.content[pos]] if !ok { panic("ok someone fucked up somewhere") } w, ok = w.To(sb.closer) if !ok { return walkerStatus{ status: statusError, walker: w, }, fmt.Errorf("%w %s", ErrNoMatching, string(sb.closer)) } pos = w.position continue } if w.content[pos] == ',' { return walkerStatus{ status: statusOK, walker: w.Pos(pos), }, nil } } return walkerStatus{ status: statusWalkerNotAffected, walker: w, }, nil } func (w walker) Current() byte { return w.content[w.position] } func (w walker) CurrentSymbol() (SymbolInfo, bool) { b, ok := stoppableByByte[w.Current()] return b, ok } func (w walker) String() string { if w.Current() == '"' { w, _ = w.SliceInner() } return string(w.content) }