github.com/mithrandie/csvq@v1.18.1/lib/excmd/args_splitter.go (about)

     1  package excmd
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"unicode"
     7  
     8  	"github.com/mithrandie/csvq/lib/parser"
     9  )
    10  
    11  type ArgsSplitter struct {
    12  	src    []rune
    13  	srcPos int
    14  	text   bytes.Buffer
    15  
    16  	err error
    17  }
    18  
    19  func (s *ArgsSplitter) Init(src string) *ArgsSplitter {
    20  	s.src = []rune(src)
    21  	s.srcPos = 0
    22  	s.text.Reset()
    23  	s.err = nil
    24  	return s
    25  }
    26  
    27  func (s *ArgsSplitter) Err() error {
    28  	return s.err
    29  }
    30  
    31  func (s *ArgsSplitter) Text() string {
    32  	return s.text.String()
    33  }
    34  
    35  func (s *ArgsSplitter) peek() rune {
    36  	if len(s.src) <= s.srcPos {
    37  		return EOF
    38  	}
    39  
    40  	return s.src[s.srcPos]
    41  }
    42  
    43  func (s *ArgsSplitter) next() rune {
    44  	ch := s.peek()
    45  	if ch == EOF {
    46  		return ch
    47  	}
    48  
    49  	s.srcPos++
    50  	return ch
    51  }
    52  
    53  func (s *ArgsSplitter) Scan() bool {
    54  	for unicode.IsSpace(s.peek()) {
    55  		s.next()
    56  	}
    57  
    58  	ch := s.next()
    59  	s.text.Reset()
    60  
    61  	switch ch {
    62  	case EOF:
    63  		return false
    64  	case parser.VariableSign:
    65  		s.text.WriteRune(ch)
    66  
    67  		if s.peek() == parser.EnvironmentVariableSign {
    68  			s.text.WriteRune(s.next())
    69  			if s.peek() == '`' {
    70  				s.text.WriteRune(s.next())
    71  				s.scanQuotedVariable('`')
    72  			} else {
    73  				s.scanString()
    74  			}
    75  		} else {
    76  			s.scanString()
    77  		}
    78  	case parser.ExternalCommandSign:
    79  		s.text.WriteRune(ch)
    80  		if s.peek() != parser.BeginExpression {
    81  			s.err = errors.New("invalid command symbol")
    82  		} else {
    83  			s.text.WriteRune(s.next())
    84  			s.scanExternalCommand()
    85  		}
    86  	case '"', '\'':
    87  		s.text.WriteRune(ch)
    88  		s.scanQuotedString(ch)
    89  	default:
    90  		s.text.WriteRune(ch)
    91  		s.scanString()
    92  	}
    93  
    94  	return s.err == nil
    95  }
    96  
    97  func (s *ArgsSplitter) scanQuotedVariable(quote rune) {
    98  	for {
    99  		ch := s.next()
   100  		if ch == EOF {
   101  			s.err = errors.New("environment variable not terminated")
   102  			break
   103  		}
   104  
   105  		s.text.WriteRune(ch)
   106  
   107  		if ch == quote {
   108  			break
   109  		}
   110  
   111  		if ch == '\\' {
   112  			switch s.peek() {
   113  			case '\\', quote:
   114  				s.text.WriteRune(s.next())
   115  			}
   116  		}
   117  	}
   118  }
   119  
   120  func (s *ArgsSplitter) scanQuotedString(quote rune) {
   121  	for {
   122  		ch := s.next()
   123  		if ch == EOF {
   124  			s.err = errors.New("string not terminated")
   125  			break
   126  		}
   127  
   128  		if ch == '\\' {
   129  			switch s.peek() {
   130  			case '\\', quote:
   131  				s.text.WriteRune(ch)
   132  				ch = s.next()
   133  			}
   134  		} else if ch == quote {
   135  			s.text.WriteRune(ch)
   136  			if s.peek() == quote {
   137  				ch = s.next()
   138  			} else {
   139  				break
   140  			}
   141  		}
   142  
   143  		s.text.WriteRune(ch)
   144  	}
   145  }
   146  
   147  func (s *ArgsSplitter) scanString() {
   148  	for {
   149  		if s.peek() == EOF || unicode.IsSpace(s.peek()) {
   150  			break
   151  		}
   152  		s.text.WriteRune(s.next())
   153  	}
   154  }
   155  
   156  func (s *ArgsSplitter) scanExternalCommand() {
   157  	for {
   158  		ch := s.next()
   159  		if ch == EOF {
   160  			s.err = errors.New("command not terminated")
   161  			break
   162  		}
   163  
   164  		if ch == parser.EndExpression {
   165  			s.text.WriteRune(ch)
   166  			break
   167  		}
   168  
   169  		if ch == '\\' {
   170  			switch s.peek() {
   171  			case parser.BeginExpression, parser.EndExpression:
   172  				ch = s.next()
   173  			}
   174  		}
   175  		s.text.WriteRune(ch)
   176  	}
   177  }