context.go raw

   1  package scanner
   2  
   3  import (
   4  	"sync"
   5  
   6  	"github.com/goccy/go-yaml/token"
   7  )
   8  
   9  const whitespace = ' '
  10  
  11  // Context context at scanning
  12  type Context struct {
  13  	idx                int
  14  	size               int
  15  	notSpaceCharPos    int
  16  	notSpaceOrgCharPos int
  17  	src                []rune
  18  	buf                []rune
  19  	obuf               []rune
  20  	tokens             token.Tokens
  21  	isRawFolded        bool
  22  	isLiteral          bool
  23  	isFolded           bool
  24  	isSingleLine       bool
  25  	literalOpt         string
  26  }
  27  
  28  var (
  29  	ctxPool = sync.Pool{
  30  		New: func() interface{} {
  31  			return createContext()
  32  		},
  33  	}
  34  )
  35  
  36  func createContext() *Context {
  37  	return &Context{
  38  		idx:          0,
  39  		tokens:       token.Tokens{},
  40  		isSingleLine: true,
  41  	}
  42  }
  43  
  44  func newContext(src []rune) *Context {
  45  	ctx := ctxPool.Get().(*Context)
  46  	ctx.reset(src)
  47  	return ctx
  48  }
  49  
  50  func (c *Context) release() {
  51  	ctxPool.Put(c)
  52  }
  53  
  54  func (c *Context) reset(src []rune) {
  55  	c.idx = 0
  56  	c.size = len(src)
  57  	c.src = src
  58  	c.tokens = c.tokens[:0]
  59  	c.resetBuffer()
  60  	c.isRawFolded = false
  61  	c.isSingleLine = true
  62  	c.isLiteral = false
  63  	c.isFolded = false
  64  	c.literalOpt = ""
  65  }
  66  
  67  func (c *Context) resetBuffer() {
  68  	c.buf = c.buf[:0]
  69  	c.obuf = c.obuf[:0]
  70  	c.notSpaceCharPos = 0
  71  	c.notSpaceOrgCharPos = 0
  72  }
  73  
  74  func (c *Context) isSaveIndentMode() bool {
  75  	return c.isLiteral || c.isFolded || c.isRawFolded
  76  }
  77  
  78  func (c *Context) breakLiteral() {
  79  	c.isLiteral = false
  80  	c.isRawFolded = false
  81  	c.isFolded = false
  82  	c.literalOpt = ""
  83  }
  84  
  85  func (c *Context) addToken(tk *token.Token) {
  86  	if tk == nil {
  87  		return
  88  	}
  89  	c.tokens = append(c.tokens, tk)
  90  }
  91  
  92  func (c *Context) addBuf(r rune) {
  93  	if len(c.buf) == 0 && r == ' ' {
  94  		return
  95  	}
  96  	c.buf = append(c.buf, r)
  97  	if r != ' ' && r != '\t' {
  98  		c.notSpaceCharPos = len(c.buf)
  99  	}
 100  }
 101  
 102  func (c *Context) addOriginBuf(r rune) {
 103  	c.obuf = append(c.obuf, r)
 104  	if r != ' ' && r != '\t' {
 105  		c.notSpaceOrgCharPos = len(c.obuf)
 106  	}
 107  }
 108  
 109  func (c *Context) removeRightSpaceFromBuf() int {
 110  	trimmedBuf := c.obuf[:c.notSpaceOrgCharPos]
 111  	buflen := len(trimmedBuf)
 112  	diff := len(c.obuf) - buflen
 113  	if diff > 0 {
 114  		c.obuf = c.obuf[:buflen]
 115  		c.buf = c.bufferedSrc()
 116  	}
 117  	return diff
 118  }
 119  
 120  func (c *Context) isDocument() bool {
 121  	return c.isLiteral || c.isFolded || c.isRawFolded
 122  }
 123  
 124  func (c *Context) isEOS() bool {
 125  	return len(c.src)-1 <= c.idx
 126  }
 127  
 128  func (c *Context) isNextEOS() bool {
 129  	return len(c.src)-1 <= c.idx+1
 130  }
 131  
 132  func (c *Context) next() bool {
 133  	return c.idx < c.size
 134  }
 135  
 136  func (c *Context) source(s, e int) string {
 137  	return string(c.src[s:e])
 138  }
 139  
 140  func (c *Context) previousChar() rune {
 141  	if c.idx > 0 {
 142  		return c.src[c.idx-1]
 143  	}
 144  	return rune(0)
 145  }
 146  
 147  func (c *Context) currentChar() rune {
 148  	if c.size > c.idx {
 149  		return c.src[c.idx]
 150  	}
 151  	return rune(0)
 152  }
 153  
 154  func (c *Context) currentCharWithSkipWhitespace() rune {
 155  	idx := c.idx
 156  	for c.size > idx {
 157  		ch := c.src[idx]
 158  		if ch != whitespace {
 159  			return ch
 160  		}
 161  		idx++
 162  	}
 163  	return rune(0)
 164  }
 165  
 166  func (c *Context) nextChar() rune {
 167  	if c.size > c.idx+1 {
 168  		return c.src[c.idx+1]
 169  	}
 170  	return rune(0)
 171  }
 172  
 173  func (c *Context) repeatNum(r rune) int {
 174  	cnt := 0
 175  	for i := c.idx; i < c.size; i++ {
 176  		if c.src[i] == r {
 177  			cnt++
 178  		} else {
 179  			break
 180  		}
 181  	}
 182  	return cnt
 183  }
 184  
 185  func (c *Context) progress(num int) {
 186  	c.idx += num
 187  }
 188  
 189  func (c *Context) nextPos() int {
 190  	return c.idx + 1
 191  }
 192  
 193  func (c *Context) existsBuffer() bool {
 194  	return len(c.bufferedSrc()) != 0
 195  }
 196  
 197  func (c *Context) bufferedSrc() []rune {
 198  	src := c.buf[:c.notSpaceCharPos]
 199  	if len(src) > 0 && src[len(src)-1] == '\n' && c.isDocument() && c.literalOpt == "-" {
 200  		// remove end '\n' character
 201  		src = src[:len(src)-1]
 202  	}
 203  	return src
 204  }
 205  
 206  func (c *Context) bufferedToken(pos *token.Position) *token.Token {
 207  	if c.idx == 0 {
 208  		return nil
 209  	}
 210  	source := c.bufferedSrc()
 211  	if len(source) == 0 {
 212  		return nil
 213  	}
 214  	var tk *token.Token
 215  	if c.isDocument() {
 216  		tk = token.String(string(source), string(c.obuf), pos)
 217  	} else {
 218  		tk = token.New(string(source), string(c.obuf), pos)
 219  	}
 220  	c.resetBuffer()
 221  	return tk
 222  }
 223  
 224  func (c *Context) lastToken() *token.Token {
 225  	if len(c.tokens) != 0 {
 226  		return c.tokens[len(c.tokens)-1]
 227  	}
 228  	return nil
 229  }
 230