module Basic101 class Program include Enumerable include Identity attr_reader :statements def self.load_file(source_path) File.open(source_path, 'r') do |file| load(file) end end def self.load(source_file) source = source_file.read parser = Parser.new transform = Transform.new tree = parser.parse(source) transform.apply(tree) rescue Parslet::ParseFailed => error line_number, column_number = error.cause.source.line_and_column(error.cause.pos) line = source.lines[line_number - 1] raise SyntaxError.new(line, line_number, column_number, error.message) end def initialize(lines = []) @statements = [] @line_number_index = {} lines.each do |line| @line_number_index[line.line_number] = @statements.size @statements += line.statements end set_statement_indices link_if_statements end def statement_count @statements.size end def [](i) @statements[i] end def index_of_line(line_number) index = @line_number_index[line_number] unless index raise UndefinedLineNumberError, "Undefined line number #{line_number}" end index end def data_items(starting_line_number = nil) if starting_line_number statements = @statements.select do |statement| statement.line_number >= starting_line_number end else statements = @statements end statements.flat_map(&:data_items) end protected def state [@statements] end private def link_if_statements stack = [] @statements.each do |statement| case statement when IfStatement stack.push statement when ElseStatement if_statement = stack.last if_statement.else_statement = statement when EndifStatement if_statement = stack.pop if_statement.end_statement = statement end end end def set_statement_indices @statements.each.with_index do |statement, i| statement.index = i end end end end