packages feed

hydra-0.15.0: src/main/haskell/Hydra/Sources/Scala/Utils.hs

module Hydra.Sources.Scala.Utils where

-- Standard imports for term-level sources outside of the kernel
import Hydra.Kernel
import Hydra.Sources.Libraries
import           Hydra.Dsl.Meta.Lib.Strings                as Strings
import           Hydra.Dsl.Meta.Phantoms                   as Phantoms
import qualified Hydra.Dsl.Annotations                     as Annotations
import qualified Hydra.Dsl.Bootstrap                       as Bootstrap
import qualified Hydra.Dsl.LiteralTypes                    as LiteralTypes
import qualified Hydra.Dsl.Literals                        as Literals
import qualified Hydra.Dsl.Paths                      as Paths
import qualified Hydra.Dsl.Ast                        as Ast
import qualified Hydra.Dsl.Meta.Base                       as MetaBase
import qualified Hydra.Dsl.Coders                     as Coders
import qualified Hydra.Dsl.Util                    as Util
import qualified Hydra.Dsl.Meta.Core                       as Core
import qualified Hydra.Dsl.Meta.Graph                      as Graph
import qualified Hydra.Dsl.Json.Model                       as Json
import qualified Hydra.Dsl.Meta.Lib.Chars                  as Chars
import qualified Hydra.Dsl.Meta.Lib.Eithers                as Eithers
import qualified Hydra.Dsl.Meta.Lib.Equality               as Equality
import qualified Hydra.Dsl.Meta.Lib.Lists                  as Lists
import qualified Hydra.Dsl.Meta.Lib.Literals               as Literals
import qualified Hydra.Dsl.Meta.Lib.Logic                  as Logic
import qualified Hydra.Dsl.Meta.Lib.Maps                   as Maps
import qualified Hydra.Dsl.Meta.Lib.Math                   as Math
import qualified Hydra.Dsl.Meta.Lib.Maybes                 as Maybes
import qualified Hydra.Dsl.Meta.Lib.Pairs                  as Pairs
import qualified Hydra.Dsl.Meta.Lib.Math                   as Math
import qualified Hydra.Dsl.Meta.Lib.Sets                   as Sets
import qualified Hydra.Dsl.Packaging                     as Packaging
import qualified Hydra.Dsl.Meta.Terms                      as MetaTerms
import qualified Hydra.Dsl.Meta.Testing                    as Testing
import qualified Hydra.Dsl.Topology                   as Topology
import qualified Hydra.Dsl.Meta.Types                      as MetaTypes
import qualified Hydra.Dsl.Typing                     as Typing
import qualified Hydra.Dsl.Util                       as Util
import qualified Hydra.Dsl.Meta.Variants                   as Variants
import qualified Hydra.Dsl.Prims                           as Prims
import qualified Hydra.Dsl.Meta.Tabular                         as Tabular
import qualified Hydra.Dsl.Terms                           as Terms
import qualified Hydra.Dsl.Tests                           as Tests
import qualified Hydra.Dsl.Types                           as Types
import qualified Hydra.Sources.Decode.Core                 as DecodeCore
import qualified Hydra.Sources.Encode.Core                 as EncodeCore
import qualified Hydra.Sources.Kernel.Terms.Adapt           as Adapt
import qualified Hydra.Sources.Kernel.Terms.All            as KernelTerms
import qualified Hydra.Sources.Kernel.Terms.Annotations    as Annotations
import qualified Hydra.Sources.Kernel.Terms.Arity          as Arity
import qualified Hydra.Sources.Kernel.Terms.Checking       as Checking
import qualified Hydra.Sources.Kernel.Terms.Constants      as Constants
import qualified Hydra.Sources.Kernel.Terms.Extract.Core   as ExtractCore
import qualified Hydra.Sources.Kernel.Terms.Extract.Util   as ExtractUtil
import qualified Hydra.Sources.Kernel.Terms.Formatting     as Formatting
import qualified Hydra.Sources.Kernel.Terms.Inference      as Inference
import qualified Hydra.Sources.Kernel.Terms.Languages      as Languages
import qualified Hydra.Sources.Kernel.Terms.Lexical        as Lexical
import qualified Hydra.Sources.Kernel.Terms.Literals       as Literals
import qualified Hydra.Sources.Kernel.Terms.Names          as Names
import qualified Hydra.Sources.Kernel.Terms.Reduction      as Reduction
import qualified Hydra.Sources.Kernel.Terms.Reflect        as Reflect
import qualified Hydra.Sources.Kernel.Terms.Strip          as Strip
import qualified Hydra.Sources.Kernel.Terms.Serialization  as Serialization
import qualified Hydra.Sources.Kernel.Terms.Show.Paths as ShowPaths
import qualified Hydra.Sources.Kernel.Terms.Show.Core      as ShowCore
import qualified Hydra.Sources.Kernel.Terms.Show.Graph     as ShowGraph
import qualified Hydra.Sources.Kernel.Terms.Show.Variants  as ShowVariants
import qualified Hydra.Sources.Kernel.Terms.Show.Typing    as ShowTyping
import qualified Hydra.Sources.Kernel.Terms.Sorting        as Sorting
import qualified Hydra.Sources.Kernel.Terms.Substitution   as Substitution
import qualified Hydra.Sources.Kernel.Terms.Templates      as Templates
import qualified Hydra.Sources.Kernel.Terms.Unification    as Unification
import qualified Hydra.Sources.Kernel.Types.All            as KernelTypes
import           Prelude hiding ((++))
import qualified Data.Int                                  as I
import qualified Data.List                                 as L
import qualified Data.Map                                  as M
import qualified Data.Set                                  as S
import qualified Data.Maybe                                as Y

