subparse backticks and dollar double quote

This commit is contained in:
sawka 2022-11-16 12:00:44 -08:00
parent ec2de4609b
commit 475d7cd647
3 changed files with 43 additions and 41 deletions

View File

@ -82,20 +82,18 @@ const (
WordTypeKey = "key" // if then else elif fi do done case esac while until for in { } ! (( [[
WordTypeSimpleVar = "svar" // simplevar $
WordTypeGroup = "grp" // contains other words e.g. "hello"foo'bar'$x
WordTypeArith = "ath"
// each of these can also be used as an entry in quoteContext
WordTypeDQ = "dq" // "
WordTypeSQ = "sq" // '
WordTypeBQ = "bq" // `
WordTypeDSQ = "dsq" // $'
WordTypeDDQ = "ddq" // $"
WordTypeVarBrace = "varb" // ${
WordTypeDP = "dp" // $(
WordTypeDPP = "dpp" // $((
WordTypeP = "p" // (
WordTypePP = "pp" // ((
WordTypeDB = "db" // $[
WordTypeDQ = "dq" // " (quote-context)
WordTypeDDQ = "ddq" // $" (quote-context)
WordTypeVarBrace = "varb" // ${ (quote-context)
WordTypeDP = "dp" // $( (quote-context)
WordTypeBQ = "bq" // ` (quote-context)
WordTypeSQ = "sq" // '
WordTypeDSQ = "dsq" // $'
WordTypeDPP = "dpp" // $(( (internals not parsed)
WordTypePP = "pp" // (( (internals not parsed)
WordTypeDB = "db" // $[ (internals not parsed)
)
type quoteContext []string
@ -268,12 +266,27 @@ func (c *parseContext) parseStrDQ() *wordType {
return w
}
func (c *parseContext) parseStrBQ() *wordType {
if c.match('`') {
func (c *parseContext) parseStrDDQ() *wordType {
if !c.match2('$', '"') {
return nil
}
newOffset, complete := c.skipToChar(1, '`', true)
w := c.makeWord(WordTypeBQ, newOffset, complete)
newContext := c.clone(c.Pos+2, WordTypeDDQ)
subWords, eofExit := newContext.tokenizeDQ()
newOffset := newContext.Pos + 2
w := c.makeWord(WordTypeDDQ, newOffset, !eofExit)
w.Subs = subWords
return w
}
func (c *parseContext) parseStrBQ() *wordType {
if !c.match('`') {
return nil
}
newContext := c.clone(c.Pos+1, WordTypeBQ)
subWords, eofExit := newContext.tokenizeRaw()
newOffset := newContext.Pos + 1
w := c.makeWord(WordTypeBQ, newOffset, !eofExit)
w.Subs = subWords
return w
}
@ -286,15 +299,6 @@ func (c *parseContext) parseStrANSI() *wordType {
return w
}
func (c *parseContext) parseStrDDQ() *wordType {
if !c.match2('$', '"') {
return nil
}
newOffset, complete := c.skipToChar(2, '"', true)
w := c.makeWord(WordTypeDDQ, newOffset, complete)
return w
}
func (c *parseContext) parseArith(mustComplete bool) *wordType {
if !c.match2('(', '(') {
return nil
@ -303,7 +307,7 @@ func (c *parseContext) parseArith(mustComplete bool) *wordType {
if mustComplete && !complete {
return nil
}
w := c.makeWord(WordTypeArith, newOffset, complete)
w := c.makeWord(WordTypePP, newOffset, complete)
return w
}
@ -358,20 +362,6 @@ func (c *parseContext) parseExpansion() *wordType {
return nil
}
func (c *parseContext) parseShellTest() *wordType {
if !c.match2('[', '[') {
return nil
}
return nil
}
func (c *parseContext) parseProcessSubstitution() *wordType {
if !c.match2('<', '(') && !c.match2('>', '(') {
return nil
}
return nil
}
// returns newOffset
func (c *parseContext) parseSimpleVarName(offset int) int {
first := true

View File

@ -43,4 +43,6 @@ func Test1(t *testing.T) {
testParse(t, `echo $(ls $)`)
testParse(t, `echo ${x:-hello\}"}"} 2nd`)
testParse(t, `echo "$(ls "foo") more $x"`)
testParse(t, "echo `ls $x \"hello $x\" \\`ls\\`; ./foo`")
testParse(t, `echo $"hello $x $(ls)"`)
}

View File

@ -221,9 +221,12 @@ func (c *parseContext) tokenizeDQ() ([]*wordType, bool) {
}
// returns (words, eofexit)
// backticks (WordTypeBQ) handle backslash in a special way, but that seems to mainly effect execution (not completion)
// de_backslash => removes initial backslash in \`, \\, and \$ before execution
func (c *parseContext) tokenizeRaw() ([]*wordType, bool) {
state := &tokenizeOutputState{}
isExpSubShell := c.QC.cur() == WordTypeDP
isInBQ := c.QC.cur() == WordTypeBQ
parenLevel := 0
eofExit := false
for {
@ -236,6 +239,10 @@ func (c *parseContext) tokenizeRaw() ([]*wordType, bool) {
c.Pos++
break
}
if isInBQ && ch == '`' {
c.Pos++
break
}
// fmt.Printf("ch %d %q\n", c.Pos, string([]rune{ch}))
foundOp, newOffset := c.parseOp(0)
if foundOp {
@ -264,6 +271,9 @@ func (c *parseContext) tokenizeRaw() ([]*wordType, bool) {
if quoteWord == nil && ch == '"' {
quoteWord = c.parseStrDQ()
}
if quoteWord == nil && ch == '`' {
quoteWord = c.parseStrBQ()
}
isNextParen := isExpSubShell && c.at(1) == ')'
if quoteWord == nil && ch == '$' && !isNextParen {
quoteWord = c.parseStrANSI()