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

coulomb.infra.show.scala Maven / Gradle / Ivy

There is a newer version: 0.9.0-RC1
Show newest version
/*
 * Copyright 2022 Erik Erlandson
 *
 * 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 coulomb.infra

object show:
    import scala.quoted.*
    import coulomb.*
    import coulomb.define.*
    import coulomb.infra.meta.*

    def show[U](using Quotes, Type[U]): Expr[String] =
        import quotes.reflect.*
        val render = (tr: TypeRepr) => {
            val AppliedType(_, List(_, a)) = tr: @unchecked
            val ConstantType(StringConstant(abbv)) = a: @unchecked
            abbv
        }
        val str = showrender(TypeRepr.of[U], render)
        Expr(str)

    def showFull[U](using Quotes, Type[U]): Expr[String] =
        import quotes.reflect.*
        val render = (tr: TypeRepr) => {
            val AppliedType(_, List(n, _)) = tr: @unchecked
            val ConstantType(StringConstant(name)) = n: @unchecked
            name
        }
        val str = showrender(TypeRepr.of[U], render)
        Expr(str)

    def showrender(using
        Quotes
    )(
        u: quotes.reflect.TypeRepr,
        render: quotes.reflect.TypeRepr => String
    ): String =
        import quotes.reflect.*
        u match
            // named unit defs take highest priority
            case namedunit(nu) => render(nu)
            // The policy goal here is that type aliases are never expanded
            // (unless covered by some namedunit(_) above).  However,
            // scala's implicit resolution logic undermines this somewhat because it
            // pre-dealiases type aliases, so the "outer" type is always dealiases
            // by the time my metaprogramming sees it.
            case typealias(_) => typestr(u)
            case unitconst(v) =>
                if (v.d == 1) v.n.toString
                else s"${v.n.toString}/${v.d.toString}"
            case flatmul(t) => termstr(t, render)
            case AppliedType(op, List(lu, unitconst1()))
                if (op =:= TypeRepr.of[/]) =>
                showrender(lu, render)
            case AppliedType(op, List(lu, ru)) if (op =:= TypeRepr.of[/]) =>
                val (ls, rs) = (paren(lu, render), paren(ru, render))
                s"${ls}/${rs}"
            case AppliedType(op, List(b, e)) if (op =:= TypeRepr.of[^]) =>
                val (bs, es) = (paren(b, render), powstr(e))
                s"${bs}^${es}"
            // treat any other type as if it is a BaseUnit,
            // with its name and abbv defined as its type-string
            case _ => typestr(u)

    def termstr(using
        Quotes
    )(
        terms: List[quotes.reflect.TypeRepr],
        render: quotes.reflect.TypeRepr => String
    ): String =
        import quotes.reflect.*
        // values for terms constructed from 'flatmul' should have >= 2 terms to start
        // so should terms should never be empty in this recursion
        terms match
            case term +: Nil => paren(term, render)
            case term +: tail =>
                val h = paren(term, render)
                val t = termstr(tail, render)
                // tail should never be empty (see above)
                term match
                    case namedPU(_) => s"${h}${t}"
                    case _          => s"${h} ${t}"
            case _ =>
                report.error(s"unrecognized termstr pattern $terms")
                ""

    object flatmul:
        def unapply(using Quotes)(
            u: quotes.reflect.TypeRepr
        ): Option[List[quotes.reflect.TypeRepr]] =
            import quotes.reflect.*
            u match
                case AppliedType(op, List(lu, ru)) if (op =:= TypeRepr.of[*]) =>
                    val lflat = lu match
                        case flatmul(lf) => lf
                        case _           => List(lu)
                    val rflat = ru match
                        case flatmul(rf) => rf
                        case _           => List(ru)
                    Some(lflat ++ rflat)
                case _ => None

    def powstr(using Quotes)(p: quotes.reflect.TypeRepr): String =
        import quotes.reflect.*
        p match
            case bigintTE(v) if (v >= 0) => v.toString
            case bigintTE(v) if (v < 0)  => s"(${v.toString})"
            case rationalTE(v)           => s"(${v.n.toString}/${v.d.toString})"
            case _                       => "!!!"

    def paren(using
        Quotes
    )(
        u: quotes.reflect.TypeRepr,
        render: quotes.reflect.TypeRepr => String
    ): String =
        val str = showrender(u, render)
        if (atomic(u)) str else s"(${str})"

    def atomic(using Quotes)(u: quotes.reflect.TypeRepr): Boolean =
        import quotes.reflect.*
        u match
            case namedunit(_) => true
            case unitconst(_) => true
            case AppliedType(op, List(namedunit(_), _))
                if (op =:= TypeRepr.of[^]) =>
                true
            case AppliedType(
                    op,
                    List(flatmul(namedPU(_) +: namedunit(_) +: Nil), _)
                ) if (op =:= TypeRepr.of[^]) =>
                true
            case flatmul(namedPU(_) +: namedunit(_) +: Nil) => true
            case AppliedType(_, _)                          => false
            case _                                          => true

    object namedunit:
        def unapply(using Quotes)(
            u: quotes.reflect.TypeRepr
        ): Option[quotes.reflect.TypeRepr] =
            import quotes.reflect.*
            u match
                case namedSU(nu) => Some(nu)
                case namedBU(nu) => Some(nu)
                case namedDU(nu) => Some(nu)
                case _           => None

    object namedSU:
        def unapply(using Quotes)(
            u: quotes.reflect.TypeRepr
        ): Option[quotes.reflect.TypeRepr] =
            import quotes.reflect.*
            Implicits.search(
                TypeRepr
                    .of[ShowUnitAlias]
                    .appliedTo(List(u, TypeBounds.empty, TypeBounds.empty))
            ) match
                case iss: ImplicitSearchSuccess =>
                    Some(
                        iss.tree.tpe.baseType(TypeRepr.of[NamedUnit].typeSymbol)
                    )
                case _ => None

    object namedPU:
        def unapply(using Quotes)(
            u: quotes.reflect.TypeRepr
        ): Option[quotes.reflect.TypeRepr] =
            import quotes.reflect.*
            Implicits.search(
                TypeRepr
                    .of[DerivedUnit]
                    .appliedTo(
                        List(
                            u,
                            TypeRepr.of[1],
                            TypeBounds.empty,
                            TypeBounds.empty,
                            TypeBounds.empty
                        )
                    )
            ) match
                case iss: ImplicitSearchSuccess =>
                    Some(
                        iss.tree.tpe.baseType(TypeRepr.of[NamedUnit].typeSymbol)
                    )
                case _ => None

    object namedBU:
        def unapply(using Quotes)(
            u: quotes.reflect.TypeRepr
        ): Option[quotes.reflect.TypeRepr] =
            import quotes.reflect.*
            Implicits.search(
                TypeRepr
                    .of[BaseUnit]
                    .appliedTo(List(u, TypeBounds.empty, TypeBounds.empty))
            ) match
                case iss: ImplicitSearchSuccess =>
                    Some(
                        iss.tree.tpe.baseType(TypeRepr.of[NamedUnit].typeSymbol)
                    )
                case _ => None

    object namedDU:
        def unapply(using Quotes)(
            u: quotes.reflect.TypeRepr
        ): Option[quotes.reflect.TypeRepr] =
            import quotes.reflect.*
            Implicits.search(
                TypeRepr
                    .of[DerivedUnit]
                    .appliedTo(
                        List(
                            u,
                            TypeBounds.empty,
                            TypeBounds.empty,
                            TypeBounds.empty,
                            TypeBounds.empty
                        )
                    )
            ) match
                case iss: ImplicitSearchSuccess =>
                    Some(
                        iss.tree.tpe.baseType(TypeRepr.of[NamedUnit].typeSymbol)
                    )
                case _ => None




© 2015 - 2024 Weber Informatics LLC | Privacy Policy