Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version = 2.6.4
preset = IntelliJ
align.preset = more

maxColumn = 130
3 changes: 1 addition & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Dependencies._


scalacOptions in ThisBuild ++= Seq("-deprecation", "-feature")

organization := "br.com.virsox.scalexpr"
Expand All @@ -11,6 +10,6 @@ version := "0.0.1-SNAPSHOT"

isSnapshot := true

scalaVersion := "2.11.7"
scalaVersion := "2.13.3"

libraryDependencies ++= (baseDeps ++ testDeps)
22 changes: 9 additions & 13 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@ import Keys._

object Dependencies {


val logback = "ch.qos.logback" % "logback-classic" % "1.1.3"
val scalaCompiler = "org.scala-lang" % "scala-compiler" % "2.11.7"
val scalaReflect = "org.scala-lang" % "scala-reflect" % "2.11.7"
val fastParse = "com.lihaoyi" %% "fastparse" % "0.3.7"
val logback = "ch.qos.logback" % "logback-classic" % "1.1.3"
val scalaCompiler = "org.scala-lang" % "scala-compiler" % "2.13.3"
val scalaReflect = "org.scala-lang" % "scala-reflect" % "2.13.3"
val fastParse = "com.lihaoyi" %% "fastparse" % "2.2.2"

// ------------------- test
val junit = "junit" % "junit" % "4.11" % Test
val mockito = "org.mockito" % "mockito-core" % "1.9.5" % Test
val scalaTest = "org.scalatest" %% "scalatest" % "2.2.6" % Test

val baseDeps = Seq(logback, fastParse, scalaCompiler, scalaReflect)
val testDeps = Seq(junit, mockito, scalaTest)

val scalaTest = "org.scalatest" %% "scalatest" % "3.2.0" % Test
val scalactic = "org.scalatest" %% "scalactic" % "3.2.0"

val baseDeps = Seq(logback, fastParse, scalaCompiler, scalaReflect)
val testDeps = Seq(scalaTest)

}
}
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=0.13.9
sbt.version=1.3.13
80 changes: 40 additions & 40 deletions src/main/scala/br/com/virsox/scalexpr/BinaryExpression.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import br.com.virsox.scalexpr.Context.{ContextLike, EmptyContext}
import scala.math.Ordering
import scala.reflect.runtime.universe._

