- package ssh_config
-
- import (
- "bytes"
- )
-
- // Define state functions
- type sshLexStateFn func() sshLexStateFn
-
- type sshLexer struct {
- inputIdx int
- input []rune // Textual source
-
- buffer []rune // Runes composing the current token
- tokens chan token
- line int
- col int
- endbufferLine int
- endbufferCol int
- }
-
- func (s *sshLexer) lexComment(previousState sshLexStateFn) sshLexStateFn {
- return func() sshLexStateFn {
- growingString := ""
- for next := s.peek(); next != '\n' && next != eof; next = s.peek() {
- if next == '\r' && s.follow("\r\n") {
- break
- }
- growingString += string(next)
- s.next()
- }
- s.emitWithValue(tokenComment, growingString)
- s.skip()
- return previousState
- }
- }
-
- // lex the space after an equals sign in a function
- func (s *sshLexer) lexRspace() sshLexStateFn {
- for {
- next := s.peek()
- if !isSpace(next) {
- break
- }
- s.skip()
- }
- return s.lexRvalue
- }
-
- func (s *sshLexer) lexEquals() sshLexStateFn {
- for {
- next := s.peek()
- if next == '=' {
- s.emit(tokenEquals)
- s.skip()
- return s.lexRspace
- }
- // TODO error handling here; newline eof etc.
- if !isSpace(next) {
- break
- }
- s.skip()
- }
- return s.lexRvalue
- }
-
- func (s *sshLexer) lexKey() sshLexStateFn {
- growingString := ""
-
- for r := s.peek(); isKeyChar(r); r = s.peek() {
- // simplified a lot here
- if isSpace(r) || r == '=' {
- s.emitWithValue(tokenKey, growingString)
- s.skip()
- return s.lexEquals
- }
- growingString += string(r)
- s.next()
- }
- s.emitWithValue(tokenKey, growingString)
- return s.lexEquals
- }
-
- func (s *sshLexer) lexRvalue() sshLexStateFn {
- growingString := ""
- for {
- next := s.peek()
- switch next {
- case '\r':
- if s.follow("\r\n") {
- s.emitWithValue(tokenString, growingString)
- s.skip()
- return s.lexVoid
- }
- case '\n':
- s.emitWithValue(tokenString, growingString)
- s.skip()
- return s.lexVoid
- case '#':
- s.emitWithValue(tokenString, growingString)
- s.skip()
- return s.lexComment(s.lexVoid)
- case eof:
- s.next()
- }
- if next == eof {
- break
- }
- growingString += string(next)
- s.next()
- }
- s.emit(tokenEOF)
- return nil
- }
-
- func (s *sshLexer) read() rune {
- r := s.peek()
- if r == '\n' {
- s.endbufferLine++
- s.endbufferCol = 1
- } else {
- s.endbufferCol++
- }
- s.inputIdx++
- return r
- }
-
- func (s *sshLexer) next() rune {
- r := s.read()
-
- if r != eof {
- s.buffer = append(s.buffer, r)
- }
- return r
- }
-
- func (s *sshLexer) lexVoid() sshLexStateFn {
- for {
- next := s.peek()
- switch next {
- case '#':
- s.skip()
- return s.lexComment(s.lexVoid)
- case '\r':
- fallthrough
- case '\n':
- s.emit(tokenEmptyLine)
- s.skip()
- continue
- }
-
- if isSpace(next) {
- s.skip()
- }
-
- if isKeyStartChar(next) {
- return s.lexKey
- }
-
- // removed IsKeyStartChar and lexKey. probably will need to readd
-
- if next == eof {
- s.next()
- break
- }
- }
-
- s.emit(tokenEOF)
- return nil
- }
-
- func (s *sshLexer) ignore() {
- s.buffer = make([]rune, 0)
- s.line = s.endbufferLine
- s.col = s.endbufferCol
- }
-
- func (s *sshLexer) skip() {
- s.next()
- s.ignore()
- }
-
- func (s *sshLexer) emit(t tokenType) {
- s.emitWithValue(t, string(s.buffer))
- }
-
- func (s *sshLexer) emitWithValue(t tokenType, value string) {
- tok := token{
- Position: Position{s.line, s.col},
- typ: t,
- val: value,
- }
- s.tokens <- tok
- s.ignore()
- }
-
- func (s *sshLexer) peek() rune {
- if s.inputIdx >= len(s.input) {
- return eof
- }
-
- r := s.input[s.inputIdx]
- return r
- }
-
- func (s *sshLexer) follow(next string) bool {
- inputIdx := s.inputIdx
- for _, expectedRune := range next {
- if inputIdx >= len(s.input) {
- return false
- }
- r := s.input[inputIdx]
- inputIdx++
- if expectedRune != r {
- return false
- }
- }
- return true
- }
-
- func (s *sshLexer) run() {
- for state := s.lexVoid; state != nil; {
- state = state()
- }
- close(s.tokens)
- }
-
- func lexSSH(input []byte) chan token {
- runes := bytes.Runes(input)
- l := &sshLexer{
- input: runes,
- tokens: make(chan token),
- line: 1,
- col: 1,
- endbufferLine: 1,
- endbufferCol: 1,
- }
- go l.run()
- return l.tokens
- }
|