{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications #-}
module Test.Expr
( Pos(..)
, Loc(..)
, (@@)
, (<~>)
, OpIndex(..)
, LExpr
, LOp
, Expr(..)
, parenthesize
, parenthesizeVerbose
) where
import Data.List (intercalate)
import GHC.Generics (Generic)
import Optics
import Expression.Reorder
{- | Span in source (by char count). -}
data Pos = Pos Int Int
deriving (Show, Eq, Generic)
instance Semigroup Pos where
Pos a b <> Pos c d = Pos (min a c) (max b d)
{- | An item with source location. -}
data Loc a = Loc
{ locPos :: Pos
, locItem :: a
}
deriving (Show, Eq, Generic)
{- | Alias for @flip Loc@. -}
infix 1 @@
(@@) :: a -> Pos -> Loc a
x @@ p = Loc p x
{- | Alias for @(<>) `on` locPos@. -}
infix 2 <~>
(<~>) :: Loc a -> Loc b -> Pos
Loc p1 _ <~> Loc p2 _ = p1 <> p2
type LExpr = Loc Expr
type LOp = Loc Fixity
{- | Operation nodes are labelled with an index, for testing purposes. -}
newtype OpIndex = OpIndex Int
deriving (Show, Eq, Generic)
data Expr
= ExprAtom Char
| ExprGroup LExpr
| ExprPrefix OpIndex LOp LExpr
| ExprPostfix OpIndex LOp LExpr
| ExprBinary OpIndex LOp LExpr LExpr
| ExprIndex OpIndex LExpr (Loc [LExpr])
deriving (Show, Eq, Generic)
instance SyntaxTree LExpr (Ambiguity, LExpr) where
-- We can write all the traversals manually, but that's too much effort!
reorderChildren = traverseOf (gplate @LExpr) reorder
structureOf (Loc _ expr) = case expr of
ExprBinary i op l r -> NodeInfix (locItem op) l r (\l2 r2 -> ExprBinary i op l2 r2 @@ l2 <~> r2)
ExprPrefix i op x -> NodePrefix (fixityPrec $ locItem op) x (\x2 -> ExprPrefix i op x2 @@ op <~> x2)
ExprPostfix i op x -> NodePostfix (fixityPrec $ locItem op) x (\x2 -> ExprPostfix i op x2 @@ x2 <~> op)
ExprIndex i x xs -> NodePostfix 99 x (\x2 -> ExprIndex i x2 xs @@ x2 <~> xs)
_ -> NodeLeaf
makeError = (,)
{- | Prints the tree with parentheses everywhere. -}
parenthesize :: LExpr -> String
parenthesize (Loc _ expr) = case expr of
ExprAtom c -> [c]
ExprGroup x -> concat ["{", parenthesize x, "}"]
ExprPrefix _ (Loc _ (Fixity _ p)) x ->
concat ["(", "~", showRounded p, ".", " ", parenthesize x, ")"]
ExprPostfix _ (Loc _ (Fixity _ p)) x ->
concat ["(", parenthesize x, " ", "!", showRounded p, ".", ")"]
ExprBinary _ (Loc _ (Fixity a p)) l r ->
concat ["(", parenthesize l, " ", showBinA a, showRounded p, ".", " ", parenthesize r, ")"]
ExprIndex _ x (Loc _ xs) ->
concat ["(", parenthesize x, " ", "@{", intercalate ", " $ map parenthesize xs, "}", ")"]
where
showBinA a = case a of
AssocLeft -> "<"
AssocRight -> ">"
AssocNone -> "="
showRounded = show @Int . round
{- | Prints the tree with parentheses everywhere.
For testing purposes, also prints:
* The index of an operator node.
* The span of an operator node.
-}
parenthesizeVerbose :: String -> LExpr -> String
parenthesizeVerbose s (Loc w expr) = case expr of
ExprAtom c -> [c]
ExprGroup x -> concat ["{", parenthesizeVerbose s x, "}"]
ExprPostfix i (Loc _ (Fixity _ p)) x ->
concat ["(", parenthesizeVerbose s x, " ", "!", showRounded p, ".", showPos i w, ")"]
ExprPrefix i (Loc _ (Fixity _ p)) x ->
concat ["(", "~", showRounded p, ".", showPos i w, " ", parenthesizeVerbose s x, ")"]
ExprBinary i (Loc _ (Fixity a p)) l r ->
concat ["(", parenthesizeVerbose s l, " ", showBinA a, showRounded p, ".", showPos i w, " ", parenthesizeVerbose s r, ")"]
ExprIndex i x (Loc _ xs) ->
concat ["(", parenthesizeVerbose s x, " ", "@", showPos i w, "{", intercalate ", " . map (parenthesizeVerbose s) $ xs, "}", ")"]
where
showBinA a = case a of
AssocLeft -> "<"
AssocRight -> ">"
AssocNone -> "="
showRounded = show @Int . round
showPos (OpIndex i) (Pos a b) = "[" <> show i <> ":" <> show a <> "-" <> show b <> "]"