packages feed

phino-0.0.81: test/ASTSpec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}

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

{- | Tests for the AST module that defines the abstract syntax tree
for phi-calculus programs including expressions, bindings, attributes, and bytes.
Attention! Most of the tests are generated by LLM. Consider that when refactoring
-}
module ASTSpec where

import AST
import Control.Monad (forM_)
import Data.List (sort)
import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy)

spec :: Spec
spec = do
  describe "Attribute Show instance renders AtLabel" $
    forM_
      [ ("simple label", AtLabel "x", "x")
      , ("unicode label", AtLabel "日本語", "日本語")
      , ("long label", AtLabel "myAttribute", "myAttribute")
      ]
      ( \(desc, attr, expected) ->
          it desc $ show attr `shouldBe` expected
      )

  describe "Attribute Show instance renders AtAlpha" $
    forM_
      [ ("zero index", Alpha 0, "α0")
      , ("positive index", Alpha 42, "α42")
      , ("large index", Alpha 999, "α999")
      ]
      ( \(desc, attr, expected) ->
          it desc $ show attr `shouldBe` expected
      )

  describe "Alpha Eq instance compares same constructors" $
    forM_
      [ ("alphas equal", Alpha 1, Alpha 1, True)
      , ("alphas differ", Alpha 1, Alpha 2, False)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Attribute Show instance renders special attributes" $
    forM_
      [ ("rho", AtRho, "ρ")
      , ("phi", AtPhi, "φ")
      , ("delta", AtDelta, "Δ")
      , ("lambda", AtLambda, "λ")
      ]
      ( \(desc, attr, expected) ->
          it desc $ show attr `shouldBe` expected
      )

  describe "Attribute Show instance renders AtMeta" $
    forM_
      [ ("simple meta", AtMeta "a", "!a")
      , ("long meta", AtMeta "attribute", "!attribute")
      , ("unicode meta", AtMeta "メタ", "!メタ")
      ]
      ( \(desc, attr, expected) ->
          it desc $ show attr `shouldBe` expected
      )

  describe "Attribute Eq instance compares same constructors" $
    forM_
      [ ("labels equal", AtLabel "x", AtLabel "x", True)
      , ("labels differ", AtLabel "x", AtLabel "y", False)
      , ("metas equal", AtMeta "a", AtMeta "a", True)
      , ("metas differ", AtMeta "a", AtMeta "b", False)
      , ("rho equals rho", AtRho, AtRho, True)
      , ("phi equals phi", AtPhi, AtPhi, True)
      , ("delta equals delta", AtDelta, AtDelta, True)
      , ("lambda equals lambda", AtLambda, AtLambda, True)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Attribute Eq instance compares different constructors" $
    forM_
      [ ("rho vs phi", AtRho, AtPhi, False)
      , ("delta vs lambda", AtDelta, AtLambda, False)
      , ("meta vs label", AtMeta "x", AtLabel "x", False)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Attribute Ord instance orders correctly" $
    it "sorts attributes by constructor order" $
      let attrs = [AtMeta "z", AtDelta, AtLambda, AtRho, AtPhi, AtLabel "a"]
          first : _ = sort attrs
          isLabel (AtLabel _) = True
          isLabel _ = False
       in first `shouldSatisfy` isLabel

  describe "Bytes Eq instance compares same constructors" $
    forM_
      [ ("empty equals empty", BtEmpty, BtEmpty, True)
      , ("one equals one", BtOne "FF", BtOne "FF", True)
      , ("one differs", BtOne "FF", BtOne "00", False)
      , ("many equals many", BtMany ["00", "01"], BtMany ["00", "01"], True)
      , ("many differs", BtMany ["00"], BtMany ["01"], False)
      , ("meta equals meta", BtMeta "b", BtMeta "b", True)
      , ("meta differs", BtMeta "b", BtMeta "c", False)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Bytes Eq instance compares different constructors" $
    forM_
      [ ("empty vs one", BtEmpty, BtOne "00", False)
      , ("one vs many", BtOne "00", BtMany ["00"], False)
      , ("many vs meta", BtMany ["00"], BtMeta "b", False)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Bytes Ord instance orders correctly" $
    it "sorts bytes by constructor order" $
      let bytes = [BtMeta "z", BtMany ["00"], BtOne "FF", BtEmpty]
          first : _ = sort bytes
       in first `shouldBe` BtEmpty

  describe "Binding Eq instance compares same constructors" $
    forM_
      [ ("tau equals tau", BiTau AtRho ExRoot, BiTau AtRho ExRoot, True)
      , ("tau differs by attr", BiTau AtRho ExRoot, BiTau AtPhi ExRoot, False)
      , ("tau differs by expr", BiTau AtRho ExRoot, BiTau AtRho ExXi, False)
      , ("meta equals meta", BiMeta "B", BiMeta "B", True)
      , ("meta differs", BiMeta "B", BiMeta "C", False)
      , ("delta equals delta", BiDelta BtEmpty, BiDelta BtEmpty, True)
      , ("delta differs", BiDelta BtEmpty, BiDelta (BtOne "00"), False)
      , ("void equals void", BiVoid AtRho, BiVoid AtRho, True)
      , ("void differs", BiVoid AtRho, BiVoid AtPhi, False)
      , ("lambda equals lambda", BiLambda (Function "Func"), BiLambda (Function "Func"), True)
      , ("lambda differs", BiLambda (Function "Func"), BiLambda (Function "Other"), False)
      , ("metalambda equals", BiLambda (FnMeta "F"), BiLambda (FnMeta "F"), True)
      , ("metalambda differs", BiLambda (FnMeta "F"), BiLambda (FnMeta "G"), False)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Binding Eq instance compares different constructors" $
    forM_
      [ ("tau vs meta", BiTau AtRho ExRoot, BiMeta "B", False)
      , ("delta vs void", BiDelta BtEmpty, BiVoid AtDelta, False)
      , ("lambda vs metalambda", BiLambda (Function "F"), BiLambda (FnMeta "F"), False)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Binding Ord instance orders correctly" $
    it "sorts bindings by constructor order" $
      let bindings = [BiLambda (FnMeta "Z"), BiLambda (Function "A"), BiVoid AtRho, BiDelta BtEmpty, BiMeta "B", BiTau AtRho ExRoot]
          first : _ = sort bindings
          isTau (BiTau _ _) = True
          isTau _ = False
       in first `shouldSatisfy` isTau

  describe "Expression Eq instance compares same constructors" $
    forM_
      [ ("formation equals", ExFormation [], ExFormation [], True)
      , ("formation differs", ExFormation [], ExFormation [BiVoid AtRho], False)
      , ("this equals this", ExXi, ExXi, True)
      , ("global equals global", ExRoot, ExRoot, True)
      , ("termination equals", ExTermination, ExTermination, True)
      , ("meta equals meta", ExMeta "e", ExMeta "e", True)
      , ("meta differs", ExMeta "e", ExMeta "f", False)
      , ("application equals", ExApplication ExRoot (ArTau AtRho ExXi), ExApplication ExRoot (ArTau AtRho ExXi), True)
      , ("dispatch equals", ExDispatch ExRoot AtRho, ExDispatch ExRoot AtRho, True)
      , ("dispatch differs", ExDispatch ExRoot AtRho, ExDispatch ExRoot AtPhi, False)
      , ("metatail equals", ExMetaTail ExRoot "t", ExMetaTail ExRoot "t", True)
      , ("metatail differs", ExMetaTail ExRoot "t", ExMetaTail ExRoot "s", False)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Expression Eq instance compares different constructors" $
    forM_
      [ ("formation vs this", ExFormation [], ExXi, False)
      , ("global vs termination", ExRoot, ExTermination, False)
      , ("meta vs dispatch", ExMeta "e", ExDispatch ExRoot AtRho, False)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Expression Ord instance orders correctly" $
    it "sorts expressions by constructor order" $
      let exprs = [ExMetaTail ExRoot "t", ExDispatch ExRoot AtRho, ExApplication ExRoot (ArTau AtRho ExRoot), ExMeta "e", ExTermination, ExRoot, ExXi, ExFormation []]
          first : _ = sort exprs
       in first `shouldBe` ExFormation []

  describe "Program Eq instance compares programs" $
    forM_
      [ ("same programs equal", Program ExRoot, Program ExRoot, True)
      , ("different programs differ", Program ExRoot, Program ExXi, False)
      ]
      ( \(desc, lhs, rhs, expected) ->
          it desc $ (lhs == rhs) `shouldBe` expected
      )

  describe "Program Ord instance orders correctly" $
    it "orders programs by expression" $
      let progs = [Program ExXi, Program ExRoot, Program (ExFormation [])]
          first : _ = sort progs
       in first `shouldBe` Program (ExFormation [])

  describe "Program Show instance renders programs" $
    it "shows program wrapper" $
      let hasProgram str = "Program" `elem` words str
       in show (Program ExRoot) `shouldSatisfy` hasProgram

  describe "countNodes counts ExFormation with non-tau bindings" $
    forM_
      [ ("Q", ExRoot, 1)
      , ("T", ExTermination, 1)
      , ("$", ExXi, 1)
      , ("dispatch on global", ExDispatch ExRoot (AtLabel "x"), 3)
      , ("application with globals", ExApplication ExRoot (ArTau AtRho ExRoot), 6)
      , ("nested expressions", ExFormation [BiTau AtRho ExRoot, BiTau AtPhi ExRoot], 9)
      , ("empty formation", ExFormation [], 1)
      , ("void binding", ExFormation [BiVoid AtRho], 5)
      , ("delta binding", ExFormation [BiDelta BtEmpty], 5)
      , ("lambda binding", ExFormation [BiLambda (Function "Func")], 5)
      , ("meta binding", ExFormation [BiMeta "B"], 3)
      , ("metalambda binding", ExFormation [BiLambda (FnMeta "F")], 5)
      , ("meta expression", ExMeta "e", 1)
      , ("metatail expression", ExMetaTail ExRoot "t", 3)
      , ("deeply nested dispatch", ExDispatch (ExDispatch ExRoot (AtLabel "a")) (AtLabel "b"), 5)
      , ("formation with dispatch inside", ExFormation [BiTau AtRho (ExDispatch ExRoot (AtLabel "x"))], 7)
      ]
      ( \(desc, expr, expected) ->
          it desc $ countNodes expr `shouldBe` expected
      )