Class: CqlRuby::CqlParser

Inherits:
Object
  • Object
show all
Defined in:
lib/cql_ruby/cql_parser.rb

Overview

Compiles CQL strings into parse trees of CQLNode subtypes.

See Also:

  • href="http://zing.z3950.org/cql/index.html">http://zing.z3950.org/cql/index.html

Constant Summary collapse

V1POINT1 =
12368
V1POINT2 =
12369
V1POINT1SORT =
12370

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(set_compat = CqlParser::V1POINT2) ⇒ CqlParser

Returns a new instance of CqlParser.



15
16
17
18
19
# File 'lib/cql_ruby/cql_parser.rb', line 15

def initialize( set_compat = CqlParser::V1POINT2 )
  @compat = set_compat
  @debug = false
  @lex_debug = false
end

Instance Attribute Details

#compatObject

Returns the value of attribute compat.



9
10
11
# File 'lib/cql_ruby/cql_parser.rb', line 9

def compat
  @compat
end

#debugObject

Returns the value of attribute debug.



9
10
11
# File 'lib/cql_ruby/cql_parser.rb', line 9

def debug
  @debug
end

#lex_debugObject

Returns the value of attribute lex_debug.



9
10
11
# File 'lib/cql_ruby/cql_parser.rb', line 9

def lex_debug
  @lex_debug
end

#lexerObject

Returns the value of attribute lexer.



9
10
11
# File 'lib/cql_ruby/cql_parser.rb', line 9

def lexer
  @lexer
end

Instance Method Details

#gather_modifiers(base) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/cql_ruby/cql_parser.rb', line 84

def gather_modifiers( base )
  log "gather modifiers"
  
  ms = ModifierSet.new( base )
  until @lexer.token_type != '/'
    match( '/' )
    raise CqlException,  "parse error: expected modifier, got: #{@lexer.render}" unless  @lexer.word?
    type = @lexer.value.downcase
    match( @lexer.token_type )
    if not relation?
      ms.add_modifier( type )
    else
      comparison = @lexer.render( @lexer.token_type, false )
      match( @lexer.token_type )
      value = match_symbol( "modifier value" )
      ms.add_modifier( type, comparison, value )
    end
  end
  ms
end

#log(s) ⇒ Object



182
183
184
# File 'lib/cql_ruby/cql_parser.rb', line 182

def log( s )
  puts "log: #{s}" if @debug
end

#match(token) ⇒ Object



146
147
148
149
150
151
# File 'lib/cql_ruby/cql_parser.rb', line 146

def match( token )
  if @lexer.token_type != token
    raise CqlException,  "parse exception expected: #{@lexer.render( token, true )}, got: #{@lexer.render(token)}"
  end
  @lexer.next_token
end

#match_symbol(symbol_type) ⇒ Object

Raises:



153
154
155
156
157
158
159
160
161
162
# File 'lib/cql_ruby/cql_parser.rb', line 153

def match_symbol( symbol_type )
  log "in match_symbol(#{symbol_type})"
  
  if CqlLexer.symbol_tokens.include?( @lexer.token_type )
    symbol = @lexer.value
    match( @lexer.token_type )
    return symbol
  end
  raise CqlException, "parse exception match_symbol, expected: #{symbol_type}, got: #{@lexer.render()}"
end

#parse(s) ⇒ Object

Raises:



21
22
23
24
25
26
27
28
29
30
# File 'lib/cql_ruby/cql_parser.rb', line 21

def parse( s )
  @lexer = CqlLexer.new( s, @lex_debug )
  log( "starting parser" )
  @lexer.next_token
  
  root = parse_top_level_prefixes( "cql.serverChoice", CqlRelation.new( @compat == CqlParser::V1POINT2 ? "=" :  "scr" ))
  raise CqlException,  "junk after end: #{@lexer.render}" unless @lexer.eof?
  
  root
end

#parse_prefix(index, relation, top_level) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/cql_ruby/cql_parser.rb', line 164

