98 lines
3.9 KiB
Haskell
98 lines
3.9 KiB
Haskell
module Ivo.Syntax.Printer (unparseAST) where
|
|
|
|
import Ivo.Syntax.Base
|
|
|
|
import Data.Functor.Base (NonEmptyF (NonEmptyF))
|
|
import Data.Functor.Foldable (cata)
|
|
import Data.List.NonEmpty (toList)
|
|
import Data.Text.Lazy (fromStrict, toStrict, intercalate, unwords, singleton)
|
|
import Data.Text.Lazy.Builder (Builder, fromText, fromLazyText, toLazyText, fromString)
|
|
import Prelude hiding (unwords)
|
|
|
|
-- I'm surprised this isn't in base somewhere.
|
|
unsnoc :: NonEmpty a -> ([a], a)
|
|
unsnoc = cata \case
|
|
NonEmptyF x' Nothing -> ([], x')
|
|
NonEmptyF x (Just (xs, x')) -> (x : xs, x')
|
|
|
|
data SyntaxType
|
|
-- | Ambiguous syntax is not necessarily finite and not guaranteed to consume any input.
|
|
= Ambiguous
|
|
-- | Block syntax is not necessarily finite but is guaranteed to consume input.
|
|
| Block
|
|
-- | Unambiguous syntax is finite and guaranteed to consume input.
|
|
| Finite
|
|
type Tagged a = (SyntaxType, a)
|
|
|
|
tag :: SyntaxType -> a -> Tagged a
|
|
tag = (,)
|
|
|
|
group :: Builder -> Builder
|
|
group x = "(" <> x <> ")"
|
|
|
|
-- | An unambiguous context has a marked beginning and end.
|
|
unambiguous :: Tagged Builder -> Builder
|
|
unambiguous (_, t) = t
|
|
|
|
-- | A final context has a marked end but no marked beginning,
|
|
-- so we provide a grouper when a beginning marker is necessary.
|
|
final :: Tagged Builder -> Builder
|
|
final (Ambiguous, t) = group t
|
|
final (_, t) = t
|
|
|
|
-- | An ambiguous context has neither a marked end nor marked beginning,
|
|
-- so we provide a grouper when an ending marker is necessary.
|
|
ambiguous :: Tagged Builder -> Builder
|
|
ambiguous (Finite, t) = t
|
|
ambiguous (_, t) = group t
|
|
|
|
-- | Turn an abstract syntax tree into the corresponding concrete syntax.
|
|
--
|
|
-- This is *not* a pretty-printer; it uses minimal whitespace.
|
|
unparseAST :: AST -> Text
|
|
unparseAST = toStrict . toLazyText . snd . cata \case
|
|
VarF name -> tag Finite $ fromText name
|
|
AppF ef exs -> unparseApp ef exs
|
|
AbsF names body -> tag Block $
|
|
let names' = fromLazyText (unwords $ map fromStrict $ toList names)
|
|
in "λ" <> names' <> ". " <> unambiguous body
|
|
LetFP defs body -> tag Block $ "let " <> unparseDefs defs <> " in " <> unambiguous body
|
|
LetRecFP def body -> tag Block $ "letrec " <> unparseDef def <> " in " <> unambiguous body
|
|
CtrF ctr e -> unparseCtr ctr e
|
|
CaseF pats ->
|
|
let pats' = fromLazyText $ intercalate "; " $ map (toLazyText . unparsePat) pats
|
|
in tag Finite $ "{ " <> pats' <> " }"
|
|
PNatF n -> tag Finite $ fromString $ show n
|
|
PListF es ->
|
|
let es' = fromLazyText $ intercalate ", " $ map (toLazyText . unambiguous) es
|
|
in tag Finite $ "[" <> es' <> "]"
|
|
PStrF s -> tag Finite $ "\"" <> fromText s <> "\""
|
|
PCharF c -> tag Finite $ "'" <> fromLazyText (singleton c)
|
|
HoleFP -> tag Finite "_"
|
|
where
|
|
unparseApp :: Tagged Builder -> NonEmpty (Tagged Builder) -> Tagged Builder
|
|
unparseApp ef (unsnoc -> (exs, efinal))
|
|
= tag Ambiguous $ foldr (\e es' -> ambiguous e <> " " <> es') (final efinal) (ef : exs)
|
|
|
|
unparseDef (name, val) = fromText name <> " = " <> unambiguous val
|
|
unparseDefs defs = fromLazyText (intercalate "; " $ map (toLazyText . unparseDef) $ toList defs)
|
|
|
|
unparseCtr :: Ctr -> [Tagged Builder] -> Tagged Builder
|
|
-- Fully-applied special syntax forms
|
|
unparseCtr CPair [x, y] = tag Finite $ "(" <> unambiguous x <> ", " <> unambiguous y <> ")"
|
|
unparseCtr CCons [x, y] = tag Finite $ "(" <> unambiguous x <> " :: " <> unambiguous y <> ")"
|
|
-- Partially-applied syntax forms
|
|
unparseCtr CUnit [] = tag Finite "()"
|
|
unparseCtr CPair [] = tag Finite "(,)"
|
|
unparseCtr CLeft [] = tag Finite "Left"
|
|
unparseCtr CRight [] = tag Finite "Right"
|
|
unparseCtr CZero [] = tag Finite "Z"
|
|
unparseCtr CSucc [] = tag Finite "S"
|
|
unparseCtr CNil [] = tag Finite "[]"
|
|
unparseCtr CCons [] = tag Finite "(::)"
|
|
unparseCtr CChar [] = tag Finite "Char"
|
|
unparseCtr ctr (x:xs) = unparseApp (unparseCtr ctr []) (x :| xs)
|
|
|
|
unparsePat (Pat ctr ns e)
|
|
= unambiguous (unparseCtr ctr (map (tag Finite . fromText) ns)) <> " -> " <> unambiguous e
|