Work on string expansion.

This commit is contained in:
Daniel Jones 2013-02-26 22:41:25 -08:00
parent 6da2555966
commit 084a45fc74
5 changed files with 208 additions and 69 deletions

68
lex.go
View file

@ -15,8 +15,7 @@ const eof rune = '\000'
const (
tokenError tokenType = iota
tokenNewline
tokenBareString
tokenQuotedString
tokenWord
tokenPipeInclude
tokenRedirInclude
tokenColon
@ -30,10 +29,8 @@ func (typ tokenType) String() string {
return "[Error]"
case tokenNewline:
return "[Newline]"
case tokenBareString:
return "[BareString]"
case tokenQuotedString:
return "[QuotedString]"
case tokenWord:
return "[Word]"
case tokenPipeInclude:
return "[PipeInclude]"
case tokenRedirInclude:
@ -209,7 +206,6 @@ func (l *lexer) run() {
// A way of determining if the current line might be a recipe.
func lexTopLevel(l *lexer) lexerStateFun {
for {
l.skipRun(" \t\r")
// emit a newline token if we are ending a non-empty line.
@ -219,7 +215,7 @@ func lexTopLevel(l *lexer) lexerStateFun {
}
l.skipRun(" \t\r\n")
if l.peek() == '\'' && l.peekN(1) == '\n' {
if l.peek() == '\\' && l.peekN(1) == '\n' {
l.next()
l.next()
l.indented = false
@ -240,17 +236,21 @@ func lexTopLevel(l *lexer) lexerStateFun {
return lexComment
case '<':
return lexInclude
case '"':
return lexDoubleQuote
case '\'':
return lexSingleQuote
case ':':
return lexColon
case '=':
return lexAssign
case '"':
return lexDoubleQuotedWord
case '\'':
return lexSingleQuotedWord
case '`':
return lexBackQuotedWord
}
return lexBareString
// TODO: No! The lexer can get stuck in a loop this way.
// Check if the next charar is a valid bare string chacter. If not, error.
return lexBareWord
}
func lexColon(l *lexer) lexerStateFun {
@ -281,25 +281,30 @@ func lexInclude(l *lexer) lexerStateFun {
return lexTopLevel
}
func lexDoubleQuote(l *lexer) lexerStateFun {
l.skip() // '"'
func lexDoubleQuotedWord(l *lexer) lexerStateFun {
l.next() // '"'
for l.peek() != '"' {
l.acceptUntil("\\\"")
if l.accept("\\") {
l.accept("\"")
}
}
l.emit(tokenQuotedString)
l.skip() // skip '"'
return lexTopLevel
l.next() // '"'
return lexBareWord
}
func lexSingleQuote(l *lexer) lexerStateFun {
l.skip() // '\''
func lexBackQuotedWord(l *lexer) lexerStateFun {
l.next() // '`'
l.acceptUntil("`")
l.next() // '`'
return lexBareWord
}
func lexSingleQuotedWord(l *lexer) lexerStateFun {
l.next() // '\''
l.acceptUntil("'")
l.emit(tokenQuotedString)
l.skip() // '\''
return lexTopLevel
l.next() // '\''
return lexBareWord
}
func lexRecipe(l *lexer) lexerStateFun {
@ -316,10 +321,19 @@ func lexRecipe(l *lexer) lexerStateFun {
return lexTopLevel
}
func lexBareString(l *lexer) lexerStateFun {
// TODO: allow escaping spaces and tabs?
// TODO: allow adjacent quoted string, e.g.: foo"bar"baz?
func lexBareWord(l *lexer) lexerStateFun {
l.acceptUntil(" \t\n\r\\=:#'\"")
l.emit(tokenBareString)
if l.peek() == '"' {
return lexDoubleQuotedWord
} else if l.peek() == '\'' {
return lexSingleQuotedWord
} else if l.peek() == '`' {
return lexBackQuotedWord
}
if l.start < l.pos {
l.emit(tokenWord)
}
return lexTopLevel
}