-- Additional imports
import qualified Hydra.Scala.Syntax as Scala
import qualified Hydra.Sources.Scala.Syntax as ScalaSyntax
import qualified Hydra.Sources.Scala.Language as ScalaLanguageSource


def :: String -> TTerm a -> TTermDefinition a
def = definitionInModule module_

ns :: Namespace
ns = Namespace "hydra.scala.utils"

scalaLanguageNs :: Namespace
scalaLanguageNs = moduleNamespace ScalaLanguageSource.module_

module_ :: Module
module_ = Module {
            moduleNamespace = ns,
            moduleDefinitions = definitions,
            moduleTermDependencies = [scalaLanguageNs, Names.ns, Formatting.ns],
            moduleTypeDependencies = (ScalaSyntax.ns:KernelTypes.kernelTypesNamespaces),
            moduleDescription = Just "Utility functions for constructing Scala AST nodes"}
  where
    definitions = [
      toDefinition nameOfType,
      toDefinition qualifyUnionFieldName,
      toDefinition sapply,
      toDefinition sapplyTypes,
      toDefinition sassign,
      toDefinition scalaEscapeName,
      toDefinition scalaReservedWordsRef,
      toDefinition scalaTypeName,
      toDefinition slambda,
      toDefinition sname,
      toDefinition sprim,
      toDefinition stapply,
      toDefinition stapply1,
      toDefinition stapply2,
      toDefinition stparam,
      toDefinition stref,
      toDefinition svar,
      toDefinition typeToString]


nameOfType :: TTermDefinition (Graph -> Type -> Y.Maybe Name)
nameOfType = def "nameOfType" $
  doc "Extract the name from a type, if it is a named type" $
  lambda "cx" $ lambda "t" $
    (cases _Type (Strip.deannotateType @@ var "t")
      (Just nothing) [
      _Type_variable>>: ("name" ~> just (var "name")),
      _Type_forall>>: ("ft" ~> nameOfType @@ var "cx" @@ (project _ForallType _ForallType_body @@ var "ft"))])

