--------------------------------------------------------------------------------
-- Schmitty the Solver
--
-- Defines the `Theory` class. The implementation of SMT-LIB terms and scripts
-- is parameterised by an instance of the `Theory` class, which determines the
-- sorts, literals, and identifiers, as well as the links to the SMT-LIB version
-- of the theory.
--
-- The `Theory` class is split up into *three* subclasses:
--
-- - The `BaseTheory` class provides the sorts, literals, and identifiers.
--   Furthermore, it contains the machinery needed to convert models back to
--   Agda terms: `Value` interprets the SMT sorts as Agda types, and `quoteSort`
--   and `quoteValue` quote sorts and values to reflected Agda syntax.
--
--   Finally, the `interpValue` function can be used to adjust the reflected
--   syntax, which can be used to keep the values in `Set`. For instance, see
--   `SMT.Theories.Core.Base`.
--
-- - The `Printable` class provides pretty-printers, which print the sorts,
--   literals, and identifiers to their corresponding SMT-LIB terms. To help
--   define these functions, this module also exports `mkSTerm`, which prints a
--   list of strings as an S-expression.
--
-- - The `Parsable` class provides parsers, which parse the sorts and values
--   output as part of the SMT-LIB model.
--
-- The `Theory` class itself merely combines instances of all three of these
-- classes.
--------------------------------------------------------------------------------

module SMT.Theory.Base where

open import Level
open import Data.List as List using (List; _∷_; [])
open import Data.String as String using (String)
open import Data.Maybe as Maybe using (Maybe)
open import Data.Product as Prod using (Σ-syntax)
open import Function using (_∘_)
import Reflection as Rfl
open import Relation.Nullary using (Dec; yes; no)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
open import Text.Parser.String using (IUniversal; Parser)


record Signature {Sort : Set} (σ : Sort) : Set where
  field
    ArgSorts : List Sort

open Signature public


module _ {Sort : Set} where

  infix 3 _↦_

  _↦_ : (σs : List Sort) (σ : Sort)  Signature σ
  Σ  _ = record { ArgSorts = Σ }

  Op₀ : (σ : Sort)  Signature σ
  Op₀ σ = []  σ

  Op₁ : (σ : Sort)  Signature σ
  Op₁ σ = σ  []  σ

  Op₂ : (σ : Sort)  Signature σ
  Op₂ σ = σ  σ  []  σ

  map : {CoreSort : Set} {φ : CoreSort} (CORE : CoreSort  Sort)  Signature φ  Signature (CORE φ)
  map CORE Φ = record { ArgSorts = List.map CORE (ArgSorts Φ) }


record BaseTheory : Set₁ where
  field
    Sort          : Set
    _≟-Sort_      : (σ σ′ : Sort)  Dec (σ  σ′)
    BOOL          : Sort
    Literal       : Sort  Set
    Identifier    : {σ : Sort}  Signature σ  Set
    quoteSort     : Sort  Rfl.Term

    -- The Value family encodes which Agda map interprets the values returned as
    -- part of a model:
    --
    --   * If the Agda equivalent is in Set₀, Value should return the type
    --     directly, and interpValue should be the identity function.
    --
    --   * If the Agda equivalent is *not* in Set₀, Value should return a
    --     universe encoding of the possible return values, and interpValue
    --     should map the elements of that universe encoding to their intended
    --     interpretation.
    --
    Value       : Sort  Set
    quoteValue  : (σ : Sort)  Value σ  Rfl.Term
    interpValue : Rfl.Term  Rfl.Term

record Printable (baseTheory : BaseTheory) : Set where
  open BaseTheory baseTheory
  field
    showSort       : Sort  String
    showLiteral    : {σ : Sort}  Literal σ  String
    showIdentifier : {σ : Sort} {Σ : Signature σ}  Identifier Σ  String

record Parsable (baseTheory : BaseTheory) : Set₁ where
  open BaseTheory baseTheory
  field
    parseSort  : ∀[ Parser Sort ]
    parseValue : (σ : Sort)  ∀[ Parser (Value σ) ]

record Theory : Set₁ where
  field
    baseTheory  : BaseTheory
    printable   : Printable   baseTheory
    parsable    : Parsable    baseTheory

  open BaseTheory  baseTheory  public
  open Printable   printable   public
  open Parsable    parsable    public


-----------------------
-- Printer utilities --
-----------------------

-- |Create an S-expression from a list of strings.
--
-- @
--   mkSTerm ("*" ∷ "4" ∷ "5") ≡ "(* 4 5)"
-- @
--
mkSTerm : List String  String
mkSTerm = String.parens  String.unwords