The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
{-# OPTIONS_GHC -fglasgow-exts -cpp #-}

{-|
    Class meta-model.  (object meta-meta-model)

>   Learn now the lore of Living Creatures!
>   First name the four, the free peoples:
>   Eldest of all, the elf-children;
>   Dwarf the delver, dark are his houses;
>   Ent the earthborn, old as mountains;
>   Man the mortal, master of horses...
-}

module Pugs.Class where
import Pugs.AST
import Pugs.Internals

{-
    instances of these objects represent the Perl 6 Class Model, ie
    with names like "Class", "Role", "Trait", etc.

  DEFINITIONS
  -----------

    Get these right, or you will burn forever in Meta-Meta-Hell.

  Haskell    S12 term    Access from Perl as
  -------    --------    -------------------
   MetaClass    -         MyClass.meta.meta
   Class      MetaClass   MyClass.meta
   ?          Class       MyClass

    Looks like we still need an angel to figure this all out!  :-)

-}

data MetaClass = MetaClass
    { clsName       :: Label
    , clsSuper      :: MetaClass
    , clsSubClasses :: Set MetaClass
    , clsProperties :: Map Label (Visibility, MetaProperty)
    , clsMethods    :: Map Label (Visibility, MetaMethod)
    --, clsAssocs     :: Map Label MetaAssoc
    --, clsRevAssocs  :: Map Label MetaAssoc
    , clsCats       :: Map Label (Visibility, MetaAssoc)
    }

{-
    Rules of these collections; note that the meta-model is *not* a
    multiple inheritance model.

    ∀ MetaClass A, B : A.clsSuper = B ↔ A ∈ B.clsSupClasses

-}

data MetaMethod = MetaMethod
    { methodParams  :: Params
    , methodInvoke  :: [Val] -> Eval Val
    }

data MetaProperty = MetaProperty
    { propType          :: Type
    , propDefault       :: Eval Val
    }

{-
  The old association metametaclass...

data MetaAssoc = MetaAssoc
    { assocSource       :: MetaClass
    , assocTarget       :: MetaClass
    , assocSourceRange  :: Range
    , assocTargetRange  :: Range
    , assocCategory     :: Category
    , assocIsComposite  :: Bool     -- if you kill this, its children
                                    -- makes no sense to live either
    }
-}

{-
    This is a bit like an association, but easier to deal with for
    writing proofs.
-}
data MetaAssoc = MetaAssoc
    { catClass       :: MetaClass
    , catPair        :: MetaAssoc
    , catRange       :: Range
    , catIsComposite :: Bool        -- if you kill this, its children
                                    -- makes no sense to live either
    , catOrdered     :: Bool        -- default false
    , catKeyed       :: Bool        -- default false
    , catCompanion   :: Label
    }

{-

    ∀ MetaClass A, MetaAssoc C : A.clsCats ∋ C ↔ C.catClass = A

    ∀ MetaAssoc C₁, C₂ : C₁.catPair = C₂ ↔ C₂.catPair = C₁

    -- can't be composite both ways

    ∀ MetaAssoc C₁, C₂ : C₁.catPair = C₂ ∧ C₁.catIsComposite
         → ¬(C₂.catIsComposite)

    -- this seems the simplest way to specify complementary categories

    ∀ MetaAssoc C₁, C₂, MetaClass M₁, M₂
       :   C₁.catPair = C₂  ∧ C₁.assocCompanion
         ∧ C₁.catClass = M₁ ∧ C₂.catClass = M₂
       → (   ∃ M₁.clsCats{C₂.catCompanion}
           ∧ ∃ M₂.clsCats{C₁.catCompanion}
           ∧ M₁.clsCats{C₂.catCompanion}[1] = C₁
           ∧ M₂.clsCats{C₁.catCompanion}[1] = C₂
           ∧ M₁.clsCats{C₂.catCompanion}[0] = M₂.clsCats{C₁.catCompanion}[0]
           )
-}
    
data Visibility = Public | Private

type Label = String

type Range = (Multi, Multi)

data Multi = Zero | One | Many

{-
  simple range sanity stuff... enforce ordering
    ∀ Range R : R[0] = One → R[1] ∈ ( One | Many )
    ∀ Range R : R[1] = One → R[0] ∈ ( Zero | One )
    ∀ Range R : R[0] = Many → R[1] = Many
    ∀ Range R : R[1] = Zero → R[1] = Zero

 -}

