1package nuon23import (4 "io"5)67// Reader reads a table's rows from NUON or JSON text as they arrive:8//9// - the table form, [[a, b]; [1, 2], [3, 4]], whose header is known10// before the first row;11// - a list, [{a: 1}, {a: 2, b: 3}]: each record is a row, and anything12// else a row of one column named "value";13// - a record, or any other single value, as one row;14// - several of these one after another, as NDJSON or a stream of15// records is written, all rows of one table.16//17// Columns are named by the rows: a row may add columns or leave some18// out, as the records of a nushell list may.19type Reader struct {20 s *scanner21 state readState22 header []string23}2425type readState uint82627const (28 between readState = iota // at the top level, between values29 inList // inside a top-level list30 inTable // inside a top-level table's rows31)3233// NewReader reads rows from r.34func NewReader(r io.Reader) *Reader { return &Reader{s: newScanner(r)} }3536// Header is the table form's column names, once its header has been read;37// nil for other shapes of table.38func (r *Reader) Header() []string { return r.header }3940// JSON reports whether everything read so far was also JSON.41func (r *Reader) JSON() bool { return !r.s.notJSON }4243// Next returns the next row, or io.EOF after the last. A syntax error is44// a *SyntaxError.45func (r *Reader) Next() (Row, error) {46 for {47 row, done, err := r.step()48 if err != nil || done {49 return row, err50 }51 }52}5354// step reads up to the next row; done is false when it only moved past55// the end of a list or table.56func (r *Reader) step() (Row, bool, error) {57 switch r.state {58 case inList, inTable:59 c, ok, err := r.s.space(true)60 if err != nil {61 return nil, true, err62 }63 if !ok {64 return nil, true, r.s.fail("unterminated list")65 }66 if c == ']' {67 r.s.take()68 r.state = between69 return nil, false, nil70 }71 if r.state == inTable {72 row, err := r.s.tableRow(r.header)73 return row, true, err74 }75 v, err := r.s.value()76 return rowOf(v), true, err77 }78 c, ok, err := r.s.space(false)79 if err != nil {80 return nil, true, err81 }82 if !ok {83 return nil, true, io.EOF84 }85 if c != '[' {86 v, err := r.s.value()87 return rowOf(v), true, err88 }89 return r.open()90}9192// open reads the start of a top-level list: its first item, or the93// header of the table form.94func (r *Reader) open() (Row, bool, error) {95 r.s.take() // [96 r.state = inList97 c, ok, err := r.s.space(true)98 if err != nil || !ok || c != '[' {99 return nil, false, err // an empty list, or items read by step100 }101 first, err := r.s.list()102 if err != nil {103 return nil, true, err104 }105 if c, _, err := r.s.space(true); err != nil || c != ';' {106 return rowOf(first), true, err107 }108 r.s.take() // ;109 r.s.notJSON = true110 if r.header, err = r.s.columns(first.List); err != nil {111 return nil, true, err112 }113 r.state = inTable114 return nil, false, nil115}116117// rowOf is a value as a row: a record's fields, or else one column118// named "value".119func rowOf(v Value) Row {120 if v.Kind == Record {121 return v.Fields122 }123 return Row{{Key: "value", Value: v}}124}