All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencypher.v9_0.parser.Expressions.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) Neo4j Sweden AB (http://neo4j.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.opencypher.v9_0.parser

import org.opencypher.v9_0.expressions
import org.opencypher.v9_0.expressions.And
import org.opencypher.v9_0.expressions.Ands
import org.opencypher.v9_0.expressions.ExistsSubClause
import org.opencypher.v9_0.expressions.Expression
import org.opencypher.v9_0.expressions.Or
import org.opencypher.v9_0.expressions.Pattern
import org.opencypher.v9_0.expressions.PatternExpression
import org.opencypher.v9_0.expressions.Variable
import org.opencypher.v9_0.util.InputPosition
import org.parboiled.scala.EMPTY
import org.parboiled.scala.Parser
import org.parboiled.scala.ReductionRule1
import org.parboiled.scala.Rule1
import org.parboiled.scala.Rule2
import org.parboiled.scala.Rule3
import org.parboiled.scala.group

import scala.collection.mutable.ListBuffer

trait Expressions extends Parser
  with Literals
  with Patterns
  with Base {

  // Precedence loosely based on http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence

  def Expression = Expression12

  private def Expression12: Rule1[expressions.Expression] = rule("an expression") {
    Expression11 ~ zeroOrMore(WS ~ (
        group(keyword("OR") ~~ Expression11) ~~>> (Or(_: expressions.Expression, _))
    ): ReductionRule1[expressions.Expression, expressions.Expression])
  }

  private def Expression11: Rule1[expressions.Expression] = rule("an expression") {
    Expression10 ~ zeroOrMore(WS ~ (
        group(keyword("XOR") ~~ Expression10) ~~>> (expressions.Xor(_: expressions.Expression, _))
    ): ReductionRule1[expressions.Expression, expressions.Expression])
  }

  private def Expression10: Rule1[expressions.Expression] = rule("an expression") {
    Expression9 ~ zeroOrMore(WS ~ (
        group(keyword("AND") ~~ Expression9) ~~>> (And(_: expressions.Expression, _))
    ): ReductionRule1[expressions.Expression, expressions.Expression])
  }

  private def Expression9: Rule1[expressions.Expression] = rule("an expression") (
      group(keyword("NOT") ~~ Expression9) ~~>> (expressions.Not(_))
    | Expression8
  )

  private def Expression8: Rule1[expressions.Expression] = rule("comparison expression") {
    val produceComparisons: (expressions.Expression, List[PartialComparison]) => InputPosition => expressions.Expression = comparisons
    Expression7 ~ zeroOrMore(WS ~ PartialComparisonExpression) ~~>> produceComparisons
  }

  private case class PartialComparison(op: (expressions.Expression, expressions.Expression) => InputPosition => expressions.Expression,
                                       expr: expressions.Expression, pos: InputPosition) {
    def apply(lhs: expressions.Expression) = op(lhs, expr)(pos)
  }

  private def PartialComparisonExpression: Rule1[PartialComparison] = (
      group(operator("=") ~~ Expression7) ~~>> { expr: expressions.Expression => pos: InputPosition => PartialComparison(eq, expr, pos) }
    | group(operator("~") ~~ Expression7) ~~>> { expr: expressions.Expression => pos: InputPosition => PartialComparison(eqv, expr, pos) }
    | group(operator("<>") ~~ Expression7) ~~>> { expr: expressions.Expression => pos: InputPosition => PartialComparison(ne, expr, pos) }
    | group(operator("!=") ~~ Expression7) ~~>> { expr: expressions.Expression => pos: InputPosition => PartialComparison(bne, expr, pos) }
    | group(operator("<") ~~ Expression7) ~~>> { expr: expressions.Expression => pos: InputPosition => PartialComparison(lt, expr, pos) }
    | group(operator(">") ~~ Expression7) ~~>> { expr: expressions.Expression => pos: InputPosition => PartialComparison(gt, expr, pos) }
    | group(operator("<=") ~~ Expression7) ~~>> { expr: expressions.Expression => pos: InputPosition => PartialComparison(lte, expr, pos) }
    | group(operator(">=") ~~ Expression7) ~~>> { expr: expressions.Expression => pos: InputPosition => PartialComparison(gte, expr, pos) } )

  private def eq(lhs:expressions.Expression, rhs:expressions.Expression): InputPosition => expressions.Expression = expressions.Equals(lhs, rhs)
  private def eqv(lhs:expressions.Expression, rhs:expressions.Expression): InputPosition => expressions.Expression = expressions.Equivalent(lhs, rhs)
  private def ne(lhs:expressions.Expression, rhs:expressions.Expression): InputPosition => expressions.Expression = expressions.NotEquals(lhs, rhs)
  private def bne(lhs:expressions.Expression, rhs:expressions.Expression): InputPosition => expressions.Expression = expressions.InvalidNotEquals(lhs, rhs)
  private def lt(lhs:expressions.Expression, rhs:expressions.Expression): InputPosition => expressions.Expression = expressions.LessThan(lhs, rhs)
  private def gt(lhs:expressions.Expression, rhs:expressions.Expression): InputPosition => expressions.Expression = expressions.GreaterThan(lhs, rhs)
  private def lte(lhs:expressions.Expression, rhs:expressions.Expression): InputPosition => expressions.Expression = expressions.LessThanOrEqual(lhs, rhs)
  private def gte(lhs:expressions.Expression, rhs:expressions.Expression): InputPosition => expressions.Expression = expressions.GreaterThanOrEqual(lhs, rhs)

  private def comparisons(first: expressions.Expression, rest: List[PartialComparison]): InputPosition => expressions.Expression = {
    rest match {
      case Nil => _ => first
      case second :: Nil => _ => second(first)
      case more =>
        var lhs = first
        val result = ListBuffer.empty[expressions.Expression]
        for (rhs <- more) {
          result.append(rhs(lhs))
          lhs = rhs.expr
        }
        Ands(result)
    }
  }

  private def Expression7: Rule1[expressions.Expression] = rule("an expression") {
    Expression6 ~ zeroOrMore(WS ~ (
      group(operator("=~") ~~ Expression6) ~~>> (expressions.RegexMatch(_: expressions.Expression, _))
        | group(keyword("IN") ~~ Expression6) ~~>> (expressions.In(_: expressions.Expression, _))
        | group(keyword("STARTS WITH") ~~ Expression6) ~~>> (expressions.StartsWith(_: expressions.Expression, _))
        | group(keyword("ENDS WITH") ~~ Expression6) ~~>> (expressions.EndsWith(_: expressions.Expression, _))
        | group(keyword("CONTAINS") ~~ Expression6) ~~>> (expressions.Contains(_: expressions.Expression, _))
        | keyword("IS NULL") ~~>> (expressions.IsNull(_: expressions.Expression))
        | keyword("IS NOT NULL") ~~>> (expressions.IsNotNull(_: expressions.Expression))
      ): ReductionRule1[expressions.Expression, expressions.Expression])
  }

  private def Expression6: Rule1[expressions.Expression] = rule("an expression") {
    Expression5 ~ zeroOrMore(WS ~ (
      group(operator("+") ~~ Expression5) ~~>> (expressions.Add(_: expressions.Expression, _))
        | group(operator("-") ~~ Expression5) ~~>> (expressions.Subtract(_: expressions.Expression, _))
      ))
  }

  private def Expression5: Rule1[expressions.Expression] = rule("an expression") {
    Expression4 ~ zeroOrMore(WS ~ (
      group(operator("*") ~~ Expression4) ~~>> (expressions.Multiply(_: expressions.Expression, _))
        | group(operator("/") ~~ Expression4) ~~>> (expressions.Divide(_: expressions.Expression, _))
        | group(operator("%") ~~ Expression4) ~~>> (expressions.Modulo(_: expressions.Expression, _))
      ))
  }

  private def Expression4: Rule1[expressions.Expression] = rule("an expression") {
    Expression3 ~ zeroOrMore(WS ~ (
      group(operator("^") ~~ Expression3) ~~>> (expressions.Pow(_: expressions.Expression, _))
      ))
  }

  private def Expression3: Rule1[expressions.Expression] = rule("an expression") (
      Expression2
    | group(operator("+") ~~ Expression2) ~~>> (expressions.UnaryAdd(_))
    | group(operator("-") ~~ Expression2) ~~>> (expressions.UnarySubtract(_))
  )

  private def Expression2: Rule1[expressions.Expression] = rule("an expression") {
    Expression1 ~ zeroOrMore(WS ~ (
        PropertyLookup
      | NodeLabelsOrRelTypes ~~>> (expressions.HasLabelsOrTypes(_: expressions.Expression, _))
      |  "[" ~~ Expression ~~ "]" ~~>> (expressions.ContainerIndex(_: expressions.Expression, _))
      | "[" ~~ optional(Expression) ~~ ".." ~~ optional(Expression) ~~ "]" ~~>> (expressions.ListSlice(_: expressions.Expression, _, _))
    ))
  }

  private def Expression1: Rule1[expressions.Expression] = rule("an expression") (
      NumberLiteral
    | StringLiteral
    | Parameter
    | OldParameter
    | keyword("TRUE") ~ push(expressions.True()(_))
    | keyword("FALSE") ~ push(expressions.False()(_))
    | keyword("NULL") ~ push(expressions.Null()(_))
    | CaseExpression
    | group(keyword("COUNT") ~~ "(" ~~ "*" ~~ ")") ~ push(expressions.CountStar()(_))
    | MapLiteral
    | MapProjection
    | ListComprehension
    | PatternComprehension
    | group("[" ~~ zeroOrMore(Expression, separator = CommaSep) ~~ "]") ~~>> (expressions.ListLiteral(_))
    | group(keyword("FILTER") ~~ "(" ~~ FilterExpression ~~ ")") ~~>> (expressions.FilterExpression(_, _, _))
    | group(keyword("EXTRACT") ~~ "(" ~~ FilterExpression ~ optional(WS ~ "|" ~~ Expression) ~~ ")") ~~>> (expressions.ExtractExpression(_, _, _, _))
    | group(keyword("REDUCE") ~~ "(" ~~ Variable ~~ "=" ~~ Expression ~~ "," ~~ IdInColl ~~ "|" ~~ Expression ~~ ")") ~~>> (expressions.ReduceExpression(_, _, _, _, _))
    | group(keyword("ALL") ~~ "(" ~~ FilterExpression ~~ ")") ~~>> (expressions.AllIterablePredicate(_, _, _))
    | group(keyword("ANY") ~~ "(" ~~ FilterExpression ~~ ")") ~~>> (expressions.AnyIterablePredicate(_, _, _))
    | group(keyword("NONE") ~~ "(" ~~ FilterExpression ~~ ")") ~~>> (expressions.NoneIterablePredicate(_, _, _))
    | group(keyword("SINGLE") ~~ "(" ~~ FilterExpression ~~ ")") ~~>> (expressions.SingleIterablePredicate(_, _, _))
    | Exists
    | ShortestPathPattern ~~> expressions.ShortestPathExpression
    | RelationshipsPattern ~~> (PatternExpression(_)(Set.empty))
    | parenthesizedExpression
    | FunctionInvocation
    | Variable
  )

  def parenthesizedExpression: Rule1[expressions.Expression] = "(" ~~ Expression ~~ ")"

  def PropertyExpression: Rule1[expressions.Property] = rule {
    Expression1 ~ oneOrMore(WS ~ PropertyLookup)
  }

  def PropertyLookup: ReductionRule1[expressions.Expression, expressions.Property] = rule("'.'") {
    operator(".") ~~ (PropertyKeyName ~~>> (expressions.Property(_: expressions.Expression, _)))
  }

  private def ExistsSubClauseExpression: Rule2[Pattern, Option[Expression]] =
    WS ~ optional(keyword("MATCH")) ~~ Pattern ~ optional(WS ~ keyword("WHERE") ~~ Expression) //TODO: Support more stuff here, notably multiple patterns

  private def FilterExpression: Rule3[Variable, expressions.Expression, Option[expressions.Expression]] =
    IdInColl ~ optional(WS ~ keyword("WHERE") ~~ Expression)

  private def IdInColl: Rule2[Variable, expressions.Expression] =
    Variable ~~ keyword("IN") ~~ Expression

  def FunctionInvocation: Rule1[expressions.FunctionInvocation] = rule("a function") {
    ((group(Namespace ~~ FunctionName ~~ "(" ~~
      (keyword("DISTINCT") ~ push(true) | EMPTY ~ push(false)) ~~
      zeroOrMore(Expression, separator = CommaSep) ~~ ")"
    ) ~~> (_.toIndexedSeq)) memoMismatches) ~~>> (expressions.FunctionInvocation(_, _, _, _))
  }

  def ListComprehension: Rule1[expressions.ListComprehension] = rule("[") {
    group("[" ~~ FilterExpression ~ optional(WS ~ "|" ~~ Expression) ~~ "]") ~~>> (expressions.ListComprehension(_, _, _, _))
  }

  def PatternComprehension: Rule1[expressions.PatternComprehension] = rule("[") {
    group("[" ~~ optional(Variable ~~ operator("=")) ~~ RelationshipsPattern ~ optional(WS ~ keyword("WHERE") ~~ Expression) ~~ "|" ~~ Expression ~~ "]") ~~>> (
      (a, b, c, d) => pos => expressions.PatternComprehension(a, b, c, d)(pos, Set.empty))
  }

  def Exists: Rule1[ExistsSubClause] =
    group(keyword("EXISTS") ~~ "{" ~~ ExistsSubClauseExpression ~~ "}") ~~>> (
      (a, b) => pos => expressions.ExistsSubClause(a, b)(pos, Set.empty)) //TODO: This should NOT be a mere expression!


  def CaseExpression: Rule1[expressions.CaseExpression] = rule("CASE") {
    (group((
        keyword("CASE") ~~ push(None) ~ oneOrMore(WS ~ CaseAlternatives)
      | keyword("CASE") ~~ Expression ~~> (Some(_)) ~ oneOrMore(WS ~ CaseAlternatives)
      ) ~ optional(WS ~
        keyword("ELSE") ~~ Expression
      ) ~~ keyword("END")
    ) memoMismatches) ~~>> (expressions.CaseExpression(_, _, _))
  }

  private def CaseAlternatives: Rule2[expressions.Expression, expressions.Expression] = rule("WHEN") {
    keyword("WHEN") ~~ Expression ~~ keyword("THEN") ~~ Expression
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy