packages feed

disco-0.1.1.0: src/Disco/Syntax/Prims.hs

{-# LANGUAGE DeriveAnyClass     #-}
{-# LANGUAGE DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Disco.Syntax.Prims
-- Copyright   :  disco team and contributors
-- Maintainer  :  byorgey@gmail.com
--
-- Concrete syntax for the prims (i.e. built-in constants) supported
-- by the language.
--
-----------------------------------------------------------------------------

-- SPDX-License-Identifier: BSD-3-Clause

module Disco.Syntax.Prims
       ( Prim(..)
       , PrimInfo(..), primTable, toPrim, primMap, primDoc, primReference
       ) where

import           GHC.Generics                     (Generic)
import           Unbound.Generics.LocallyNameless

import           Data.Map                         (Map)
import qualified Data.Map                         as M

import           Data.Data                        (Data)
import           Disco.Syntax.Operators
import           Disco.Util                       ((==>))

------------------------------------------------------------
-- Prims
------------------------------------------------------------

-- | Primitives, /i.e./ built-in constants.
data Prim where
  PrimUOp        :: UOp -> Prim -- ^ Unary operator
  PrimBOp        :: BOp -> Prim -- ^ Binary operator

  PrimLeft       :: Prim        -- ^ Left injection into a sum type.
  PrimRight      :: Prim        -- ^ Right injection into a sum type.

  PrimSqrt       :: Prim        -- ^ Integer square root (@sqrt@)
  PrimFloor      :: Prim        -- ^ Floor of fractional type (@floor@)
  PrimCeil       :: Prim        -- ^ Ceiling of fractional type (@ceiling@)
  PrimAbs        :: Prim        -- ^ Absolute value (@abs@)

  PrimSize       :: Prim        -- ^ Size of a set (XXX should be in library)
  PrimPower      :: Prim        -- ^ Power set (XXX or bag?)

  PrimList       :: Prim        -- ^ Container -> list conversion
  PrimBag        :: Prim        -- ^ Container -> bag conversion
  PrimSet        :: Prim        -- ^ Container -> set conversion

  PrimB2C        :: Prim        -- ^ bag -> set of counts conversion
  PrimC2B        :: Prim        -- ^ set of counts -> bag conversion
  PrimMapToSet   :: Prim        -- ^ Map k v -> Set (k × v)
  PrimSetToMap   :: Prim        -- ^ Set (k × v) -> Map k v

  PrimSummary    :: Prim        -- ^ Get Adjacency list of Graph
  PrimVertex     :: Prim        -- ^ Construct a graph Vertex
  PrimEmptyGraph :: Prim        -- ^ Empty graph
  PrimOverlay    :: Prim        -- ^ Overlay two Graphs
  PrimConnect    :: Prim        -- ^ Connect Graph to another with directed edges

  PrimInsert     :: Prim        -- ^ Insert into map
  PrimLookup     :: Prim        -- ^ Get value associated with key in map

  PrimEach       :: Prim        -- ^ Each operation for containers
  PrimReduce     :: Prim        -- ^ Reduce operation for containers
  PrimFilter     :: Prim        -- ^ Filter operation for containers
  PrimJoin       :: Prim        -- ^ Monadic join for containers
  PrimMerge      :: Prim        -- ^ Generic merge operation for bags/sets

  PrimIsPrime    :: Prim        -- ^ Efficient primality test
  PrimFactor     :: Prim        -- ^ Factorization
  PrimFrac       :: Prim        -- ^ Turn a rational into a pair (num, denom)

  PrimCrash      :: Prim        -- ^ Crash

  PrimUntil      :: Prim        -- ^ @[x, y, z .. e]@

  PrimHolds      :: Prim        -- ^ Test whether a proposition holds

  PrimLookupSeq  :: Prim        -- ^ Lookup OEIS sequence
  PrimExtendSeq  :: Prim        -- ^ Extend OEIS sequence
  deriving (Show, Read, Eq, Ord, Generic, Alpha, Subst t, Data)

------------------------------------------------------------
-- Concrete syntax for prims
------------------------------------------------------------

-- | An info record for a single primitive name, containing the
--   primitive itself, its concrete syntax, and whether it is
--   "exposed", /i.e./ available to be used in the surface syntax of
--   the basic language.  Unexposed prims can only be referenced by
--   enabling the Primitives language extension and prefixing their
--   name by @$@.
data PrimInfo =
  PrimInfo
  { thePrim     :: Prim
  , primSyntax  :: String
  , primExposed :: Bool
    -- Is the prim available in the normal syntax of the language?
    --
    --   primExposed = True means that the bare primSyntax can be used
    --   in the surface syntax, and the prim will be pretty-printed as
    --   the primSyntax.
    --
    --   primExposed = False means that the only way to enter it is to
    --   enable the Primitives language extension and write a $
    --   followed by the primSyntax.  The prim will be pretty-printed with a $
    --   prefix.
    --
    --   In no case is a prim a reserved word.
  }

-- | A table containing a 'PrimInfo' record for every non-operator
--   'Prim' recognized by the language.
primTable :: [PrimInfo]
primTable =
  [ PrimInfo PrimLeft      "left"           True
  , PrimInfo PrimRight     "right"          True

  , PrimInfo (PrimUOp Not) "not"            True
  , PrimInfo PrimSqrt      "sqrt"           True
  , PrimInfo PrimFloor     "floor"          True
  , PrimInfo PrimCeil      "ceiling"        True
  , PrimInfo PrimAbs       "abs"            True

  , PrimInfo PrimSize      "size"           True
  , PrimInfo PrimPower     "power"          True

  , PrimInfo PrimList      "list"           True
  , PrimInfo PrimBag       "bag"            True
  , PrimInfo PrimSet       "set"            True

  , PrimInfo PrimB2C       "bagCounts"      True
  , PrimInfo PrimC2B       "bagFromCounts"  True
  , PrimInfo PrimMapToSet  "mapToSet"       True
  , PrimInfo PrimSetToMap  "map"            True

  , PrimInfo PrimSummary   "summary"        True
  , PrimInfo PrimVertex    "vertex"         True
  , PrimInfo PrimEmptyGraph "emptyGraph"     True
  , PrimInfo PrimOverlay   "overlay"        True
  , PrimInfo PrimConnect   "connect"        True

  , PrimInfo PrimInsert    "insert"         True
  , PrimInfo PrimLookup    "lookup"         True

  , PrimInfo PrimEach      "each"           True
  , PrimInfo PrimReduce    "reduce"         True
  , PrimInfo PrimFilter    "filter"         True
  , PrimInfo PrimJoin      "join"           False
  , PrimInfo PrimMerge     "merge"          False

  , PrimInfo PrimIsPrime   "isPrime"        False
  , PrimInfo PrimFactor    "factor"         False
  , PrimInfo PrimFrac      "frac"           False

  , PrimInfo PrimCrash     "crash"          False

  , PrimInfo PrimUntil     "until"          False

  , PrimInfo PrimHolds     "holds"          True

  , PrimInfo PrimLookupSeq "lookupSequence" False
  , PrimInfo PrimExtendSeq "extendSequence" False
  ]

-- | Find any exposed prims with the given name.
toPrim :: String -> [Prim]
toPrim x = [ p | PrimInfo p syn True <- primTable, syn == x ]

-- | A convenient map from each 'Prim' to its info record.
primMap :: Map Prim PrimInfo
primMap = M.fromList $
  [ (p, pinfo) | pinfo@(PrimInfo p _ _) <- primTable ]

-- | A map from some primitives to a short descriptive string,
--   to be shown by the :doc command.
primDoc :: Map Prim String
primDoc = M.fromList
  [ PrimUOp Neg ==> "Arithmetic negation."
  , PrimBOp Add  ==> "The sum of two numbers, types, or graphs."
  , PrimBOp Sub  ==> "The difference of two numbers."
  , PrimBOp SSub ==> "The difference of two numbers, with a lower bound of 0."
  , PrimBOp Mul  ==> "The product of two numbers, types, or graphs."
  , PrimBOp Div  ==> "Divide two numbers."
  , PrimBOp IDiv ==> "The integer quotient of two numbers, rounded down."
  , PrimBOp Mod  ==> "a mod b is the remainder when a is divided by b."
  , PrimBOp Exp  ==> "Exponentiation.  a ^ b is a raised to the b power."
  , PrimUOp Fact ==> "n! computes the factorial of n, that is, 1 * 2 * ... * n."
  , PrimFloor    ==> "floor(x) is the largest integer which is <= x."
  , PrimCeil     ==> "ceiling(x) is the smallest integer which is >= x."
  , PrimAbs      ==> "abs(x) is the absolute value of x.  Also written |x|."
  , PrimUOp Not  ==> "Logical negation: not(true) = false and not(false) = true."
  , PrimBOp And  ==> "Logical conjunction (and): true /\\ true = true; otherwise x /\\ y = false."
  , PrimBOp Or   ==> "Logical disjunction (or): false \\/ false = false; otherwise x \\/ y = true."
  , PrimBOp Impl ==> "Logical implication (implies): true -> false = false; otherwise x -> y = true."
  , PrimBOp Eq   ==> "Equality test.  x == y is true if x and y are equal."
  , PrimBOp Neq  ==> "Inequality test.  x /= y is true if x and y are unequal."
  , PrimBOp Lt   ==> "Less-than test. x < y is true if x is less than (but not equal to) y."
  , PrimBOp Gt   ==> "Greater-than test. x > y is true if x is greater than (but not equal to) y."
  , PrimBOp Leq  ==> "Less-than-or-equal test. x <= y is true if x is less than or equal to y."
  , PrimBOp Geq  ==> "Greater-than-or-equal test. x >= y is true if x is greater than or equal to y."

  ]

-- | A map from some primitives to their corresponding page in the
--   Disco language reference
--   (https://disco-lang.readthedocs.io/en/latest/reference/index.html).
primReference :: Map Prim String
primReference = M.fromList
  [ PrimBOp Add  ==> "addition"
  , PrimBOp Sub  ==> "subtraction"
  , PrimBOp SSub ==> "subtraction"
  , PrimBOp Mul  ==> "multiplication"
  , PrimBOp Div  ==> "division"
  , PrimBOp IDiv ==> "integerdiv"
  , PrimBOp Mod  ==> "mod"
  , PrimBOp Exp  ==> "exponentiation"
  , PrimUOp Fact ==> "factorial"
  , PrimFloor    ==> "round"
  , PrimCeil     ==> "round"
  , PrimAbs      ==> "abs"
  , PrimUOp Not  ==> "logic-ops"
  , PrimBOp And  ==> "logic-ops"
  , PrimBOp Or   ==> "logic-ops"
  , PrimBOp Impl ==> "logic-ops"
  ]