sourcebrainfuck::Lexer.fan

**
** Transform the sourcecode in a list of `Token` ready to be 
** interpreted.
**
class Lexer
{
  **
  ** Current char in source code
  **
  Int index := 0 { private set }
  
  **
  ** Current line number in source code
  **
  Int line := 1 { private set }
  
  **
  ** Current column number in source code
  **
  Int column := 1 { private set }
  
  **
  ** Tokens readed
  **
  Token[] tokens := Token[,] { private set }

  **
  ** Last instruction read.
  **
  Instruction? instruction { private set }

  ** 
  ** Last token readed
  **
  Token? token { private set }
  
  **
  ** Source code
  **
  Str source { private set }

  **
  ** Creates the lexer
  **
  new make(Str source)
  {
    this.source = source
  }

  **
  ** Current char in source code
  **
  Int currentChar()
  {
    return source[index]
  }

  **
  ** Advance the reader to the next character
  **
  Void advance()
  {
    ++index
    ++column
  }

  **
  ** 'true' if finished reading the source code
  **
  Bool isRunning()
  {
    return index < source.size
  }

  Void addLastToken()
  {
    if (token != null ) 
    {
      tokens.add(token)
    }
  }

  Void readInstruction()
  {
    switch(currentChar)
    {
      case  '+': 
        instruction = Instruction.incrementData
      case  '-': 
        instruction = Instruction.decrementData
      case  '>': 
        instruction = Instruction.incrementPointer
      case  '<': 
        instruction = Instruction.decrementPointer
      case  ',': 
        instruction = Instruction.input
      case  '.': 
        instruction = Instruction.output
      case  '[': 
        instruction = Instruction.jumpForward
      case  ']': 
        instruction = Instruction.jumpBackward
      case '\n':
        ++line; column = 1
        instruction = null
      default:
        instruction = null
    }
  }

  Void readToken()
  {    
    if (instruction != null) 
      token = Token(instruction, line, column)
    else
      token =  null
  }

  This tokenize()
  {
    while(isRunning)
    {
      readInstruction
      readToken
      addLastToken
      advance
    }
    
    return this
  }
}