class DDQL::Parser

Attributes

tokens[R]

Public Class Methods

from_tokens(tokens) click to toggle source
# File lib/ddql/parser.rb, line 26
def self.from_tokens(tokens)
  opener = tokens.doubly_linked!.find { |node| node.value.type == TokenType::NESTED_OPENER }
  if opener
    closer = tokens.find_from_tail { |node| node.value.type == TokenType::NESTED_CLOSER }
    new_tokens  = DDQL::LinkedList.new
    current = opener
    while (current = current.next) && !(current === closer)
      new_tokens << current.dup
    end
    new_tokens.tail.next = nil
    new(tokens: tokens.replace!(
      from: opener,
      to: closer,
      with: ResolvedToken.new(sub_query: from_tokens(new_tokens).parse)),
    )
  else
    new(tokens: tokens)
  end
end
new(expression: nil, tokens: nil) click to toggle source
# File lib/ddql/parser.rb, line 50
def initialize(expression: nil, tokens: nil)
  @expression = expression
  @depth      = 0
  if expression
    @tokens = Lexer.lex(expression)
  else
    @tokens = tokens
  end
  raise "tokens cannot be determined" if @tokens.nil? || @tokens.empty?
end
parse(expr) click to toggle source
# File lib/ddql/parser.rb, line 46
def self.parse(expr)
  from_tokens(Lexer.lex(expr)).record_expression(expr).parse
end

Public Instance Methods

consume(token_type) click to toggle source
# File lib/ddql/parser.rb, line 61
def consume(token_type)
  token = @tokens.poll
  if token.nil? || token_type != token.type
    message  = "Expected token[#{token_type.name}], got #{token.nil? ? 'nil' : "[#{token.type.name}]"}\n"
    if token
      message += "    #{@expression}\n"
      message += "    #{' ' * token.location}^"
    end
    raise ParseException, message
  end
  token
end
parse(precedence: 0) click to toggle source
# File lib/ddql/parser.rb, line 74
def parse(precedence: 0)
  @depth += 1
  token   = @tokens.poll

  raise NoTokenException if token.nil?

  expression = token.parse(self)
  while precedence < next_precedence
    token = @tokens.poll
    expression = token.parse(self, expression: expression)
  end
  @depth -= 1

  if @depth == 0 && !peek.nil?
    raise ParseException, "Unable to fully parse expression; token[#{peek}], possible cause: invalid operator"
  end

  expression
end
peek() click to toggle source
# File lib/ddql/parser.rb, line 94
def peek
  @tokens.peek
end
record_expression(expr) click to toggle source
# File lib/ddql/parser.rb, line 98
def record_expression(expr)
  @expression = expr
  self
end
until(token_type) click to toggle source

supports reading until the next token_type, does not support nested reads of token_type

@raise [RuntimeError] if token_type is not found @return [LinkedList<Token>] tokens exclusive of the final token_type

# File lib/ddql/parser.rb, line 108
def until(token_type)
  new_list = LinkedList.new.doubly_linked!
  while token = @tokens.poll
    if token.type?(token_type)
      return new_list
    else
      new_list << token
    end
  end
  raise "expected to consume tokens up to type[#{token_type.name}]"
end

Private Instance Methods

next_precedence() click to toggle source
# File lib/ddql/parser.rb, line 121
def next_precedence
  token = peek
  return 0 if token.nil?
  Operators.instance.cache[token.op_data]&.precedence || 0
end