data Category = Unordered | Ordered | Keyed

data Type = Int | Str

{-
    these classes represent the Perl 6 Class model and/or type system

    So far, there exists only this pseudo-code :)

    ∀ initTree Node N ∃ MetaClass M : M.clsName = N

    Note: in the below expression, N₁ ∋ N₂ means (N₂ is a direct
    child member of N₁ within the tree it exists in)

    ∀ initTree Node N₁, N₂, MetaClass M₁, M₂ 
      : N₁ ∋ N₂ ∧ N₁ = M₁.clsName ∧ N₂ = M₂.clsName
      → M₁.subClasses ∋ M₂ 

    -- 

  Note: what follows might all be kack, and is written by someone who
        hasn't read http://xrl.us/tapl, which is not ideal.  Maybe
        someone who has will come along later and fix this.  Or maybe
        I'll get through the book soon :).  Don't hold your breath...

{-
  PkgIsGlobal is not quite right - a package is global if it exists
  in the global package namespace.  Packages either need to
  know their "own" namespace for $?PACKAGE to work (perhaps...), or
  have a back-reference to the namespace they exist in that has a
  String category that is the name, or something like that.  consider
  this a FIXME :-)
 -}

  Package := MetaClass where clsName = "Package"
  Package.clsProperties =
        { pkgName = MetaProperty { type = Symbol } 
        , pkgIsGlobal = MetaProperty { type = Bool  }
        , pkgStash = MetaProperty { type = Map (sigil, Symbol) Object }
        }

  -- Package->has_many("pkgChildren" => Package)
  -- Package->maybe_has_one("pkgParent" => Package)
  Package.clsCats =
        { pkgChildren = 
              (Public, MetaAssoc
                { catIsComposite = true,
                  catRange = (Zero, One),
                  catCompanion = "pkgParent",
                  catPair = MetaAssoc {
                     catClass = Package,
                     catRange = (Zero, Many),
                  },
                })
        }

{-
  Traits - just what do we know about them?  They're mentioned in S02,
           S04, etc as applying to Packages, Blocks, etc.  There is a
           *lot* in S06 on block traits...

           Perhaps *all* objects should be able to have generic
           "Traits" in the Meta-Model ?

           Or are traits just the word we use to mean a property of
           something in the MetaModel?  In the context of packages,
           they seem to be more generic than that.  This is why I have
           made this specifically a PkgTrait class
 -}
  PkgTrait := MetaClass where clsName = "PkgTrait"
    
  Module := MetaClass where clsName = "Module"
  Module.clsProperties =
        { modVersion = MetaProperty { type = Version }
        , modAuthorizer = MetaProperty { type = String }
        }

  Module.clsMethods =
        { modName = MetaMethod
              { methodInvoke = ( self.pkgName
                               ~ "-" ~ self.modVersion
                               ~ "-" ~ self.modAuthorizer ) }
        }

  Module.clsAssocs =
        { modTraits = (Public, MetaAssoc
                      { catIsComposite = true,
                        catRange = (Zero, Many),
                        catCompanion = "pkgParent",
                        catKeyed = true,
                        catPair = MetaAssoc
                                   ( { catClass = PkgTrait,
                                       catRange = (One, One) } ),
                      })
        }
  
  Class := MetaClass where clsName = "Class"
  Class.clsAssocs =
        { isa = (Public, MetaAssoc
                         { catOrdered = true,
                           catRange = (Zero, Many),
                           catCompanion = "subClasses",
                           catPair = MetaAssoc
                                     { catRange = (Zero, Many),
                                       catClass = Class }
                         }),
          methods = (Public, MetaAssoc
                             { catKeyed = true,
                               catRange = (Zero, Many),
                               catCompanion = "Class",
                               catPair = MetaAssoc
                                         { catRange = (One, One),
                                           catClass = Method
                                         } }),
        }

  

  -- starting to look like the beginning again?  :)

  ∀ Class C₁, C₂ : C₁.superClasses ∋ C₂
                 ↔ C₂.subClasses ∋ C₁ ∧ C₂ ∉ C₁.subClasses

  -- hmm, anyone know how to induct the above to disallow circular inheritance?

  -- & (reading TAPL)

-}