Fix misparsed $ after variable name

If a $ sign occurs after a variable name, the ninja string parser
fails to check if it is a $$ or a ${.  Go to the
parseDollarStartState instead of the parseDollarState.

Since it is not yet known if the $ is the beginning of a new
variable (${ or $<alphanumeric>) or a string ($$), an empty string
separator cannot be added to the ninjaString strings list.  Instead,
add functions to push strings or variables onto the ninjaString,
and automatically add the blank separator if two variables are
pushed in a row.

Change-Id: Ia1cae6259b1d7e4f633f61b9eadb2a2028bbd5f0
This commit is contained in:
Colin Cross 2015-04-14 16:10:21 -07:00
parent 11bcca9098
commit b247893deb
2 changed files with 35 additions and 13 deletions

View file

@ -59,6 +59,21 @@ type parseState struct {
result *ninjaString
}
func (ps *parseState) pushVariable(v Variable) {
if len(ps.result.variables) == len(ps.result.strings) {
// Last push was a variable, we need a blank string separator
ps.result.strings = append(ps.result.strings, "")
}
ps.result.variables = append(ps.result.variables, v)
}
func (ps *parseState) pushString(s string) {
if len(ps.result.strings) != len(ps.result.variables) {
panic("oops, pushed string after string")
}
ps.result.strings = append(ps.result.strings, s)
}
type stateFunc func(*parseState, int, rune) (stateFunc, error)
// parseNinjaString parses an unescaped ninja string (i.e. all $<something>
@ -98,7 +113,7 @@ func parseStringState(state *parseState, i int, r rune) (stateFunc, error) {
return parseDollarStartState, nil
case r == eof:
state.result.strings = append(state.result.strings, state.str[state.stringStart:i])
state.pushString(state.str[state.stringStart:i])
return nil, nil
default:
@ -112,7 +127,7 @@ func parseDollarStartState(state *parseState, i int, r rune) (stateFunc, error)
r >= '0' && r <= '9', r == '_', r == '-':
// The beginning of a of the variable name. Output the string and
// keep going.
state.result.strings = append(state.result.strings, state.str[state.stringStart:i-1])
state.pushString(state.str[state.stringStart:i-1])
return parseDollarState, nil
case r == '$':
@ -123,7 +138,7 @@ func parseDollarStartState(state *parseState, i int, r rune) (stateFunc, error)
case r == '{':
// This is a bracketted variable name (e.g. "${blah.blah}"). Output
// the string and keep going.
state.result.strings = append(state.result.strings, state.str[state.stringStart:i-1])
state.pushString(state.str[state.stringStart:i-1])
state.varStart = i + 1
return parseBracketsState, nil
@ -153,14 +168,11 @@ func parseDollarState(state *parseState, i int, r rune) (stateFunc, error) {
return nil, err
}
state.result.variables = append(state.result.variables, v)
state.pushVariable(v)
state.varStart = i + 1
state.stringStart = i
// We always have a string in between variables, even if it's an
// empty one.
state.result.strings = append(state.result.strings, "")
return parseDollarState, nil
return parseDollarStartState, nil
case r == eof:
// This is the end of the variable name.
@ -169,10 +181,10 @@ func parseDollarState(state *parseState, i int, r rune) (stateFunc, error) {
return nil, err
}
state.result.variables = append(state.result.variables, v)
state.pushVariable(v)
// We always end with a string, even if it's an empty one.
state.result.strings = append(state.result.strings, "")
state.pushString("")
return nil, nil
@ -184,7 +196,7 @@ func parseDollarState(state *parseState, i int, r rune) (stateFunc, error) {
return nil, err
}
state.result.variables = append(state.result.variables, v)
state.pushVariable(v)
state.stringStart = i
return parseStringState, nil
}
@ -210,7 +222,7 @@ func parseBracketsState(state *parseState, i int, r rune) (stateFunc, error) {
return nil, err
}
state.result.variables = append(state.result.variables, v)
state.pushVariable(v)
state.stringStart = i + 1
return parseStringState, nil

View file

@ -55,6 +55,16 @@ var ninjaParseTestCases = []struct {
vars: nil,
strs: []string{"foo $$ bar"},
},
{
input: "$foo${bar}",
vars: []string{"foo", "bar"},
strs: []string{"","", ""},
},
{
input: "$foo$$",
vars: []string{"foo"},
strs: []string{"","$$"},
},
{
input: "foo $ bar",
err: "invalid character after '$' at byte offset 5",