...

Source file src/github.com/magiconair/properties/lex.go

Documentation: github.com/magiconair/properties

     1  // Copyright 2013-2022 Frank Schroeder. 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  // Parts of the lexer are from the template/text/parser package
     6  // For these parts the following applies:
     7  //
     8  // Copyright 2011 The Go Authors. All rights reserved.
     9  // Use of this source code is governed by a BSD-style
    10  // license that can be found in the LICENSE file of the go 1.2
    11  // distribution.
    12  
    13  package properties
    14  
    15  import (
    16  	"fmt"
    17  	"strconv"
    18  	"strings"
    19  	"unicode/utf8"
    20  )
    21  
    22  // item represents a token or text string returned from the scanner.
    23  type item struct {
    24  	typ itemType // The type of this item.
    25  	pos int      // The starting position, in bytes, of this item in the input string.
    26  	val string   // The value of this item.
    27  }
    28  
    29  func (i item) String() string {
    30  	switch {
    31  	case i.typ == itemEOF:
    32  		return "EOF"
    33  	case i.typ == itemError:
    34  		return i.val
    35  	case len(i.val) > 10:
    36  		return fmt.Sprintf("%.10q...", i.val)
    37  	}
    38  	return fmt.Sprintf("%q", i.val)
    39  }
    40  
    41  // itemType identifies the type of lex items.
    42  type itemType int
    43  
    44  const (
    45  	itemError itemType = iota // error occurred; value is text of error
    46  	itemEOF
    47  	itemKey     // a key
    48  	itemValue   // a value
    49  	itemComment // a comment
    50  )
    51  
    52  // defines a constant for EOF
    53  const eof = -1
    54  
    55  // permitted whitespace characters space, FF and TAB
    56  const whitespace = " \f\t"
    57  
    58  // stateFn represents the state of the scanner as a function that returns the next state.
    59  type stateFn func(*lexer) stateFn
    60  
    61  // lexer holds the state of the scanner.
    62  type lexer struct {
    63  	input   string    // the string being scanned
    64  	state   stateFn   // the next lexing function to enter
    65  	pos     int       // current position in the input
    66  	start   int       // start position of this item
    67  	width   int       // width of last rune read from input
    68  	lastPos int       // position of most recent item returned by nextItem
    69  	runes   []rune    // scanned runes for this item
    70  	items   chan item // channel of scanned items
    71  }
    72  
    73  // next returns the next rune in the input.
    74  func (l *lexer) next() rune {
    75  	if l.pos >= len(l.input) {
    76  		l.width = 0
    77  		return eof
    78  	}
    79  	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
    80  	l.width = w
    81  	l.pos += l.width
    82  	return r
    83  }
    84  
    85  // peek returns but does not consume the next rune in the input.
    86  func (l *lexer) peek() rune {
    87  	r := l.next()
    88  	l.backup()
    89  	return r
    90  }
    91  
    92  // backup steps back one rune. Can only be called once per call of next.
    93  func (l *lexer) backup() {
    94  	l.pos -= l.width
    95  }
    96  
    97  // emit passes an item back to the client.
    98  func (l *lexer) emit(t itemType) {
    99  	i := item{t, l.start, string(l.runes)}
   100  	l.items <- i
   101  	l.start = l.pos
   102  	l.runes = l.runes[:0]
   103  }
   104  
   105  // ignore skips over the pending input before this point.
   106  func (l *lexer) ignore() {
   107  	l.start = l.pos
   108  }
   109  
   110  // appends the rune to the current value
   111  func (l *lexer) appendRune(r rune) {
   112  	l.runes = append(l.runes, r)
   113  }
   114  
   115  // accept consumes the next rune if it's from the valid set.
   116  func (l *lexer) accept(valid string) bool {
   117  	if strings.ContainsRune(valid, l.next()) {
   118  		return true
   119  	}
   120  	l.backup()
   121  	return false
   122  }
   123  
   124  // acceptRun consumes a run of runes from the valid set.
   125  func (l *lexer) acceptRun(valid string) {
   126  	for strings.ContainsRune(valid, l.next()) {
   127  	}
   128  	l.backup()
   129  }
   130  
   131  // lineNumber reports which line we're on, based on the position of
   132  // the previous item returned by nextItem. Doing it this way
   133  // means we don't have to worry about peek double counting.
   134  func (l *lexer) lineNumber() int {
   135  	return 1 + strings.Count(l.input[:l.lastPos], "\n")
   136  }
   137  
   138  // errorf returns an error token and terminates the scan by passing
   139  // back a nil pointer that will be the next state, terminating l.nextItem.
   140  func (l *lexer) errorf(format string, args ...interface{}) stateFn {
   141  	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
   142  	return nil
   143  }
   144  
   145  // nextItem returns the next item from the input.
   146  func (l *lexer) nextItem() item {
   147  	i := <-l.items
   148  	l.lastPos = i.pos
   149  	return i
   150  }
   151  
   152  // lex creates a new scanner for the input string.
   153  func lex(input string) *lexer {
   154  	l := &lexer{
   155  		input: input,
   156  		items: make(chan item),
   157  		runes: make([]rune, 0, 32),
   158  	}
   159  	go l.run()
   160  	return l
   161  }
   162  
   163  // run runs the state machine for the lexer.
   164  func (l *lexer) run() {
   165  	for l.state = lexBeforeKey(l); l.state != nil; {
   166  		l.state = l.state(l)
   167  	}
   168  }
   169  
   170  // state functions
   171  
   172  // lexBeforeKey scans until a key begins.
   173  func lexBeforeKey(l *lexer) stateFn {
   174  	switch r := l.next(); {
   175  	case isEOF(r):
   176  		l.emit(itemEOF)
   177  		return nil
   178  
   179  	case isEOL(r):
   180  		l.ignore()
   181  		return lexBeforeKey
   182  
   183  	case isComment(r):
   184  		return lexComment
   185  
   186  	case isWhitespace(r):
   187  		l.ignore()
   188  		return lexBeforeKey
   189  
   190  	default:
   191  		l.backup()
   192  		return lexKey
   193  	}
   194  }
   195  
   196  // lexComment scans a comment line. The comment character has already been scanned.
   197  func lexComment(l *lexer) stateFn {
   198  	l.acceptRun(whitespace)
   199  	l.ignore()
   200  	for {
   201  		switch r := l.next(); {
   202  		case isEOF(r):
   203  			l.ignore()
   204  			l.emit(itemEOF)
   205  			return nil
   206  		case isEOL(r):
   207  			l.emit(itemComment)
   208  			return lexBeforeKey
   209  		default:
   210  			l.appendRune(r)
   211  		}
   212  	}
   213  }
   214  
   215  // lexKey scans the key up to a delimiter
   216  func lexKey(l *lexer) stateFn {
   217  	var r rune
   218  
   219  Loop:
   220  	for {
   221  		switch r = l.next(); {
   222  
   223  		case isEscape(r):
   224  			err := l.scanEscapeSequence()
   225  			if err != nil {
   226  				return l.errorf(err.Error())
   227  			}
   228  
   229  		case isEndOfKey(r):
   230  			l.backup()
   231  			break Loop
   232  
   233  		case isEOF(r):
   234  			break Loop
   235  
   236  		default:
   237  			l.appendRune(r)
   238  		}
   239  	}
   240  
   241  	if len(l.runes) > 0 {
   242  		l.emit(itemKey)
   243  	}
   244  
   245  	if isEOF(r) {
   246  		l.emit(itemEOF)
   247  		return nil
   248  	}
   249  
   250  	return lexBeforeValue
   251  }
   252  
   253  // lexBeforeValue scans the delimiter between key and value.
   254  // Leading and trailing whitespace is ignored.
   255  // We expect to be just after the key.
   256  func lexBeforeValue(l *lexer) stateFn {
   257  	l.acceptRun(whitespace)
   258  	l.accept(":=")
   259  	l.acceptRun(whitespace)
   260  	l.ignore()
   261  	return lexValue
   262  }
   263  
   264  // lexValue scans text until the end of the line. We expect to be just after the delimiter.
   265  func lexValue(l *lexer) stateFn {
   266  	for {
   267  		switch r := l.next(); {
   268  		case isEscape(r):
   269  			if isEOL(l.peek()) {
   270  				l.next()
   271  				l.acceptRun(whitespace)
   272  			} else {
   273  				err := l.scanEscapeSequence()
   274  				if err != nil {
   275  					return l.errorf(err.Error())
   276  				}
   277  			}
   278  
   279  		case isEOL(r):
   280  			l.emit(itemValue)
   281  			l.ignore()
   282  			return lexBeforeKey
   283  
   284  		case isEOF(r):
   285  			l.emit(itemValue)
   286  			l.emit(itemEOF)
   287  			return nil
   288  
   289  		default:
   290  			l.appendRune(r)
   291  		}
   292  	}
   293  }
   294  
   295  // scanEscapeSequence scans either one of the escaped characters
   296  // or a unicode literal. We expect to be after the escape character.
   297  func (l *lexer) scanEscapeSequence() error {
   298  	switch r := l.next(); {
   299  
   300  	case isEscapedCharacter(r):
   301  		l.appendRune(decodeEscapedCharacter(r))
   302  		return nil
   303  
   304  	case atUnicodeLiteral(r):
   305  		return l.scanUnicodeLiteral()
   306  
   307  	case isEOF(r):
   308  		return fmt.Errorf("premature EOF")
   309  
   310  	// silently drop the escape character and append the rune as is
   311  	default:
   312  		l.appendRune(r)
   313  		return nil
   314  	}
   315  }
   316  
   317  // scans a unicode literal in the form \uXXXX. We expect to be after the \u.
   318  func (l *lexer) scanUnicodeLiteral() error {
   319  	// scan the digits
   320  	d := make([]rune, 4)
   321  	for i := 0; i < 4; i++ {
   322  		d[i] = l.next()
   323  		if d[i] == eof || !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) {
   324  			return fmt.Errorf("invalid unicode literal")
   325  		}
   326  	}
   327  
   328  	// decode the digits into a rune
   329  	r, err := strconv.ParseInt(string(d), 16, 0)
   330  	if err != nil {
   331  		return err
   332  	}
   333  
   334  	l.appendRune(rune(r))
   335  	return nil
   336  }
   337  
   338  // decodeEscapedCharacter returns the unescaped rune. We expect to be after the escape character.
   339  func decodeEscapedCharacter(r rune) rune {
   340  	switch r {
   341  	case 'f':
   342  		return '\f'
   343  	case 'n':
   344  		return '\n'
   345  	case 'r':
   346  		return '\r'
   347  	case 't':
   348  		return '\t'
   349  	default:
   350  		return r
   351  	}
   352  }
   353  
   354  // atUnicodeLiteral reports whether we are at a unicode literal.
   355  // The escape character has already been consumed.
   356  func atUnicodeLiteral(r rune) bool {
   357  	return r == 'u'
   358  }
   359  
   360  // isComment reports whether we are at the start of a comment.
   361  func isComment(r rune) bool {
   362  	return r == '#' || r == '!'
   363  }
   364  
   365  // isEndOfKey reports whether the rune terminates the current key.
   366  func isEndOfKey(r rune) bool {
   367  	return strings.ContainsRune(" \f\t\r\n:=", r)
   368  }
   369  
   370  // isEOF reports whether we are at EOF.
   371  func isEOF(r rune) bool {
   372  	return r == eof
   373  }
   374  
   375  // isEOL reports whether we are at a new line character.
   376  func isEOL(r rune) bool {
   377  	return r == '\n' || r == '\r'
   378  }
   379  
   380  // isEscape reports whether the rune is the escape character which
   381  // prefixes unicode literals and other escaped characters.
   382  func isEscape(r rune) bool {
   383  	return r == '\\'
   384  }
   385  
   386  // isEscapedCharacter reports whether we are at one of the characters that need escaping.
   387  // The escape character has already been consumed.
   388  func isEscapedCharacter(r rune) bool {
   389  	return strings.ContainsRune(" :=fnrt", r)
   390  }
   391  
   392  // isWhitespace reports whether the rune is a whitespace character.
   393  func isWhitespace(r rune) bool {
   394  	return strings.ContainsRune(whitespace, r)
   395  }
   396  

View as plain text