typed-peg-0.1.0.0: src/PEG/Grammar.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Grammar type and the acyclicity constraint.
--
-- A 'Grammar' bundles a set of named rules ('Rules') and a start expression.
-- The 'Acyclic' constraint is checked at the definition site of every
-- 'Grammar' value: if any non-terminal is left-recursive (its own name appears
-- in its own FIRST set), GHC emits a 'GHC.TypeLits.TypeError' naming the
-- offending non-terminal.
module PEG.Grammar
( Rules (..)
, Grammar (..)
, Acyclic
) where
import Data.Kind (Constraint, Type)
import GHC.TypeLits (ErrorMessage (..), Symbol, TypeError)
import PEG.Syntax (Name, PExp)
import PEG.TyLevel (Elem)
import PEG.Type
-- | A typed, heterogeneous list of named grammar rules.
--
-- @'Rules' env defs@ is a list of rules whose bodies reference non-terminals
-- in @env@ and whose definitions together form @defs@.
data Rules (env :: Env) (defs :: Env) where
RNil :: Rules env '[]
RCons :: Name s
-> PExp env ty a
-> Rules env rest
-> Rules env ('(s, 'EnvEntry ty a) ': rest)
type family Acyclic (env :: Env) :: Constraint where
Acyclic '[] = ()
Acyclic ('(s, 'EnvEntry ty _) ': rest) =
(NotLeftRec s (Elem s (First ty)) ty, Acyclic rest)
type family NotLeftRec (s :: Symbol) (b :: Bool) (ty :: Ty) :: Constraint where
NotLeftRec _ 'False _ = ()
NotLeftRec s 'True ty =
TypeError ('Text "Left-recursive non-terminal: " ':<>: 'ShowType s
':$$: 'Text "Its head set already contains itself: "
':<>: 'ShowType (First ty)
':$$: 'Text "Violates the acyclicity condition i `notElem` Gamma(i).F.")
-- | A complete PEG grammar: a set of mutually recursive rules and a start
-- expression.
--
-- Constructing a 'Grammar' value discharges the 'Acyclic' constraint, so
-- any left-recursion in @env@ becomes a compile-time type error.
data Grammar (env :: Env) (startTy :: Ty) (startA :: Type) where
Grammar :: Acyclic env
=> Rules env env
-> PExp env startTy startA
-> Grammar env startTy startA