github.com/go-enjin/golang-org-x-text@v0.12.1-enjin.2/internal/format/parser.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package format
     6  
     7  import (
     8  	"reflect"
     9  	"unicode/utf8"
    10  )
    11  
    12  // A Parser parses a format string. The result from the parse are set in the
    13  // struct fields.
    14  type Parser struct {
    15  	Verb rune
    16  
    17  	WidthPresent bool
    18  	PrecPresent  bool
    19  	Minus        bool
    20  	Plus         bool
    21  	Sharp        bool
    22  	Space        bool
    23  	Zero         bool
    24  
    25  	// For the formats %+v %#v, we set the plusV/sharpV flags
    26  	// and clear the plus/sharp flags since %+v and %#v are in effect
    27  	// different, flagless formats set at the top level.
    28  	PlusV  bool
    29  	SharpV bool
    30  
    31  	HasIndex bool
    32  
    33  	Width int
    34  	Prec  int // precision
    35  
    36  	// retain arguments across calls.
    37  	Args []interface{}
    38  	// retain current argument number across calls
    39  	ArgNum int
    40  
    41  	// reordered records whether the format string used argument reordering.
    42  	Reordered bool
    43  	// goodArgNum records whether the most recent reordering directive was valid.
    44  	goodArgNum bool
    45  
    46  	// position info
    47  	format   string
    48  	startPos int
    49  	endPos   int
    50  	Status   Status
    51  }
    52  
    53  // Reset initializes a parser to scan format strings for the given args.
    54  func (p *Parser) Reset(args []interface{}) {
    55  	p.Args = args
    56  	p.ArgNum = 0
    57  	p.startPos = 0
    58  	p.Reordered = false
    59  }
    60  
    61  // Text returns the part of the format string that was parsed by the last call
    62  // to Scan. It returns the original substitution clause if the current scan
    63  // parsed a substitution.
    64  func (p *Parser) Text() string { return p.format[p.startPos:p.endPos] }
    65  
    66  // SetFormat sets a new format string to parse. It does not reset the argument
    67  // count.
    68  func (p *Parser) SetFormat(format string) {
    69  	p.format = format
    70  	p.startPos = 0
    71  	p.endPos = 0
    72  }
    73  
    74  // Status indicates the result type of a call to Scan.
    75  type Status int
    76  
    77  const (
    78  	StatusText Status = iota
    79  	StatusSubstitution
    80  	StatusBadWidthSubstitution
    81  	StatusBadPrecSubstitution
    82  	StatusNoVerb
    83  	StatusBadArgNum
    84  	StatusMissingArg
    85  )
    86  
    87  // ClearFlags reset the parser to default behavior.
    88  func (p *Parser) ClearFlags() {
    89  	p.WidthPresent = false
    90  	p.PrecPresent = false
    91  	p.Minus = false
    92  	p.Plus = false
    93  	p.Sharp = false
    94  	p.Space = false
    95  	p.Zero = false
    96  
    97  	p.PlusV = false
    98  	p.SharpV = false
    99  
   100  	p.HasIndex = false
   101  }
   102  
   103  // Scan scans the next part of the format string and sets the status to
   104  // indicate whether it scanned a string literal, substitution or error.
   105  func (p *Parser) Scan() bool {
   106  	p.Status = StatusText
   107  	format := p.format
   108  	end := len(format)
   109  	if p.endPos >= end {
   110  		return false
   111  	}
   112  	afterIndex := false // previous item in format was an index like [3].
   113  
   114  	p.startPos = p.endPos
   115  	p.goodArgNum = true
   116  	i := p.startPos
   117  	for i < end && format[i] != '%' {
   118  		i++
   119  	}
   120  	if i > p.startPos {
   121  		p.endPos = i
   122  		return true
   123  	}
   124  	// Process one verb
   125  	i++
   126  
   127  	p.Status = StatusSubstitution
   128  
   129  	// Do we have flags?
   130  	p.ClearFlags()
   131  
   132  simpleFormat:
   133  	for ; i < end; i++ {
   134  		c := p.format[i]
   135  		switch c {
   136  		case '#':
   137  			p.Sharp = true
   138  		case '0':
   139  			p.Zero = !p.Minus // Only allow zero padding to the left.
   140  		case '+':
   141  			p.Plus = true
   142  		case '-':
   143  			p.Minus = true
   144  			p.Zero = false // Do not pad with zeros to the right.
   145  		case ' ':
   146  			p.Space = true
   147  		default:
   148  			// Fast path for common case of ascii lower case simple verbs
   149  			// without precision or width or argument indices.
   150  			if 'a' <= c && c <= 'z' && p.ArgNum < len(p.Args) {
   151  				if c == 'v' {
   152  					// Go syntax
   153  					p.SharpV = p.Sharp
   154  					p.Sharp = false
   155  					// Struct-field syntax
   156  					p.PlusV = p.Plus
   157  					p.Plus = false
   158  				}
   159  				p.Verb = rune(c)
   160  				p.ArgNum++
   161  				p.endPos = i + 1
   162  				return true
   163  			}
   164  			// Format is more complex than simple flags and a verb or is malformed.
   165  			break simpleFormat
   166  		}
   167  	}
   168  
   169  	// Do we have an explicit argument index?
   170  	i, afterIndex = p.updateArgNumber(format, i)
   171  
   172  	// Do we have width?
   173  	if i < end && format[i] == '*' {
   174  		i++
   175  		p.Width, p.WidthPresent = p.intFromArg()
   176  
   177  		if !p.WidthPresent {
   178  			p.Status = StatusBadWidthSubstitution
   179  		}
   180  
   181  		// We have a negative width, so take its value and ensure
   182  		// that the minus flag is set
   183  		if p.Width < 0 {
   184  			p.Width = -p.Width
   185  			p.Minus = true
   186  			p.Zero = false // Do not pad with zeros to the right.
   187  		}
   188  		afterIndex = false
   189  	} else {
   190  		p.Width, p.WidthPresent, i = parsenum(format, i, end)
   191  		if afterIndex && p.WidthPresent { // "%[3]2d"
   192  			p.goodArgNum = false
   193  		}
   194  	}
   195  
   196  	// Do we have precision?
   197  	if i+1 < end && format[i] == '.' {
   198  		i++
   199  		if afterIndex { // "%[3].2d"
   200  			p.goodArgNum = false
   201  		}
   202  		i, afterIndex = p.updateArgNumber(format, i)
   203  		if i < end && format[i] == '*' {
   204  			i++
   205  			p.Prec, p.PrecPresent = p.intFromArg()
   206  			// Negative precision arguments don't make sense
   207  			if p.Prec < 0 {
   208  				p.Prec = 0
   209  				p.PrecPresent = false
   210  			}
   211  			if !p.PrecPresent {
   212  				p.Status = StatusBadPrecSubstitution
   213  			}
   214  			afterIndex = false
   215  		} else {
   216  			p.Prec, p.PrecPresent, i = parsenum(format, i, end)
   217  			if !p.PrecPresent {
   218  				p.Prec = 0
   219  				p.PrecPresent = true
   220  			}
   221  		}
   222  	}
   223  
   224  	if !afterIndex {
   225  		i, afterIndex = p.updateArgNumber(format, i)
   226  	}
   227  	p.HasIndex = afterIndex
   228  
   229  	if i >= end {
   230  		p.endPos = i
   231  		p.Status = StatusNoVerb
   232  		return true
   233  	}
   234  
   235  	verb, w := utf8.DecodeRuneInString(format[i:])
   236  	p.endPos = i + w
   237  	p.Verb = verb
   238  
   239  	switch {
   240  	case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec.
   241  		p.startPos = p.endPos - 1
   242  		p.Status = StatusText
   243  	case !p.goodArgNum:
   244  		p.Status = StatusBadArgNum
   245  	case p.ArgNum >= len(p.Args): // No argument left over to print for the current verb.
   246  		p.Status = StatusMissingArg
   247  		p.ArgNum++
   248  	case verb == 'v':
   249  		// Go syntax
   250  		p.SharpV = p.Sharp
   251  		p.Sharp = false
   252  		// Struct-field syntax
   253  		p.PlusV = p.Plus
   254  		p.Plus = false
   255  		fallthrough
   256  	default:
   257  		p.ArgNum++
   258  	}
   259  	return true
   260  }
   261  
   262  // intFromArg gets the ArgNumth element of Args. On return, isInt reports
   263  // whether the argument has integer type.
   264  func (p *Parser) intFromArg() (num int, isInt bool) {
   265  	if p.ArgNum < len(p.Args) {
   266  		arg := p.Args[p.ArgNum]
   267  		num, isInt = arg.(int) // Almost always OK.
   268  		if !isInt {
   269  			// Work harder.
   270  			switch v := reflect.ValueOf(arg); v.Kind() {
   271  			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   272  				n := v.Int()
   273  				if int64(int(n)) == n {
   274  					num = int(n)
   275  					isInt = true
   276  				}
   277  			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   278  				n := v.Uint()
   279  				if int64(n) >= 0 && uint64(int(n)) == n {
   280  					num = int(n)
   281  					isInt = true
   282  				}
   283  			default:
   284  				// Already 0, false.
   285  			}
   286  		}
   287  		p.ArgNum++
   288  		if tooLarge(num) {
   289  			num = 0
   290  			isInt = false
   291  		}
   292  	}
   293  	return
   294  }
   295  
   296  // parseArgNumber returns the value of the bracketed number, minus 1
   297  // (explicit argument numbers are one-indexed but we want zero-indexed).
   298  // The opening bracket is known to be present at format[0].
   299  // The returned values are the index, the number of bytes to consume
   300  // up to the closing paren, if present, and whether the number parsed
   301  // ok. The bytes to consume will be 1 if no closing paren is present.
   302  func parseArgNumber(format string) (index int, wid int, ok bool) {
   303  	// There must be at least 3 bytes: [n].
   304  	if len(format) < 3 {
   305  		return 0, 1, false
   306  	}
   307  
   308  	// Find closing bracket.
   309  	for i := 1; i < len(format); i++ {
   310  		if format[i] == ']' {
   311  			width, ok, newi := parsenum(format, 1, i)
   312  			if !ok || newi != i {
   313  				return 0, i + 1, false
   314  			}
   315  			return width - 1, i + 1, true // arg numbers are one-indexed and skip paren.
   316  		}
   317  	}
   318  	return 0, 1, false
   319  }
   320  
   321  // updateArgNumber returns the next argument to evaluate, which is either the value of the passed-in
   322  // argNum or the value of the bracketed integer that begins format[i:]. It also returns
   323  // the new value of i, that is, the index of the next byte of the format to process.
   324  func (p *Parser) updateArgNumber(format string, i int) (newi int, found bool) {
   325  	if len(format) <= i || format[i] != '[' {
   326  		return i, false
   327  	}
   328  	p.Reordered = true
   329  	index, wid, ok := parseArgNumber(format[i:])
   330  	if ok && 0 <= index && index < len(p.Args) {
   331  		p.ArgNum = index
   332  		return i + wid, true
   333  	}
   334  	p.goodArgNum = false
   335  	return i + wid, ok
   336  }
   337  
   338  // tooLarge reports whether the magnitude of the integer is
   339  // too large to be used as a formatting width or precision.
   340  func tooLarge(x int) bool {
   341  	const max int = 1e6
   342  	return x > max || x < -max
   343  }
   344  
   345  // parsenum converts ASCII to integer.  num is 0 (and isnum is false) if no number present.
   346  func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
   347  	if start >= end {
   348  		return 0, false, end
   349  	}
   350  	for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ {
   351  		if tooLarge(num) {
   352  			return 0, false, end // Overflow; crazy long number most likely.
   353  		}
   354  		num = num*10 + int(s[newi]-'0')
   355  		isnum = true
   356  	}
   357  	return
   358  }