From 0f002b85cb1577aee8fdcb8d2390bbd21d8bcf29 Mon Sep 17 00:00:00 2001 From: frank shao Date: Thu, 31 Aug 2017 15:30:25 -0400 Subject: [PATCH] Adding type class for operator map for type configurable parser --- .../facaiy/math/expression/Expression.scala | 76 +++---------------- .../facaiy/math/expression/MathExp.scala | 7 +- .../facaiy/math/expression/Operations.scala | 69 +++++++++++++++++ 3 files changed, 86 insertions(+), 66 deletions(-) create mode 100644 src/main/scala/io/github/facaiy/math/expression/Operations.scala diff --git a/src/main/scala/io/github/facaiy/math/expression/Expression.scala b/src/main/scala/io/github/facaiy/math/expression/Expression.scala index 24300a2..8161688 100644 --- a/src/main/scala/io/github/facaiy/math/expression/Expression.scala +++ b/src/main/scala/io/github/facaiy/math/expression/Expression.scala @@ -1,6 +1,7 @@ package io.github.facaiy.math.expression import io.github.facaiy.math.expression.compiler.parser._ +import scala.reflect.ClassTag /** * Created by facai on 6/19/17. @@ -13,20 +14,23 @@ case class Expression[A, B](eval: A => B) { } object Expression { - import FunctionRegister._ + import Operations.Operators - def toExpression(ast: MathExpAST): Expression[String => Double, Double] = ast match { - case Constant(d: Double) => Expression(_ => d) + def toExpression[T: ClassTag : Operators](ast: MathExpAST): Expression[String => T, T] = ast match { + case Constant(d: T) => Expression(_ => d) case c @ Constant(_) => throw new IllegalArgumentException(c.toString) case Variable(n) => Expression(f => f(n)) case Operator2(op, v1, v2) => - toExpression(v1).map2(toExpression(v2))(function2(op)) + val a: Expression[(String) => T, T] = toExpression[T](v1) + a.map2[T, T](toExpression[T](v2))(implicitly[Operators[T]].binaryOps(op)) + case f @ OperatorN(op, as) => - val args = sequence(as.map(toExpression)).map(_.toArray) - args.map{ xs: Array[Double] => + val a: Expression[(String) => T, List[T]] = sequence[(String) => T, T](as.map(toExpression[T] _)) + val args: Expression[(String) => T, Array[T]] = a.map(d => d.toArray[T]) + args.map{ xs: Array[T] => xs.size match { - case 1 => function1(op)(xs.head) - case 2 => function2(op)(xs.head, xs(2)) + case 1 => implicitly[Operators[T]].unaryOps(op)(xs.head) + case 2 => implicitly[Operators[T]].binaryOps(op)(xs.head, xs(2)) case _ => throw new UnsupportedOperationException(f.toString) } } @@ -39,59 +43,3 @@ object Expression { (e, acc) => Expression(x => e.eval(x) :: acc.eval(x))) } -object FunctionRegister { - val function1: Map[String, Double => Double] = Map( - // scala.math - // Rounding - "ceil" -> Math.ceil, - "floor" -> Math.floor, - "rint" -> Math.rint, - "round" -> ((x: Double) => Math.round(x)).andThen(_.toDouble), - // Exponential and Logarithmic - "exp" -> Math.exp, - "expm1" -> Math.expm1, - "log" -> Math.log, - "log10" -> Math.log10, - "log1p" -> Math.log1p, - // Trigonometric - "acos" -> Math.acos, - "asin" -> Math.asin, - "atan" -> Math.atan, - "cos" -> Math.cos, - "sin" -> Math.sin, - "tan" -> Math.tan, - // Angular Measurement Conversion - "toDegrees" -> Math.toDegrees, - "toRadians" -> Math.toRadians, - // Hyperbolic - "cosh" -> Math.cosh, - "sinh" -> Math.sinh, - "tanh" -> Math.tanh, - // Absolute Values - "abs" -> Math.abs, - // Signs - "signum" -> Math.signum, - // Root Extraction - "cbrt" -> Math.cbrt, - "sqrt" -> Math.sqrt, - // Unit of Least Precision - "ulp" -> Math.ulp - ) - - val function2: Map[String, (Double, Double) => Double] = Map( - "+" -> (_ + _), - "-" -> (_ - _), - "*" -> (_ * _), - "/" -> (_ / _), - // scala.math - // Minimum and Maximum - "max" -> Math.max, - "min" -> Math.min, - // Exponential and Logarithmic - "**" -> Math.pow, - "pow" -> Math.pow, - // Polar Coordinates - "atan2" -> Math.atan2, - "hypot" -> Math.hypot - ) -} diff --git a/src/main/scala/io/github/facaiy/math/expression/MathExp.scala b/src/main/scala/io/github/facaiy/math/expression/MathExp.scala index 72d0a43..066edd5 100644 --- a/src/main/scala/io/github/facaiy/math/expression/MathExp.scala +++ b/src/main/scala/io/github/facaiy/math/expression/MathExp.scala @@ -1,6 +1,8 @@ package io.github.facaiy.math.expression +import io.github.facaiy.math.expression.Operations.Operators import io.github.facaiy.math.expression.compiler.MathExpCompiler +import scala.reflect.ClassTag /** * Created by facai on 6/19/17. @@ -10,9 +12,10 @@ case class MathExpScannerError(msg: String) extends MathExpError case class MathExpParserError(msg: String) extends MathExpError object MathExp { - def parse(s: String): Expression[String => Double, Double] = + def parse[T: ClassTag: Operators](s: String): Expression[String => T, T] = { MathExpCompiler(s) match { - case Right(ts) => Expression.toExpression(ts) + case Right(ts) => Expression.toExpression[T](ts) case Left(e) => throw new IllegalArgumentException(e.toString) } + } } diff --git a/src/main/scala/io/github/facaiy/math/expression/Operations.scala b/src/main/scala/io/github/facaiy/math/expression/Operations.scala new file mode 100644 index 0000000..4114950 --- /dev/null +++ b/src/main/scala/io/github/facaiy/math/expression/Operations.scala @@ -0,0 +1,69 @@ +package io.github.facaiy.math.expression + +/** + * Created by fshao on 9/5/17. + */ +object Operations { + trait Operators[T] { + def unaryOps: Map[String, T => T] + def binaryOps: Map[String, (T, T) => T] + } + object Operators { + implicit object DoubleOps extends Operators[Double] { + val unaryOps: Map[String, Double => Double] = Map( + // scala.math + // Rounding + "ceil" -> Math.ceil, + "floor" -> Math.floor, + "rint" -> Math.rint, + "round" -> ((x: Double) => Math.round(x)).andThen(_.toDouble), + // Exponential and Logarithmic + "exp" -> Math.exp, + "expm1" -> Math.expm1, + "log" -> Math.log, + "log10" -> Math.log10, + "log1p" -> Math.log1p, + // Trigonometric + "acos" -> Math.acos, + "asin" -> Math.asin, + "atan" -> Math.atan, + "cos" -> Math.cos, + "sin" -> Math.sin, + "tan" -> Math.tan, + // Angular Measurement Conversion + "toDegrees" -> Math.toDegrees, + "toRadians" -> Math.toRadians, + // Hyperbolic + "cosh" -> Math.cosh, + "sinh" -> Math.sinh, + "tanh" -> Math.tanh, + // Absolute Values + "abs" -> Math.abs, + // Signs + "signum" -> Math.signum, + // Root Extraction + "cbrt" -> Math.cbrt, + "sqrt" -> Math.sqrt, + // Unit of Least Precision + "ulp" -> Math.ulp + ) + + val binaryOps: Map[String, (Double, Double) => Double] = Map( + "+" -> (_ + _), + "-" -> (_ - _), + "*" -> (_ * _), + "/" -> (_ / _), + // scala.math + // Minimum and Maximum + "max" -> Math.max, + "min" -> Math.min, + // Exponential and Logarithmic + "**" -> Math.pow, + "pow" -> Math.pow, + // Polar Coordinates + "atan2" -> Math.atan2, + "hypot" -> Math.hypot + ) + } + } +} \ No newline at end of file