Exemplo n.º 1
0
// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
	if l.pos > ast.Pos(len(l.input)) {
		l.pos = ast.Pos(len(l.input))
	}
	l.lastEmit = item{t, l.pos, l.input[l.start:l.pos]}
	l.items <- l.lastEmit
	l.start = l.pos
}
Exemplo n.º 2
0
// next returns the next rune in the input.
func (l *lexer) next() (r rune) {
	if l.pos >= ast.Pos(len(l.input)) {
		l.width = 0
		return eof
	}
	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
	l.pos += ast.Pos(l.width)
	return r
}
Exemplo n.º 3
0
func maybeEmitText(l *lexer, backup int) {
	if l.pos-ast.Pos(backup) > l.start {
		l.pos -= ast.Pos(backup)
		if allSpaceWithNewline(l.input[l.start:l.pos]) {
			l.ignore()
		} else {
			l.emit(itemText)
		}
		l.pos += ast.Pos(backup)
	}
}
Exemplo n.º 4
0
// scanNumber scans a number according to Soy's specification.
//
// It returns the scanned itemType (itemFloat or itemInteger) and a flag
// indicating if an error was found.
//
// Floats must be in decimal and must either:
//
//     - Have digits both before and after the decimal point (both can be
//       a single 0), e.g. 0.5, -100.0, or
//     - Have a lower-case e that represents scientific notation,
//       e.g. -3e-3, 6.02e23.
//
// Integers can be:
//
//     - decimal (e.g. -827)
//     - hexadecimal (must begin with 0x and must use capital A-F,
//       e.g. 0x1A2B).
func scanNumber(l *lexer) (typ itemType, ok bool) {
	typ = itemInteger
	// Optional leading sign.
	hasSign := l.accept("+-")
	if ast.Pos(len(l.input)) >= l.pos+2 && l.input[l.pos:l.pos+2] == "0x" {
		// Hexadecimal.
		if hasSign {
			// No signs for hexadecimals.
			return
		}
		l.acceptRun("0x")
		if !l.acceptRun(hexDigits) {
			// Requires at least one digit.
			return
		}
		if l.accept(".") {
			// No dots for hexadecimals.
			return
		}
	} else {
		// Decimal.
		if !l.acceptRun(decDigits) {
			// Requires at least one digit.
			return
		}
		if l.accept(".") {
			// Float.
			if !l.acceptRun(decDigits) {
				// Requires a digit after the dot.
				return
			}
			typ = itemFloat
		} else {
			if (!hasSign && l.input[l.start] == '0' && l.pos > l.start+1) ||
				(hasSign && l.input[l.start+1] == '0' && l.pos > l.start+2) {
				// Integers can't start with 0.
				return
			}
		}
		if l.accept("e") {
			l.accept("+-")
			if !l.acceptRun(decDigits) {
				// A digit is required after the scientific notation.
				return
			}
			typ = itemFloat
		}
	}
	// Next thing must not be alphanumeric.
	if isAlphaNumeric(l.peek()) {
		l.next()
		return
	}
	ok = true
	return
}
Exemplo n.º 5
0
// lexLiteral scans until a closing literal delimiter, "{\literal}".
// It emits the literal text and the closing tag.
//
// A literal section contains raw text and may include braces.
// itemLiteral has already been emitted.
func lexLiteral(l *lexer) stateFn {
	// emit the closing of the initial {literal} tag
	var ch = l.next()
	for isSpace(ch) {
		ch = l.next()
	}
	if ch != '}' {
		return l.errorf("expected closing tag after {literal..")
	}
	if l.doubleDelim && l.next() != '}' {
		return l.errorf("expected double closing braces in tag")
	}
	l.emit(itemRightDelim)

	// accept everything as itemText until we see the {/literal}
	var end bool
	var delimLen int
	for {
		if !l.doubleDelim && strings.HasPrefix(l.input[l.pos:], "{/literal}") {
			end, delimLen = true, 1
		} else if l.doubleDelim && strings.HasPrefix(l.input[l.pos:], "{{/literal}}") {
			end, delimLen = true, 2
		}
		if end {
			if l.pos > l.start {
				l.emit(itemText)
			}
			l.pos += ast.Pos(delimLen)
			l.emit(itemLeftDelim)
			l.pos += ast.Pos(len("/literal"))
			l.emit(itemLiteralEnd)
			l.pos += ast.Pos(delimLen)
			l.emit(itemRightDelim)
			return lexText
		}
		if l.next() == eof {
			return l.errorf("unclosed literal")
		}
	}
	return lexText
}
Exemplo n.º 6
0
Arquivo: lexer.go Projeto: leobcn/soy
// lexLiteral scans until a closing literal delimiter, "{\literal}".
// It emits the literal text and the closing tag.
//
// A literal section contains raw text and may include braces.
// itemLiteral has already been emitted.
func lexLiteral(l *lexer) stateFn {
	// emit the closing of the initial {literal} tag
	var ch = l.next()
	for isSpace(ch) {
		ch = l.next()
	}
	if ch != '}' {
		return l.errorf("expected closing tag after {literal..")
	}
	if l.doubleDelim && l.next() != '}' {
		return l.errorf("expected double closing braces in tag")
	}
	l.emit(itemRightDelim)

	// Fast forward through the literal section.
	var expectClose, delimLen = "{/literal}", 1
	if l.doubleDelim {
		expectClose, delimLen = "{{/literal}}", 2
	}
	var i = strings.Index(l.input[l.pos:], expectClose)
	if i == -1 {
		return l.errorf("unclosed literal")
	}
	l.pos += ast.Pos(i)

	// Accept everything as itemText until we see the {/literal}
	// Emit the other various tokens.
	if i > 0 {
		l.emit(itemText)
	}
	l.pos += ast.Pos(delimLen)
	l.emit(itemLeftDelim)
	l.pos += ast.Pos(len("/literal"))
	l.emit(itemLiteralEnd)
	l.pos += ast.Pos(delimLen)
	l.emit(itemRightDelim)
	return lexText
}
Exemplo n.º 7
0
func lexSoyDocParam(l *lexer) {
	l.pos += ast.Pos(len("@param"))
	switch ch := l.next(); {
	case ch == '?':
		if l.next() != ' ' {
			return
		}
		l.backup()
		l.emit(itemSoyDocOptionalParam)
	case ch == ' ':
		l.backup()
		l.emit(itemSoyDocParam)
	default:
		return // what a fakeout
	}

	// skip all spaces
	for {
		var r = l.next()
		if r == eof || !isSpace(r) {
			break
		}
	}
	l.backup()
	l.ignore()

	// extract the param
	for {
		var r = l.next()
		if isSpaceEOL(r) || r == eof {
			l.pos--
			l.emit(itemIdent)
			// don't skip newlines. the outer routine needs to know about it
			if isSpace(r) || r == eof {
				l.pos++
			}
			l.ignore()
			break
		}
	}
}
Exemplo n.º 8
0
// backup steps back one rune. Can only be called once per call of next.
func (l *lexer) backup() {
	l.pos -= ast.Pos(l.width)
}