cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

cat/module-path
012 / internal / sheet / filescan.go
filescan.go 6.8K
1package sheet23import (4 "bufio"5 "encoding/json"6 "errors"7 "fmt"8 "io"9 "unicode/utf8"10)1112// scanner reads JSON a value at a time from a stream, for the .01213// reader (fileread.go): the cells are read one by one straight into the14// sheet, and everything else, which is small, is captured whole and15// given to encoding/json, so it decodes as it always has. Strings16// without escapes are read without encoding/json; the rest go through17// it, so text decodes exactly as it would there.18type scanner struct {19 r *bufio.Reader20 buf []byte // scratch for a string longer than the reader's buffer or escaped21 key []byte // the key being read22}2324func newScanner(r io.Reader) *scanner { return &scanner{r: bufio.NewReaderSize(r, 64<<10)} }2526// errSyntax is returned for anything that isn't the JSON expected.27var errSyntax = errors.New("invalid JSON")2829// syntax describes a byte that wasn't expected.30func syntax(c byte, want string) error {31 return fmt.Errorf("%w: %q where %s was expected", errSyntax, c, want)32}3334// eofErr is err, turning a clean EOF in the middle of a value into35// io.ErrUnexpectedEOF, as encoding/json does.36func eofErr(err error) error {37 if err == io.EOF {38 return io.ErrUnexpectedEOF39 }40 return err41}4243// peek skips white space and returns the next byte without reading it.44func (sc *scanner) peek() (byte, error) {45 for {46 c, err := sc.r.ReadByte()47 if err != nil {48 return 0, eofErr(err)49 }50 switch c {51 case ' ', '\t', '\n', '\r':52 continue53 }54 return c, sc.r.UnreadByte()55 }56}5758// next skips white space and reads the next byte.59func (sc *scanner) next() (byte, error) {60 if _, err := sc.peek(); err != nil {61 return 0, err62 }63 return sc.r.ReadByte()64}6566// expect reads the next byte, which must be c.67func (sc *scanner) expect(c byte) error {68 got, err := sc.next()69 if err == nil && got != c {70 err = syntax(got, fmt.Sprintf("%q", c))71 }72 return err73}7475// null reads the literal null if it comes next, and reports whether it76// did.77func (sc *scanner) null() (bool, error) {78 c, err := sc.peek()79 if err != nil || c != 'n' {80 return false, err81 }82 raw, err := sc.raw()83 if err == nil && string(raw) != "null" {84 err = fmt.Errorf("%w: %q", errSyntax, raw)85 }86 return err == nil, err87}8889// members reads an object, calling fn with each key once the reader is90// at its value, which fn must read.91func (sc *scanner) members(fn func(key string) error) error {92 return sc.fields(func(key []byte, plain bool) error {93 s := string(key)94 if !plain {95 if err := json.Unmarshal(quote(key), &s); err != nil {96 return err97 }98 }99 return fn(s)100 })101}102103// fields is members giving each key as written between its quotes, and104// whether that is its text (see text); the bytes are valid until the105// value is read.106func (sc *scanner) fields(fn func(key []byte, plain bool) error) error {107 if err := sc.expect('{'); err != nil {108 return err109 }110 return sc.list('}', func() error {111 text, plain, err := sc.text()112 if err != nil {113 return err114 }115 sc.key = append(sc.key[:0], text...) // reading on may move text116 if err := sc.expect(':'); err != nil {117 return err118 }119 return fn(sc.key, plain)120 })121}122123// elements reads an array, calling fn for each element, which fn must124// read.125func (sc *scanner) elements(fn func() error) error {126 if err := sc.expect('['); err != nil {127 return err128 }129 return sc.list(']', fn)130}131132// list reads the items of an object or array up to its closing byte,133// with fn reading each.134func (sc *scanner) list(end byte, fn func() error) error {135 c, err := sc.peek()136 if err != nil {137 return err138 }139 if c == end {140 _, err = sc.r.ReadByte()141 return err142 }143 for {144 if err := fn(); err != nil {145 return err146 }147 c, err := sc.next()148 switch {149 case err != nil:150 return err151 case c == end:152 return nil153 case c != ',':154 return syntax(c, fmt.Sprintf(`"," or %q`, end))155 }156 }157}158159// unquote is the text of a string written as body between its quotes,160// plain or not (see text).161func unquote(body []byte, plain bool) (string, error) {162 if plain {163 return string(body), nil164 }165 var s string166 err := json.Unmarshal(quote(body), &s)167 return s, err168}169170// quote is body between quotes, in a new slice.171func quote(body []byte) []byte {172 out := make([]byte, 0, len(body)+2)173 return append(append(append(out, '"'), body...), '"')174}175176// text reads a string as it is written between its quotes, and reports177// whether it is plain: no escapes, control characters or invalid UTF-8,178// so those bytes are its text. They are valid until the next read.179func (sc *scanner) text() ([]byte, bool, error) {180 if err := sc.expect('"'); err != nil {181 return nil, false, err182 }183 part, err := sc.r.ReadSlice('"')184 if err == nil && !escapedQuote(part) { // the common case: no copy185 body := part[:len(part)-1]186 return body, plainText(body), nil187 }188 sc.buf = append(sc.buf[:0], part...)189 for err != nil || escapedQuote(sc.buf) {190 if err != nil && err != bufio.ErrBufferFull {191 return nil, false, eofErr(err)192 }193 part, err = sc.r.ReadSlice('"')194 sc.buf = append(sc.buf, part...)195 }196 body := sc.buf[:len(sc.buf)-1]197 return body, plainText(body), nil198}199200// plainText reports whether a string's body as written is its text.201func plainText(body []byte) bool {202 for _, c := range body {203 if c < 0x20 || c == '\\' {204 return false205 }206 }207 return utf8.Valid(body)208}209210// escapedQuote reports whether the quote ending s is escaped: preceded211// by an odd number of backslashes.212func escapedQuote(s []byte) bool {213 n := 0214 for i := len(s) - 2; i >= 0 && s[i] == '\\'; i-- {215 n++216 }217 return n%2 == 1218}219220// raw reads the next value whole, as it is written. It checks only221// that brackets balance outside strings; encoding/json checks the rest222// when the value is decoded.223func (sc *scanner) raw() (json.RawMessage, error) {224 c, err := sc.peek()225 if err != nil {226 return nil, err227 }228 switch c {229 case '"':230 body, _, err := sc.text()231 return quote(body), err232 case '{', '[':233 return sc.nested()234 }235 var out []byte236 for {237 c, err := sc.r.ReadByte()238 if err == io.EOF && len(out) > 0 {239 return out, nil240 }241 if err != nil {242 return nil, eofErr(err)243 }244 switch c {245 case ',', '}', ']', ' ', '\t', '\n', '\r':246 if len(out) == 0 { // a key without its value: "sheets":,247 return nil, syntax(c, "a value")248 }249 return out, sc.r.UnreadByte()250 }251 out = append(out, c)252 }253}254255// nested reads an object or array whole.256func (sc *scanner) nested() (json.RawMessage, error) {257 var out []byte258 depth := 0259 for {260 c, err := sc.r.ReadByte()261 if err != nil {262 return nil, eofErr(err)263 }264 switch c {265 case '"':266 if err := sc.r.UnreadByte(); err != nil {267 return nil, err268 }269 body, _, err := sc.text()270 if err != nil {271 return nil, err272 }273 out = append(append(append(out, '"'), body...), '"')274 continue275 case '{', '[':276 depth++277 case '}', ']':278 depth--279 }280 out = append(out, c)281 if depth == 0 {282 return out, nil283 }284 }285}