// lexer holds the state of the scanner.
type lexer struct {
- name string // the name of the input; used only for error reports
- input string // the string being scanned
- leftDelim string // start of action
- rightDelim string // end of action
- pos Pos // current position in the input
- start Pos // start position of this item
- width Pos // width of last rune read from input
- items chan item // channel of scanned items
- parenDepth int // nesting depth of ( ) exprs
- line int // 1+number of newlines seen
- startLine int // start line of this item
+ name string // the name of the input; used only for error reports
+ input string // the string being scanned
+ leftDelim string // start of action
+ rightDelim string // end of action
+ trimRightDelim string // end of action with trim marker
+ pos Pos // current position in the input
+ start Pos // start position of this item
+ width Pos // width of last rune read from input
+ items chan item // channel of scanned items
+ parenDepth int // nesting depth of ( ) exprs
+ line int // 1+number of newlines seen
+ startLine int // start line of this item
}
// next returns the next rune in the input.
right = rightDelim
}
l := &lexer{
- name: name,
- input: input,
- leftDelim: left,
- rightDelim: right,
- items: make(chan item),
- line: 1,
- startLine: 1,
+ name: name,
+ input: input,
+ leftDelim: left,
+ rightDelim: right,
+ trimRightDelim: rightTrimMarker + right,
+ items: make(chan item),
+ line: 1,
+ startLine: 1,
}
go l.run()
return l
// atRightDelim reports whether the lexer is at a right delimiter, possibly preceded by a trim marker.
func (l *lexer) atRightDelim() (delim, trimSpaces bool) {
- if strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
- return true, false
- }
- // The right delim might have the marker before.
- if strings.HasPrefix(l.input[l.pos:], rightTrimMarker) &&
- strings.HasPrefix(l.input[l.pos+trimMarkerLen:], l.rightDelim) {
+ if strings.HasPrefix(l.input[l.pos:], l.trimRightDelim) { // With trim marker.
return true, true
}
+ if strings.HasPrefix(l.input[l.pos:], l.rightDelim) { // Without trim marker.
+ return true, false
+ }
return false, false
}
case r == eof || isEndOfLine(r):
return l.errorf("unclosed action")
case isSpace(r):
+ l.backup() // Put space back in case we have " -}}".
return lexSpace
case r == '=':
l.emit(itemAssign)
}
// lexSpace scans a run of space characters.
-// One space has already been seen.
+// We have not consumed the first space, which is known to be present.
+// Take care if there is a trim-marked right delimiter, which starts with a space.
func lexSpace(l *lexer) stateFn {
- for isSpace(l.peek()) {
+ var r rune
+ var numSpaces int
+ for {
+ r = l.peek()
+ if !isSpace(r) {
+ break
+ }
l.next()
+ numSpaces++
+ }
+ // Be careful about a trim-marked closing delimiter, which has a minus
+ // after a space. We know there is a space, so check for the '-' that might follow.
+ if strings.HasPrefix(l.input[l.pos-1:], l.trimRightDelim) {
+ l.backup() // Before the space.
+ if numSpaces == 1 {
+ return lexRightDelim // On the delim, so go right to that.
+ }
}
l.emit(itemSpace)
return lexInsideAction
{"trim left", "x \r\n\t{{- 3}}", noError, `"x"{{3}}`},
{"trim right", "{{3 -}}\n\n\ty", noError, `{{3}}"y"`},
{"trim left and right", "x \r\n\t{{- 3 -}}\n\n\ty", noError, `"x"{{3}}"y"`},
+ {"trim with extra spaces", "x\n{{- 3 -}}\ny", noError, `"x"{{3}}"y"`},
{"comment trim left", "x \r\n\t{{- /* hi */}}", noError, `"x"`},
{"comment trim right", "{{/* hi */ -}}\n\n\ty", noError, `"y"`},
{"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x""y"`},