1package nuon23import (4 "bufio"5 "errors"6 "fmt"7 "io"8 "strings"9)1011// maxDepth is how deeply lists and records may nest.12const maxDepth = 2561314// SyntaxError is text that isn't NUON, with where it went wrong.15type SyntaxError struct {16 Line, Col int // 1-based17 Msg string18}1920func (e *SyntaxError) Error() string {21 return fmt.Sprintf("line %d, column %d: %s", e.Line, e.Col, e.Msg)22}2324// scanner reads NUON text a byte at a time from a buffered reader,25// counting lines and columns for errors, and noting whether everything26// read so far was also JSON.27type scanner struct {28 r *bufio.Reader29 line, col int30 notJSON bool31 depth int32 started bool // past a byte order mark33}3435func newScanner(r io.Reader) *scanner {36 br, ok := r.(*bufio.Reader)37 if !ok {38 br = bufio.NewReaderSize(r, 64<<10)39 }40 return &scanner{r: br, line: 1, col: 1}41}4243// skipBOM drops a UTF-8 byte order mark at the start, once. It isn't44// done in newScanner, which mustn't wait for a pipe's first bytes.45func (s *scanner) skipBOM() {46 if s.started {47 return48 }49 s.started = true50 if s.peekAt(0) == 0xEF {51 if b, _ := s.r.Peek(3); string(b) == "\xEF\xBB\xBF" {52 s.r.Discard(3)53 }54 }55}5657func (s *scanner) fail(format string, args ...any) error {58 return &SyntaxError{Line: s.line, Col: s.col, Msg: fmt.Sprintf(format, args...)}59}6061// peek returns the next byte without taking it; ok is false at the end.62func (s *scanner) peek() (byte, bool, error) {63 b, err := s.r.Peek(1)64 if len(b) == 1 {65 return b[0], true, nil66 }67 if errors.Is(err, io.EOF) {68 return 0, false, nil69 }70 return 0, false, err71}7273// peekAt returns the byte n ahead (0 is the next), or 0.74func (s *scanner) peekAt(n int) byte {75 b, _ := s.r.Peek(n + 1)76 if len(b) == n+1 {77 return b[n]78 }79 return 080}8182// take consumes the next byte.83func (s *scanner) take() (byte, error) {84 c, err := s.r.ReadByte()85 if err != nil {86 if errors.Is(err, io.EOF) {87 return 0, s.fail("unexpected end of input")88 }89 return 0, err90 }91 if c == '\n' {92 s.line, s.col = s.line+1, 193 } else if c&0xC0 != 0x80 {94 s.col++95 }96 return c, nil97}9899// expect consumes c or fails.100func (s *scanner) expect(c byte) error {101 got, ok, err := s.peek()102 switch {103 case err != nil:104 return err105 case !ok:106 return s.fail("expected %q, found the end", c)107 case got != c:108 return s.fail("expected %q, found %q", c, got)109 }110 _, err = s.take()111 return err112}113114// valid replaces bytes that aren't UTF-8 with U+FFFD, so strings read115// are text and write back as they read.116func valid(s string) string { return strings.ToValidUTF8(s, "�") }117118func isSpace(c byte) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' }119120// space skips whitespace and comments; with commas, also the commas121// that may separate items of lists and records. It returns the next122// byte, ok false at the end.123func (s *scanner) space(commas bool) (byte, bool, error) {124 s.skipBOM()125 for {126 c, ok, err := s.peek()127 if err != nil || !ok {128 return 0, false, err129 }130 switch {131 case isSpace(c), commas && c == ',':132 s.take()133 case c == '#':134 s.notJSON = true135 if err := s.skipLine(); err != nil {136 return 0, false, err137 }138 default:139 return c, true, nil140 }141 }142}143144func (s *scanner) skipLine() error {145 for {146 c, ok, err := s.peek()147 if err != nil || !ok || c == '\n' {148 return err149 }150 s.take()151 }152}153154// delimits reports whether c ends a bare word. In a key, a colon does.155func delimits(c byte, key bool) bool {156 switch c {157 case ' ', '\t', '\n', '\r', ',', '[', ']', '{', '}', '(', ')', ';', '"', '\'', '`':158 return true159 case ':':160 return key161 }162 return false163}164165// bare reads a bare word: a number, a size, a date or an unquoted166// string.167func (s *scanner) bare(key bool) (string, error) {168 var b strings.Builder169 for {170 c, ok, err := s.peek()171 if err != nil {172 return "", err173 }174 if !ok || delimits(c, key) {175 break176 }177 s.take()178 b.WriteByte(c)179 }180 if b.Len() == 0 {181 c, _, _ := s.peek()182 return "", s.fail("unexpected %q", c)183 }184 return valid(b.String()), nil185}186187// str reads a quoted string at the next byte: "..." with escapes, or188// '...' and `...` taken as written.189func (s *scanner) str() (string, error) {190 q, err := s.take()191 if err != nil {192 return "", err193 }194 if q != '"' {195 s.notJSON = true196 }197 var b strings.Builder198 for {199 c, err := s.take()200 if err != nil {201 return "", err202 }203 switch {204 case c == q:205 return valid(b.String()), nil206 case c == '\\' && q == '"':207 if err := s.escape(&b); err != nil {208 return "", err209 }210 default:211 b.WriteByte(c)212 }213 }214}215216// raw reads a raw string, r#'...'# with any number of #.217func (s *scanner) raw() (string, error) {218 s.notJSON = true219 s.take() // r220 hashes := 0221 for s.peekAt(0) == '#' {222 s.take()223 hashes++224 }225 if err := s.expect('\''); err != nil {226 return "", err227 }228 end := "'" + strings.Repeat("#", hashes)229 var b strings.Builder230 for {231 c, err := s.take()232 if err != nil {233 return "", err234 }235 b.WriteByte(c)236 if c == '#' || hashes == 0 && c == '\'' {237 if text := b.String(); strings.HasSuffix(text, end) {238 return valid(strings.TrimSuffix(text, end)), nil239 }240 }241 }242}243244// isRaw reports whether a raw string starts at the next byte.245func (s *scanner) isRaw() bool {246 if s.peekAt(0) != 'r' || s.peekAt(1) != '#' {247 return false248 }249 for i := 1; ; i++ {250 switch s.peekAt(i) {251 case '#':252 case '\'':253 return true254 default:255 return false256 }257 }258}259260var simpleEscapes = map[byte]byte{'"': '"', '\\': '\\', '/': '/', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', '\'': '\'', '0': 0}261262// escape reads what follows a backslash in a double-quoted string.263func (s *scanner) escape(b *strings.Builder) error {264 c, err := s.take()265 if err != nil {266 return err267 }268 if r, ok := simpleEscapes[c]; ok {269 b.WriteByte(r)270 return nil271 }272 if c != 'u' {273 return s.fail("unknown escape \\%c", c)274 }275 if s.peekAt(0) == '{' {276 s.notJSON = true277 return s.braceEscape(b)278 }279 r, err := s.hex4()280 if err != nil {281 return err282 }283 if r >= 0xD800 && r < 0xDC00 && s.peekAt(0) == '\\' && s.peekAt(1) == 'u' {284 s.take()285 s.take()286 lo, err := s.hex4()287 if err != nil {288 return err289 }290 r = 0x10000 + (r-0xD800)<<10 + (lo - 0xDC00)291 }292 b.WriteRune(r)293 return nil294}295296// braceEscape reads \u{1F600}, after the u.297func (s *scanner) braceEscape(b *strings.Builder) error {298 s.take() // {299 var r rune300 for n := 0; ; n++ {301 c, err := s.take()302 if err != nil {303 return err304 }305 if c == '}' && n > 0 {306 b.WriteRune(r)307 return nil308 }309 d, ok := hexDigit(c)310 if !ok || n == 6 {311 return s.fail("bad \\u{...} escape")312 }313 r = r<<4 | rune(d)314 }315}316317func (s *scanner) hex4() (rune, error) {318 var r rune319 for range 4 {320 c, err := s.take()321 if err != nil {322 return 0, err323 }324 d, ok := hexDigit(c)325 if !ok {326 return 0, s.fail("bad \\u escape")327 }328 r = r<<4 | rune(d)329 }330 return r, nil331}332333func hexDigit(c byte) (byte, bool) {334 switch {335 case c >= '0' && c <= '9':336 return c - '0', true337 case c >= 'a' && c <= 'f':338 return c - 'a' + 10, true339 case c >= 'A' && c <= 'F':340 return c - 'A' + 10, true341 }342 return 0, false343}