main
Raw Download raw file
  1package ssh_config
  2
  3import (
  4	"bytes"
  5)
  6
  7// Define state functions
  8type sshLexStateFn func() sshLexStateFn
  9
 10type sshLexer struct {
 11	inputIdx int
 12	input    []rune // Textual source
 13
 14	buffer        []rune // Runes composing the current token
 15	tokens        chan token
 16	line          int
 17	col           int
 18	endbufferLine int
 19	endbufferCol  int
 20}
 21
 22func (s *sshLexer) lexComment(previousState sshLexStateFn) sshLexStateFn {
 23	return func() sshLexStateFn {
 24		growingString := ""
 25		for next := s.peek(); next != '\n' && next != eof; next = s.peek() {
 26			if next == '\r' && s.follow("\r\n") {
 27				break
 28			}
 29			growingString += string(next)
 30			s.next()
 31		}
 32		s.emitWithValue(tokenComment, growingString)
 33		s.skip()
 34		return previousState
 35	}
 36}
 37
 38// lex the space after an equals sign in a function
 39func (s *sshLexer) lexRspace() sshLexStateFn {
 40	for {
 41		next := s.peek()
 42		if !isSpace(next) {
 43			break
 44		}
 45		s.skip()
 46	}
 47	return s.lexRvalue
 48}
 49
 50func (s *sshLexer) lexEquals() sshLexStateFn {
 51	for {
 52		next := s.peek()
 53		if next == '=' {
 54			s.emit(tokenEquals)
 55			s.skip()
 56			return s.lexRspace
 57		}
 58		// TODO error handling here; newline eof etc.
 59		if !isSpace(next) {
 60			break
 61		}
 62		s.skip()
 63	}
 64	return s.lexRvalue
 65}
 66
 67func (s *sshLexer) lexKey() sshLexStateFn {
 68	growingString := ""
 69
 70	for r := s.peek(); isKeyChar(r); r = s.peek() {
 71		// simplified a lot here
 72		if isSpace(r) || r == '=' {
 73			s.emitWithValue(tokenKey, growingString)
 74			s.skip()
 75			return s.lexEquals
 76		}
 77		growingString += string(r)
 78		s.next()
 79	}
 80	s.emitWithValue(tokenKey, growingString)
 81	return s.lexEquals
 82}
 83
 84func (s *sshLexer) lexRvalue() sshLexStateFn {
 85	growingString := ""
 86	for {
 87		next := s.peek()
 88		switch next {
 89		case '\r':
 90			if s.follow("\r\n") {
 91				s.emitWithValue(tokenString, growingString)
 92				s.skip()
 93				return s.lexVoid
 94			}
 95		case '\n':
 96			s.emitWithValue(tokenString, growingString)
 97			s.skip()
 98			return s.lexVoid
 99		case '#':
100			s.emitWithValue(tokenString, growingString)
101			s.skip()
102			return s.lexComment(s.lexVoid)
103		case eof:
104			s.next()
105		}
106		if next == eof {
107			break
108		}
109		growingString += string(next)
110		s.next()
111	}
112	s.emit(tokenEOF)
113	return nil
114}
115
116func (s *sshLexer) read() rune {
117	r := s.peek()
118	if r == '\n' {
119		s.endbufferLine++
120		s.endbufferCol = 1
121	} else {
122		s.endbufferCol++
123	}
124	s.inputIdx++
125	return r
126}
127
128func (s *sshLexer) next() rune {
129	r := s.read()
130
131	if r != eof {
132		s.buffer = append(s.buffer, r)
133	}
134	return r
135}
136
137func (s *sshLexer) lexVoid() sshLexStateFn {
138	for {
139		next := s.peek()
140		switch next {
141		case '#':
142			s.skip()
143			return s.lexComment(s.lexVoid)
144		case '\r':
145			fallthrough
146		case '\n':
147			s.emit(tokenEmptyLine)
148			s.skip()
149			continue
150		}
151
152		if isSpace(next) {
153			s.skip()
154		}
155
156		if isKeyStartChar(next) {
157			return s.lexKey
158		}
159
160		// removed IsKeyStartChar and lexKey. probably will need to readd
161
162		if next == eof {
163			s.next()
164			break
165		}
166	}
167
168	s.emit(tokenEOF)
169	return nil
170}
171
172func (s *sshLexer) ignore() {
173	s.buffer = make([]rune, 0)
174	s.line = s.endbufferLine
175	s.col = s.endbufferCol
176}
177
178func (s *sshLexer) skip() {
179	s.next()
180	s.ignore()
181}
182
183func (s *sshLexer) emit(t tokenType) {
184	s.emitWithValue(t, string(s.buffer))
185}
186
187func (s *sshLexer) emitWithValue(t tokenType, value string) {
188	tok := token{
189		Position: Position{s.line, s.col},
190		typ:      t,
191		val:      value,
192	}
193	s.tokens <- tok
194	s.ignore()
195}
196
197func (s *sshLexer) peek() rune {
198	if s.inputIdx >= len(s.input) {
199		return eof
200	}
201
202	r := s.input[s.inputIdx]
203	return r
204}
205
206func (s *sshLexer) follow(next string) bool {
207	inputIdx := s.inputIdx
208	for _, expectedRune := range next {
209		if inputIdx >= len(s.input) {
210			return false
211		}
212		r := s.input[inputIdx]
213		inputIdx++
214		if expectedRune != r {
215			return false
216		}
217	}
218	return true
219}
220
221func (s *sshLexer) run() {
222	for state := s.lexVoid; state != nil; {
223		state = state()
224	}
225	close(s.tokens)
226}
227
228func lexSSH(input []byte) chan token {
229	runes := bytes.Runes(input)
230	l := &sshLexer{
231		input:         runes,
232		tokens:        make(chan token),
233		line:          1,
234		col:           1,
235		endbufferLine: 1,
236		endbufferCol:  1,
237	}
238	go l.run()
239	return l.tokens
240}