open import Data.Nat
open import Data.Fin using (Fin; #_; suc; zero)
open import Data.List as List hiding (null)
open import Data.List.Membership.Propositional
open import Data.List.Relation.Unary.Any
open import Data.List.Relation.Unary.All
open import Data.Integer hiding (suc)
open import Data.Product hiding (map)
open import Data.Unit
open import Data.Star hiding (return ; _>>=_ ; map)
open import Relation.Binary.PropositionalEquality hiding ([_])
open ≡-Reasoning
open import Data.Empty

-- This file contains the syntax for the definitional interpreter for
-- MJ using scopes and frames, described in Section 5 of the paper.

-- The definitions are parameterized by a scope graph of size `k`, as
-- usual:

module MJSF.Syntax (k : ℕ) where

private
  Scope : Set
  Scope = Fin k

-- Return types, method parameters, and field types in MJ are given by
-- `VTy`:

data VTy : Set where
  void : VTy
  int : VTy
  ref : Scope → VTy

-- The scope graph library is parameterized by a single kind of type,
-- to be used for all declarations.  But there are three different
-- kinds of names (declarations) in MJ: value declarations (e.g., for
-- fields and method parameters); method declarations; and class
-- declarations.
--
-- We use the following type to "tag" and distinguish the various
-- kinds of declarations.

data Ty : Set where
  vᵗ : VTy → Ty
  mᵗ : List VTy → VTy → Ty
  cᵗ : Scope → Scope → Ty

-- We specialize the scopes-and-frames library to the size of our
-- scope graph, and the tagged type for declarations above:

open import ScopesFrames.ScopesFrames k Ty hiding (Scope)

-------------
-- HAS TAG --
-------------

-- As summarized above, declarations may be either value-typed,
-- method-typed, or class-typed.  We introduce some parameterized
-- predicates which only hold for a particular tag.  This is useful
-- for saying that some list of types `ts : List Ty` only contains
-- value types, and that a particular property holds for each of those
-- value types.  For example, the type `All (#v (λ _ → ⊤)) ts`
-- guarantees that `ts` contains only value types, and does not
-- constrain the value types further (due to the use of `⊤`).

data #v : (VTy → Set) → Ty → Set where
  #v' : ∀ {t p} → p t → #v p (vᵗ t)

data #m : (List VTy → VTy → Set) → Ty → Set where
  #m' : ∀ {ts rt p} → p ts rt → #m p (mᵗ ts rt)

data #c (sʳ : Scope) (p : Scope → Set) : Ty → Set where
  #c' : ∀ {s} → p s → #c sʳ p (cᵗ sʳ s)

module SyntaxG (g : Graph) where

  open UsesGraph g

  -------------------------------
  -- SUBTYPING AND INHERITANCE --
  -------------------------------

  -- As summarized in the paper, we define sub-typing in terms of
  -- inheritance edges between class scopes in the scope graph:

  _<:_ : Scope → Scope → Set
  _<:_ = _⟶_

  -- Scope graphs may be cyclic, and there is (in theory) nothing that
  -- prevents classes from mutually extending one another, thereby
  -- creating cyclic inheritance chains.  The following `Inherits`
  -- type witnesses that a given class scope has a finite set of
  -- inheritance steps.  This property is useful for defining object
  -- initialization in a way that lets Agda prove that object
  -- initialization takes finite time (i.e., no need for fuel).

  data Inherits : Scope → Scope → Set where
    obj   : ∀ s {ds sʳ} ⦃ shape : g s ≡ (ds , [ sʳ ]) ⦄ → Inherits s s
    super : ∀ {s ds sʳ sᵖ s'} ⦃ shape : g s ≡ (ds , sʳ ∷ sᵖ ∷ []) ⦄ → Inherits sᵖ s' → Inherits s s'

  ------------
  -- SYNTAX --
  ------------

  -- The expressions of MJ where the `s` in `Expr s t` is the lexical
  -- context scope:

  data Expr (s : Scope) : VTy → Set where
    call     :  ∀ {s' ts t} → Expr s (ref s') → -- receiver
                (s' ↦ (mᵗ ts t)) →              -- path to method declaration
                All (Expr s) ts →               -- argument list
                Expr s t
    get      :  ∀ {s' t} → Expr s (ref s') → (s' ↦ vᵗ t) → Expr s t
    var      :  ∀ {t} → (s ↦ vᵗ t) → Expr s t
    new      :  ∀ {sʳ s'} → s ↦ cᵗ sʳ s' → Expr s (ref s') -- path to class declaration
    null     :  ∀ {s'} → Expr s (ref s')
    num      :  ℤ → Expr s int
    iop      :  (ℤ → ℤ → ℤ) → (l r : Expr s int) → Expr s int
    upcast   :  ∀ {t' t} → t' <: t → Expr s (ref t') → Expr s (ref t)
    this     :  ∀ {s' self} → s ⟶ s' → self ∈ edgesOf s' → -- the `self` of objects is given by the lexical parent edge of a method
                Expr s (ref self)

  -- The statements of MJ where the `s` in `Stmt s t s'` is the
  -- lexical context scope before the statement `t` is the return type
  -- of the statement, and `s'` is the leixcal context scope after the
  -- statement.

  mutual
    data Stmt (s : Scope)(r : VTy) : Scope → Set where
      run   : ∀ {t'} → Expr s t' → Stmt s r s
      ifz   : ∀ {s' s'' : Scope} → Expr s int → Stmt s r s → Stmt s r s → Stmt s r s -- branches are blocks
      set   : ∀ {s' t'} → Expr s (ref s') → (s' ↦ vᵗ t') → Expr s t' → Stmt s r s
      loc   : ∀ (s' : Scope)(t' : VTy)⦃ shape : g s' ≡ ([ vᵗ t' ] , [ s ]) ⦄ → Stmt s r s' -- local variable scope `s'` is connected to lexical context scope `s`
      asgn  : ∀ {t'} → (s ↦ vᵗ t') → Expr s t' → Stmt s r s
      ret   : Expr s r → Stmt s r s
      block : ∀ {s'} → Stmts s r s' → Stmt s r s

    -- A block of statements is given by a sequence of statements
    -- whose scopes are lexically nested (if at all):

    Stmts : Scope → VTy → Scope → Set
    Stmts s r s' = Star (λ s s' → Stmt s r s') s s'

  -- Method bodies have a mandatory return expression, except for
  -- `void` typed method bodies:

  data Body (s : Scope) : VTy → Set where
    body      : ∀ {s' t} → Stmts s t s' → Expr s' t → Body s t
    body-void : ∀ {s'} → Stmts s void s' → Body s void

  -- A method defines the scope shape mandating that the method type
  -- parameters are declarations of the method body scope, and that
  -- the method body scope is connected to the scope of the class
  -- where they are defined.  The `s` in `Meth s ts t` is the scope of
  -- the surrounding class.

  data Meth (s : Scope) : List VTy → VTy → Set where
    meth  :  ∀ {ts rt}(s' : Scope)
             ⦃ shape : g s' ≡ (List.map vᵗ ts , [ s ]) ⦄ →
             Body s' rt →
             Meth s ts rt

  -- A class definition has an optional path to a super class; the
  -- only difference between a `class0` and a `class1` is that a
  -- `class1` has a path from the root scope `sʳ` to another class
  -- declaration, i.e., the super class.
  --
  -- The `sʳ` in `Class sʳ s` is the root scope (representing the
  -- class table), and the `s` is the scope of the defined class.
  --
  -- We distinguish three kinds of class members in MJ:
  --
  -- * Fields, typed by `fs`.
  --
  -- * Methods, typed by `ms`, where for each method type signature
  --   `mᵗ ts t` in `ms` there is a well-typed method `Meth s ts t`.
  --
  -- * Overriding methods, typed by `oms`, where for each method type
  --   signature `mᵗ ts t` in `oms` there is a path from the current
  --   class scope to a method declaration in a parent class scope,
  --   and a well-typed method `Meth s ts t` which overrides the
  --   method corresponding to that declaration.

  data Class (sʳ s : Scope) : Set where
    class1  :  ∀ {ms fs oms sᵖ} →
               sʳ ↦ (cᵗ sʳ sᵖ) →
               ⦃ shape  :  g s  ≡  (ms ++ fs ,  sʳ ∷ sᵖ ∷ []) ⦄ →
               All (#m (Meth s)) ms →
               All (#v (λ _ → ⊤)) fs →
               All (#m (λ ts rt → (s ↦ (mᵗ ts rt)) × Meth s ts rt )) oms → -- overrides
               Class sʳ s
    class0 : ∀ {ms fs oms} →
             ⦃ shape : g s ≡ (ms ++ fs , [ sʳ ]) ⦄ →
             All (#m (Meth s)) ms →   -- only methods
             All (#v (λ _ → ⊤)) fs →  -- only values
             All (#m (λ ts rt → (s ↦ (mᵗ ts rt)) × Meth s ts rt )) oms → -- overrides
             Class sʳ s

  -- A program consists of a sequence of well-typed class definitions
  -- and a main function to be evaluated in the context of the root
  -- scope.  The root scope has only class-typed declarations, and has
  -- no parent edges:

  data Program (sʳ : Scope)(a : VTy) : Set where
    program :
      ∀ cs ⦃ shape : g sʳ ≡ (cs , []) ⦄ →
        -- implementation of all the classes
        All (#c sʳ (λ s → Class sʳ s × ∃ λ s' → Inherits s s')) cs →
        -- main function
        Body sʳ a →
        Program sʳ a