package format
import (
"reflect"
"unicode/utf8"
)
type Parser struct {
Verb rune
WidthPresent bool
PrecPresent bool
Minus bool
Plus bool
Sharp bool
Space bool
Zero bool
PlusV bool
SharpV bool
HasIndex bool
Width int
Prec int
Args []interface {}
ArgNum int
Reordered bool
goodArgNum bool
format string
startPos int
endPos int
Status Status
}
func (p *Parser ) Reset (args []interface {}) {
p .Args = args
p .ArgNum = 0
p .startPos = 0
p .Reordered = false
}
func (p *Parser ) Text () string { return p .format [p .startPos :p .endPos ] }
func (p *Parser ) SetFormat (format string ) {
p .format = format
p .startPos = 0
p .endPos = 0
}
type Status int
const (
StatusText Status = iota
StatusSubstitution
StatusBadWidthSubstitution
StatusBadPrecSubstitution
StatusNoVerb
StatusBadArgNum
StatusMissingArg
)
func (p *Parser ) ClearFlags () {
p .WidthPresent = false
p .PrecPresent = false
p .Minus = false
p .Plus = false
p .Sharp = false
p .Space = false
p .Zero = false
p .PlusV = false
p .SharpV = false
p .HasIndex = false
}
func (p *Parser ) Scan () bool {
p .Status = StatusText
format := p .format
end := len (format )
if p .endPos >= end {
return false
}
afterIndex := false
p .startPos = p .endPos
p .goodArgNum = true
i := p .startPos
for i < end && format [i ] != '%' {
i ++
}
if i > p .startPos {
p .endPos = i
return true
}
i ++
p .Status = StatusSubstitution
p .ClearFlags ()
simpleFormat :
for ; i < end ; i ++ {
c := p .format [i ]
switch c {
case '#' :
p .Sharp = true
case '0' :
p .Zero = !p .Minus
case '+' :
p .Plus = true
case '-' :
p .Minus = true
p .Zero = false
case ' ' :
p .Space = true
default :
if 'a' <= c && c <= 'z' && p .ArgNum < len (p .Args ) {
if c == 'v' {
p .SharpV = p .Sharp
p .Sharp = false
p .PlusV = p .Plus
p .Plus = false
}
p .Verb = rune (c )
p .ArgNum ++
p .endPos = i + 1
return true
}
break simpleFormat
}
}
i , afterIndex = p .updateArgNumber (format , i )
if i < end && format [i ] == '*' {
i ++
p .Width , p .WidthPresent = p .intFromArg ()
if !p .WidthPresent {
p .Status = StatusBadWidthSubstitution
}
if p .Width < 0 {
p .Width = -p .Width
p .Minus = true
p .Zero = false
}
afterIndex = false
} else {
p .Width , p .WidthPresent , i = parsenum (format , i , end )
if afterIndex && p .WidthPresent {
p .goodArgNum = false
}
}
if i +1 < end && format [i ] == '.' {
i ++
if afterIndex {
p .goodArgNum = false
}
i , afterIndex = p .updateArgNumber (format , i )
if i < end && format [i ] == '*' {
i ++
p .Prec , p .PrecPresent = p .intFromArg ()
if p .Prec < 0 {
p .Prec = 0
p .PrecPresent = false
}
if !p .PrecPresent {
p .Status = StatusBadPrecSubstitution
}
afterIndex = false
} else {
p .Prec , p .PrecPresent , i = parsenum (format , i , end )
if !p .PrecPresent {
p .Prec = 0
p .PrecPresent = true
}
}
}
if !afterIndex {
i , afterIndex = p .updateArgNumber (format , i )
}
p .HasIndex = afterIndex
if i >= end {
p .endPos = i
p .Status = StatusNoVerb
return true
}
verb , w := utf8 .DecodeRuneInString (format [i :])
p .endPos = i + w
p .Verb = verb
switch {
case verb == '%' :
p .startPos = p .endPos - 1
p .Status = StatusText
case !p .goodArgNum :
p .Status = StatusBadArgNum
case p .ArgNum >= len (p .Args ):
p .Status = StatusMissingArg
p .ArgNum ++
case verb == 'v' :
p .SharpV = p .Sharp
p .Sharp = false
p .PlusV = p .Plus
p .Plus = false
fallthrough
default :
p .ArgNum ++
}
return true
}
func (p *Parser ) intFromArg () (num int , isInt bool ) {
if p .ArgNum < len (p .Args ) {
arg := p .Args [p .ArgNum ]
num , isInt = arg .(int )
if !isInt {
switch v := reflect .ValueOf (arg ); v .Kind () {
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
n := v .Int ()
if int64 (int (n )) == n {
num = int (n )
isInt = true
}
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
n := v .Uint ()
if int64 (n ) >= 0 && uint64 (int (n )) == n {
num = int (n )
isInt = true
}
default :
}
}
p .ArgNum ++
if tooLarge (num ) {
num = 0
isInt = false
}
}
return
}
func parseArgNumber(format string ) (index int , wid int , ok bool ) {
if len (format ) < 3 {
return 0 , 1 , false
}
for i := 1 ; i < len (format ); i ++ {
if format [i ] == ']' {
width , ok , newi := parsenum (format , 1 , i )
if !ok || newi != i {
return 0 , i + 1 , false
}
return width - 1 , i + 1 , true
}
}
return 0 , 1 , false
}
func (p *Parser ) updateArgNumber (format string , i int ) (newi int , found bool ) {
if len (format ) <= i || format [i ] != '[' {
return i , false
}
p .Reordered = true
index , wid , ok := parseArgNumber (format [i :])
if ok && 0 <= index && index < len (p .Args ) {
p .ArgNum = index
return i + wid , true
}
p .goodArgNum = false
return i + wid , ok
}
func tooLarge(x int ) bool {
const max int = 1e6
return x > max || x < -max
}
func parsenum(s string , start , end int ) (num int , isnum bool , newi int ) {
if start >= end {
return 0 , false , end
}
for newi = start ; newi < end && '0' <= s [newi ] && s [newi ] <= '9' ; newi ++ {
if tooLarge (num ) {
return 0 , false , end
}
num = num *10 + int (s [newi ]-'0' )
isnum = true
}
return
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .