func MappingKey(pos *Position) *Token {
	return &Token{
		Type:          MappingKeyType,
		CharacterType: CharacterTypeIndicator,
		Indicator:     BlockStructureIndicator,
		Value:         string(MappingKeyCharacter),
		Origin:        string(MappingKeyCharacter),
		Position:      pos,
	}
}
MappingKey create token for MappingKey
package token
import (
	"fmt"
	"strings"
)
type Character byte
const (
	SequenceEntryCharacter Character = '-'
	MappingKeyCharacter = '?'
	MappingValueCharacter = ':'
	CollectEntryCharacter = ','
	SequenceStartCharacter = '['
	SequenceEndCharacter = ']'
	MappingStartCharacter = '{'
	MappingEndCharacter = '}'
	CommentCharacter = '#'
	AnchorCharacter = '&'
	AliasCharacter = '*'
	TagCharacter = '!'
	LiteralCharacter = '|'
	FoldedCharacter = '>'
	SingleQuoteCharacter = '\''
	DoubleQuoteCharacter = '"'
	DirectiveCharacter = '%'
	SpaceCharacter = ' '
	LineBreakCharacter = '\n'
)
type Type int
const (
	UnknownType Type = iota
	DocumentHeaderType
	DocumentEndType
	SequenceEntryType
	MappingKeyType
	MappingValueType
	MergeKeyType
	CollectEntryType
	SequenceStartType
	SequenceEndType
	MappingStartType
	MappingEndType
	CommentType
	AnchorType
	AliasType
	TagType
	LiteralType
	FoldedType
	SingleQuoteType
	DoubleQuoteType
	DirectiveType
	SpaceType
	NullType
	InfinityType
	NanType
	IntegerType
	BinaryIntegerType
	OctetIntegerType
	HexIntegerType
	FloatType
	StringType
	BoolType
)
func (t Type) String() string {
	switch t {
	case UnknownType:
		return "Unknown"
	case DocumentHeaderType:
		return "DocumentHeader"
	case DocumentEndType:
		return "DocumentEnd"
	case SequenceEntryType:
		return "SequenceEntry"
	case MappingKeyType:
		return "MappingKey"
	case MappingValueType:
		return "MappingValue"
	case MergeKeyType:
		return "MergeKey"
	case CollectEntryType:
		return "CollectEntry"
	case SequenceStartType:
		return "SequenceStart"
	case SequenceEndType:
		return "SequenceEnd"
	case MappingStartType:
		return "MappingStart"
	case MappingEndType:
		return "MappingEnd"
	case CommentType:
		return "Comment"
	case AnchorType:
		return "Anchor"
	case AliasType:
		return "Alias"
	case TagType:
		return "Tag"
	case LiteralType:
		return "Literal"
	case FoldedType:
		return "Folded"
	case SingleQuoteType:
		return "SingleQuote"
	case DoubleQuoteType:
		return "DoubleQuote"
	case DirectiveType:
		return "Directive"
	case SpaceType:
		return "Space"
	case StringType:
		return "String"
	case BoolType:
		return "Bool"
	case IntegerType:
		return "Integer"
	case BinaryIntegerType:
		return "BinaryInteger"
	case OctetIntegerType:
		return "OctetInteger"
	case HexIntegerType:
		return "HexInteger"
	case FloatType:
		return "Float"
	case NullType:
		return "Null"
	case InfinityType:
		return "Infinity"
	case NanType:
		return "Nan"
	}
	return ""
}
type CharacterType int
const (
	CharacterTypeIndicator CharacterType = iota
	CharacterTypeWhiteSpace
	CharacterTypeMiscellaneous
	CharacterTypeEscaped
)
func (c CharacterType) String() string {
	switch c {
	case CharacterTypeIndicator:
		return "Indicator"
	case CharacterTypeWhiteSpace:
		return "WhiteSpcae"
	case CharacterTypeMiscellaneous:
		return "Miscellaneous"
	case CharacterTypeEscaped:
		return "Escaped"
	}
	return ""
}
type Indicator int
const (
	NotIndicator Indicator = iota
	BlockStructureIndicator
	FlowCollectionIndicator
	CommentIndicator
	NodePropertyIndicator
	BlockScalarIndicator
	QuotedScalarIndicator
	DirectiveIndicator
	InvalidUseOfReservedIndicator
)
func (i Indicator) String() string {
	switch i {
	case NotIndicator:
		return "NotIndicator"
	case BlockStructureIndicator:
		return "BlockStructure"
	case FlowCollectionIndicator:
		return "FlowCollection"
	case CommentIndicator:
		return "Comment"
	case NodePropertyIndicator:
		return "NodeProperty"
	case BlockScalarIndicator:
		return "BlockScalar"
	case QuotedScalarIndicator:
		return "QuotedScalar"
	case DirectiveIndicator:
		return "Directive"
	case InvalidUseOfReservedIndicator:
		return "InvalidUseOfReserved"
	}
	return ""
}
var (
	reservedNullKeywords = []string{
		"null",
		"Null",
		"NULL",
		"~",
	}
	reservedBoolKeywords = []string{
		"true",
		"True",
		"TRUE",
		"false",
		"False",
		"FALSE",
	}
	reservedInfKeywords = []string{
		".inf",
		".Inf",
		".INF",
		"-.inf",
		"-.Inf",
		"-.INF",
	}
	reservedNanKeywords = []string{
		".nan",
		".NaN",
		".NAN",
	}
	reservedKeywordMap = map[string]func(string, string, *Position) *Token{}
)
func reservedKeywordToken(typ Type, value, org string, pos *Position) *Token {
	return &Token{
		Type:          typ,
		CharacterType: CharacterTypeMiscellaneous,
		Indicator:     NotIndicator,
		Value:         value,
		Origin:        org,
		Position:      pos,
	}
}
func init() {
	for _, keyword := range reservedNullKeywords {
		reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token {
			return reservedKeywordToken(NullType, value, org, pos)
		}
	}
	for _, keyword := range reservedBoolKeywords {
		reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token {
			return reservedKeywordToken(BoolType, value, org, pos)
		}
	}
	for _, keyword := range reservedInfKeywords {
		reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token {
			return reservedKeywordToken(InfinityType, value, org, pos)
		}
	}
	for _, keyword := range reservedNanKeywords {
		reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token {
			return reservedKeywordToken(NanType, value, org, pos)
		}
	}
}
type ReservedTagKeyword string
const (
	IntegerTag ReservedTagKeyword = "!!int"
	FloatTag ReservedTagKeyword = "!!float"
	NullTag ReservedTagKeyword = "!!null"
	SequenceTag ReservedTagKeyword = "!!seq"
	MappingTag ReservedTagKeyword = "!!map"
	StringTag ReservedTagKeyword = "!!str"
	BinaryTag ReservedTagKeyword = "!!binary"
	OrderedMapTag ReservedTagKeyword = "!!omap"
	SetTag ReservedTagKeyword = "!!set"
	TimestampTag ReservedTagKeyword = "!!timestamp"
)
var (
	ReservedTagKeywordMap = map[ReservedTagKeyword]func(string, string, *Position) *Token{
		IntegerTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
		FloatTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
		NullTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
		SequenceTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
		MappingTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
		StringTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
		BinaryTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
		OrderedMapTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
		SetTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
		TimestampTag: func(value, org string, pos *Position) *Token {
			return &Token{
				Type:          TagType,
				CharacterType: CharacterTypeIndicator,
				Indicator:     NodePropertyIndicator,
				Value:         value,
				Origin:        org,
				Position:      pos,
			}
		},
	}
)
type numType int
const (
	numTypeNone numType = iota
	numTypeBinary
	numTypeOctet
	numTypeHex
	numTypeFloat
)
type numStat struct {
	isNum bool
	typ   numType
}
func getNumberStat(str string) *numStat {
	stat := &numStat{}
	if str == "" {
		return stat
	}
	if str == "-" || str == "." || str == "+" || str == "_" {
		return stat
	}
	if str[0] == '_' {
		return stat
	}
	dotFound := false
	isNegative := false
	isExponent := false
	if str[0] == '-' {
		isNegative = true
	}
	for idx, c := range str {
		switch c {
		case 'x':
			if (isNegative && idx == 2) || (!isNegative && idx == 1) {
				continue
			}
		case 'o':
			if (isNegative && idx == 2) || (!isNegative && idx == 1) {
				continue
			}
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			continue
		case 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F':
			if (len(str) > 2 && str[0] == '0' && str[1] == 'x') ||
				(len(str) > 3 && isNegative && str[1] == '0' && str[2] == 'x') {
				continue
			}
			if c == 'b' && ((isNegative && idx == 2) || (!isNegative && idx == 1)) {
				continue
			}
			if (c == 'e' || c == 'E') && dotFound {
				isExponent = true
				continue
			}
		case '.':
			if dotFound {
				return stat
			}
			dotFound = true
			continue
		case '-':
			if idx == 0 || isExponent {
				continue
			}
		case '+':
			if idx == 0 || isExponent {
				continue
			}
		case '_':
			continue
		}
		return stat
	}
	stat.isNum = true
	switch {
	case dotFound:
		stat.typ = numTypeFloat
	case strings.HasPrefix(str, "0b") || strings.HasPrefix(str, "-0b"):
		stat.typ = numTypeBinary
	case strings.HasPrefix(str, "0x") || strings.HasPrefix(str, "-0x"):
		stat.typ = numTypeHex
	case strings.HasPrefix(str, "0o") || strings.HasPrefix(str, "-0o"):
		stat.typ = numTypeOctet
	case (len(str) > 1 && str[0] == '0') || (len(str) > 1 && str[0] == '-' && str[1] == '0'):
		stat.typ = numTypeOctet
	}
	return stat
}
func looksLikeTimeValue(value string) bool {
	for i, c := range value {
		switch c {
		case ':', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			continue
		case '0':
			if i == 0 {
				return false
			}
			continue
		}
		return false
	}
	return true
}
func IsNeedQuoted(value string) bool {
	if value == "" {
		return true
	}
	if _, exists := reservedKeywordMap[value]; exists {
		return true
	}
	if stat := getNumberStat(value); stat.isNum {
		return true
	}
	first := value[0]
	switch first {
	case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"':
		return true
	}
	last := value[len(value)-1]
	switch last {
	case ':':
		return true
	}
	if looksLikeTimeValue(value) {
		return true
	}
	for i, c := range value {
		switch c {
		case '#', '\\':
			return true
		case ':':
			if i+1 < len(value) && value[i+1] == ' ' {
				return true
			}
		}
	}
	return false
}
func LiteralBlockHeader(value string) string {
	lbc := DetectLineBreakCharacter(value)
	switch {
	case !strings.Contains(value, lbc):
		return ""
	case strings.HasSuffix(value, fmt.Sprintf("%s%s", lbc, lbc)):
		return "|+"
	case strings.HasSuffix(value, lbc):
		return "|"
	default:
		return "|-"
	}
}
func New(value string, org string, pos *Position) *Token {
	fn := reservedKeywordMap[value]
	if fn != nil {
		return fn(value, org, pos)
	}
	if stat := getNumberStat(value); stat.isNum {
		tk := &Token{
			Type:          IntegerType,
			CharacterType: CharacterTypeMiscellaneous,
			Indicator:     NotIndicator,
			Value:         value,
			Origin:        org,
			Position:      pos,
		}
		switch stat.typ {
		case numTypeFloat:
			tk.Type = FloatType
		case numTypeBinary:
			tk.Type = BinaryIntegerType
		case numTypeOctet:
			tk.Type = OctetIntegerType
		case numTypeHex:
			tk.Type = HexIntegerType
		}
		return tk
	}
	return String(value, org, pos)
}
type Position struct {
	Line        int
	Column      int
	Offset      int
	IndentNum   int
	IndentLevel int
}
func (p *Position) String() string {
	return fmt.Sprintf("[level:%d,line:%d,column:%d,offset:%d]", p.IndentLevel, p.Line, p.Column, p.Offset)
}
type Token struct {
	Type          Type
	CharacterType CharacterType
	Indicator     Indicator
	Value         string
	Origin        string
	Position      *Position
	Next          *Token
	Prev          *Token
}
func (t *Token) PreviousType() Type {
	if t.Prev != nil {
		return t.Prev.Type
	}
	return UnknownType
}
func (t *Token) NextType() Type {
	if t.Next != nil {
		return t.Next.Type
	}
	return UnknownType
}
func (t *Token) AddColumn(col int) {
	if t == nil {
		return
	}
	t.Position.Column += col
}
func (t *Token) Clone() *Token {
	if t == nil {
		return nil
	}
	copied := *t
	if t.Position != nil {
		pos := *(t.Position)
		copied.Position = &pos
	}
	return &copied
}
type Tokens []*Token
func (t *Tokens) add(tk *Token) {
	tokens := *t
	if len(tokens) == 0 {
		tokens = append(tokens, tk)
	} else {
		last := tokens[len(tokens)-1]
		last.Next = tk
		tk.Prev = last
		tokens = append(tokens, tk)
	}
	*t = tokens
}
func (t *Tokens) Add(tks ...*Token) {
	for _, tk := range tks {
		t.add(tk)
	}
}
func (t Tokens) Dump() {
	for _, tk := range t {
		fmt.Printf("- %+v\n", tk)
	}
}
func String(value string, org string, pos *Position) *Token {
	return &Token{
		Type:          StringType,
		CharacterType: CharacterTypeMiscellaneous,
		Indicator:     NotIndicator,
		Value:         value,
		Origin:        org,
		Position:      pos,
	}
}
func SequenceEntry(org string, pos *Position) *Token {
	return &Token{
		Type:          SequenceEntryType,
		CharacterType: CharacterTypeIndicator,
		Indicator:     BlockStructureIndicator,
		Value:         string(SequenceEntryCharacter),
		Origin:        org,
		Position:      pos,
	}
}