packages feed

hypertypes-0.2.2: test/LangC.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}

module LangC where

import TypeLang (Name)

import Control.Lens.Operators
import Data.List.NonEmpty (NonEmpty (..), cons)
import Hyper
import Hyper.Class.Morph (morphMapped1)
import Hyper.Recurse (wrap, (##>>))
import Hyper.Syntax
import Hyper.Syntax.Row (RowExtend (..))

import Prelude

-- Demonstrating de-sugaring of a sugar-language to a core language:

-- * Let-expressions are replaced with redexes

-- * Cases and if-else expressions are replaced with applied lambda-cases

data CoreForms l h
    = CLit Int
    | CApp (App l h)
    | CVar (Var Name l h)
    | CLam (Lam Name l h)
    | CRecEmpty
    | CRecExtend (RowExtend Name l l h)
    | CGetField (h :# l) Name
    | CLamCaseEmpty
    | CLamCaseExtend (RowExtend Name l l h)
    | CInject (h :# l) Name
    deriving (Generic)

newtype LangCore h = LangCore (CoreForms LangCore h)

data IfThen h = IfThen (h :# LangSugar) (h :# LangSugar)
data Case h = Case Name Name (h :# LangSugar)

data LangSugar h
    = SBase (CoreForms LangSugar h)
    | SLet (Let Name LangSugar h)
    | SCase (h :# LangSugar) [Case h]
    | SIfElse (NonEmpty (IfThen h)) (h :# LangSugar)

makeHMorph ''CoreForms
makeHTraversableAndBases ''CoreForms
makeHTraversableAndBases ''LangCore
makeHTraversableAndBases ''IfThen
makeHTraversableAndBases ''Case
makeHTraversableAndBases ''LangSugar

instance RNodes LangSugar
instance RTraversable LangSugar
instance c LangSugar => Recursively c LangSugar

desugar :: Pure # LangSugar -> Pure # LangCore
desugar (Pure body) =
    case body of
        SBase x ->
            -- Note how we desugar all of the base forms without any boilerplate!
            x & morphMapped1 %~ desugar & core
        SLet x ->
            cLam v i `cApp` e
            where
                Let v i e = x & morphMapped1 %~ desugar
        SCase e h ->
            foldr step cAbsurd h `cApp` desugar e
            where
                step (Case c v b) = cAddLamCase c (v `cLam` desugar b)
        SIfElse g e ->
            foldr step (desugar e) g
            where
                step (IfThen c t) r =
                    cAddLamCase
                        "True"
                        (cLam "_" (desugar t))
                        (cAddLamCase "False" (cLam "_" r) cAbsurd)
                        `cApp` desugar c
    where
        core = Pure . LangCore
        cApp x = core . CApp . App x
        cLam v = core . CLam . Lam v
        cAbsurd = core CLamCaseEmpty
        cAddLamCase c h = core . CLamCaseExtend . RowExtend c h

-- Lift core language into the surface language
coreToSugar :: Pure # LangCore -> Pure # LangSugar
coreToSugar (Pure (LangCore x)) = x & morphMapped1 %~ coreToSugar & SBase & Pure

-- Convert top-level expression to sugared form when possible
sugarizeTop :: LangSugar # Pure -> LangSugar # Pure
sugarizeTop top@(SBase (CApp (App (Pure (SBase func)) arg))) =
    case func of
        CLam (Lam v b) -> Let v arg b & SLet
        CLamCaseExtend (RowExtend c0 (Pure (SBase (CLam h0))) r0) ->
            go ((c0, h0) :| []) r0
            where
                go cases (Pure (SBase CLamCaseEmpty)) =
                    case cases of
                        ("True", t) :| [("False", f)] | checkIf t f -> makeIf t f
                        ("False", f) :| [("True", t)] | checkIf t f -> makeIf t f
                        _ ->
                            cases ^.. traverse
                                <&> (\(n, Lam v b) -> Case n v b)
                                & SCase arg
                    where
                        makeIf t f =
                            case f ^. lamOut of
                                Pure (SIfElse is e) -> SIfElse (cons i is) e
                                _ -> SIfElse (pure i) (f ^. lamOut)
                            where
                                i = IfThen arg (t ^. lamOut)
                go cases (Pure (SBase (CLamCaseExtend (RowExtend c (Pure (SBase (CLam h))) r)))) =
                    go (cons (c, h) cases) r
                go _ _ = top
                checkIf t f = checkIfBranch t && checkIfBranch f
                checkIfBranch (Lam v b) = not (usesVar v b)
        _ -> top
sugarizeTop x = x

usesVar :: Name -> Pure # LangSugar -> Bool
usesVar v (Pure (SBase (CVar (Var x)))) = v == x
usesVar v (Pure x) = any (usesVar v) (x ^.. hfolded1)

sugarize :: Pure # LangSugar -> Pure # LangSugar
sugarize = wrap (Proxy @((~) LangSugar) ##>> Pure . sugarizeTop)