Class: DDQL::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/ddql/parser.rb

Defined Under Namespace

Classes: NoTokenException, ParseException, ResolvedToken

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(expression: nil, tokens: nil) ⇒ Parser

Returns a new instance of Parser.



50
51
52
53
54
55
56
57
58
59
# 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

Instance Attribute Details

#tokensObject (readonly)

Returns the value of attribute tokens.



3
4
5
# File 'lib/ddql/parser.rb', line 3

def tokens
  @tokens
end

Class Method Details

.from_tokens(tokens) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 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

.parse(expr) ⇒ Object



46
47
48
# File 'lib/ddql/parser.rb', line 46

def self.parse(expr)
  from_tokens(Lexer.lex(expr)).record_expression(expr).parse
end

Instance Method Details

#consume(token_type) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
# 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) ⇒ Object

Raises:



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# 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

#peekObject



94
95
96
# File 'lib/ddql/parser.rb', line 94

def peek
  @tokens.peek
end

#record_expression(expr) ⇒ Object



98
99
100
101
# File 'lib/ddql/parser.rb', line 98

def record_expression(expr)
  @expression = expr
  self
end

#until(token_type) ⇒ LinkedList<Token>

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

Returns:

Raises:

  • (RuntimeError)

    if token_type is not found



108
109
110
111
112
113
114
115
116
117
118
# 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