Skip to content

Commit

Permalink
Unary: define unary operations on numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
kitbellew committed Mar 7, 2024
1 parent 3647095 commit a6fe036
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
@@ -0,0 +1,65 @@
package scala.meta.internal.trees

import scala.meta.tokens.Token

private[meta] sealed trait Unary {
def op: String
}

private[meta] object Unary {

private val numericOpMap = Seq[Numeric](Plus, Minus, Tilde).map(x => x.op -> x).toMap
val opMap = numericOpMap ++ Seq[Unary](Not).map(x => x.op -> x)

def unapply(token: Token.Ident): Option[(String, Unary)] = {
val op = token.text
opMap.get(op).map(op -> _)
}

sealed trait Numeric extends Unary {
def apply(value: BigInt): BigInt
// could return None if not applicable (such as `~`)
def apply(value: BigDecimal): Option[BigDecimal]
}

object Numeric {
def unapply(token: Token.Ident): Option[(String, Numeric)] = {
val op = token.text
numericOpMap.get(op).map(op -> _)
}
}

sealed trait Logical extends Unary {
def apply(value: Boolean): Boolean
}

case object Noop extends Numeric {
val op = ""
def apply(value: BigInt): BigInt = value
def apply(value: BigDecimal): Option[BigDecimal] = Some(value)
}

case object Plus extends Numeric {
val op = "+"
def apply(value: BigInt): BigInt = value
def apply(value: BigDecimal): Option[BigDecimal] = Some(value)
}

case object Minus extends Numeric {
val op = "-"
def apply(value: BigInt): BigInt = -value
def apply(value: BigDecimal): Option[BigDecimal] = Some(-value)
}

case object Tilde extends Numeric {
val op = "~"
def apply(value: BigInt): BigInt = ~value
def apply(value: BigDecimal): Option[BigDecimal] = None
}

case object Not extends Logical {
val op = "!"
def apply(value: Boolean): Boolean = !value
}

}
Expand Up @@ -43,7 +43,7 @@ package object trees {
// some heuristic is needed to govern associativity and precedence of unquoted operators
def isLeftAssoc: Boolean = value.last != ':'

def isUnaryOp: Boolean = Set("-", "+", "~", "!").contains(value)
def isUnaryOp: Boolean = Unary.opMap.contains(value)

def isAssignmentOp = value match {
case "!=" | "<=" | ">=" | "" => false
Expand Down
19 changes: 19 additions & 0 deletions tests/shared/src/test/scala/scala/meta/tests/trees/TreeSuite.scala
Expand Up @@ -3,10 +3,29 @@ package trees

import munit._
import scala.meta._
import scala.meta.internal.trees._

class TreeSuite extends FunSuite {
test("Name.unapply") {
assert(Name.unapply(q"a").contains("a"))
assert(Name.unapply(t"a").contains("a"))
}

Seq(
("+", Unary.Plus),
("-", Unary.Minus),
("~", Unary.Tilde),
("!", Unary.Not)
).foreach { case (op, unary) =>
test(s"Unary.$unary") {
assertEquals(unary.op, op)
assertEquals(Unary.opMap.get(op), Some(unary))
assert(op.isUnaryOp)
}
}

test(s"Unary opMap size") {
assertEquals(Unary.opMap.size, 4)
}

}

0 comments on commit a6fe036

Please sign in to comment.