/brainfuck/lib/brainfuck_parser.rb

https://github.com/vybirjan/MI-RUB-ukoly · Ruby · 123 lines · 97 code · 22 blank · 4 comment · 9 complexity · 39bca8314b528cd7626e6eaa8446b0d0 MD5 · raw file

  1. require_relative 'brainfuck_ast.rb'
  2. INC_POINTER = '>'
  3. DEC_POINTER = '<'
  4. INC_VALUE = '+'
  5. DEC_VALUE = '-'
  6. PRINT_VALUE = '.'
  7. READ_VALUE = ','
  8. LOOP_START = '['
  9. LOOP_END = ']'
  10. NEW_LINE = "\n"
  11. class BrainfuckParseError < RuntimeError
  12. attr_reader :line
  13. attr_reader :column
  14. def initialize(message, line, column)
  15. super(message)
  16. @line = line
  17. @column = column
  18. end
  19. end
  20. class BrainfuckParser
  21. def initialize
  22. @current_line = 1
  23. @current_column = 1
  24. @first_command = nil
  25. @previous_command = nil
  26. @loop_starts = []
  27. end
  28. def parse_code(text)
  29. text.each_char {|char|
  30. case char
  31. when INC_POINTER
  32. initialize_element(IncrementPointer.new(@current_line, @current_column))
  33. when DEC_POINTER
  34. initialize_element(DecrementPointer.new(@current_line, @current_column))
  35. when INC_VALUE
  36. initialize_element(IncrementValue.new(@current_line, @current_column))
  37. when DEC_VALUE
  38. initialize_element(DecrementValue.new(@current_line, @current_column))
  39. when PRINT_VALUE
  40. initialize_element(PrintValue.new(@current_line, @current_column))
  41. when READ_VALUE
  42. initialize_element(ReadInput.new(@current_line, @current_column))
  43. when LOOP_START
  44. case_loop_start
  45. when LOOP_END
  46. case_loop_end
  47. when NEW_LINE
  48. @current_line = @current_line + 1
  49. @current_column = 1
  50. else
  51. #ignore all other characters
  52. end
  53. @current_column = @current_column + 1
  54. }
  55. return self
  56. end
  57. def get_start
  58. if(@previous_command.is_a?(LoopEnd))
  59. @loop_starts.pop
  60. end
  61. if(!@loop_starts.empty?)
  62. start = @loop_starts.pop
  63. raise BrainfuckParseError.new("Unexpected end of file - unclosed loop found", start.line, start.column)
  64. end
  65. return @first_command
  66. end
  67. private
  68. def initialize_element(elm)
  69. # set as first command if its first
  70. if(@first_command == nil)
  71. @first_command = elm
  72. end
  73. #finish loop pointers
  74. if(@previous_command.is_a?(LoopEnd))
  75. if(@loop_starts.empty?)
  76. raise BrainfuckParseError.new("Unexpected loop end - missing matching loop start", @current_line, @current_column)
  77. end
  78. loopStart = @loop_starts.pop
  79. loopStart.loopEnd = elm
  80. end
  81. #link previous command to this one
  82. if(@previous_command != nil)
  83. @previous_command.next = elm
  84. end
  85. @previous_command = elm
  86. end
  87. def case_loop_start
  88. start = LoopStart.new(@current_line, @current_column)
  89. initialize_element(start)
  90. @loop_starts.push(start)
  91. end
  92. def case_loop_end
  93. loopEnd = LoopEnd.new(@current_line, @current_column)
  94. initialize_element(loopEnd)
  95. if(@loop_starts.empty?)
  96. raise BrainfuckParseError.new("Unexpected loop end - missing matching loop start", @current_line, @current_column)
  97. end
  98. loopStart = @loop_starts.last
  99. loopEnd.loopHead = loopStart.next
  100. end
  101. end