...

Source file src/text/template/parse/lex.go

Documentation: text/template/parse

		 1  // Copyright 2011 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 parse
		 6  
		 7  import (
		 8  	"fmt"
		 9  	"strings"
		10  	"unicode"
		11  	"unicode/utf8"
		12  )
		13  
		14  // item represents a token or text string returned from the scanner.
		15  type item struct {
		16  	typ	itemType // The type of this item.
		17  	pos	Pos			// The starting position, in bytes, of this item in the input string.
		18  	val	string	 // The value of this item.
		19  	line int			// The line number at the start of this item.
		20  }
		21  
		22  func (i item) String() string {
		23  	switch {
		24  	case i.typ == itemEOF:
		25  		return "EOF"
		26  	case i.typ == itemError:
		27  		return i.val
		28  	case i.typ > itemKeyword:
		29  		return fmt.Sprintf("<%s>", i.val)
		30  	case len(i.val) > 10:
		31  		return fmt.Sprintf("%.10q...", i.val)
		32  	}
		33  	return fmt.Sprintf("%q", i.val)
		34  }
		35  
		36  // itemType identifies the type of lex items.
		37  type itemType int
		38  
		39  const (
		40  	itemError				itemType = iota // error occurred; value is text of error
		41  	itemBool												 // boolean constant
		42  	itemChar												 // printable ASCII character; grab bag for comma etc.
		43  	itemCharConstant								 // character constant
		44  	itemComment											// comment text
		45  	itemComplex											// complex constant (1+2i); imaginary is just a number
		46  	itemAssign											 // equals ('=') introducing an assignment
		47  	itemDeclare											// colon-equals (':=') introducing a declaration
		48  	itemEOF
		49  	itemField			// alphanumeric identifier starting with '.'
		50  	itemIdentifier // alphanumeric identifier not starting with '.'
		51  	itemLeftDelim	// left action delimiter
		52  	itemLeftParen	// '(' inside action
		53  	itemNumber		 // simple number, including imaginary
		54  	itemPipe			 // pipe symbol
		55  	itemRawString	// raw quoted string (includes quotes)
		56  	itemRightDelim // right action delimiter
		57  	itemRightParen // ')' inside action
		58  	itemSpace			// run of spaces separating arguments
		59  	itemString		 // quoted string (includes quotes)
		60  	itemText			 // plain text
		61  	itemVariable	 // variable starting with '$', such as '$' or	'$1' or '$hello'
		62  	// Keywords appear after all the rest.
		63  	itemKeyword	// used only to delimit the keywords
		64  	itemBlock		// block keyword
		65  	itemDot			// the cursor, spelled '.'
		66  	itemDefine	 // define keyword
		67  	itemElse		 // else keyword
		68  	itemEnd			// end keyword
		69  	itemIf			 // if keyword
		70  	itemNil			// the untyped nil constant, easiest to treat as a keyword
		71  	itemRange		// range keyword
		72  	itemTemplate // template keyword
		73  	itemWith		 // with keyword
		74  )
		75  
		76  var key = map[string]itemType{
		77  	".":				itemDot,
		78  	"block":		itemBlock,
		79  	"define":	 itemDefine,
		80  	"else":		 itemElse,
		81  	"end":			itemEnd,
		82  	"if":			 itemIf,
		83  	"range":		itemRange,
		84  	"nil":			itemNil,
		85  	"template": itemTemplate,
		86  	"with":		 itemWith,
		87  }
		88  
		89  const eof = -1
		90  
		91  // Trimming spaces.
		92  // If the action begins "{{- " rather than "{{", then all space/tab/newlines
		93  // preceding the action are trimmed; conversely if it ends " -}}" the
		94  // leading spaces are trimmed. This is done entirely in the lexer; the
		95  // parser never sees it happen. We require an ASCII space (' ', \t, \r, \n)
		96  // to be present to avoid ambiguity with things like "{{-3}}". It reads
		97  // better with the space present anyway. For simplicity, only ASCII
		98  // does the job.
		99  const (
	 100  	spaceChars		= " \t\r\n"	// These are the space characters defined by Go itself.
	 101  	trimMarker		= '-'				// Attached to left/right delimiter, trims trailing spaces from preceding/following text.
	 102  	trimMarkerLen = Pos(1 + 1) // marker plus space before or after
	 103  )
	 104  
	 105  // stateFn represents the state of the scanner as a function that returns the next state.
	 106  type stateFn func(*lexer) stateFn
	 107  
	 108  // lexer holds the state of the scanner.
	 109  type lexer struct {
	 110  	name				string		// the name of the input; used only for error reports
	 111  	input			 string		// the string being scanned
	 112  	leftDelim	 string		// start of action
	 113  	rightDelim	string		// end of action
	 114  	emitComment bool			// emit itemComment tokens.
	 115  	pos				 Pos			 // current position in the input
	 116  	start			 Pos			 // start position of this item
	 117  	width			 Pos			 // width of last rune read from input
	 118  	items			 chan item // channel of scanned items
	 119  	parenDepth	int			 // nesting depth of ( ) exprs
	 120  	line				int			 // 1+number of newlines seen
	 121  	startLine	 int			 // start line of this item
	 122  }
	 123  
	 124  // next returns the next rune in the input.
	 125  func (l *lexer) next() rune {
	 126  	if int(l.pos) >= len(l.input) {
	 127  		l.width = 0
	 128  		return eof
	 129  	}
	 130  	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	 131  	l.width = Pos(w)
	 132  	l.pos += l.width
	 133  	if r == '\n' {
	 134  		l.line++
	 135  	}
	 136  	return r
	 137  }
	 138  
	 139  // peek returns but does not consume the next rune in the input.
	 140  func (l *lexer) peek() rune {
	 141  	r := l.next()
	 142  	l.backup()
	 143  	return r
	 144  }
	 145  
	 146  // backup steps back one rune. Can only be called once per call of next.
	 147  func (l *lexer) backup() {
	 148  	l.pos -= l.width
	 149  	// Correct newline count.
	 150  	if l.width == 1 && l.input[l.pos] == '\n' {
	 151  		l.line--
	 152  	}
	 153  }
	 154  
	 155  // emit passes an item back to the client.
	 156  func (l *lexer) emit(t itemType) {
	 157  	l.items <- item{t, l.start, l.input[l.start:l.pos], l.startLine}
	 158  	l.start = l.pos
	 159  	l.startLine = l.line
	 160  }
	 161  
	 162  // ignore skips over the pending input before this point.
	 163  func (l *lexer) ignore() {
	 164  	l.line += strings.Count(l.input[l.start:l.pos], "\n")
	 165  	l.start = l.pos
	 166  	l.startLine = l.line
	 167  }
	 168  
	 169  // accept consumes the next rune if it's from the valid set.
	 170  func (l *lexer) accept(valid string) bool {
	 171  	if strings.ContainsRune(valid, l.next()) {
	 172  		return true
	 173  	}
	 174  	l.backup()
	 175  	return false
	 176  }
	 177  
	 178  // acceptRun consumes a run of runes from the valid set.
	 179  func (l *lexer) acceptRun(valid string) {
	 180  	for strings.ContainsRune(valid, l.next()) {
	 181  	}
	 182  	l.backup()
	 183  }
	 184  
	 185  // errorf returns an error token and terminates the scan by passing
	 186  // back a nil pointer that will be the next state, terminating l.nextItem.
	 187  func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	 188  	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...), l.startLine}
	 189  	return nil
	 190  }
	 191  
	 192  // nextItem returns the next item from the input.
	 193  // Called by the parser, not in the lexing goroutine.
	 194  func (l *lexer) nextItem() item {
	 195  	return <-l.items
	 196  }
	 197  
	 198  // drain drains the output so the lexing goroutine will exit.
	 199  // Called by the parser, not in the lexing goroutine.
	 200  func (l *lexer) drain() {
	 201  	for range l.items {
	 202  	}
	 203  }
	 204  
	 205  // lex creates a new scanner for the input string.
	 206  func lex(name, input, left, right string, emitComment bool) *lexer {
	 207  	if left == "" {
	 208  		left = leftDelim
	 209  	}
	 210  	if right == "" {
	 211  		right = rightDelim
	 212  	}
	 213  	l := &lexer{
	 214  		name:				name,
	 215  		input:			 input,
	 216  		leftDelim:	 left,
	 217  		rightDelim:	right,
	 218  		emitComment: emitComment,
	 219  		items:			 make(chan item),
	 220  		line:				1,
	 221  		startLine:	 1,
	 222  	}
	 223  	go l.run()
	 224  	return l
	 225  }
	 226  
	 227  // run runs the state machine for the lexer.
	 228  func (l *lexer) run() {
	 229  	for state := lexText; state != nil; {
	 230  		state = state(l)
	 231  	}
	 232  	close(l.items)
	 233  }
	 234  
	 235  // state functions
	 236  
	 237  const (
	 238  	leftDelim		= "{{"
	 239  	rightDelim	 = "}}"
	 240  	leftComment	= "/*"
	 241  	rightComment = "*/"
	 242  )
	 243  
	 244  // lexText scans until an opening action delimiter, "{{".
	 245  func lexText(l *lexer) stateFn {
	 246  	l.width = 0
	 247  	if x := strings.Index(l.input[l.pos:], l.leftDelim); x >= 0 {
	 248  		ldn := Pos(len(l.leftDelim))
	 249  		l.pos += Pos(x)
	 250  		trimLength := Pos(0)
	 251  		if hasLeftTrimMarker(l.input[l.pos+ldn:]) {
	 252  			trimLength = rightTrimLength(l.input[l.start:l.pos])
	 253  		}
	 254  		l.pos -= trimLength
	 255  		if l.pos > l.start {
	 256  			l.line += strings.Count(l.input[l.start:l.pos], "\n")
	 257  			l.emit(itemText)
	 258  		}
	 259  		l.pos += trimLength
	 260  		l.ignore()
	 261  		return lexLeftDelim
	 262  	}
	 263  	l.pos = Pos(len(l.input))
	 264  	// Correctly reached EOF.
	 265  	if l.pos > l.start {
	 266  		l.line += strings.Count(l.input[l.start:l.pos], "\n")
	 267  		l.emit(itemText)
	 268  	}
	 269  	l.emit(itemEOF)
	 270  	return nil
	 271  }
	 272  
	 273  // rightTrimLength returns the length of the spaces at the end of the string.
	 274  func rightTrimLength(s string) Pos {
	 275  	return Pos(len(s) - len(strings.TrimRight(s, spaceChars)))
	 276  }
	 277  
	 278  // atRightDelim reports whether the lexer is at a right delimiter, possibly preceded by a trim marker.
	 279  func (l *lexer) atRightDelim() (delim, trimSpaces bool) {
	 280  	if hasRightTrimMarker(l.input[l.pos:]) && strings.HasPrefix(l.input[l.pos+trimMarkerLen:], l.rightDelim) { // With trim marker.
	 281  		return true, true
	 282  	}
	 283  	if strings.HasPrefix(l.input[l.pos:], l.rightDelim) { // Without trim marker.
	 284  		return true, false
	 285  	}
	 286  	return false, false
	 287  }
	 288  
	 289  // leftTrimLength returns the length of the spaces at the beginning of the string.
	 290  func leftTrimLength(s string) Pos {
	 291  	return Pos(len(s) - len(strings.TrimLeft(s, spaceChars)))
	 292  }
	 293  
	 294  // lexLeftDelim scans the left delimiter, which is known to be present, possibly with a trim marker.
	 295  func lexLeftDelim(l *lexer) stateFn {
	 296  	l.pos += Pos(len(l.leftDelim))
	 297  	trimSpace := hasLeftTrimMarker(l.input[l.pos:])
	 298  	afterMarker := Pos(0)
	 299  	if trimSpace {
	 300  		afterMarker = trimMarkerLen
	 301  	}
	 302  	if strings.HasPrefix(l.input[l.pos+afterMarker:], leftComment) {
	 303  		l.pos += afterMarker
	 304  		l.ignore()
	 305  		return lexComment
	 306  	}
	 307  	l.emit(itemLeftDelim)
	 308  	l.pos += afterMarker
	 309  	l.ignore()
	 310  	l.parenDepth = 0
	 311  	return lexInsideAction
	 312  }
	 313  
	 314  // lexComment scans a comment. The left comment marker is known to be present.
	 315  func lexComment(l *lexer) stateFn {
	 316  	l.pos += Pos(len(leftComment))
	 317  	i := strings.Index(l.input[l.pos:], rightComment)
	 318  	if i < 0 {
	 319  		return l.errorf("unclosed comment")
	 320  	}
	 321  	l.pos += Pos(i + len(rightComment))
	 322  	delim, trimSpace := l.atRightDelim()
	 323  	if !delim {
	 324  		return l.errorf("comment ends before closing delimiter")
	 325  	}
	 326  	if l.emitComment {
	 327  		l.emit(itemComment)
	 328  	}
	 329  	if trimSpace {
	 330  		l.pos += trimMarkerLen
	 331  	}
	 332  	l.pos += Pos(len(l.rightDelim))
	 333  	if trimSpace {
	 334  		l.pos += leftTrimLength(l.input[l.pos:])
	 335  	}
	 336  	l.ignore()
	 337  	return lexText
	 338  }
	 339  
	 340  // lexRightDelim scans the right delimiter, which is known to be present, possibly with a trim marker.
	 341  func lexRightDelim(l *lexer) stateFn {
	 342  	trimSpace := hasRightTrimMarker(l.input[l.pos:])
	 343  	if trimSpace {
	 344  		l.pos += trimMarkerLen
	 345  		l.ignore()
	 346  	}
	 347  	l.pos += Pos(len(l.rightDelim))
	 348  	l.emit(itemRightDelim)
	 349  	if trimSpace {
	 350  		l.pos += leftTrimLength(l.input[l.pos:])
	 351  		l.ignore()
	 352  	}
	 353  	return lexText
	 354  }
	 355  
	 356  // lexInsideAction scans the elements inside action delimiters.
	 357  func lexInsideAction(l *lexer) stateFn {
	 358  	// Either number, quoted string, or identifier.
	 359  	// Spaces separate arguments; runs of spaces turn into itemSpace.
	 360  	// Pipe symbols separate and are emitted.
	 361  	delim, _ := l.atRightDelim()
	 362  	if delim {
	 363  		if l.parenDepth == 0 {
	 364  			return lexRightDelim
	 365  		}
	 366  		return l.errorf("unclosed left paren")
	 367  	}
	 368  	switch r := l.next(); {
	 369  	case r == eof:
	 370  		return l.errorf("unclosed action")
	 371  	case isSpace(r):
	 372  		l.backup() // Put space back in case we have " -}}".
	 373  		return lexSpace
	 374  	case r == '=':
	 375  		l.emit(itemAssign)
	 376  	case r == ':':
	 377  		if l.next() != '=' {
	 378  			return l.errorf("expected :=")
	 379  		}
	 380  		l.emit(itemDeclare)
	 381  	case r == '|':
	 382  		l.emit(itemPipe)
	 383  	case r == '"':
	 384  		return lexQuote
	 385  	case r == '`':
	 386  		return lexRawQuote
	 387  	case r == '$':
	 388  		return lexVariable
	 389  	case r == '\'':
	 390  		return lexChar
	 391  	case r == '.':
	 392  		// special look-ahead for ".field" so we don't break l.backup().
	 393  		if l.pos < Pos(len(l.input)) {
	 394  			r := l.input[l.pos]
	 395  			if r < '0' || '9' < r {
	 396  				return lexField
	 397  			}
	 398  		}
	 399  		fallthrough // '.' can start a number.
	 400  	case r == '+' || r == '-' || ('0' <= r && r <= '9'):
	 401  		l.backup()
	 402  		return lexNumber
	 403  	case isAlphaNumeric(r):
	 404  		l.backup()
	 405  		return lexIdentifier
	 406  	case r == '(':
	 407  		l.emit(itemLeftParen)
	 408  		l.parenDepth++
	 409  	case r == ')':
	 410  		l.emit(itemRightParen)
	 411  		l.parenDepth--
	 412  		if l.parenDepth < 0 {
	 413  			return l.errorf("unexpected right paren %#U", r)
	 414  		}
	 415  	case r <= unicode.MaxASCII && unicode.IsPrint(r):
	 416  		l.emit(itemChar)
	 417  	default:
	 418  		return l.errorf("unrecognized character in action: %#U", r)
	 419  	}
	 420  	return lexInsideAction
	 421  }
	 422  
	 423  // lexSpace scans a run of space characters.
	 424  // We have not consumed the first space, which is known to be present.
	 425  // Take care if there is a trim-marked right delimiter, which starts with a space.
	 426  func lexSpace(l *lexer) stateFn {
	 427  	var r rune
	 428  	var numSpaces int
	 429  	for {
	 430  		r = l.peek()
	 431  		if !isSpace(r) {
	 432  			break
	 433  		}
	 434  		l.next()
	 435  		numSpaces++
	 436  	}
	 437  	// Be careful about a trim-marked closing delimiter, which has a minus
	 438  	// after a space. We know there is a space, so check for the '-' that might follow.
	 439  	if hasRightTrimMarker(l.input[l.pos-1:]) && strings.HasPrefix(l.input[l.pos-1+trimMarkerLen:], l.rightDelim) {
	 440  		l.backup() // Before the space.
	 441  		if numSpaces == 1 {
	 442  			return lexRightDelim // On the delim, so go right to that.
	 443  		}
	 444  	}
	 445  	l.emit(itemSpace)
	 446  	return lexInsideAction
	 447  }
	 448  
	 449  // lexIdentifier scans an alphanumeric.
	 450  func lexIdentifier(l *lexer) stateFn {
	 451  Loop:
	 452  	for {
	 453  		switch r := l.next(); {
	 454  		case isAlphaNumeric(r):
	 455  			// absorb.
	 456  		default:
	 457  			l.backup()
	 458  			word := l.input[l.start:l.pos]
	 459  			if !l.atTerminator() {
	 460  				return l.errorf("bad character %#U", r)
	 461  			}
	 462  			switch {
	 463  			case key[word] > itemKeyword:
	 464  				l.emit(key[word])
	 465  			case word[0] == '.':
	 466  				l.emit(itemField)
	 467  			case word == "true", word == "false":
	 468  				l.emit(itemBool)
	 469  			default:
	 470  				l.emit(itemIdentifier)
	 471  			}
	 472  			break Loop
	 473  		}
	 474  	}
	 475  	return lexInsideAction
	 476  }
	 477  
	 478  // lexField scans a field: .Alphanumeric.
	 479  // The . has been scanned.
	 480  func lexField(l *lexer) stateFn {
	 481  	return lexFieldOrVariable(l, itemField)
	 482  }
	 483  
	 484  // lexVariable scans a Variable: $Alphanumeric.
	 485  // The $ has been scanned.
	 486  func lexVariable(l *lexer) stateFn {
	 487  	if l.atTerminator() { // Nothing interesting follows -> "$".
	 488  		l.emit(itemVariable)
	 489  		return lexInsideAction
	 490  	}
	 491  	return lexFieldOrVariable(l, itemVariable)
	 492  }
	 493  
	 494  // lexVariable scans a field or variable: [.$]Alphanumeric.
	 495  // The . or $ has been scanned.
	 496  func lexFieldOrVariable(l *lexer, typ itemType) stateFn {
	 497  	if l.atTerminator() { // Nothing interesting follows -> "." or "$".
	 498  		if typ == itemVariable {
	 499  			l.emit(itemVariable)
	 500  		} else {
	 501  			l.emit(itemDot)
	 502  		}
	 503  		return lexInsideAction
	 504  	}
	 505  	var r rune
	 506  	for {
	 507  		r = l.next()
	 508  		if !isAlphaNumeric(r) {
	 509  			l.backup()
	 510  			break
	 511  		}
	 512  	}
	 513  	if !l.atTerminator() {
	 514  		return l.errorf("bad character %#U", r)
	 515  	}
	 516  	l.emit(typ)
	 517  	return lexInsideAction
	 518  }
	 519  
	 520  // atTerminator reports whether the input is at valid termination character to
	 521  // appear after an identifier. Breaks .X.Y into two pieces. Also catches cases
	 522  // like "$x+2" not being acceptable without a space, in case we decide one
	 523  // day to implement arithmetic.
	 524  func (l *lexer) atTerminator() bool {
	 525  	r := l.peek()
	 526  	if isSpace(r) {
	 527  		return true
	 528  	}
	 529  	switch r {
	 530  	case eof, '.', ',', '|', ':', ')', '(':
	 531  		return true
	 532  	}
	 533  	// Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will
	 534  	// succeed but should fail) but only in extremely rare cases caused by willfully
	 535  	// bad choice of delimiter.
	 536  	if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r {
	 537  		return true
	 538  	}
	 539  	return false
	 540  }
	 541  
	 542  // lexChar scans a character constant. The initial quote is already
	 543  // scanned. Syntax checking is done by the parser.
	 544  func lexChar(l *lexer) stateFn {
	 545  Loop:
	 546  	for {
	 547  		switch l.next() {
	 548  		case '\\':
	 549  			if r := l.next(); r != eof && r != '\n' {
	 550  				break
	 551  			}
	 552  			fallthrough
	 553  		case eof, '\n':
	 554  			return l.errorf("unterminated character constant")
	 555  		case '\'':
	 556  			break Loop
	 557  		}
	 558  	}
	 559  	l.emit(itemCharConstant)
	 560  	return lexInsideAction
	 561  }
	 562  
	 563  // lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
	 564  // isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
	 565  // and "089" - but when it's wrong the input is invalid and the parser (via
	 566  // strconv) will notice.
	 567  func lexNumber(l *lexer) stateFn {
	 568  	if !l.scanNumber() {
	 569  		return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
	 570  	}
	 571  	if sign := l.peek(); sign == '+' || sign == '-' {
	 572  		// Complex: 1+2i. No spaces, must end in 'i'.
	 573  		if !l.scanNumber() || l.input[l.pos-1] != 'i' {
	 574  			return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
	 575  		}
	 576  		l.emit(itemComplex)
	 577  	} else {
	 578  		l.emit(itemNumber)
	 579  	}
	 580  	return lexInsideAction
	 581  }
	 582  
	 583  func (l *lexer) scanNumber() bool {
	 584  	// Optional leading sign.
	 585  	l.accept("+-")
	 586  	// Is it hex?
	 587  	digits := "0123456789_"
	 588  	if l.accept("0") {
	 589  		// Note: Leading 0 does not mean octal in floats.
	 590  		if l.accept("xX") {
	 591  			digits = "0123456789abcdefABCDEF_"
	 592  		} else if l.accept("oO") {
	 593  			digits = "01234567_"
	 594  		} else if l.accept("bB") {
	 595  			digits = "01_"
	 596  		}
	 597  	}
	 598  	l.acceptRun(digits)
	 599  	if l.accept(".") {
	 600  		l.acceptRun(digits)
	 601  	}
	 602  	if len(digits) == 10+1 && l.accept("eE") {
	 603  		l.accept("+-")
	 604  		l.acceptRun("0123456789_")
	 605  	}
	 606  	if len(digits) == 16+6+1 && l.accept("pP") {
	 607  		l.accept("+-")
	 608  		l.acceptRun("0123456789_")
	 609  	}
	 610  	// Is it imaginary?
	 611  	l.accept("i")
	 612  	// Next thing mustn't be alphanumeric.
	 613  	if isAlphaNumeric(l.peek()) {
	 614  		l.next()
	 615  		return false
	 616  	}
	 617  	return true
	 618  }
	 619  
	 620  // lexQuote scans a quoted string.
	 621  func lexQuote(l *lexer) stateFn {
	 622  Loop:
	 623  	for {
	 624  		switch l.next() {
	 625  		case '\\':
	 626  			if r := l.next(); r != eof && r != '\n' {
	 627  				break
	 628  			}
	 629  			fallthrough
	 630  		case eof, '\n':
	 631  			return l.errorf("unterminated quoted string")
	 632  		case '"':
	 633  			break Loop
	 634  		}
	 635  	}
	 636  	l.emit(itemString)
	 637  	return lexInsideAction
	 638  }
	 639  
	 640  // lexRawQuote scans a raw quoted string.
	 641  func lexRawQuote(l *lexer) stateFn {
	 642  Loop:
	 643  	for {
	 644  		switch l.next() {
	 645  		case eof:
	 646  			return l.errorf("unterminated raw quoted string")
	 647  		case '`':
	 648  			break Loop
	 649  		}
	 650  	}
	 651  	l.emit(itemRawString)
	 652  	return lexInsideAction
	 653  }
	 654  
	 655  // isSpace reports whether r is a space character.
	 656  func isSpace(r rune) bool {
	 657  	return r == ' ' || r == '\t' || r == '\r' || r == '\n'
	 658  }
	 659  
	 660  // isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
	 661  func isAlphaNumeric(r rune) bool {
	 662  	return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
	 663  }
	 664  
	 665  func hasLeftTrimMarker(s string) bool {
	 666  	return len(s) >= 2 && s[0] == trimMarker && isSpace(rune(s[1]))
	 667  }
	 668  
	 669  func hasRightTrimMarker(s string) bool {
	 670  	return len(s) >= 2 && isSpace(rune(s[0])) && s[1] == trimMarker
	 671  }
	 672  

View as plain text