class SCIM::Query::Filter::Parser

Constants

IsOperator
NextToken
Op
Ops
Paren

Tokenizing regexen:

Sep
Str
Unary
Word

Attributes

rpn[RW]

Public Instance Methods

assert_close() click to toggle source
# File lib/scim/query/filter/parser.rb, line 144
def assert_close
  return true if peek == ')'
  parse_error "Unexpected token '%s'. Expected ')'"
end
assert_eos() click to toggle source
# File lib/scim/query/filter/parser.rb, line 149
def assert_eos
  return true if eos
  parse_error "Unexpected token '%s'. Expected EOS"
end
assert_not_op() click to toggle source
# File lib/scim/query/filter/parser.rb, line 139
def assert_not_op
  return true if ! peek_operator
  parse_error "Unexpected operator '%s'"
end
assert_op() click to toggle source
# File lib/scim/query/filter/parser.rb, line 134
def assert_op
  return true if peek_operator
  parse_error "Unexpected token '%s'. Expected operator"
end
eos() click to toggle source
# File lib/scim/query/filter/parser.rb, line 122
def eos;  @tokens.empty? end
get_tree() click to toggle source
# File lib/scim/query/filter/parser.rb, line 107
def get_tree
  tree = []
  if not @stack.empty?
    op = tree[0] = @stack.pop
    tree[1] = Ops[@stack.last] ? get_tree : @stack.pop
    tree.insert 1, Ops[@stack.last] ? get_tree : @stack.pop \
      if not Unary[op]
  end
  tree
end
lex(input) click to toggle source

Split input into tokens

# File lib/scim/query/filter/parser.rb, line 70
def lex input
  input = input.clone
  tokens = []
  while ! input.empty? do
    input.sub! NextToken, '' \
      or fail "Can't lex input here: '#{input}'"
    tokens.push $1
  end
  tokens
end
parse(input) click to toggle source
# File lib/scim/query/filter/parser.rb, line 35
def parse input
  @input = input                  # Save for error msgs
  @tokens = lex input
  @rpn = parse_expr
  assert_eos
  self
end
parse_error(msg) click to toggle source

Error handling methods:

# File lib/scim/query/filter/parser.rb, line 130
def parse_error msg
  fail "#{sprintf(msg, *@tokens, 'EOS')}.\nInput: '#{@input}'\n"
end
parse_expr() click to toggle source
# File lib/scim/query/filter/parser.rb, line 43
def parse_expr
  ast = []
  until eos or peek == ')'
    assert_not_op
    ast.push(start_group ? parse_group : pop.downcase)
    break if (eos or peek == ')')
    assert_op
    ast.push pop.downcase
    if !Unary[ast.last]
      assert_not_op
      ast.push(start_group ? parse_group : pop)
    end
    break if eos or peek == ')'
    assert_op
    ast.push pop.downcase
  end
  to_rpn ast
end
parse_group() click to toggle source
# File lib/scim/query/filter/parser.rb, line 62
def parse_group
  pop                 # pop '(' token
  ast = parse_expr
  assert_close && pop # pop ')' token
  ast
end
peek() click to toggle source
# File lib/scim/query/filter/parser.rb, line 120
def peek; @tokens.first  end
peek_operator() click to toggle source
# File lib/scim/query/filter/parser.rb, line 124
def peek_operator
  not(eos) and peek.downcase.match IsOperator
end
pop() click to toggle source
# File lib/scim/query/filter/parser.rb, line 121
def pop;  @tokens.shift  end
start_group() click to toggle source
# File lib/scim/query/filter/parser.rb, line 123
def start_group; peek == '(' end
to_rpn(ast) click to toggle source

Turn parsed tokens into an RPN stack en.wikipedia.org/wiki/Shunting_yard_algorithm

# File lib/scim/query/filter/parser.rb, line 83
def to_rpn ast
  out, ops = [], []
  out.push ast.shift if not ast.empty?
  while not ast.empty? do
    op = ast.shift
    precedence = Ops[op] \
      or fail "Unknown operator '#{op}'"
    while not ops.empty? do
      break if precedence > Ops[ops.first]
      out.push ops.shift
    end
    ops.unshift op
    out.push ast.shift if not Unary[op]
  end
  (out.concat ops).flatten
end
tree() click to toggle source
# File lib/scim/query/filter/parser.rb, line 102
def tree
  @stack = @rpn.clone
  get_tree
end