lexer.go 4.1 KB

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