| // Copyright 2017 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package format | |
| import ( | |
| "reflect" | |
| "unicode/utf8" | |
| ) | |
| // A Parser parses a format string. The result from the parse are set in the | |
| // struct fields. | |
| type Parser struct { | |
| Verb rune | |
| WidthPresent bool | |
| PrecPresent bool | |
| Minus bool | |
| Plus bool | |
| Sharp bool | |
| Space bool | |
| Zero bool | |
| // For the formats %+v %#v, we set the plusV/sharpV flags | |
| // and clear the plus/sharp flags since %+v and %#v are in effect | |
| // different, flagless formats set at the top level. | |
| PlusV bool | |
| SharpV bool | |
| HasIndex bool | |
| Width int | |
| Prec int // precision | |
| // retain arguments across calls. | |
| Args []interface{} | |
| // retain current argument number across calls | |
| ArgNum int | |
| // reordered records whether the format string used argument reordering. | |
| Reordered bool | |
| // goodArgNum records whether the most recent reordering directive was valid. | |
| goodArgNum bool | |
| // position info | |
| format string | |
| startPos int | |
| endPos int | |
| Status Status | |
| } | |
| // Reset initializes a parser to scan format strings for the given args. | |
| func (p *Parser) Reset(args []interface{}) { | |
| p.Args = args | |
| p.ArgNum = 0 | |
| p.startPos = 0 | |
| p.Reordered = false | |
| } | |
| // Text returns the part of the format string that was parsed by the last call | |
| // to Scan. It returns the original substitution clause if the current scan | |
| // parsed a substitution. | |
| func (p *Parser) Text() string { return p.format[p.startPos:p.endPos] } | |
| // SetFormat sets a new format string to parse. It does not reset the argument | |
| // count. | |
| func (p *Parser) SetFormat(format string) { | |
| p.format = format | |
| p.startPos = 0 | |
| p.endPos = 0 | |
| } | |
| // Status indicates the result type of a call to Scan. | |
| type Status int | |
| const ( | |
| StatusText Status = iota | |
| StatusSubstitution | |
| StatusBadWidthSubstitution | |
| StatusBadPrecSubstitution | |
| StatusNoVerb | |
| StatusBadArgNum | |
| StatusMissingArg | |
| ) | |
| // ClearFlags reset the parser to default behavior. | |
| 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 | |
| } | |
| // Scan scans the next part of the format string and sets the status to | |
| // indicate whether it scanned a string literal, substitution or error. | |
| func (p *Parser) Scan() bool { | |
| p.Status = StatusText | |
| format := p.format | |
| end := len(format) | |
| if p.endPos >= end { | |
| return false | |
| } | |
| afterIndex := false // previous item in format was an index like [3]. | |
| 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 | |
| } | |
| // Process one verb | |
| i++ | |
| p.Status = StatusSubstitution | |
| // Do we have flags? | |
| p.ClearFlags() | |
| simpleFormat: | |
| for ; i < end; i++ { | |
| c := p.format[i] | |
| switch c { | |
| case '#': | |
| p.Sharp = true | |
| case '0': | |
| p.Zero = !p.Minus // Only allow zero padding to the left. | |
| case '+': | |
| p.Plus = true | |
| case '-': | |
| p.Minus = true | |
| p.Zero = false // Do not pad with zeros to the right. | |
| case ' ': | |
| p.Space = true | |
| default: | |
| // Fast path for common case of ascii lower case simple verbs | |
| // without precision or width or argument indices. | |
| if 'a' <= c && c <= 'z' && p.ArgNum < len(p.Args) { | |
| if c == 'v' { | |
| // Go syntax | |
| p.SharpV = p.Sharp | |
| p.Sharp = false | |
| // Struct-field syntax | |
| p.PlusV = p.Plus | |
| p.Plus = false | |
| } | |
| p.Verb = rune(c) | |
| p.ArgNum++ | |
| p.endPos = i + 1 | |
| return true | |
| } | |
| // Format is more complex than simple flags and a verb or is malformed. | |
| break simpleFormat | |
| } | |
| } | |
| // Do we have an explicit argument index? | |
| i, afterIndex = p.updateArgNumber(format, i) | |
| // Do we have width? | |
| if i < end && format[i] == '*' { | |
| i++ | |
| p.Width, p.WidthPresent = p.intFromArg() | |
| if !p.WidthPresent { | |
| p.Status = StatusBadWidthSubstitution | |
| } | |
| // We have a negative width, so take its value and ensure | |
| // that the minus flag is set | |
| if p.Width < 0 { | |
| p.Width = -p.Width | |
| p.Minus = true | |
| p.Zero = false // Do not pad with zeros to the right. | |
| } | |
| afterIndex = false | |
| } else { | |
| p.Width, p.WidthPresent, i = parsenum(format, i, end) | |
| if afterIndex && p.WidthPresent { // "%[3]2d" | |
| p.goodArgNum = false | |
| } | |
| } | |
| // Do we have precision? | |
| if i+1 < end && format[i] == '.' { | |
| i++ | |
| if afterIndex { // "%[3].2d" | |
| p.goodArgNum = false | |
| } | |
| i, afterIndex = p.updateArgNumber(format, i) | |
| if i < end && format[i] == '*' { | |
| i++ | |
| p.Prec, p.PrecPresent = p.intFromArg() | |
| // Negative precision arguments don't make sense | |
| 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 == '%': // Percent does not absorb operands and ignores f.wid and f.prec. | |
| p.startPos = p.endPos - 1 | |
| p.Status = StatusText | |
| case !p.goodArgNum: | |
| p.Status = StatusBadArgNum | |
| case p.ArgNum >= len(p.Args): // No argument left over to print for the current verb. | |
| p.Status = StatusMissingArg | |
| p.ArgNum++ | |
| case verb == 'v': | |
| // Go syntax | |
| p.SharpV = p.Sharp | |
| p.Sharp = false | |
| // Struct-field syntax | |
| p.PlusV = p.Plus | |
| p.Plus = false | |
| fallthrough | |
| default: | |
| p.ArgNum++ | |
| } | |
| return true | |
| } | |
| // intFromArg gets the ArgNumth element of Args. On return, isInt reports | |
| // whether the argument has integer type. | |
| func (p *Parser) intFromArg() (num int, isInt bool) { | |
| if p.ArgNum < len(p.Args) { | |
| arg := p.Args[p.ArgNum] | |
| num, isInt = arg.(int) // Almost always OK. | |
| if !isInt { | |
| // Work harder. | |
| 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: | |
| // Already 0, false. | |
| } | |
| } | |
| p.ArgNum++ | |
| if tooLarge(num) { | |
| num = 0 | |
| isInt = false | |
| } | |
| } | |
| return | |
| } | |
| // parseArgNumber returns the value of the bracketed number, minus 1 | |
| // (explicit argument numbers are one-indexed but we want zero-indexed). | |
| // The opening bracket is known to be present at format[0]. | |
| // The returned values are the index, the number of bytes to consume | |
| // up to the closing paren, if present, and whether the number parsed | |
| // ok. The bytes to consume will be 1 if no closing paren is present. | |
| func parseArgNumber(format string) (index int, wid int, ok bool) { | |
| // There must be at least 3 bytes: [n]. | |
| if len(format) < 3 { | |
| return 0, 1, false | |
| } | |
| // Find closing bracket. | |
| 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 // arg numbers are one-indexed and skip paren. | |
| } | |
| } | |
| return 0, 1, false | |
| } | |
| // updateArgNumber returns the next argument to evaluate, which is either the value of the passed-in | |
| // argNum or the value of the bracketed integer that begins format[i:]. It also returns | |
| // the new value of i, that is, the index of the next byte of the format to process. | |
| 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 | |
| } | |
| // tooLarge reports whether the magnitude of the integer is | |
| // too large to be used as a formatting width or precision. | |
| func tooLarge(x int) bool { | |
| const max int = 1e6 | |
| return x > max || x < -max | |
| } | |
| // parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present. | |
| 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 // Overflow; crazy long number most likely. | |
| } | |
| num = num*10 + int(s[newi]-'0') | |
| isnum = true | |
| } | |
| return | |
| } |