You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1037 lines
25 KiB
1037 lines
25 KiB
3 years ago
|
package token
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// Character type for character
|
||
|
type Character byte
|
||
|
|
||
|
const (
|
||
|
// SequenceEntryCharacter character for sequence entry
|
||
|
SequenceEntryCharacter Character = '-'
|
||
|
// MappingKeyCharacter character for mapping key
|
||
|
MappingKeyCharacter = '?'
|
||
|
// MappingValueCharacter character for mapping value
|
||
|
MappingValueCharacter = ':'
|
||
|
// CollectEntryCharacter character for collect entry
|
||
|
CollectEntryCharacter = ','
|
||
|
// SequenceStartCharacter character for sequence start
|
||
|
SequenceStartCharacter = '['
|
||
|
// SequenceEndCharacter character for sequence end
|
||
|
SequenceEndCharacter = ']'
|
||
|
// MappingStartCharacter character for mapping start
|
||
|
MappingStartCharacter = '{'
|
||
|
// MappingEndCharacter character for mapping end
|
||
|
MappingEndCharacter = '}'
|
||
|
// CommentCharacter character for comment
|
||
|
CommentCharacter = '#'
|
||
|
// AnchorCharacter character for anchor
|
||
|
AnchorCharacter = '&'
|
||
|
// AliasCharacter character for alias
|
||
|
AliasCharacter = '*'
|
||
|
// TagCharacter character for tag
|
||
|
TagCharacter = '!'
|
||
|
// LiteralCharacter character for literal
|
||
|
LiteralCharacter = '|'
|
||
|
// FoldedCharacter character for folded
|
||
|
FoldedCharacter = '>'
|
||
|
// SingleQuoteCharacter character for single quote
|
||
|
SingleQuoteCharacter = '\''
|
||
|
// DoubleQuoteCharacter character for double quote
|
||
|
DoubleQuoteCharacter = '"'
|
||
|
// DirectiveCharacter character for directive
|
||
|
DirectiveCharacter = '%'
|
||
|
// SpaceCharacter character for space
|
||
|
SpaceCharacter = ' '
|
||
|
// LineBreakCharacter character for line break
|
||
|
LineBreakCharacter = '\n'
|
||
|
)
|
||
|
|
||
|
// Type type identifier for token
|
||
|
type Type int
|
||
|
|
||
|
const (
|
||
|
// UnknownType reserve for invalid type
|
||
|
UnknownType Type = iota
|
||
|
// DocumentHeaderType type for DocumentHeader token
|
||
|
DocumentHeaderType
|
||
|
// DocumentEndType type for DocumentEnd token
|
||
|
DocumentEndType
|
||
|
// SequenceEntryType type for SequenceEntry token
|
||
|
SequenceEntryType
|
||
|
// MappingKeyType type for MappingKey token
|
||
|
MappingKeyType
|
||
|
// MappingValueType type for MappingValue token
|
||
|
MappingValueType
|
||
|
// MergeKeyType type for MergeKey token
|
||
|
MergeKeyType
|
||
|
// CollectEntryType type for CollectEntry token
|
||
|
CollectEntryType
|
||
|
// SequenceStartType type for SequenceStart token
|
||
|
SequenceStartType
|
||
|
// SequenceEndType type for SequenceEnd token
|
||
|
SequenceEndType
|
||
|
// MappingStartType type for MappingStart token
|
||
|
MappingStartType
|
||
|
// MappingEndType type for MappingEnd token
|
||
|
MappingEndType
|
||
|
// CommentType type for Comment token
|
||
|
CommentType
|
||
|
// AnchorType type for Anchor token
|
||
|
AnchorType
|
||
|
// AliasType type for Alias token
|
||
|
AliasType
|
||
|
// TagType type for Tag token
|
||
|
TagType
|
||
|
// LiteralType type for Literal token
|
||
|
LiteralType
|
||
|
// FoldedType type for Folded token
|
||
|
FoldedType
|
||
|
// SingleQuoteType type for SingleQuote token
|
||
|
SingleQuoteType
|
||
|
// DoubleQuoteType type for DoubleQuote token
|
||
|
DoubleQuoteType
|
||
|
// DirectiveType type for Directive token
|
||
|
DirectiveType
|
||
|
// SpaceType type for Space token
|
||
|
SpaceType
|
||
|
// NullType type for Null token
|
||
|
NullType
|
||
|
// InfinityType type for Infinity token
|
||
|
InfinityType
|
||
|
// NanType type for Nan token
|
||
|
NanType
|
||
|
// IntegerType type for Integer token
|
||
|
IntegerType
|
||
|
// BinaryIntegerType type for BinaryInteger token
|
||
|
BinaryIntegerType
|
||
|
// OctetIntegerType type for OctetInteger token
|
||
|
OctetIntegerType
|
||
|
// HexIntegerType type for HexInteger token
|
||
|
HexIntegerType
|
||
|
// FloatType type for Float token
|
||
|
FloatType
|
||
|
// StringType type for String token
|
||
|
StringType
|
||
|
// BoolType type for Bool token
|
||
|
BoolType
|
||
|
)
|
||
|
|
||
|
// String type identifier to text
|
||
|
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 ""
|
||
|
}
|
||
|
|
||
|
// CharacterType type for character category
|
||
|
type CharacterType int
|
||
|
|
||
|
const (
|
||
|
// CharacterTypeIndicator type of indicator character
|
||
|
CharacterTypeIndicator CharacterType = iota
|
||
|
// CharacterTypeWhiteSpace type of white space character
|
||
|
CharacterTypeWhiteSpace
|
||
|
// CharacterTypeMiscellaneous type of miscellaneous character
|
||
|
CharacterTypeMiscellaneous
|
||
|
// CharacterTypeEscaped type of escaped character
|
||
|
CharacterTypeEscaped
|
||
|
)
|
||
|
|
||
|
// String character type identifier to text
|
||
|
func (c CharacterType) String() string {
|
||
|
switch c {
|
||
|
case CharacterTypeIndicator:
|
||
|
return "Indicator"
|
||
|
case CharacterTypeWhiteSpace:
|
||
|
return "WhiteSpcae"
|
||
|
case CharacterTypeMiscellaneous:
|
||
|
return "Miscellaneous"
|
||
|
case CharacterTypeEscaped:
|
||
|
return "Escaped"
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// Indicator type for indicator
|
||
|
type Indicator int
|
||
|
|
||
|
const (
|
||
|
// NotIndicator not indicator
|
||
|
NotIndicator Indicator = iota
|
||
|
// BlockStructureIndicator indicator for block structure ( '-', '?', ':' )
|
||
|
BlockStructureIndicator
|
||
|
// FlowCollectionIndicator indicator for flow collection ( '[', ']', '{', '}', ',' )
|
||
|
FlowCollectionIndicator
|
||
|
// CommentIndicator indicator for comment ( '#' )
|
||
|
CommentIndicator
|
||
|
// NodePropertyIndicator indicator for node property ( '!', '&', '*' )
|
||
|
NodePropertyIndicator
|
||
|
// BlockScalarIndicator indicator for block scalar ( '|', '>' )
|
||
|
BlockScalarIndicator
|
||
|
// QuotedScalarIndicator indicator for quoted scalar ( ''', '"' )
|
||
|
QuotedScalarIndicator
|
||
|
// DirectiveIndicator indicator for directive ( '%' )
|
||
|
DirectiveIndicator
|
||
|
// InvalidUseOfReservedIndicator indicator for invalid use of reserved keyword ( '@', '`' )
|
||
|
InvalidUseOfReservedIndicator
|
||
|
)
|
||
|
|
||
|
// String indicator to text
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ReservedTagKeyword type of reserved tag keyword
|
||
|
type ReservedTagKeyword string
|
||
|
|
||
|
const (
|
||
|
// IntegerTag `!!int` tag
|
||
|
IntegerTag ReservedTagKeyword = "!!int"
|
||
|
// FloatTag `!!float` tag
|
||
|
FloatTag ReservedTagKeyword = "!!float"
|
||
|
// NullTag `!!null` tag
|
||
|
NullTag ReservedTagKeyword = "!!null"
|
||
|
// SequenceTag `!!seq` tag
|
||
|
SequenceTag ReservedTagKeyword = "!!seq"
|
||
|
// MappingTag `!!map` tag
|
||
|
MappingTag ReservedTagKeyword = "!!map"
|
||
|
// StringTag `!!str` tag
|
||
|
StringTag ReservedTagKeyword = "!!str"
|
||
|
// BinaryTag `!!binary` tag
|
||
|
BinaryTag ReservedTagKeyword = "!!binary"
|
||
|
// OrderedMapTag `!!omap` tag
|
||
|
OrderedMapTag ReservedTagKeyword = "!!omap"
|
||
|
// SetTag `!!set` tag
|
||
|
SetTag ReservedTagKeyword = "!!set"
|
||
|
// TimestampTag `!!timestamp` tag
|
||
|
TimestampTag ReservedTagKeyword = "!!timestamp"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// ReservedTagKeywordMap map for reserved tag keywords
|
||
|
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') {
|
||
|
// hex number
|
||
|
continue
|
||
|
}
|
||
|
if c == 'b' && ((isNegative && idx == 2) || (!isNegative && idx == 1)) {
|
||
|
// binary number
|
||
|
continue
|
||
|
}
|
||
|
if (c == 'e' || c == 'E') && dotFound {
|
||
|
// exponent
|
||
|
isExponent = true
|
||
|
continue
|
||
|
}
|
||
|
case '.':
|
||
|
if dotFound {
|
||
|
// multiple dot
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// IsNeedQuoted whether need quote for passed string or not
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// LiteralBlockHeader detect literal block scalar header
|
||
|
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 "|-"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// New create reserved keyword token or number token and other string token
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
// Position type for position in YAML document
|
||
|
type Position struct {
|
||
|
Line int
|
||
|
Column int
|
||
|
Offset int
|
||
|
IndentNum int
|
||
|
IndentLevel int
|
||
|
}
|
||
|
|
||
|
// String position to text
|
||
|
func (p *Position) String() string {
|
||
|
return fmt.Sprintf("[level:%d,line:%d,column:%d,offset:%d]", p.IndentLevel, p.Line, p.Column, p.Offset)
|
||
|
}
|
||
|
|
||
|
// Token type for token
|
||
|
type Token struct {
|
||
|
Type Type
|
||
|
CharacterType CharacterType
|
||
|
Indicator Indicator
|
||
|
Value string
|
||
|
Origin string
|
||
|
Position *Position
|
||
|
Next *Token
|
||
|
Prev *Token
|
||
|
}
|
||
|
|
||
|
// PreviousType previous token type
|
||
|
func (t *Token) PreviousType() Type {
|
||
|
if t.Prev != nil {
|
||
|
return t.Prev.Type
|
||
|
}
|
||
|
return UnknownType
|
||
|
}
|
||
|
|
||
|
// NextType next token type
|
||
|
func (t *Token) NextType() Type {
|
||
|
if t.Next != nil {
|
||
|
return t.Next.Type
|
||
|
}
|
||
|
return UnknownType
|
||
|
}
|
||
|
|
||
|
// AddColumn append column number to current position of column
|
||
|
func (t *Token) AddColumn(col int) {
|
||
|
if t == nil {
|
||
|
return
|
||
|
}
|
||
|
t.Position.Column += col
|
||
|
}
|
||
|
|
||
|
// Clone copy token ( preserve Prev/Next reference )
|
||
|
func (t *Token) Clone() *Token {
|
||
|
if t == nil {
|
||
|
return nil
|
||
|
}
|
||
|
copied := *t
|
||
|
if t.Position != nil {
|
||
|
pos := *(t.Position)
|
||
|
copied.Position = &pos
|
||
|
}
|
||
|
return &copied
|
||
|
}
|
||
|
|
||
|
// Tokens type of token collection
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// Add append new some tokens
|
||
|
func (t *Tokens) Add(tks ...*Token) {
|
||
|
for _, tk := range tks {
|
||
|
t.add(tk)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Dump dump all token structures for debugging
|
||
|
func (t Tokens) Dump() {
|
||
|
for _, tk := range t {
|
||
|
fmt.Printf("- %+v\n", tk)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String create token for String
|
||
|
func String(value string, org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: StringType,
|
||
|
CharacterType: CharacterTypeMiscellaneous,
|
||
|
Indicator: NotIndicator,
|
||
|
Value: value,
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SequenceEntry create token for SequenceEntry
|
||
|
func SequenceEntry(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: SequenceEntryType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: BlockStructureIndicator,
|
||
|
Value: string(SequenceEntryCharacter),
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MappingKey create token for MappingKey
|
||
|
func MappingKey(pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: MappingKeyType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: BlockStructureIndicator,
|
||
|
Value: string(MappingKeyCharacter),
|
||
|
Origin: string(MappingKeyCharacter),
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MappingValue create token for MappingValue
|
||
|
func MappingValue(pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: MappingValueType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: BlockStructureIndicator,
|
||
|
Value: string(MappingValueCharacter),
|
||
|
Origin: string(MappingValueCharacter),
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// CollectEntry create token for CollectEntry
|
||
|
func CollectEntry(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: CollectEntryType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: FlowCollectionIndicator,
|
||
|
Value: string(CollectEntryCharacter),
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SequenceStart create token for SequenceStart
|
||
|
func SequenceStart(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: SequenceStartType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: FlowCollectionIndicator,
|
||
|
Value: string(SequenceStartCharacter),
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SequenceEnd create token for SequenceEnd
|
||
|
func SequenceEnd(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: SequenceEndType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: FlowCollectionIndicator,
|
||
|
Value: string(SequenceEndCharacter),
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MappingStart create token for MappingStart
|
||
|
func MappingStart(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: MappingStartType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: FlowCollectionIndicator,
|
||
|
Value: string(MappingStartCharacter),
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MappingEnd create token for MappingEnd
|
||
|
func MappingEnd(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: MappingEndType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: FlowCollectionIndicator,
|
||
|
Value: string(MappingEndCharacter),
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment create token for Comment
|
||
|
func Comment(value string, org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: CommentType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: CommentIndicator,
|
||
|
Value: value,
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Anchor create token for Anchor
|
||
|
func Anchor(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: AnchorType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: NodePropertyIndicator,
|
||
|
Value: string(AnchorCharacter),
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Alias create token for Alias
|
||
|
func Alias(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: AliasType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: NodePropertyIndicator,
|
||
|
Value: string(AliasCharacter),
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tag create token for Tag
|
||
|
func Tag(value string, org string, pos *Position) *Token {
|
||
|
fn := ReservedTagKeywordMap[ReservedTagKeyword(value)]
|
||
|
if fn != nil {
|
||
|
return fn(value, org, pos)
|
||
|
}
|
||
|
return &Token{
|
||
|
Type: TagType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: NodePropertyIndicator,
|
||
|
Value: value,
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Literal create token for Literal
|
||
|
func Literal(value string, org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: LiteralType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: BlockScalarIndicator,
|
||
|
Value: value,
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Folded create token for Folded
|
||
|
func Folded(value string, org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: FoldedType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: BlockScalarIndicator,
|
||
|
Value: value,
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SingleQuote create token for SingleQuote
|
||
|
func SingleQuote(value string, org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: SingleQuoteType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: QuotedScalarIndicator,
|
||
|
Value: value,
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DoubleQuote create token for DoubleQuote
|
||
|
func DoubleQuote(value string, org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: DoubleQuoteType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: QuotedScalarIndicator,
|
||
|
Value: value,
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Directive create token for Directive
|
||
|
func Directive(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: DirectiveType,
|
||
|
CharacterType: CharacterTypeIndicator,
|
||
|
Indicator: DirectiveIndicator,
|
||
|
Value: string(DirectiveCharacter),
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Space create token for Space
|
||
|
func Space(pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: SpaceType,
|
||
|
CharacterType: CharacterTypeWhiteSpace,
|
||
|
Indicator: NotIndicator,
|
||
|
Value: string(SpaceCharacter),
|
||
|
Origin: string(SpaceCharacter),
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MergeKey create token for MergeKey
|
||
|
func MergeKey(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: MergeKeyType,
|
||
|
CharacterType: CharacterTypeMiscellaneous,
|
||
|
Indicator: NotIndicator,
|
||
|
Value: "<<",
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DocumentHeader create token for DocumentHeader
|
||
|
func DocumentHeader(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: DocumentHeaderType,
|
||
|
CharacterType: CharacterTypeMiscellaneous,
|
||
|
Indicator: NotIndicator,
|
||
|
Value: "---",
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DocumentEnd create token for DocumentEnd
|
||
|
func DocumentEnd(org string, pos *Position) *Token {
|
||
|
return &Token{
|
||
|
Type: DocumentEndType,
|
||
|
CharacterType: CharacterTypeMiscellaneous,
|
||
|
Indicator: NotIndicator,
|
||
|
Value: "...",
|
||
|
Origin: org,
|
||
|
Position: pos,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DetectLineBreakCharacter detect line break character in only one inside scalar content scope.
|
||
|
func DetectLineBreakCharacter(src string) string {
|
||
|
nc := strings.Count(src, "\n")
|
||
|
rc := strings.Count(src, "\r")
|
||
|
rnc := strings.Count(src, "\r\n")
|
||
|
switch {
|
||
|
case nc == rnc && rc == rnc:
|
||
|
return "\r\n"
|
||
|
case rc > nc:
|
||
|
return "\r"
|
||
|
default:
|
||
|
return "\n"
|
||
|
}
|
||
|
}
|