class Omnium::Parser::Core

The parser will verify the format of a list of tokens (syntax analysis) by 'recursive-descent'.

Public Class Methods

new(lexer) click to toggle source
# File lib/omnium/parser/core.rb, line 12
def initialize(lexer)
  @lexer = lexer
  @token = nil
  @consumed_token = nil
end

Public Instance Methods

parse() click to toggle source
# File lib/omnium/parser/core.rb, line 18
def parse
  @token = @lexer.next_token
  node = program

  error(expected_type: eof_token, actual_type: @token.type) unless eof?

  node
end

Private Instance Methods

assignment_statement() click to toggle source
# File lib/omnium/parser/core.rb, line 126
def assignment_statement
  # assignment_statement : variable ASSIGN expr
  left = identifier
  consume(assignment_token)

  Assignment.new(left, @consumed_token, expr)
end
block() click to toggle source
# File lib/omnium/parser/core.rb, line 40
def block
  # block : variable_declarations compound_statement
  Block.new(variable_declarations, compound_statement)
end
compound_statement() click to toggle source
# File lib/omnium/parser/core.rb, line 89
def compound_statement
  # compound_statement : BEGIN statement_list END
  consume(begin_token)
  nodes = statement_list
  consume(end_token)

  Compound.new.append(nodes)
end
consume(type) click to toggle source
# File lib/omnium/parser/core.rb, line 221
def consume(type)
  # verify the type of @token and advance @token to next_token
  unless type == @token.type
    error(expected_type: type, actual_type: @token.type)
  end

  @consumed_token = @token
  @token = @lexer.next_token
end
empty() click to toggle source
# File lib/omnium/parser/core.rb, line 142
def empty
  # no operation rule
  NoOperation.new
end
expr() click to toggle source
# File lib/omnium/parser/core.rb, line 147
def expr
  # expr : term ((PLUS | MINUS) term)*
  node = term

  while plus? || minus?
    if plus?
      consume(plus_token)
    elsif minus?
      consume(minus_token)
    end

    node = BinaryOperator.new(node, @consumed_token, term)
  end

  node
end
factor() click to toggle source
# File lib/omnium/parser/core.rb, line 181
def factor
  # factor : PLUS  factor
  #        | MINUS factor
  #        | INTEGER
  #        | LPAREN expr RPAREN
  #        | identifier
  if plus?
    consume(plus_token)
    return UnaryOperator.new(@consumed_token, factor)
  elsif minus?
    consume(minus_token)
    return UnaryOperator.new(@consumed_token, factor)
  elsif integer?
    consume(integer_token)
    return Number.new(@consumed_token)
  elsif real?
    consume(real_token)
    return Number.new(@consumed_token)
  elsif left_parenthesis?
    consume(left_parenthesis_token)
    node = expr
    consume(right_parenthesis_token)
    return node
  elsif identifier?
    return identifier
  end

  expected_types = [
    plus_token,
    minus_token,
    integer_token,
    real_token,
    left_parenthesis_token,
    right_parenthesis_token,
    identifier_token
  ]

  error(expected_type: expected_types, actual_type: @token.type)
end
identifier() click to toggle source
# File lib/omnium/parser/core.rb, line 134
def identifier
  # variable : ID
  node = Identifier.new(@token)
  consume(identifier_token)

  node
end
program() click to toggle source
# File lib/omnium/parser/core.rb, line 29
def program
  # program : PROGRAM variable SEMI block DOT
  consume(program_token)
  name = identifier.name
  consume(semicolon_token)
  node = Program.new(name, block)
  consume(dot_token)

  node
end
statement() click to toggle source
# File lib/omnium/parser/core.rb, line 113
def statement
  # statement : compound_statement
  #           | assignment_statement
  #           | empty
  if begin?
    return compound_statement
  elsif identifier?
    return assignment_statement
  end

  empty
end
statement_list() click to toggle source
# File lib/omnium/parser/core.rb, line 98
def statement_list
  # statement_list : statement
  #               | statement SEMI statement_list
  nodes = [statement]

  while semicolon?
    consume(semicolon_token)
    nodes << statement
  end

  error("Invalid identifier found: #{@token.inspect}") if identifier?

  nodes
end
term() click to toggle source
# File lib/omnium/parser/core.rb, line 164
def term
  # term : factor ((MULTIPLY | DIVIDE) factor)*
  node = factor

  while multiply? || divide?
    if multiply?
      consume(multiply_token)
    elsif divide?
      consume(divide_token)
    end

    node = BinaryOperator.new(node, @consumed_token, factor)
  end

  node
end
variable_data_type() click to toggle source
# File lib/omnium/parser/core.rb, line 77
def variable_data_type
  # variable_data_type : INTEGER
  #                    | FLOAT
  if int?
    consume(int_token)
  elsif float?
    consume(float_token)
  end

  DataType.new(@consumed_token)
end
variable_declaration() click to toggle source
# File lib/omnium/parser/core.rb, line 62
def variable_declaration
  # variable_declaration : ID (COMMA ID)* COLON variable_data_type
  identifiers = [identifier]

  while comma?
    consume(comma_token)
    identifiers << identifier
  end

  consume(colon_token)

  data_type = variable_data_type
  identifiers.map { |identifier| VariableDeclaration.new(identifier, data_type) }
end
variable_declarations() click to toggle source
# File lib/omnium/parser/core.rb, line 45
def variable_declarations
  # variable_declarations : VAR (variable_declaration SEMI)+
  #                       | empty
  declarations = []

  if var?
    consume(var_token)

    while identifier?
      declarations << variable_declaration
      consume(semicolon_token)
    end
  end

  declarations.flatten
end