def parse_prefix( index, relation, top_level )
  log "prefix mapping"
  
  match( '>' )
  name = nil
  identifier = match_symbol( "prefix-name" )
  if @lexer.token_type == '='
    match('=')
    name = identifier
    identifier = match_symbol( "prefix-identifier" )
  end
  node = top_level ? 
    parse_top_level_prefixes( index, relation ) :
    parse_query( index, relation )
  
  CqlPrefixNode.new( name, identifier, node )
end

#parse_query(index, relation) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/cql_ruby/cql_parser.rb', line 59

def parse_query( index, relation )
  log "parse_query"
  
  term = parse_term( index, relation )
  until @lexer.eof? or @lexer.token_type == ')' or @lexer.token_type == CqlLexer::TT_SORTBY
    if [CqlLexer::TT_AND,CqlLexer::TT_OR,CqlLexer::TT_NOT,CqlLexer::TT_PROX].include?( @lexer.token_type)
      token_type = @lexer.token_type
      value = @lexer.value
      match( token_type )
      ms = gather_modifiers( value )
      term2 = parse_term( index, relation )
      term = case token_type
              when CqlLexer::TT_AND then CqlAndNode.new( term, term2, ms )
              when CqlLexer::TT_OR then CqlOrNode.new( term, term2, ms )
              when CqlLexer::TT_NOT then CqlNotNode.new( term, term2, ms )
              when CqlLexer::TT_PROX then CqlProxNode.new( term, term2, ms )
            end
      
    else
      raise CqlException,  "parse exception: expect boolean, got: #{@lexer.render}"
    end
  end
  term
end

#parse_term(index, relation) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/cql_ruby/cql_parser.rb', line 105

def parse_term( index, relation )
  log "parse_term"
  
  while true
    if @lexer.token_type == '('
      log "parenthesised form"
      match( '(' )
      expr = parse_query( index, relation )
      match( ')' )
      return expr
    elsif @lexer.token_type == '>'
      return parse_prefix( index, relation, false )
    end
    
    log "non-parenthesized form"
    word = match_symbol( "index or term" )
    break if not relation? and not @lexer.word? 
    
    index = word
    relstr = @lexer.word? ? @lexer.value : @lexer.render( @lexer.token_type, false )
    relation = CqlRelation.new( relstr )
    match( @lexer.token_type )
    ms = gather_modifiers( relstr )
    relation.set_modifiers( ms )
    log( "index='#{index}', relation='#{relation.to_cql}'")
  end

  # change backslashed double quotes in term to plain double-quotes
  # since the backslashes have served their purpose. 
  word.gsub!('\\"', '"')
  
  node = CqlTermNode.new( index, relation, word )
  log( "made term node #{node.to_cql}" )
  node
end

#parse_top_level_prefixes(index, relation) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/cql_ruby/cql_parser.rb', line 32

def parse_top_level_prefixes( index, relation )
  log "topl level prefix mapping"
  
  if @lexer.token_type == '>'
    return parse_prefix( index, relation, true )
  end
  
  node = parse_query( index, relation )
  if ( @compat == V1POINT2 || @compat == V1POINT1SORT ) && @lexer.token_type == CqlLexer::TT_SORTBY
    match( @lexer.token_type )
    log "sortspec"
    
    sortnode = CqlSortNode.new( node )
    until @lexer.eof?
      sortindex = match_symbol( "sort index" )
      ms = gather_modifiers( sortindex )
      sortnode.add_sort_index( ms )
    end
    
    if sortnode.keys.empty?
      raise CqlException,  "parse exception: no sort keys"
    end
    node = sortnode
  end
  node
end

#relation?Boolean

Returns:

  • (Boolean)


141
142
143
144
# File 'lib/cql_ruby/cql_parser.rb', line 141

def relation?
  log "relation?: checking token_type=#{@lexer.token_type} (#{@lexer.render})"
  ['<','>','=',CqlLexer::TT_LE,CqlLexer::TT_GE,CqlLexer::TT_NE,CqlLexer::TT_EQEQ].include?(@lexer.token_type)
end