packages feed

phino-0.0.115: test/LiningSpec.hs

{-# LANGUAGE OverloadedStrings #-}

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

module LiningSpec where

import Bytes (NonFinite (..))
import CST
import Control.Monad (forM_)
import Lining (LineFormat (..), toSingleLine, withLineFormat)
import Test.Hspec (Spec, describe, it, shouldBe)

leafExpr :: EXPRESSION
leafExpr = EX_GLOBAL Φ

multilineFormation :: EXPRESSION
multilineFormation =
  EX_FORMATION LSB EOL (TAB 1) (BI_PAIR (PA_TAU (AT_LABEL "x") ARROW leafExpr) (BDS_EMPTY (TAB 1)) (TAB 1)) EOL (TAB 0) RSB

singlelineFormation :: EXPRESSION
singlelineFormation =
  EX_FORMATION LSB NO_EOL TAB' (BI_PAIR (PA_TAU (AT_LABEL "x") ARROW leafExpr) (BDS_EMPTY (TAB 1)) TAB') NO_EOL TAB' RSB

biPair :: BINDING
biPair = BI_PAIR (PA_TAU (AT_LABEL "x") ARROW leafExpr) (BDS_PAIR EOL (TAB 1) (PA_TAU (AT_LABEL "y") ARROW leafExpr) (BDS_EMPTY (TAB 1))) (TAB 1)