/***
/** *
* Trait for binary expressions.
* @tparam A Type of the left operand.
* @tparam B Type of the right operand.
Expand Down Expand Up @@ -35,25 +35,26 @@ object GreaterEqualsThan extends RelationalOperator
* @tparam T Type of the operands.
*/
case class RelationalExpression[T: Ordering](left: Expression[T], op: RelationalOperator, right: Expression[T])
extends BooleanExpr
with BinaryExpression[T, T, Boolean] {
extends BooleanExpr
with BinaryExpression[T, T, Boolean] {

/** Ordering object. */
val ordering = implicitly[Ordering[T]]
val ordering: Ordering[T] = implicitly[Ordering[T]]

/**
* Resolves the relational expression.
* @param context Not used directly by the expression, but it is passed recursively to resolve the operands.
* @return Value of the expression (always a boolean).
*/
override def resolve[A](context: A = EmptyContext)(implicit ctxLike: ContextLike[A]): Boolean = op match {
case Equals => left.resolve(context) == right.resolve(context)
case NotEquals => left.resolve(context) != right.resolve(context)
case LessThan => ordering.lt(left.resolve(context), right.resolve(context))
case LessEqualsThan => ordering.lteq(left.resolve(context), right.resolve(context))
case GreaterThan => ordering.gt(left.resolve(context), right.resolve(context))
case GreaterEqualsThan => ordering.gteq(left.resolve(context), right.resolve(context))
}
override def resolve[A](context: A = EmptyContext)(implicit ctxLike: ContextLike[A]): Boolean =
op match {
case Equals => left.resolve(context) == right.resolve(context)
case NotEquals => left.resolve(context) != right.resolve(context)
case LessThan => ordering.lt(left.resolve(context), right.resolve(context))
case LessEqualsThan => ordering.lteq(left.resolve(context), right.resolve(context))
case GreaterThan => ordering.gt(left.resolve(context), right.resolve(context))
case GreaterEqualsThan => ordering.gteq(left.resolve(context), right.resolve(context))
}
}

// ---------------------------------------------------------------------------------------------------------
Expand All @@ -70,63 +71,62 @@ object Or extends LogicalOperator
* @param right Right operand.
*/
case class LogicalExpression(left: Expression[Boolean], op: LogicalOperator, right: Expression[Boolean])
extends BooleanExpr
with BinaryExpression[Boolean, Boolean, Boolean] {
extends BooleanExpr
with BinaryExpression[Boolean, Boolean, Boolean] {

/**
* Resolves the logical operation.
* @param context Not used directly by the expression, but it is passed recursively to resolve the operands.
* @return Value of the expression.
*/
override def resolve[A](context: A = EmptyContext)(implicit ctxLike: ContextLike[A]): Boolean = op match {
case And => left.resolve(context) && right.resolve(context)
case Or => left.resolve(context) || right.resolve(context)
}
override def resolve[A](context: A = EmptyContext)(implicit ctxLike: ContextLike[A]): Boolean =
op match {
case And => left.resolve(context) && right.resolve(context)
case Or => left.resolve(context) || right.resolve(context)
}
}

// ---------------------------------------------------------------------------------------------------------

/** Arithmetic operators. */
class ArithmeticOperator
object Plus extends ArithmeticOperator
object Plus extends ArithmeticOperator
object Minus extends ArithmeticOperator
object Times extends ArithmeticOperator
object Div extends ArithmeticOperator

/***
/** *
* Arithmetic operation. Both operands must be of the same type. Expression results in the same type.
* @param left Left operand.
* @param op Arithmetic operation.
* @param right Right operand.
* @tparam T Type of the operands and return type.
*/
case class ArithmeticExpression[T: Numeric: TypeTag](left: Expression[T], op: ArithmeticOperator, right: Expression[T])
extends NumericExpr[T]
with BinaryExpression[T, T, T] {
extends NumericExpr[T]
with BinaryExpression[T, T, T] {

/**
* Resolves the arithmetic operation.
* @param context Not used directly by the expression, but it is passed recursively to resolve the operands.
* @return Value of the expression.
*/
override def resolve[A](context: A = EmptyContext)(implicit ctxLike: ContextLike[A]): T = op match {
case Plus => numericEvidence.plus(left.resolve(context), right.resolve(context))
case Minus => numericEvidence.minus(left.resolve(context), right.resolve(context))
case Times => numericEvidence.times(left.resolve(context), right.resolve(context))
case Div => {

// division is not defined in the Numeric type
if (typeOf[T] =:= typeOf[Double]) {
(numericEvidence.toDouble(left.resolve(context)) / numericEvidence.toDouble(right.resolve(context))).asInstanceOf[T]
} else if (typeOf[T] =:= typeOf[Float]) {
(numericEvidence.toFloat(left.resolve(context)) / numericEvidence.toFloat(right.resolve(context))).asInstanceOf[T]
} else if (typeOf[T] =:= typeOf[Long]) {
(numericEvidence.toLong(left.resolve(context)) / numericEvidence.toLong(right.resolve(context))).asInstanceOf[T]
} else {
(numericEvidence.toInt(left.resolve(context)) / numericEvidence.toInt(right.resolve(context))).asInstanceOf[T]
}
override def resolve[A](context: A = EmptyContext)(implicit ctxLike: ContextLike[A]): T =
op match {
case Plus => numericEvidence.plus(left.resolve(context), right.resolve(context))
case Minus => numericEvidence.minus(left.resolve(context), right.resolve(context))
case Times => numericEvidence.times(left.resolve(context), right.resolve(context))
case Div =>
// division is not defined in the Numeric type
if (typeOf[T] =:= typeOf[Double]) {
(numericEvidence.toDouble(left.resolve(context)) / numericEvidence.toDouble(right.resolve(context))).asInstanceOf[T]
} else if (typeOf[T] =:= typeOf[Float]) {
(numericEvidence.toFloat(left.resolve(context)) / numericEvidence.toFloat(right.resolve(context))).asInstanceOf[T]
} else if (typeOf[T] =:= typeOf[Long]) {
(numericEvidence.toLong(left.resolve(context)) / numericEvidence.toLong(right.resolve(context))).asInstanceOf[T]
} else {
(numericEvidence.toInt(left.resolve(context)) / numericEvidence.toInt(right.resolve(context))).asInstanceOf[T]
}
}
}

}

171 changes: 171 additions & 0 deletions src/main/scala/br/com/virsox/scalexpr/DateParser.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package br.com.virsox.scalexpr

import java.time.Instant

import fastparse._, SingleLineWhitespace._

trait DateParser {
this: ExpressionParser =>
// ---------------------------------------------------------------
// -------------- Date literal parsers ---------------------
// ---------------------------------------------------------------
def yearParser[_: P]: P[Unit] = P(digits.rep(min = 4, max = 4))
def monthParser[_: P]: P[Unit] = P(StringIn("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"))
def dayParser[_: P]: P[Unit] =
P(
StringIn(
"01",
"02",
"03",
"04",
"05",
"06",
"07",
"08",
"09",
"10",
"11",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"19",
"20",
"21",
"22",
"23",
"24",
"25",
"26",
"27",
"28",
"29",
"30",
"31"
)
)

// months with 31 days
val months31 = Seq(1, 3, 5, 7, 8, 10, 12)

// months with 30 days
val months30 = Seq(4, 6, 9, 11)

// parses a date and validates it
def dateParser[_: P]: P[String] =
P(yearParser.! ~ "-" ~ monthParser.! ~ "-" ~ dayParser.!)
.map { case (year, month, day) => (year.toInt, month.toInt, day.toInt) }
.filter {
case (year, month, day) =>
if (months31.contains(month) && day > 31) false
else if (months30.contains(month) && day > 30) false
else if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) && day > 29) false // leap year
else if (day > 28) false
true
}
.!

def hourParser[_: P]: P[Unit] =
P(
StringIn(
"00",
"01",
"02",
"03",
"04",
"05",
"06",
"07",
"08",
"09",
"10",
"11",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"19",
"20",
"21",
"22",
"23"
)
)
def minSecondParser[_: P]: P[Unit] =
P(
StringIn(
"00",
"01",
"02",
"03",
"04",
"05",
"06",
"07",
"08",
"09",
"10",
"11",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"19",
"20",
"21",
"22",
"23",
"24",
"25",
"26",
"27",
"28",
"29",
"30",
"31",
"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",
"58",
"59"
)
)

def milliParser[_: P]: P[Unit] = P(digits.rep(min = 3, max = 3))
def timeParser[_: P]: P[Unit] = P(hourParser ~ ":" ~ minSecondParser ~ ":" ~ minSecondParser ~ "." ~ milliParser)
def dateTimeParser[_: P]: P[String] = P(dateParser ~ "T" ~ timeParser ~ "Z").!
def dateTimeLiteral[_: P]: P[DateTimeConstant] = dateTimeParser.map(s => DateTimeConstant(Instant.parse(s)))

}
Loading