github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/token/token.go (about)

     1  package token
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  )
     7  
     8  // Character type for character
     9  type Character byte
    10  
    11  const (
    12  	// SequenceEntryCharacter character for sequence entry
    13  	SequenceEntryCharacter Character = '-'
    14  	// MappingKeyCharacter character for mapping key
    15  	MappingKeyCharacter = '?'
    16  	// MappingValueCharacter character for mapping value
    17  	MappingValueCharacter = ':'
    18  	// CollectEntryCharacter character for collect entry
    19  	CollectEntryCharacter = ','
    20  	// SequenceStartCharacter character for sequence start
    21  	SequenceStartCharacter = '['
    22  	// SequenceEndCharacter character for sequence end
    23  	SequenceEndCharacter = ']'
    24  	// MappingStartCharacter character for mapping start
    25  	MappingStartCharacter = '{'
    26  	// MappingEndCharacter character for mapping end
    27  	MappingEndCharacter = '}'
    28  	// CommentCharacter character for comment
    29  	CommentCharacter = '#'
    30  	// AnchorCharacter character for anchor
    31  	AnchorCharacter = '&'
    32  	// AliasCharacter character for alias
    33  	AliasCharacter = '*'
    34  	// TagCharacter character for tag
    35  	TagCharacter = '!'
    36  	// LiteralCharacter character for literal
    37  	LiteralCharacter = '|'
    38  	// FoldedCharacter character for folded
    39  	FoldedCharacter = '>'
    40  	// SingleQuoteCharacter character for single quote
    41  	SingleQuoteCharacter = '\''
    42  	// DoubleQuoteCharacter character for double quote
    43  	DoubleQuoteCharacter = '"'
    44  	// DirectiveCharacter character for directive
    45  	DirectiveCharacter = '%'
    46  	// SpaceCharacter character for space
    47  	SpaceCharacter = ' '
    48  	// LineBreakCharacter character for line break
    49  	LineBreakCharacter = '\n'
    50  )
    51  
    52  // Type type identifier for token
    53  type Type int
    54  
    55  const (
    56  	// UnknownType reserve for invalid type
    57  	UnknownType Type = iota
    58  	// DocumentHeaderType type for DocumentHeader token
    59  	DocumentHeaderType
    60  	// DocumentEndType type for DocumentEnd token
    61  	DocumentEndType
    62  	// SequenceEntryType type for SequenceEntry token
    63  	SequenceEntryType
    64  	// MappingKeyType type for MappingKey token
    65  	MappingKeyType
    66  	// MappingValueType type for MappingValue token
    67  	MappingValueType
    68  	// MergeKeyType type for MergeKey token
    69  	MergeKeyType
    70  	// CollectEntryType type for CollectEntry token
    71  	CollectEntryType
    72  	// SequenceStartType type for SequenceStart token
    73  	SequenceStartType
    74  	// SequenceEndType type for SequenceEnd token
    75  	SequenceEndType
    76  	// MappingStartType type for MappingStart token
    77  	MappingStartType
    78  	// MappingEndType type for MappingEnd token
    79  	MappingEndType
    80  	// CommentType type for Comment token
    81  	CommentType
    82  	// AnchorType type for Anchor token
    83  	AnchorType
    84  	// AliasType type for Alias token
    85  	AliasType
    86  	// TagType type for Tag token
    87  	TagType
    88  	// LiteralType type for Literal token
    89  	LiteralType
    90  	// FoldedType type for Folded token
    91  	FoldedType
    92  	// SingleQuoteType type for SingleQuote token
    93  	SingleQuoteType
    94  	// DoubleQuoteType type for DoubleQuote token
    95  	DoubleQuoteType
    96  	// DirectiveType type for Directive token
    97  	DirectiveType
    98  	// SpaceType type for Space token
    99  	SpaceType
   100  	// NullType type for Null token
   101  	NullType
   102  	// InfinityType type for Infinity token
   103  	InfinityType
   104  	// NanType type for Nan token
   105  	NanType
   106  	// IntegerType type for Integer token
   107  	IntegerType
   108  	// BinaryIntegerType type for BinaryInteger token
   109  	BinaryIntegerType
   110  	// OctetIntegerType type for OctetInteger token
   111  	OctetIntegerType
   112  	// HexIntegerType type for HexInteger token
   113  	HexIntegerType
   114  	// FloatType type for Float token
   115  	FloatType
   116  	// StringType type for String token
   117  	StringType
   118  	// BoolType type for Bool token
   119  	BoolType
   120  )
   121  
   122  // String type identifier to text
   123  func (t Type) String() string {
   124  	switch t {
   125  	case UnknownType:
   126  		return "Unknown"
   127  	case DocumentHeaderType:
   128  		return "DocumentHeader"
   129  	case DocumentEndType:
   130  		return "DocumentEnd"
   131  	case SequenceEntryType:
   132  		return "SequenceEntry"
   133  	case MappingKeyType:
   134  		return "MappingKey"
   135  	case MappingValueType:
   136  		return "MappingValue"
   137  	case MergeKeyType:
   138  		return "MergeKey"
   139  	case CollectEntryType:
   140  		return "CollectEntry"
   141  	case SequenceStartType:
   142  		return "SequenceStart"
   143  	case SequenceEndType:
   144  		return "SequenceEnd"
   145  	case MappingStartType:
   146  		return "MappingStart"
   147  	case MappingEndType:
   148  		return "MappingEnd"
   149  	case CommentType:
   150  		return "Comment"
   151  	case AnchorType:
   152  		return "Anchor"
   153  	case AliasType:
   154  		return "Alias"
   155  	case TagType:
   156  		return "Tag"
   157  	case LiteralType:
   158  		return "Literal"
   159  	case FoldedType:
   160  		return "Folded"
   161  	case SingleQuoteType:
   162  		return "SingleQuote"
   163  	case DoubleQuoteType:
   164  		return "DoubleQuote"
   165  	case DirectiveType:
   166  		return "Directive"
   167  	case SpaceType:
   168  		return "Space"
   169  	case StringType:
   170  		return "String"
   171  	case BoolType:
   172  		return "Bool"
   173  	case IntegerType:
   174  		return "Integer"
   175  	case BinaryIntegerType:
   176  		return "BinaryInteger"
   177  	case OctetIntegerType:
   178  		return "OctetInteger"
   179  	case HexIntegerType:
   180  		return "HexInteger"
   181  	case FloatType:
   182  		return "Float"
   183  	case NullType:
   184  		return "Null"
   185  	case InfinityType:
   186  		return "Infinity"
   187  	case NanType:
   188  		return "Nan"
   189  	}
   190  	return ""
   191  }
   192  
   193  // CharacterType type for character category
   194  type CharacterType int
   195  
   196  const (
   197  	// CharacterTypeIndicator type of indicator character
   198  	CharacterTypeIndicator CharacterType = iota
   199  	// CharacterTypeWhiteSpace type of white space character
   200  	CharacterTypeWhiteSpace
   201  	// CharacterTypeMiscellaneous type of miscellaneous character
   202  	CharacterTypeMiscellaneous
   203  	// CharacterTypeEscaped type of escaped character
   204  	CharacterTypeEscaped
   205  )
   206  
   207  // String character type identifier to text
   208  func (c CharacterType) String() string {
   209  	switch c {
   210  	case CharacterTypeIndicator:
   211  		return "Indicator"
   212  	case CharacterTypeWhiteSpace:
   213  		return "WhiteSpcae"
   214  	case CharacterTypeMiscellaneous:
   215  		return "Miscellaneous"
   216  	case CharacterTypeEscaped:
   217  		return "Escaped"
   218  	}
   219  	return ""
   220  }
   221  
   222  // Indicator type for indicator
   223  type Indicator int
   224  
   225  const (
   226  	// NotIndicator not indicator
   227  	NotIndicator Indicator = iota
   228  	// BlockStructureIndicator indicator for block structure ( '-', '?', ':' )
   229  	BlockStructureIndicator
   230  	// FlowCollectionIndicator indicator for flow collection ( '[', ']', '{', '}', ',' )
   231  	FlowCollectionIndicator
   232  	// CommentIndicator indicator for comment ( '#' )
   233  	CommentIndicator
   234  	// NodePropertyIndicator indicator for node property ( '!', '&', '*' )
   235  	NodePropertyIndicator
   236  	// BlockScalarIndicator indicator for block scalar ( '|', '>' )
   237  	BlockScalarIndicator
   238  	// QuotedScalarIndicator indicator for quoted scalar ( ''', '"' )
   239  	QuotedScalarIndicator
   240  	// DirectiveIndicator indicator for directive ( '%' )
   241  	DirectiveIndicator
   242  	// InvalidUseOfReservedIndicator indicator for invalid use of reserved keyword ( '@', '`' )
   243  	InvalidUseOfReservedIndicator
   244  )
   245  
   246  // String indicator to text
   247  func (i Indicator) String() string {
   248  	switch i {
   249  	case NotIndicator:
   250  		return "NotIndicator"
   251  	case BlockStructureIndicator:
   252  		return "BlockStructure"
   253  	case FlowCollectionIndicator:
   254  		return "FlowCollection"
   255  	case CommentIndicator:
   256  		return "Comment"
   257  	case NodePropertyIndicator:
   258  		return "NodeProperty"
   259  	case BlockScalarIndicator:
   260  		return "BlockScalar"
   261  	case QuotedScalarIndicator:
   262  		return "QuotedScalar"
   263  	case DirectiveIndicator:
   264  		return "Directive"
   265  	case InvalidUseOfReservedIndicator:
   266  		return "InvalidUseOfReserved"
   267  	}
   268  	return ""
   269  }
   270  
   271  var (
   272  	reservedNullKeywords = []string{
   273  		"null",
   274  		"Null",
   275  		"NULL",
   276  		"~",
   277  	}
   278  	reservedBoolKeywords = []string{
   279  		"true",
   280  		"True",
   281  		"TRUE",
   282  		"false",
   283  		"False",
   284  		"FALSE",
   285  	}
   286  	reservedInfKeywords = []string{
   287  		".inf",
   288  		".Inf",
   289  		".INF",
   290  		"-.inf",
   291  		"-.Inf",
   292  		"-.INF",
   293  	}
   294  	reservedNanKeywords = []string{
   295  		".nan",
   296  		".NaN",
   297  		".NAN",
   298  	}
   299  	reservedKeywordMap = map[string]func(string, string, *Position) *Token{}
   300  )
   301  
   302  func reservedKeywordToken(typ Type, value, org string, pos *Position) *Token {
   303  	return &Token{
   304  		Type:          typ,
   305  		CharacterType: CharacterTypeMiscellaneous,
   306  		Indicator:     NotIndicator,
   307  		Value:         value,
   308  		Origin:        org,
   309  		Position:      pos,
   310  	}
   311  }
   312  
   313  func init() {
   314  	for _, keyword := range reservedNullKeywords {
   315  		reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token {
   316  			return reservedKeywordToken(NullType, value, org, pos)
   317  		}
   318  	}
   319  	for _, keyword := range reservedBoolKeywords {
   320  		reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token {
   321  			return reservedKeywordToken(BoolType, value, org, pos)
   322  		}
   323  	}
   324  	for _, keyword := range reservedInfKeywords {
   325  		reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token {
   326  			return reservedKeywordToken(InfinityType, value, org, pos)
   327  		}
   328  	}
   329  	for _, keyword := range reservedNanKeywords {
   330  		reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token {
   331  			return reservedKeywordToken(NanType, value, org, pos)
   332  		}
   333  	}
   334  }
   335  
   336  // ReservedTagKeyword type of reserved tag keyword
   337  type ReservedTagKeyword string
   338  
   339  const (
   340  	// IntegerTag `!!int` tag
   341  	IntegerTag ReservedTagKeyword = "!!int"
   342  	// FloatTag `!!float` tag
   343  	FloatTag ReservedTagKeyword = "!!float"
   344  	// NullTag `!!null` tag
   345  	NullTag ReservedTagKeyword = "!!null"
   346  	// SequenceTag `!!seq` tag
   347  	SequenceTag ReservedTagKeyword = "!!seq"
   348  	// MappingTag `!!map` tag
   349  	MappingTag ReservedTagKeyword = "!!map"
   350  	// StringTag `!!str` tag
   351  	StringTag ReservedTagKeyword = "!!str"
   352  	// BinaryTag `!!binary` tag
   353  	BinaryTag ReservedTagKeyword = "!!binary"
   354  	// OrderedMapTag `!!omap` tag
   355  	OrderedMapTag ReservedTagKeyword = "!!omap"
   356  	// SetTag `!!set` tag
   357  	SetTag ReservedTagKeyword = "!!set"
   358  	// TimestampTag `!!timestamp` tag
   359  	TimestampTag ReservedTagKeyword = "!!timestamp"
   360  )
   361  
   362  // ReservedTagKeywordMap map for reserved tag keywords
   363  var ReservedTagKeywordMap = map[ReservedTagKeyword]func(string, string, *Position) *Token{
   364  	IntegerTag: func(value, org string, pos *Position) *Token {
   365  		return &Token{
   366  			Type:          TagType,
   367  			CharacterType: CharacterTypeIndicator,
   368  			Indicator:     NodePropertyIndicator,
   369  			Value:         value,
   370  			Origin:        org,
   371  			Position:      pos,
   372  		}
   373  	},
   374  	FloatTag: func(value, org string, pos *Position) *Token {
   375  		return &Token{
   376  			Type:          TagType,
   377  			CharacterType: CharacterTypeIndicator,
   378  			Indicator:     NodePropertyIndicator,
   379  			Value:         value,
   380  			Origin:        org,
   381  			Position:      pos,
   382  		}
   383  	},
   384  	NullTag: func(value, org string, pos *Position) *Token {
   385  		return &Token{
   386  			Type:          TagType,
   387  			CharacterType: CharacterTypeIndicator,
   388  			Indicator:     NodePropertyIndicator,
   389  			Value:         value,
   390  			Origin:        org,
   391  			Position:      pos,
   392  		}
   393  	},
   394  	SequenceTag: func(value, org string, pos *Position) *Token {
   395  		return &Token{
   396  			Type:          TagType,
   397  			CharacterType: CharacterTypeIndicator,
   398  			Indicator:     NodePropertyIndicator,
   399  			Value:         value,
   400  			Origin:        org,
   401  			Position:      pos,
   402  		}
   403  	},
   404  	MappingTag: func(value, org string, pos *Position) *Token {
   405  		return &Token{
   406  			Type:          TagType,
   407  			CharacterType: CharacterTypeIndicator,
   408  			Indicator:     NodePropertyIndicator,
   409  			Value:         value,
   410  			Origin:        org,
   411  			Position:      pos,
   412  		}
   413  	},
   414  	StringTag: func(value, org string, pos *Position) *Token {
   415  		return &Token{
   416  			Type:          TagType,
   417  			CharacterType: CharacterTypeIndicator,
   418  			Indicator:     NodePropertyIndicator,
   419  			Value:         value,
   420  			Origin:        org,
   421  			Position:      pos,
   422  		}
   423  	},
   424  	BinaryTag: func(value, org string, pos *Position) *Token {
   425  		return &Token{
   426  			Type:          TagType,
   427  			CharacterType: CharacterTypeIndicator,
   428  			Indicator:     NodePropertyIndicator,
   429  			Value:         value,
   430  			Origin:        org,
   431  			Position:      pos,
   432  		}
   433  	},
   434  	OrderedMapTag: func(value, org string, pos *Position) *Token {
   435  		return &Token{
   436  			Type:          TagType,
   437  			CharacterType: CharacterTypeIndicator,
   438  			Indicator:     NodePropertyIndicator,
   439  			Value:         value,
   440  			Origin:        org,
   441  			Position:      pos,
   442  		}
   443  	},
   444  	SetTag: func(value, org string, pos *Position) *Token {
   445  		return &Token{
   446  			Type:          TagType,
   447  			CharacterType: CharacterTypeIndicator,
   448  			Indicator:     NodePropertyIndicator,
   449  			Value:         value,
   450  			Origin:        org,
   451  			Position:      pos,
   452  		}
   453  	},
   454  	TimestampTag: func(value, org string, pos *Position) *Token {
   455  		return &Token{
   456  			Type:          TagType,
   457  			CharacterType: CharacterTypeIndicator,
   458  			Indicator:     NodePropertyIndicator,
   459  			Value:         value,
   460  			Origin:        org,
   461  			Position:      pos,
   462  		}
   463  	},
   464  }
   465  
   466  type numType int
   467  
   468  const (
   469  	numTypeNone numType = iota
   470  	numTypeBinary
   471  	numTypeOctet
   472  	numTypeHex
   473  	numTypeFloat
   474  )
   475  
   476  type numStat struct {
   477  	isNum bool
   478  	typ   numType
   479  }
   480  
   481  func getNumberStat(str string) *numStat {
   482  	stat := &numStat{}
   483  	if str == "" {
   484  		return stat
   485  	}
   486  	if str == "-" || str == "." || str == "+" || str == "_" {
   487  		return stat
   488  	}
   489  	if str[0] == '_' {
   490  		return stat
   491  	}
   492  	dotFound := false
   493  	isNegative := false
   494  	isExponent := false
   495  	if str[0] == '-' {
   496  		isNegative = true
   497  	}
   498  	for idx, c := range str {
   499  		switch c {
   500  		case 'x':
   501  			if (isNegative && idx == 2) || (!isNegative && idx == 1) {
   502  				continue
   503  			}
   504  		case 'o':
   505  			if (isNegative && idx == 2) || (!isNegative && idx == 1) {
   506  				continue
   507  			}
   508  		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
   509  			continue
   510  		case 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F':
   511  			if (len(str) > 2 && str[0] == '0' && str[1] == 'x') ||
   512  				(len(str) > 3 && isNegative && str[1] == '0' && str[2] == 'x') {
   513  				// hex number
   514  				continue
   515  			}
   516  			if c == 'b' && ((isNegative && idx == 2) || (!isNegative && idx == 1)) {
   517  				// binary number
   518  				continue
   519  			}
   520  			if (c == 'e' || c == 'E') && dotFound {
   521  				// exponent
   522  				isExponent = true
   523  				continue
   524  			}
   525  		case '.':
   526  			if dotFound {
   527  				// multiple dot
   528  				return stat
   529  			}
   530  			dotFound = true
   531  			continue
   532  		case '-':
   533  			if idx == 0 || isExponent {
   534  				continue
   535  			}
   536  		case '+':
   537  			if idx == 0 || isExponent {
   538  				continue
   539  			}
   540  		case '_':
   541  			continue
   542  		}
   543  		return stat
   544  	}
   545  	stat.isNum = true
   546  	switch {
   547  	case dotFound:
   548  		stat.typ = numTypeFloat
   549  	case strings.HasPrefix(str, "0b") || strings.HasPrefix(str, "-0b"):
   550  		stat.typ = numTypeBinary
   551  	case strings.HasPrefix(str, "0x") || strings.HasPrefix(str, "-0x"):
   552  		stat.typ = numTypeHex
   553  	case strings.HasPrefix(str, "0o") || strings.HasPrefix(str, "-0o"):
   554  		stat.typ = numTypeOctet
   555  	case (len(str) > 1 && str[0] == '0') || (len(str) > 1 && str[0] == '-' && str[1] == '0'):
   556  		stat.typ = numTypeOctet
   557  	}
   558  	return stat
   559  }
   560  
   561  func looksLikeTimeValue(value string) bool {
   562  	for i, c := range value {
   563  		switch c {
   564  		case ':', '1', '2', '3', '4', '5', '6', '7', '8', '9':
   565  			continue
   566  		case '0':
   567  			if i == 0 {
   568  				return false
   569  			}
   570  			continue
   571  		}
   572  		return false
   573  	}
   574  	return true
   575  }
   576  
   577  // IsNeedQuoted whether need quote for passed string or not
   578  func IsNeedQuoted(value string) bool {
   579  	if value == "" {
   580  		return true
   581  	}
   582  	if _, exists := reservedKeywordMap[value]; exists {
   583  		return true
   584  	}
   585  	if stat := getNumberStat(value); stat.isNum {
   586  		return true
   587  	}
   588  	first := value[0]
   589  	switch first {
   590  	case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"':
   591  		return true
   592  	}
   593  	last := value[len(value)-1]
   594  	switch last {
   595  	case ':':
   596  		return true
   597  	}
   598  	if looksLikeTimeValue(value) {
   599  		return true
   600  	}
   601  	for i, c := range value {
   602  		switch c {
   603  		case '#', '\\':
   604  			return true
   605  		case ':':
   606  			if i+1 < len(value) && value[i+1] == ' ' {
   607  				return true
   608  			}
   609  		}
   610  	}
   611  	return false
   612  }
   613  
   614  // LiteralBlockHeader detect literal block scalar header
   615  func LiteralBlockHeader(value string) string {
   616  	lbc := DetectLineBreakCharacter(value)
   617  
   618  	switch {
   619  	case !strings.Contains(value, lbc):
   620  		return ""
   621  	case strings.HasSuffix(value, fmt.Sprintf("%s%s", lbc, lbc)):
   622  		return "|+"
   623  	case strings.HasSuffix(value, lbc):
   624  		return "|"
   625  	default:
   626  		return "|-"
   627  	}
   628  }
   629  
   630  // New create reserved keyword token or number token and other string token
   631  func New(value string, org string, pos *Position) *Token {
   632  	fn := reservedKeywordMap[value]
   633  	if fn != nil {
   634  		return fn(value, org, pos)
   635  	}
   636  	if stat := getNumberStat(value); stat.isNum {
   637  		tk := &Token{
   638  			Type:          IntegerType,
   639  			CharacterType: CharacterTypeMiscellaneous,
   640  			Indicator:     NotIndicator,
   641  			Value:         value,
   642  			Origin:        org,
   643  			Position:      pos,
   644  		}
   645  		switch stat.typ {
   646  		case numTypeFloat:
   647  			tk.Type = FloatType
   648  		case numTypeBinary:
   649  			tk.Type = BinaryIntegerType
   650  		case numTypeOctet:
   651  			tk.Type = OctetIntegerType
   652  		case numTypeHex:
   653  			tk.Type = HexIntegerType
   654  		}
   655  		return tk
   656  	}
   657  	return String(value, org, pos)
   658  }
   659  
   660  // Position type for position in YAML document
   661  type Position struct {
   662  	Line        int
   663  	Column      int
   664  	Offset      int
   665  	IndentNum   int
   666  	IndentLevel int
   667  }
   668  
   669  // String position to text
   670  func (p *Position) String() string {
   671  	return fmt.Sprintf("[level:%d,line:%d,column:%d,offset:%d]", p.IndentLevel, p.Line, p.Column, p.Offset)
   672  }
   673  
   674  // Token type for token
   675  type Token struct {
   676  	Type          Type
   677  	CharacterType CharacterType
   678  	Indicator     Indicator
   679  	Value         string
   680  	Origin        string
   681  	Position      *Position
   682  	Next          *Token
   683  	Prev          *Token
   684  }
   685  
   686  // PreviousType previous token type
   687  func (t *Token) PreviousType() Type {
   688  	if t.Prev != nil {
   689  		return t.Prev.Type
   690  	}
   691  	return UnknownType
   692  }
   693  
   694  // NextType next token type
   695  func (t *Token) NextType() Type {
   696  	if t.Next != nil {
   697  		return t.Next.Type
   698  	}
   699  	return UnknownType
   700  }
   701  
   702  // AddColumn append column number to current position of column
   703  func (t *Token) AddColumn(col int) {
   704  	if t == nil {
   705  		return
   706  	}
   707  	t.Position.Column += col
   708  }
   709  
   710  // Clone copy token ( preserve Prev/Next reference )
   711  func (t *Token) Clone() *Token {
   712  	if t == nil {
   713  		return nil
   714  	}
   715  	copied := *t
   716  	if t.Position != nil {
   717  		pos := *(t.Position)
   718  		copied.Position = &pos
   719  	}
   720  	return &copied
   721  }
   722  
   723  // Tokens type of token collection
   724  type Tokens []*Token
   725  
   726  func (t *Tokens) add(tk *Token) {
   727  	tokens := *t
   728  	if len(tokens) == 0 {
   729  		tokens = append(tokens, tk)
   730  	} else {
   731  		last := tokens[len(tokens)-1]
   732  		last.Next = tk
   733  		tk.Prev = last
   734  		tokens = append(tokens, tk)
   735  	}
   736  	*t = tokens
   737  }
   738  
   739  // Add append new some tokens
   740  func (t *Tokens) Add(tks ...*Token) {
   741  	for _, tk := range tks {
   742  		t.add(tk)
   743  	}
   744  }
   745  
   746  // Dump dump all token structures for debugging
   747  func (t Tokens) Dump() {
   748  	for _, tk := range t {
   749  		fmt.Printf("- %+v\n", tk)
   750  	}
   751  }
   752  
   753  // String create token for String
   754  func String(value string, org string, pos *Position) *Token {
   755  	return &Token{
   756  		Type:          StringType,
   757  		CharacterType: CharacterTypeMiscellaneous,
   758  		Indicator:     NotIndicator,
   759  		Value:         value,
   760  		Origin:        org,
   761  		Position:      pos,
   762  	}
   763  }
   764  
   765  // SequenceEntry create token for SequenceEntry
   766  func SequenceEntry(org string, pos *Position) *Token {
   767  	return &Token{
   768  		Type:          SequenceEntryType,
   769  		CharacterType: CharacterTypeIndicator,
   770  		Indicator:     BlockStructureIndicator,
   771  		Value:         string(SequenceEntryCharacter),
   772  		Origin:        org,
   773  		Position:      pos,
   774  	}
   775  }
   776  
   777  // MappingKey create token for MappingKey
   778  func MappingKey(pos *Position) *Token {
   779  	return &Token{
   780  		Type:          MappingKeyType,
   781  		CharacterType: CharacterTypeIndicator,
   782  		Indicator:     BlockStructureIndicator,
   783  		Value:         string(MappingKeyCharacter),
   784  		Origin:        string(MappingKeyCharacter),
   785  		Position:      pos,
   786  	}
   787  }
   788  
   789  // MappingValue create token for MappingValue
   790  func MappingValue(pos *Position) *Token {
   791  	return &Token{
   792  		Type:          MappingValueType,
   793  		CharacterType: CharacterTypeIndicator,
   794  		Indicator:     BlockStructureIndicator,
   795  		Value:         string(MappingValueCharacter),
   796  		Origin:        string(MappingValueCharacter),
   797  		Position:      pos,
   798  	}
   799  }
   800  
   801  // CollectEntry create token for CollectEntry
   802  func CollectEntry(org string, pos *Position) *Token {
   803  	return &Token{
   804  		Type:          CollectEntryType,
   805  		CharacterType: CharacterTypeIndicator,
   806  		Indicator:     FlowCollectionIndicator,
   807  		Value:         string(CollectEntryCharacter),
   808  		Origin:        org,
   809  		Position:      pos,
   810  	}
   811  }
   812  
   813  // SequenceStart create token for SequenceStart
   814  func SequenceStart(org string, pos *Position) *Token {
   815  	return &Token{
   816  		Type:          SequenceStartType,
   817  		CharacterType: CharacterTypeIndicator,
   818  		Indicator:     FlowCollectionIndicator,
   819  		Value:         string(SequenceStartCharacter),
   820  		Origin:        org,
   821  		Position:      pos,
   822  	}
   823  }
   824  
   825  // SequenceEnd create token for SequenceEnd
   826  func SequenceEnd(org string, pos *Position) *Token {
   827  	return &Token{
   828  		Type:          SequenceEndType,
   829  		CharacterType: CharacterTypeIndicator,
   830  		Indicator:     FlowCollectionIndicator,
   831  		Value:         string(SequenceEndCharacter),
   832  		Origin:        org,
   833  		Position:      pos,
   834  	}
   835  }
   836  
   837  // MappingStart create token for MappingStart
   838  func MappingStart(org string, pos *Position) *Token {
   839  	return &Token{
   840  		Type:          MappingStartType,
   841  		CharacterType: CharacterTypeIndicator,
   842  		Indicator:     FlowCollectionIndicator,
   843  		Value:         string(MappingStartCharacter),
   844  		Origin:        org,
   845  		Position:      pos,
   846  	}
   847  }
   848  
   849  // MappingEnd create token for MappingEnd
   850  func MappingEnd(org string, pos *Position) *Token {
   851  	return &Token{
   852  		Type:          MappingEndType,
   853  		CharacterType: CharacterTypeIndicator,
   854  		Indicator:     FlowCollectionIndicator,
   855  		Value:         string(MappingEndCharacter),
   856  		Origin:        org,
   857  		Position:      pos,
   858  	}
   859  }
   860  
   861  // Comment create token for Comment
   862  func Comment(value string, org string, pos *Position) *Token {
   863  	return &Token{
   864  		Type:          CommentType,
   865  		CharacterType: CharacterTypeIndicator,
   866  		Indicator:     CommentIndicator,
   867  		Value:         value,
   868  		Origin:        org,
   869  		Position:      pos,
   870  	}
   871  }
   872  
   873  // Anchor create token for Anchor
   874  func Anchor(org string, pos *Position) *Token {
   875  	return &Token{
   876  		Type:          AnchorType,
   877  		CharacterType: CharacterTypeIndicator,
   878  		Indicator:     NodePropertyIndicator,
   879  		Value:         string(AnchorCharacter),
   880  		Origin:        org,
   881  		Position:      pos,
   882  	}
   883  }
   884  
   885  // Alias create token for Alias
   886  func Alias(org string, pos *Position) *Token {
   887  	return &Token{
   888  		Type:          AliasType,
   889  		CharacterType: CharacterTypeIndicator,
   890  		Indicator:     NodePropertyIndicator,
   891  		Value:         string(AliasCharacter),
   892  		Origin:        org,
   893  		Position:      pos,
   894  	}
   895  }
   896  
   897  // Tag create token for Tag
   898  func Tag(value string, org string, pos *Position) *Token {
   899  	fn := ReservedTagKeywordMap[ReservedTagKeyword(value)]
   900  	if fn != nil {
   901  		return fn(value, org, pos)
   902  	}
   903  	return &Token{
   904  		Type:          TagType,
   905  		CharacterType: CharacterTypeIndicator,
   906  		Indicator:     NodePropertyIndicator,
   907  		Value:         value,
   908  		Origin:        org,
   909  		Position:      pos,
   910  	}
   911  }
   912  
   913  // Literal create token for Literal
   914  func Literal(value string, org string, pos *Position) *Token {
   915  	return &Token{
   916  		Type:          LiteralType,
   917  		CharacterType: CharacterTypeIndicator,
   918  		Indicator:     BlockScalarIndicator,
   919  		Value:         value,
   920  		Origin:        org,
   921  		Position:      pos,
   922  	}
   923  }
   924  
   925  // Folded create token for Folded
   926  func Folded(value string, org string, pos *Position) *Token {
   927  	return &Token{
   928  		Type:          FoldedType,
   929  		CharacterType: CharacterTypeIndicator,
   930  		Indicator:     BlockScalarIndicator,
   931  		Value:         value,
   932  		Origin:        org,
   933  		Position:      pos,
   934  	}
   935  }
   936  
   937  // SingleQuote create token for SingleQuote
   938  func SingleQuote(value string, org string, pos *Position) *Token {
   939  	return &Token{
   940  		Type:          SingleQuoteType,
   941  		CharacterType: CharacterTypeIndicator,
   942  		Indicator:     QuotedScalarIndicator,
   943  		Value:         value,
   944  		Origin:        org,
   945  		Position:      pos,
   946  	}
   947  }
   948  
   949  // DoubleQuote create token for DoubleQuote
   950  func DoubleQuote(value string, org string, pos *Position) *Token {
   951  	return &Token{
   952  		Type:          DoubleQuoteType,
   953  		CharacterType: CharacterTypeIndicator,
   954  		Indicator:     QuotedScalarIndicator,
   955  		Value:         value,
   956  		Origin:        org,
   957  		Position:      pos,
   958  	}
   959  }
   960  
   961  // Directive create token for Directive
   962  func Directive(org string, pos *Position) *Token {
   963  	return &Token{
   964  		Type:          DirectiveType,
   965  		CharacterType: CharacterTypeIndicator,
   966  		Indicator:     DirectiveIndicator,
   967  		Value:         string(DirectiveCharacter),
   968  		Origin:        org,
   969  		Position:      pos,
   970  	}
   971  }
   972  
   973  // Space create token for Space
   974  func Space(pos *Position) *Token {
   975  	return &Token{
   976  		Type:          SpaceType,
   977  		CharacterType: CharacterTypeWhiteSpace,
   978  		Indicator:     NotIndicator,
   979  		Value:         string(SpaceCharacter),
   980  		Origin:        string(SpaceCharacter),
   981  		Position:      pos,
   982  	}
   983  }
   984  
   985  // MergeKey create token for MergeKey
   986  func MergeKey(org string, pos *Position) *Token {
   987  	return &Token{
   988  		Type:          MergeKeyType,
   989  		CharacterType: CharacterTypeMiscellaneous,
   990  		Indicator:     NotIndicator,
   991  		Value:         "<<",
   992  		Origin:        org,
   993  		Position:      pos,
   994  	}
   995  }
   996  
   997  // DocumentHeader create token for DocumentHeader
   998  func DocumentHeader(org string, pos *Position) *Token {
   999  	return &Token{
  1000  		Type:          DocumentHeaderType,
  1001  		CharacterType: CharacterTypeMiscellaneous,
  1002  		Indicator:     NotIndicator,
  1003  		Value:         "---",
  1004  		Origin:        org,
  1005  		Position:      pos,
  1006  	}
  1007  }
  1008  
  1009  // DocumentEnd create token for DocumentEnd
  1010  func DocumentEnd(org string, pos *Position) *Token {
  1011  	return &Token{
  1012  		Type:          DocumentEndType,
  1013  		CharacterType: CharacterTypeMiscellaneous,
  1014  		Indicator:     NotIndicator,
  1015  		Value:         "...",
  1016  		Origin:        org,
  1017  		Position:      pos,
  1018  	}
  1019  }
  1020  
  1021  // DetectLineBreakCharacter detect line break character in only one inside scalar content scope.
  1022  func DetectLineBreakCharacter(src string) string {
  1023  	nc := strings.Count(src, "\n")
  1024  	rc := strings.Count(src, "\r")
  1025  	rnc := strings.Count(src, "\r\n")
  1026  	switch {
  1027  	case nc == rnc && rc == rnc:
  1028  		return "\r\n"
  1029  	case rc > nc:
  1030  		return "\r"
  1031  	default:
  1032  		return "\n"
  1033  	}
  1034  }