qualifyUnionFieldName :: TTermDefinition (String -> Y.Maybe Name -> Name -> String)
qualifyUnionFieldName = def "qualifyUnionFieldName" $
  doc "Qualify a union field name, optionally prefixing with the Scala type name" $
  lambda "dlft" $ lambda "sname" $ lambda "fname" $
    Maybes.maybe
      (var "dlft")
      ("n" ~> (scalaTypeName @@ true @@ var "n") ++ string ".")
      (var "sname")
    ++ (scalaEscapeName @@ (Core.unName $ var "fname"))

sapply :: TTermDefinition (Scala.Data -> [Scala.Data] -> Scala.Data)
sapply = def "sapply" $
  doc "Apply a Scala data expression to a list of arguments" $
  lambda "fun" $ lambda "args" $
    inject _Data _Data_apply (
      record _Data_Apply [
        _Data_Apply_fun>>: var "fun",
        _Data_Apply_args>>: var "args"])

sapplyTypes :: TTermDefinition (Scala.Data -> [Scala.Type] -> Scala.Data)
sapplyTypes = def "sapplyTypes" $
  doc "Apply explicit type parameters to a Scala expression (e.g. f[A, B])" $
  lambda "fun" $ lambda "typeArgs" $ lets [
    -- Convert each Scala type to its string representation
    "typeToStr">: ("t" ~> asTerm typeToString @@ var "t"),
    "typeStrings">: Lists.map (var "typeToStr") (var "typeArgs"),
    "typeArgStr">: Strings.cat (list [string "[", Strings.intercalate (string ", ") (var "typeStrings"), string "]"])] $
    -- Combine function with type args: extract name from fun, append type args
    cases _Data (var "fun")
      (Just $ var "fun") -- If not a name ref, can't add type args
      [_Data_ref>>: ("ref" ~> cases _Data_Ref (var "ref")
        (Just $ var "fun")
        [_Data_Ref_name>>: ("dn" ~> lets [
          "nameStr">: project _Data_Name _Data_Name_value @@ var "dn",
          "rawName">: unwrap Scala._PredefString @@ var "nameStr"] $
          sname @@ (var "rawName" ++ var "typeArgStr"))])]

sassign :: TTermDefinition (Scala.Data -> Scala.Data -> Scala.Data)
sassign = def "sassign" $
  doc "Create a Scala assignment expression" $
  lambda "lhs" $ lambda "rhs" $
    inject _Data _Data_assign (
      record _Data_Assign [
        _Data_Assign_lhs>>: var "lhs",
        _Data_Assign_rhs>>: var "rhs"])

