package shell import ( "bytes" "io" "log" ) type SubshellParameters struct{} func (p *SubshellParameters) SubParsers() []Parser { return []Parser{ &BaseParser{ parameters: &StatementParameters{}, }, } } func (p *SubshellParameters) Enter(i *CharIterator) error { return i.Next() } func (p *SubshellParameters) Supports(charsBefore []rune, r rune) bool { if r == '(' { return len(charsBefore) == 0 || countBackslashSuffixes(charsBefore)%2 == 0 } return false } func (p *SubshellParameters) ShouldLeave(charsBefore []rune, r rune) bool { if r == ')' { return len(charsBefore) == 0 || countBackslashSuffixes(charsBefore)%2 == 0 } return false } func (p *SubshellParameters) MakeToken() Token { return &Subshell{} } type Subshell struct { BaseToken } func (s *Subshell) Evaluate(state State, stdin io.Reader, stdout io.Writer, stderr io.Writer) uint8 { var ret uint8 for _, token := range s.Tokens() { if eval, ok := token.(Evalable); ok { ret = state.Eval(eval, stdin, stdout, stderr) } else { logstr := "shell: Unexpected token: " + token.String() log.Println(logstr) stderr.Write([]byte(logstr)) return 2 } } return ret } type Backtick struct { BaseToken } type BacktickParameters struct{} func (p *BacktickParameters) MakeToken() Token { return &Backtick{} } func (p *BacktickParameters) Enter(i *CharIterator) error { return i.Next() } func (p *BacktickParameters) Supports(charsBefore []rune, r rune) bool { if r == '`' { return len(charsBefore) == 0 || countBackslashSuffixes(charsBefore)%2 == 0 } return false } func (p *BacktickParameters) ShouldLeave(charsBefore []rune, r rune) bool { if r == '`' { return len(charsBefore) == 0 || countBackslashSuffixes(charsBefore)%2 == 0 } return false } func (b *Backtick) Expand(state State, stdin io.Reader, stderr io.Writer) ([]string, uint8) { var ret uint8 stdout := bytes.NewBuffer([]byte{}) for _, token := range b.Tokens() { if eval, ok := token.(Evalable); ok { ret = state.Eval(eval, stdin, stdout, stderr) } else { logstr := "shell: Unexpected token: " + token.String() log.Println(logstr) stderr.Write([]byte(logstr)) } } return []string{stdout.String()}, ret } func (p *BacktickParameters) SubParsers() []Parser { return []Parser{ &BaseParser{ parameters: &StatementInsideBacktickparameters{}, }, } } type StatementInsideBacktickparameters struct { StatementParameters } // Note: this is a bit of a hack. There is no way to know whether the new // backtick is supposed to open a new backtick statement within the backtick, or // close the current one. At least not in the current implementation that // doesn't allow us to look ahead. Which means that backticks inside backticks // are not supported. func (p *StatementInsideBacktickparameters) ShouldLeave(charsBefore []rune, r rune) bool { if r == '\n' || r == ';' || r == ')' || r == '`' { return countBackslashSuffixes(charsBefore)%2 == 0 } return false } func (p *StatementInsideBacktickparameters) Leave(i *CharIterator) error { if i.Current() == '`' { return i.Previous() } return nil }