david-mccullars/scim2-filter

View on GitHub
lib/scim2/filter/parser.racc

Summary

Maintainability
Test Coverage
class Scim2::Filter::Parser

token EQ NE GT GE LT LE CO SW EW PR AND OR NOT LPAREN RPAREN LBRACKET RBRACKET
token NULL BOOLEAN NUMBER STRING ATTRNAME SCHEMA DOT

rule
  #
  # We must separate OR and AND filter rules in order to ensure the order of operations
  # is properly followed.  Specifically, the following two filters are the same:
  #
  #   w AND x OR y AND z
  #   (w AND x) OR (y AND z)
  #
  # Separating the rules the parser will aggressively gobble up AND rules first, leaving
  # OR rules for the very last as it bubbles back up the recursion stack
  #
  filter
    : non_or_filter
    | filter OR non_or_filter
        {
          filter1, op, filter2 = val
          result = handler.on_logical_filter(filter1, filter2, op: op, context: result)
        }

  non_or_filter
    : non_boolean_filter
    | non_or_filter AND non_boolean_filter
        {
          filter1, op, filter2 = val
          result = handler.on_logical_filter(filter1, filter2, op: op, context: result)
        }

  non_boolean_filter
    : attribute_filter
    | nested_filter
    | grouped_filter
    | not_filter

  attribute_filter
    : attr_path PR
        {
          attr, op = val
          result = handler.on_attribute_filter(attr[:path], nil, op: op, schema: attr[:schema], context: result)
        }
    | attr_path comp_op comp_value
        {
          attr, op, v = val
          result = handler.on_attribute_filter(attr[:path], v, op: op, schema: attr[:schema], context: result)
        }

  nested_filter
    : nested_filter_start filter RBRACKET
      {
        attr, filter, _ = val
        result = handler.on_nested_filter(attr[:path], filter, schema: attr[:schema], context: result)
      }

  nested_filter_start
    : attr_path LBRACKET
      {
        attr, _ = val
        handler.before_nested_filter(attr[:path], schema: attr[:schema], context: result) if handler.respond_to?(:before_nested_filter)
        result = attr
      }

  grouped_filter
    : LPAREN filter RPAREN
      {
        result = val[1]
      }

  not_filter
    : NOT grouped_filter
      {
        _, filter = val
        result = handler.on_not_filter(filter, context: result)
      }

  attr_path
    : SCHEMA attr_path_elements
        {
          schema, path = val
          result = { schema: schema, path: path }
        }
    | attr_path_elements
        {
          result = { path: val.last }
        }

  attr_path_elements
    : attr_path_elements DOT ATTRNAME
        {
          result = [*val.first, val.last]
        }
    | ATTRNAME
        {
          result = [val.last]
        }

  comp_op
    : EQ
    | NE
    | GT
    | GE
    | LT
    | LE
    | CO
    | SW
    | EW

  comp_value
    : BOOLEAN
    | NULL
    | NUMBER
    | STRING
end