scalaEscapeName :: TTermDefinition (String -> String)
scalaEscapeName = def "scalaEscapeName" $
  doc "Sanitize a name for Scala: escape reserved words, replace invalid characters" $
  lambda "s" $ lets [
    -- Replace apostrophes with underscores (Scala doesn't allow ' in identifiers)
    "sanitized">: Strings.fromList (Lists.map
      ("c" ~> Logic.ifElse (Equality.equal (var "c") (int32 39)) (int32 95) (var "c"))
      (Strings.toList (var "s"))),
    -- Replace lone underscore with _x (Scala _ is a wildcard, can't be used as param name)
    "sanitized2">: Logic.ifElse (Equality.equal (var "sanitized") (string "_"))
      (string "_x")
      (var "sanitized"),
    -- Rename toString to toString_ (conflicts with Any.toString in case class fields)
    "sanitized3">: Logic.ifElse (Equality.equal (var "sanitized2") (string "toString"))
      (string "toString_")
      (var "sanitized2"),
    "needsBackticks">: Logic.or
      (Sets.member (var "sanitized3") (asTerm scalaReservedWordsRef))
      -- Names ending in _ cause lexer ambiguity with _: type ascription
      (Logic.and
        (Equality.gt (Strings.length (var "sanitized3")) (int32 0))
        (Equality.equal (Maybes.fromMaybe (int32 0) (Strings.maybeCharAt (Math.sub (Strings.length (var "sanitized3")) (int32 1)) (var "sanitized3"))) (int32 95)))] $
    Logic.ifElse (var "needsBackticks")
      (Strings.cat (list [string "`", var "sanitized3", string "`"]))
      (var "sanitized3")

scalaReservedWordsRef :: TTermDefinition (S.Set String)
scalaReservedWordsRef = def "scalaReservedWords" $
  doc "Reference to scalaReservedWords from the language module" $
  TTerm $ TermVariable $ Name "hydra.scala.language.scalaReservedWords"

scalaTypeName :: TTermDefinition (Bool -> Name -> String)
scalaTypeName = def "scalaTypeName" $
  doc "Convert a Hydra name to a Scala type name" $
  lambda "qualify" $ lambda "name" $
    Logic.ifElse
      (Logic.or (var "qualify") (Sets.member (Names.localNameOf @@ var "name") (asTerm scalaReservedWordsRef)))
      (Core.unName $ var "name")
      (Names.localNameOf @@ var "name")

slambda :: TTermDefinition (String -> Scala.Data -> Y.Maybe Scala.Type -> Scala.Data)
slambda = def "slambda" $
  doc "Create a Scala lambda (function) expression" $
  lambda "v" $ lambda "body" $ lambda "sdom" $
    inject _Data _Data_functionData (
      inject _Data_FunctionData _Data_FunctionData_function (
        record _Data_Function [
          _Data_Function_params>>: list [
            record _Data_Param [
              _Data_Param_mods>>: list ([] :: [TTerm Scala.Mod]),
              _Data_Param_name>>: inject Scala._Name Scala._Name_value (var "v"),
              _Data_Param_decltpe>>: var "sdom",
              _Data_Param_default>>: nothing]],
          _Data_Function_body>>: var "body"]))

sname :: TTermDefinition (String -> Scala.Data)
sname = def "sname" $
  doc "Create a Scala name reference" $
  lambda "s" $
    inject _Data _Data_ref (
      inject _Data_Ref _Data_Ref_name (
        record _Data_Name [
          _Data_Name_value>>: wrap _PredefString (var "s")]))

sprim :: TTermDefinition (Name -> Scala.Data)
sprim = def "sprim" $
  doc "Create a Scala primitive reference from a Hydra name" $
  lambda "name" $ lets [
    "qname">: Names.qualifyName @@ var "name",
    "prefix">: Packaging.unNamespace (Maybes.fromMaybe (wrap _Namespace (string "")) (Packaging.qualifiedNameNamespace $ var "qname")),
    "local">: scalaEscapeName @@ (Packaging.qualifiedNameLocal $ var "qname")] $
    sname @@ (var "prefix" ++ string "." ++ var "local")

stapply :: TTermDefinition (Scala.Type -> [Scala.Type] -> Scala.Type)
stapply = def "stapply" $
  doc "Apply a Scala type to a list of type arguments" $
  lambda "t" $ lambda "args" $
    inject Scala._Type Scala._Type_apply (
      record _Type_Apply [
        _Type_Apply_tpe>>: var "t",
        _Type_Apply_args>>: var "args"])

stapply1 :: TTermDefinition (Scala.Type -> Scala.Type -> Scala.Type)
stapply1 = def "stapply1" $
  doc "Apply a Scala type to one type argument" $
  lambda "t1" $ lambda "t2" $
    stapply @@ var "t1" @@ list [var "t2"]

stapply2 :: TTermDefinition (Scala.Type -> Scala.Type -> Scala.Type -> Scala.Type)
stapply2 = def "stapply2" $
  doc "Apply a Scala type to two type arguments" $
  lambda "t1" $ lambda "t2" $ lambda "t3" $
    stapply @@ var "t1" @@ list [var "t2", var "t3"]

stparam :: TTermDefinition (Name -> Scala.Type_Param)
stparam = def "stparam" $
  doc "Create a Scala type parameter from a Hydra name, capitalizing to avoid collision with value params" $
  lambda "name" $ lets [
    "v">: Formatting.capitalize @@ (Core.unName $ var "name")] $
    record _Type_Param [
      _Type_Param_mods>>: list ([] :: [TTerm Scala.Mod]),
      _Type_Param_name>>: inject Scala._Name Scala._Name_value (var "v"),
      _Type_Param_tparams>>: list ([] :: [TTerm Scala.Type_Param]),
      _Type_Param_tbounds>>: list ([] :: [TTerm Scala.TypeBounds]),
      _Type_Param_vbounds>>: list ([] :: [TTerm Scala.Type]),
      _Type_Param_cbounds>>: list ([] :: [TTerm Scala.Type])]

stref :: TTermDefinition (String -> Scala.Type)
stref = def "stref" $
  doc "Create a Scala type reference by name" $
  lambda "s" $
    inject Scala._Type Scala._Type_ref (
      inject _Type_Ref _Type_Ref_name (
        record _Type_Name [
          _Type_Name_value>>: var "s"]))

svar :: TTermDefinition (Name -> Scala.Pat)
svar = def "svar" $
  doc "Create a Scala pattern variable" $
  lambda "name" $ lets [
    "v">: Core.unName $ var "name"] $
    inject _Pat _Pat_var (
      record _Pat_Var [
        _Pat_Var_name>>: record _Data_Name [
          _Data_Name_value>>: wrap _PredefString (var "v")]])

typeToString :: TTermDefinition (Scala.Type -> String)
typeToString = def "typeToString" $
  doc "Convert a Scala type to its string representation" $
  lambda "t" $
    cases Scala._Type (var "t")
      (Just $ string "Any") [
      Scala._Type_ref>>: ("tr" ~> cases Scala._Type_Ref (var "tr")
        (Just $ string "Any")
        [Scala._Type_Ref_name>>: ("tn" ~> project Scala._Type_Name Scala._Type_Name_value @@ var "tn")]),
      Scala._Type_var>>: ("tv" ~> project Scala._Type_Name Scala._Type_Name_value @@ (project Scala._Type_Var Scala._Type_Var_name @@ var "tv")),
      Scala._Type_functionType>>: ("ft" ~> cases Scala._Type_FunctionType (var "ft")
        (Just $ string "Any") [
        Scala._Type_FunctionType_function>>: ("fn" ~> lets [
          "params">: Lists.map (asTerm typeToString) (project Scala._Type_Function Scala._Type_Function_params @@ var "fn"),
          "res">: asTerm typeToString @@ (project Scala._Type_Function Scala._Type_Function_res @@ var "fn")] $
          Strings.cat (list [string "(", Strings.intercalate (string ", ") (var "params"), string ") => ", var "res"]))]),
      Scala._Type_apply>>: ("ta" ~> lets [
        "base">: asTerm typeToString @@ (project Scala._Type_Apply Scala._Type_Apply_tpe @@ var "ta"),
        "argStrs">: Lists.map (asTerm typeToString) (project Scala._Type_Apply Scala._Type_Apply_args @@ var "ta")] $
        Strings.cat (list [var "base", string "[", Strings.intercalate (string ", ") (var "argStrs"), string "]"]))]


-- Scala Meta type/constructor name references

_Data = Scala._Data
_Data_Apply = Scala._Data_Apply
_Data_Apply_fun = Name "fun"
_Data_Apply_args = Name "args"
_Data_Assign = Scala._Data_Assign
_Data_Assign_lhs = Name "lhs"
_Data_Assign_rhs = Name "rhs"
_Data_FunctionData = Scala._Data_FunctionData
_Data_FunctionData_function = Name "function"
_Data_Function = Scala._Data_Function
_Data_Function_params = Name "params"
_Data_Function_body = Name "body"
_Data_Match = Scala._Data_Match
_Data_Name = Scala._Data_Name
_Data_Name_value = Name "value"
_Data_Param = Scala._Data_Param
_Data_Param_mods = Name "mods"
_Data_Param_name = Name "name"
_Data_Param_decltpe = Name "decltpe"
_Data_Param_default = Name "default"
_Data_Ref = Scala._Data_Ref
_Data_Ref_name = Name "name"

_Data_apply = Scala._Data_apply
_Data_assign = Scala._Data_assign
_Data_functionData = Scala._Data_functionData
_Data_ref = Scala._Data_ref
_Data_lit = Scala._Data_lit
_Data_match = Scala._Data_match

-- Note: _ForallType and _ForallType_body come from Hydra.Kernel (Hydra.Core)

-- Note: _Name and _Name_value from Scala.Meta are used via the Scala. prefix
-- to avoid ambiguity with Hydra.Kernel._Name

-- Note: Scala._Type is used via the Scala. prefix to avoid ambiguity with Hydra.Kernel._Type
_Type_Apply = Scala._Type_Apply
_Type_Apply_tpe = Name "tpe"
_Type_Apply_args = Name "args"
_Type_FunctionType = Scala._Type_FunctionType
_Type_Function = Scala._Type_Function
_Type_Function_params = Name "params"
_Type_Function_res = Name "res"
_Type_Lambda = Scala._Type_Lambda
_Type_Lambda_tparams = Name "tparams"
_Type_Lambda_tpe = Name "tpe"
_Type_Name = Scala._Type_Name
_Type_Name_value = Name "value"
_Type_Param = Scala._Type_Param
_Type_Param_mods = Name "mods"
_Type_Param_name = Name "name"
_Type_Param_tparams = Name "tparams"
_Type_Param_tbounds = Name "tbounds"
_Type_Param_vbounds = Name "vbounds"
_Type_Param_cbounds = Name "cbounds"
_Type_Ref = Scala._Type_Ref
_Type_Ref_name = Name "name"
_Type_Var = Scala._Type_Var
_Type_Var_name = Name "name"

_Type_apply = Scala._Type_apply
_Type_functionType = Scala._Type_functionType
_Type_lambda = Scala._Type_lambda
_Type_ref = Scala._Type_ref
_Type_var = Scala._Type_var

_Pat = Scala._Pat
_Pat_Var = Scala._Pat_Var
_Pat_Var_name = Name "name"
_Pat_Extract = Scala._Pat_Extract
_Pat_Extract_fun = Name "fun"
_Pat_Extract_args = Name "args"

_Pat_var = Scala._Pat_var
_Pat_extract = Name "extract"

_PredefString = Scala._PredefString

_Lit = Scala._Lit
_Lit_boolean = Scala._Lit_boolean
_Lit_float = Scala._Lit_float
_Lit_double = Scala._Lit_double
_Lit_short = Scala._Lit_short
_Lit_int = Scala._Lit_int
_Lit_long = Scala._Lit_long
_Lit_byte = Scala._Lit_byte
_Lit_string = Scala._Lit_string

_Stat = Scala._Stat
_Stat_defn = Scala._Stat_defn
_Stat_importExport = Scala._Stat_importExport

_Defn = Scala._Defn
_Defn_Class = Scala._Defn_Class
_Defn_Def = Scala._Defn_Def
_Defn_Enum = Scala._Defn_Enum
_Defn_EnumCase = Scala._Defn_EnumCase
_Defn_Type = Scala._Defn_Type
_Defn_Val = Scala._Defn_Val

_Ctor_Primary = Scala._Ctor_Primary
_Template = Scala._Template
_Self = Scala._Self
_Mod = Scala._Mod
_Mod_case = Name "case"
_Pkg = Scala._Pkg
_Import = Scala._Import
_Importer = Scala._Importer
_Importee = Scala._Importee
_ImportExportStat = Scala._ImportExportStat
_Init = Scala._Init
_Case = Scala._Case