expectedBiPair :: BINDING
expectedBiPair = BI_PAIR (PA_TAU (AT_LABEL "x") ARROW leafExpr) (BDS_PAIR NO_EOL TAB' (PA_TAU (AT_LABEL "y") ARROW leafExpr) (BDS_EMPTY (TAB 1))) TAB'

spec :: Spec
spec = do
  describe "toSingleLine on EXPRESSION" $ do
    forM_
      [ ("collapses a multiline formation into one line", multilineFormation, singlelineFormation)
      ,
        ( "keeps the BI_EMPTY special case, only touching the outer tabs"
        , EX_FORMATION LSB EOL (TAB 1) (BI_EMPTY (TAB 1)) EOL (TAB 0) RSB
        , EX_FORMATION LSB NO_EOL NO_TAB (BI_EMPTY (TAB 1)) NO_EOL NO_TAB RSB
        )
      ,
        ( "recurses through EX_DISPATCH"
        , EX_DISPATCH multilineFormation NO_SPACE (AT_LABEL "y")
        , EX_DISPATCH singlelineFormation NO_SPACE (AT_LABEL "y")
        )
      ,
        ( "recurses through EX_APPLICATION"
        , EX_APPLICATION multilineFormation NO_SPACE EOL (TAB 1) (AA_TAUS biPair) EOL (TAB 0) 1
        , EX_APPLICATION singlelineFormation NO_SPACE NO_EOL TAB' (AA_TAUS expectedBiPair) NO_EOL TAB' 1
        )
      , ("recurses through EX_PHI_MEET", EX_PHI_MEET (Just "p") 3 multilineFormation, EX_PHI_MEET (Just "p") 3 singlelineFormation)
      , ("recurses through EX_PHI_AGAIN", EX_PHI_AGAIN Nothing 4 multilineFormation, EX_PHI_AGAIN Nothing 4 singlelineFormation)
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

    it "leaves every other constructor untouched" $
      forM_
        [ EX_GLOBAL Φ
        , EX_XI XI
        , EX_ATTR (AT_LABEL "z")
        , EX_TERMINATION DEAD
        , EX_STRING "hi" (TAB 2) []
        , EX_NUMBER (Left 5) (TAB 2) []
        , EX_NONFINITE Φ NfNan (TAB 2) []
        , EX_META (META NO_EXCL E "x")
        , EX_BYTES BT_EMPTY
        ]
        (\node -> toSingleLine node `shouldBe` node)

  describe "toSingleLine on BINDING" $
    forM_
      [ ("recurses through BI_PAIR, forcing TAB'", biPair, expectedBiPair)
      ,
        ( "recurses through BI_META, keeping the meta untouched"
        , BI_META (META NO_EXCL B "X") (BDS_EMPTY (TAB 1)) (TAB 1)
        , BI_META (META NO_EXCL B "X") (BDS_EMPTY (TAB 1)) TAB'
        )
      , ("leaves BI_EMPTY untouched", BI_EMPTY (TAB 1), BI_EMPTY (TAB 1))
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

  describe "toSingleLine on BINDINGS" $
    forM_
      [
        ( "recurses through BDS_PAIR, forcing TAB'"
        , BDS_PAIR EOL (TAB 1) (PA_TAU (AT_LABEL "x") ARROW leafExpr) (BDS_EMPTY (TAB 1))
        , BDS_PAIR NO_EOL TAB' (PA_TAU (AT_LABEL "x") ARROW leafExpr) (BDS_EMPTY (TAB 1))
        )
      ,
        ( "recurses through BDS_META, keeping the meta untouched"
        , BDS_META EOL (TAB 1) (META NO_EXCL B "X") (BDS_EMPTY (TAB 1))
        , BDS_META NO_EOL TAB' (META NO_EXCL B "X") (BDS_EMPTY (TAB 1))
        )
      , ("leaves BDS_EMPTY untouched", BDS_EMPTY (TAB 1), BDS_EMPTY (TAB 1))
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

  describe "toSingleLine on PAIR" $
    forM_
      [
        ( "recurses through PA_TAU"
        , PA_TAU (AT_LABEL "x") ARROW multilineFormation
        , PA_TAU (AT_LABEL "x") ARROW singlelineFormation
        )
      ,
        ( "recurses through PA_ALPHA"
        , PA_ALPHA (AL_IDX ALPHA 0) ARROW multilineFormation
        , PA_ALPHA (AL_IDX ALPHA 0) ARROW singlelineFormation
        )
      ,
        ( "recurses through PA_FORMATION"
        , PA_FORMATION (AT_LABEL "x") [AT_RHO RHO] ARROW multilineFormation
        , PA_FORMATION (AT_LABEL "x") [AT_RHO RHO] ARROW singlelineFormation
        )
      , ("leaves every other constructor untouched", PA_VOID (AT_LABEL "x") ARROW EMPTY, PA_VOID (AT_LABEL "x") ARROW EMPTY)
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

  describe "toSingleLine on APP_BINDING" $
    it "recurses into the pair" $
      toSingleLine (APP_BINDING (PA_TAU (AT_LABEL "x") ARROW multilineFormation))
        `shouldBe` APP_BINDING (PA_TAU (AT_LABEL "x") ARROW singlelineFormation)

  describe "toSingleLine on APP_ARGUMENT" $
    forM_
      [
        ( "recurses through AA_TAU"
        , AA_TAU (APP_BINDING (PA_TAU (AT_LABEL "x") ARROW multilineFormation))
        , AA_TAU (APP_BINDING (PA_TAU (AT_LABEL "x") ARROW singlelineFormation))
        )
      , ("recurses through AA_TAUS", AA_TAUS biPair, AA_TAUS expectedBiPair)
      ,
        ( "recurses through AA_EXPRS"
        , AA_EXPRS (APP_ARG multilineFormation AAS_EMPTY)
        , AA_EXPRS (APP_ARG singlelineFormation AAS_EMPTY)
        )
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

  describe "toSingleLine on APP_ARG" $
    it "recurses through both fields" $ do
      let appArg = APP_ARG multilineFormation (AAS_EXPR EOL (TAB 1) leafExpr AAS_EMPTY)
          expected = APP_ARG singlelineFormation (AAS_EXPR NO_EOL TAB' leafExpr AAS_EMPTY)
      toSingleLine appArg `shouldBe` expected

  describe "toSingleLine on APP_ARGS" $
    forM_
      [
        ( "recurses through AAS_EXPR"
        , AAS_EXPR EOL (TAB 1) multilineFormation AAS_EMPTY
        , AAS_EXPR NO_EOL TAB' singlelineFormation AAS_EMPTY
        )
      , ("leaves AAS_EMPTY untouched", AAS_EMPTY, AAS_EMPTY)
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

  describe "toSingleLine on SET" $
    forM_
      [ ("recurses through ST_BINDING", ST_BINDING biPair, ST_BINDING expectedBiPair)
      , ("leaves ST_ATTRIBUTES untouched", ST_ATTRIBUTES [AT_LABEL "x"], ST_ATTRIBUTES [AT_LABEL "x"])
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

  describe "toSingleLine on NUMBER" $ do
    forM_
      [ ("recurses through LENGTH", LENGTH biPair, LENGTH expectedBiPair)
      , ("recurses through DOMAIN", DOMAIN biPair, DOMAIN expectedBiPair)
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

    it "leaves IDX_META and LITERAL untouched" $ do
      let idxMeta = IDX_META (META NO_EXCL I "x")
          literalNum = LITERAL 5
      toSingleLine idxMeta `shouldBe` idxMeta
      toSingleLine literalNum `shouldBe` literalNum

  describe "toSingleLine on COMPARABLE" $
    forM_
      [ ("leaves CMP_ATTR untouched", CMP_ATTR (AT_LABEL "x"), CMP_ATTR (AT_LABEL "x"))
      , ("recurses through CMP_EXPR", CMP_EXPR multilineFormation, CMP_EXPR singlelineFormation)
      , ("recurses through CMP_NUM", CMP_NUM (LENGTH biPair), CMP_NUM (LENGTH expectedBiPair))
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

  describe "toSingleLine on CONDITION" $ do
    let stBinding = ST_BINDING biPair
        coBelongs = CO_BELONGS (AT_LABEL "x") IN stBinding
    forM_
      [ ("recurses through CO_BELONGS", coBelongs, CO_BELONGS (AT_LABEL "x") IN (ST_BINDING expectedBiPair))
      ,
        ( "recurses through every condition in CO_LOGIC"
        , CO_LOGIC [coBelongs, CO_EMPTY] AND
        , CO_LOGIC [CO_BELONGS (AT_LABEL "x") IN (ST_BINDING expectedBiPair), CO_EMPTY] AND
        )
      , ("recurses through CO_NF", CO_NF multilineFormation, CO_NF singlelineFormation)
      , ("recurses through CO_ABSOLUTE", CO_ABSOLUTE multilineFormation IN, CO_ABSOLUTE singlelineFormation IN)
      , ("recurses through CO_NOT", CO_NOT coBelongs, CO_NOT (CO_BELONGS (AT_LABEL "x") IN (ST_BINDING expectedBiPair)))
      ,
        ( "recurses through CO_COMPARE"
        , CO_COMPARE (CMP_ATTR (AT_LABEL "x")) EQUAL (CMP_EXPR multilineFormation)
        , CO_COMPARE (CMP_ATTR (AT_LABEL "x")) EQUAL (CMP_EXPR singlelineFormation)
        )
      , ("recurses through CO_MATCHES", CO_MATCHES "abc" multilineFormation, CO_MATCHES "abc" singlelineFormation)
      , ("recurses through CO_PART_OF", CO_PART_OF multilineFormation biPair, CO_PART_OF singlelineFormation expectedBiPair)
      ,
        ( "recurses through every group in CO_DISJOINT"
        , CO_DISJOINT [AT_LABEL "x"] [biPair]
        , CO_DISJOINT [AT_LABEL "x"] [expectedBiPair]
        )
      , ("recurses through CO_FORMATION", CO_FORMATION multilineFormation, CO_FORMATION singlelineFormation)
      , ("leaves CO_EMPTY untouched", CO_EMPTY, CO_EMPTY)
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

  describe "toSingleLine on EXTRA_ARG" $
    forM_
      [ ("recurses through ARG_EXPR", ARG_EXPR multilineFormation, ARG_EXPR singlelineFormation)
      , ("recurses through ARG_BINDING", ARG_BINDING biPair, ARG_BINDING expectedBiPair)
      , ("leaves ARG_ATTR untouched", ARG_ATTR (AT_LABEL "x"), ARG_ATTR (AT_LABEL "x"))
      , ("leaves ARG_BYTES untouched", ARG_BYTES BT_EMPTY, ARG_BYTES BT_EMPTY)
      ]
      (\(desc, node, expected) -> it desc (toSingleLine node `shouldBe` expected))

  describe "toSingleLine on EXTRA" $
    it "recurses through meta and every arg, keeping func untouched" $ do
      let extra = EXTRA (ARG_EXPR multilineFormation) "func" [ARG_ATTR (AT_LABEL "x"), ARG_BYTES BT_EMPTY]
          expected = EXTRA (ARG_EXPR singlelineFormation) "func" [ARG_ATTR (AT_LABEL "x"), ARG_BYTES BT_EMPTY]
      toSingleLine extra `shouldBe` expected

  describe "withLineFormat" $ do
    it "MULTILINE is the identity" $
      withLineFormat MULTILINE multilineFormation `shouldBe` multilineFormation
    it "SINGLELINE is toSingleLine" $
      withLineFormat SINGLELINE multilineFormation `shouldBe` toSingleLine multilineFormation