packages feed

phino-0.0.115: test/EncodingSpec.hs

{-# LANGUAGE OverloadedStrings #-}

-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com
-- SPDX-License-Identifier: MIT

module EncodingSpec where

import Bytes (NonFinite (..))
import CST
import Control.Monad (forM_)
import Encoding (Encoding (..), toASCII, withEncoding)
import Test.Hspec (Spec, describe, it, shouldBe)

leafExpr :: EXPRESSION
leafExpr = EX_GLOBAL Φ

leafExprASCII :: EXPRESSION
leafExprASCII = EX_GLOBAL Q

biPair :: BINDING
biPair = BI_PAIR (PA_TAU (AT_PHI PHI) ARROW leafExpr) (BDS_EMPTY (TAB 1)) (TAB 1)

biPairASCII :: BINDING
biPairASCII = BI_PAIR (PA_TAU (AT_PHI AT) ARROW' leafExprASCII) (BDS_EMPTY (TAB 1)) (TAB 1)

spec :: Spec
spec = do
  describe "toASCII on EXPRESSION" $ do
    forM_
      [ ("EX_GLOBAL becomes Q", toASCII (EX_GLOBAL Φ), EX_GLOBAL Q)
      , ("EX_XI becomes $", toASCII (EX_XI XI), EX_XI DOLLAR)
      , ("EX_ATTR recurses into its attribute", toASCII (EX_ATTR (AT_PHI PHI)), EX_ATTR (AT_PHI AT))
      , ("EX_TERMINATION becomes T", toASCII (EX_TERMINATION DEAD), EX_TERMINATION T)
      ,
        ( "EX_FORMATION recurses into its binding and forces LSB'/RSB'"
        , toASCII (EX_FORMATION LSB EOL (TAB 1) biPair EOL (TAB 0) RSB)
        , EX_FORMATION LSB' EOL (TAB 1) biPairASCII EOL (TAB 0) RSB'
        )
      ,
        ( "EX_DISPATCH recurses into its expression and attribute"
        , toASCII (EX_DISPATCH leafExpr NO_SPACE (AT_RHO RHO))
        , EX_DISPATCH leafExprASCII NO_SPACE (AT_RHO CARET)
        )
      ,
        ( "EX_APPLICATION recurses into its expression and argument"
        , toASCII (EX_APPLICATION leafExpr NO_SPACE EOL (TAB 1) (AA_TAUS biPair) EOL (TAB 0) 1)
        , EX_APPLICATION leafExprASCII NO_SPACE EOL (TAB 1) (AA_TAUS biPairASCII) EOL (TAB 0) 1
        )
      ,
        ( "EX_META with an 'n'-headed meta becomes N'"
        , toASCII (EX_META (META NO_EXCL N "abc"))
        , EX_META (META EXCL N' "abc")
        )
      ,
        ( "EX_META with a 'k'-headed meta becomes K'"
        , toASCII (EX_META (META NO_EXCL K "abc"))
        , EX_META (META EXCL K' "abc")
        )
      ,
        ( "EX_META with any other head becomes E'"
        , toASCII (EX_META (META NO_EXCL E "abc"))
        , EX_META (META EXCL E' "abc")
        )
      ,
        ( "EX_NONFINITE switches the root to its ASCII spelling"
        , toASCII (EX_NONFINITE Φ NfNan (TAB 1) [])
        , EX_NONFINITE Q NfNan (TAB 1) []
        )
      ,
        ( "EX_PHI_MEET recurses into its expression"
        , toASCII (EX_PHI_MEET (Just "p") 3 leafExpr)
        , EX_PHI_MEET (Just "p") 3 leafExprASCII
        )
      ,
        ( "EX_PHI_AGAIN recurses into its expression"
        , toASCII (EX_PHI_AGAIN Nothing 4 leafExpr)
        , EX_PHI_AGAIN Nothing 4 leafExprASCII
        )
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

    it "leaves every other constructor untouched" $ do
      toASCII (EX_STRING "hi" (TAB 1) []) `shouldBe` EX_STRING "hi" (TAB 1) []
      toASCII (EX_NUMBER (Left 5) (TAB 1) []) `shouldBe` EX_NUMBER (Left 5) (TAB 1) []
      toASCII (EX_BYTES BT_EMPTY) `shouldBe` EX_BYTES BT_EMPTY

  describe "toASCII on APP_BINDING" $
    it "recurses into the pair" $
      toASCII (APP_BINDING (PA_TAU (AT_PHI PHI) ARROW leafExpr)) `shouldBe` APP_BINDING (PA_TAU (AT_PHI AT) ARROW' leafExprASCII)

  describe "toASCII on BINDING" $
    forM_
      [ ("recurses through BI_PAIR", toASCII biPair, biPairASCII)
      ,
        ( "recurses through BI_META, forcing the meta head to B'"
        , toASCII (BI_META (META NO_EXCL B "X") (BDS_EMPTY (TAB 1)) (TAB 1))
        , BI_META (META EXCL B' "X") (BDS_EMPTY (TAB 1)) (TAB 1)
        )
      , ("leaves BI_EMPTY untouched", toASCII (BI_EMPTY (TAB 1)), BI_EMPTY (TAB 1))
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on BINDINGS" $
    forM_
      [
        ( "recurses through BDS_PAIR"
        , toASCII (BDS_PAIR EOL (TAB 1) (PA_TAU (AT_PHI PHI) ARROW leafExpr) (BDS_EMPTY (TAB 1)))
        , BDS_PAIR EOL (TAB 1) (PA_TAU (AT_PHI AT) ARROW' leafExprASCII) (BDS_EMPTY (TAB 1))
        )
      ,
        ( "recurses through BDS_META, forcing the meta head to B'"
        , toASCII (BDS_META EOL (TAB 1) (META NO_EXCL B "X") (BDS_EMPTY (TAB 1)))
        , BDS_META EOL (TAB 1) (META EXCL B' "X") (BDS_EMPTY (TAB 1))
        )
      , ("leaves BDS_EMPTY untouched", toASCII (BDS_EMPTY (TAB 1)), BDS_EMPTY (TAB 1))
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on APP_ARGUMENT" $
    forM_
      [
        ( "recurses through AA_TAU"
        , toASCII (AA_TAU (APP_BINDING (PA_TAU (AT_PHI PHI) ARROW leafExpr)))
        , AA_TAU (APP_BINDING (PA_TAU (AT_PHI AT) ARROW' leafExprASCII))
        )
      , ("recurses through AA_TAUS", toASCII (AA_TAUS biPair), AA_TAUS biPairASCII)
      ,
        ( "recurses through AA_EXPRS"
        , toASCII (AA_EXPRS (APP_ARG leafExpr AAS_EMPTY))
        , AA_EXPRS (APP_ARG leafExprASCII AAS_EMPTY)
        )
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on APP_ARG" $
    it "recurses through both fields" $
      toASCII (APP_ARG leafExpr (AAS_EXPR EOL (TAB 1) leafExpr AAS_EMPTY))
        `shouldBe` APP_ARG leafExprASCII (AAS_EXPR EOL (TAB 1) leafExprASCII AAS_EMPTY)

  describe "toASCII on APP_ARGS" $
    forM_
      [
        ( "recurses through AAS_EXPR"
        , toASCII (AAS_EXPR EOL (TAB 1) leafExpr AAS_EMPTY)
        , AAS_EXPR EOL (TAB 1) leafExprASCII AAS_EMPTY
        )
      , ("leaves AAS_EMPTY untouched", toASCII AAS_EMPTY, AAS_EMPTY)
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on PAIR" $
    forM_
      [
        ( "recurses through PA_TAU, forcing the arrow to ARROW'"
        , toASCII (PA_TAU (AT_PHI PHI) ARROW leafExpr)
        , PA_TAU (AT_PHI AT) ARROW' leafExprASCII
        )
      ,
        ( "recurses through PA_ALPHA, forcing the arrow to ARROW'"
        , toASCII (PA_ALPHA (AL_IDX ALPHA 0) ARROW leafExpr)
        , PA_ALPHA (AL_IDX ALPHA' 0) ARROW' leafExprASCII
        )
      ,
        ( "recurses through PA_FORMATION, forcing the arrow to ARROW'"
        , toASCII (PA_FORMATION (AT_PHI PHI) [AT_RHO RHO] ARROW leafExpr)
        , PA_FORMATION (AT_PHI AT) [AT_RHO CARET] ARROW' leafExprASCII
        )
      ,
        ( "PA_VOID forces the arrow and the void marker"
        , toASCII (PA_VOID (AT_PHI PHI) ARROW EMPTY)
        , PA_VOID (AT_PHI AT) ARROW' QUESTION
        )
      , ("PA_LAMBDA becomes PA_LAMBDA'", toASCII (PA_LAMBDA "Func"), PA_LAMBDA' "Func")
      , ("PA_DELTA becomes PA_DELTA'", toASCII (PA_DELTA BT_EMPTY), PA_DELTA' BT_EMPTY)
      ,
        ( "PA_META_LAMBDA becomes PA_META_LAMBDA' with head F'"
        , toASCII (PA_META_LAMBDA (META NO_EXCL F "fn"))
        , PA_META_LAMBDA' (META EXCL F' "fn")
        )
      ,
        ( "PA_META_DELTA becomes PA_META_DELTA' with head D'"
        , toASCII (PA_META_DELTA (META NO_EXCL D "dl"))
        , PA_META_DELTA' (META EXCL D' "dl")
        )
      , ("leaves an already-ASCII PA_LAMBDA' untouched", toASCII (PA_LAMBDA' "Func"), PA_LAMBDA' "Func")
      , ("leaves an already-ASCII PA_DELTA' untouched", toASCII (PA_DELTA' BT_EMPTY), PA_DELTA' BT_EMPTY)
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on ALPHA" $
    forM_
      [ ("recurses through AL_IDX", toASCII (AL_IDX ALPHA 7), AL_IDX ALPHA' 7)
      ,
        ( "recurses through AL_META"
        , toASCII (AL_META ALPHA (META NO_EXCL I "abc"))
        , AL_META ALPHA' (META EXCL I' "abc")
        )
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on ATTRIBUTE" $
    forM_
      [ ("AT_PHI becomes AT", toASCII (AT_PHI PHI), AT_PHI AT)
      , ("AT_RHO becomes CARET", toASCII (AT_RHO RHO), AT_RHO CARET)
      , ("AT_META forces the head to A", toASCII (AT_META (META NO_EXCL TAU "abc")), AT_META (META EXCL A "abc"))
      , ("leaves AT_LABEL untouched", toASCII (AT_LABEL "x"), AT_LABEL "x")
      , ("leaves AT_LAMBDA untouched", toASCII (AT_LAMBDA LAMBDA), AT_LAMBDA LAMBDA)
      , ("leaves AT_DELTA untouched", toASCII (AT_DELTA DELTA), AT_DELTA DELTA)
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on SET" $
    forM_
      [ ("recurses through ST_BINDING", toASCII (ST_BINDING biPair), ST_BINDING biPairASCII)
      ,
        ( "maps toASCII over ST_ATTRIBUTES"
        , toASCII (ST_ATTRIBUTES [AT_PHI PHI, AT_RHO RHO])
        , ST_ATTRIBUTES [AT_PHI AT, AT_RHO CARET]
        )
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on NUMBER" $
    forM_
      [ ("IDX_META forces the head to I'", toASCII (IDX_META (META NO_EXCL I "abc")), IDX_META (META EXCL I' "abc"))
      , ("recurses through LENGTH", toASCII (LENGTH biPair), LENGTH biPairASCII)
      , ("recurses through DOMAIN", toASCII (DOMAIN biPair), DOMAIN biPairASCII)
      , ("leaves LITERAL untouched", toASCII (LITERAL 5), LITERAL 5)
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on COMPARABLE" $
    forM_
      [ ("recurses through CMP_ATTR", toASCII (CMP_ATTR (AT_PHI PHI)), CMP_ATTR (AT_PHI AT))
      , ("recurses through CMP_EXPR", toASCII (CMP_EXPR leafExpr), CMP_EXPR leafExprASCII)
      , ("recurses through CMP_NUM", toASCII (CMP_NUM (LENGTH biPair)), CMP_NUM (LENGTH biPairASCII))
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on CONDITION" $
    forM_
      [
        ( "recurses through CO_BELONGS"
        , toASCII (CO_BELONGS (AT_PHI PHI) IN (ST_BINDING biPair))
        , CO_BELONGS (AT_PHI AT) IN (ST_BINDING biPairASCII)
        )
      ,
        ( "maps toASCII over CO_LOGIC"
        , toASCII (CO_LOGIC [CO_NF leafExpr, CO_EMPTY] AND)
        , CO_LOGIC [CO_NF leafExprASCII, CO_EMPTY] AND
        )
      , ("recurses through CO_NF", toASCII (CO_NF leafExpr), CO_NF leafExprASCII)
      , ("recurses through CO_ABSOLUTE", toASCII (CO_ABSOLUTE leafExpr IN), CO_ABSOLUTE leafExprASCII IN)
      , ("recurses through CO_NOT", toASCII (CO_NOT (CO_NF leafExpr)), CO_NOT (CO_NF leafExprASCII))
      ,
        ( "recurses through CO_COMPARE"
        , toASCII (CO_COMPARE (CMP_ATTR (AT_PHI PHI)) EQUAL (CMP_EXPR leafExpr))
        , CO_COMPARE (CMP_ATTR (AT_PHI AT)) EQUAL (CMP_EXPR leafExprASCII)
        )
      , ("recurses through CO_MATCHES", toASCII (CO_MATCHES "abc" leafExpr), CO_MATCHES "abc" leafExprASCII)
      , ("recurses through CO_PART_OF", toASCII (CO_PART_OF leafExpr biPair), CO_PART_OF leafExprASCII biPairASCII)
      ,
        ( "maps toASCII over CO_DISJOINT"
        , toASCII (CO_DISJOINT [AT_PHI PHI] [biPair])
        , CO_DISJOINT [AT_PHI AT] [biPairASCII]
        )
      , ("recurses through CO_FORMATION", toASCII (CO_FORMATION leafExpr), CO_FORMATION leafExprASCII)
      , ("leaves CO_EMPTY untouched", toASCII CO_EMPTY, CO_EMPTY)
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on EXTRA_ARG" $
    forM_
      [ ("recurses through ARG_ATTR", toASCII (ARG_ATTR (AT_PHI PHI)), ARG_ATTR (AT_PHI AT))
      , ("recurses through ARG_EXPR", toASCII (ARG_EXPR leafExpr), ARG_EXPR leafExprASCII)
      , ("recurses through ARG_BINDING", toASCII (ARG_BINDING biPair), ARG_BINDING biPairASCII)
      , ("leaves ARG_BYTES untouched", toASCII (ARG_BYTES BT_EMPTY), ARG_BYTES BT_EMPTY)
      ]
      (\(desc, actual, expected) -> it desc (actual `shouldBe` expected))

  describe "toASCII on EXTRA" $
    it "recurses through meta and every arg, keeping func untouched" $
      toASCII (EXTRA (ARG_ATTR (AT_PHI PHI)) "func" [ARG_EXPR leafExpr, ARG_BYTES BT_EMPTY])
        `shouldBe` EXTRA (ARG_ATTR (AT_PHI AT)) "func" [ARG_EXPR leafExprASCII, ARG_BYTES BT_EMPTY]

  describe "withEncoding" $ do
    it "UNICODE is the identity" $ withEncoding UNICODE biPair `shouldBe` biPair
    it "ASCII is toASCII" $ withEncoding ASCII biPair `shouldBe` toASCII biPair