packages feed

phino-0.0.0.1: test/ParserSpec.hs

-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com
-- SPDX-License-Identifier: MIT
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}

{-# HLINT ignore "Use tuple-section" #-}

module ParserSpec where

import Ast
import Control.Monad (forM_)
import Data.Either (isLeft, isRight)
import Misc (allPathsIn)
import Parser
import System.FilePath (takeBaseName)
import Test.Hspec (Example (Arg), Expectation, Spec, SpecWith, describe, it, runIO, shouldBe, shouldSatisfy)

test ::
  (Eq a, Show a) =>
  (String -> Either String a) ->
  [(String, Maybe a)] ->
  SpecWith (Arg Expectation)
test function useCases =
  forM_ useCases $ \(ipt, res) ->
    it ipt $ case res of
      Just right -> function ipt `shouldBe` Right right
      _ -> function ipt `shouldSatisfy` isLeft

spec :: Spec
spec = do
  describe "parse program" $
    test
      parseProgram
      [ ("Q -> [[]]", Just (Program (ExFormation []))),
        ("Q -> T(x -> Q)", Just (Program (ExApplication ExTermination [BiTau (AtLabel "x") ExGlobal]))),
        ("Q -> Q.org.eolang", Just (Program (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")))),
        ("Q -> [[x -> $, y -> ?]]", Just (Program (ExFormation [BiTau (AtLabel "x") ExThis, BiVoid (AtLabel "y")]))),
        ("{[[foo ↦ QQ]]}", Just (Program (ExFormation [BiTau (AtLabel "foo") (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang"))])))
      ]

  describe "parse expression" $
    test
      parseExpression
      [ ("Q.!a", Just (ExDispatch ExGlobal (AtMeta "a"))),
        ("[[]](!a1 -> $)", Just (ExApplication (ExFormation []) [BiTau (AtMeta "a1") ExThis])),
        ( "[[]](~0 -> $)(~11 -> Q)",
          Just
            ( ExApplication
                ( ExApplication
                    (ExFormation [])
                    [BiTau (AtAlpha 0) ExThis]
                )
                [BiTau (AtAlpha 11) ExGlobal]
            )
        ),
        ("[[]](x -> $, y -> Q)", Just (ExApplication (ExFormation []) [BiTau (AtLabel "x") ExThis, BiTau (AtLabel "y") ExGlobal])),
        ("[[!B, !B1]]", Just (ExFormation [BiMeta "B", BiMeta "B1"])),
        ("[[!B2, !a2 -> $]]", Just (ExFormation [BiMeta "B2", BiTau (AtMeta "a2") ExThis])),
        ("!e0", Just (ExMeta "e0")),
        ("[[x -> !e]]", Just (ExFormation [BiTau (AtLabel "x") (ExMeta "e")])),
        ("[[!a -> !e1]]", Just (ExFormation [BiTau (AtMeta "a") (ExMeta "e1")])),
        ("Q * !t", Just (ExMetaTail ExGlobal "t")),
        ("[[]](x -> $) * !t1", Just (ExMetaTail (ExApplication (ExFormation []) [BiTau (AtLabel "x") ExThis]) "t1")),
        ("[[D> --]]", Just (ExFormation [BiDelta "--"])),
        ("[[D> 1F-]]", Just (ExFormation [BiDelta "1F-"])),
        ("[[\n  L> Func,\n  D> 00-\n]]", Just (ExFormation [BiLambda "Func", BiDelta "00-"])),
        ("[[D> 1F-2A-00]]", Just (ExFormation [BiDelta "1F-2A-00"])),
        ("[[D> !b0]]", Just (ExFormation [BiMetaDelta "b0"])),
        ("[[L> Function]]", Just (ExFormation [BiLambda "Function"])),
        ("[[L> !F3]]", Just (ExFormation [BiMetaLambda "F3"])),
        ("[[x() -> [[]] ]]", Just (ExFormation [BiTau (AtLabel "x") (ExFormation [])])),
        ( "[[y(^,@,z) -> [[q -> Q.a]] ]]",
          Just
            ( ExFormation
                [ BiTau
                    (AtLabel "y")
                    ( ExFormation
                        [ BiVoid AtRho,
                          BiVoid AtPhi,
                          BiVoid (AtLabel "z"),
                          BiTau (AtLabel "q") (ExDispatch ExGlobal (AtLabel "a"))
                        ]
                    )
                ]
            )
        ),
        ( "!e(x(^,@) -> [[w -> !e1]])",
          Just
            ( ExApplication
                (ExMeta "e")
                [ BiTau
                    (AtLabel "x")
                    ( ExFormation
                        [ BiVoid AtRho,
                          BiVoid AtPhi,
                          BiTau (AtLabel "w") (ExMeta "e1")
                        ]
                    )
                ]
            )
        ),
        ( "[[x -> y.z, a -> ~1, w -> ^, u -> @, p -> !a, q -> !e]]",
          Just
            ( ExFormation
                [ BiTau
                    (AtLabel "x")
                    (ExDispatch (ExDispatch ExThis (AtLabel "y")) (AtLabel "z")),
                  BiTau
                    (AtLabel "a")
                    (ExDispatch ExThis (AtAlpha 1)),
                  BiTau
                    (AtLabel "w")
                    (ExDispatch ExThis AtRho),
                  BiTau
                    (AtLabel "u")
                    (ExDispatch ExThis AtPhi),
                  BiTau
                    (AtLabel "p")
                    (ExDispatch ExThis (AtMeta "a")),
                  BiTau
                    (AtLabel "q")
                    (ExMeta "e")
                ]
            )
        ),
        ( "Q.x(~1, y, [[]].z, Q.y(^,@))",
          Just
            ( ExApplication
                (ExDispatch ExGlobal (AtLabel "x"))
                [ BiTau (AtAlpha 0) (ExDispatch ExThis (AtAlpha 1)),
                  BiTau (AtAlpha 1) (ExDispatch ExThis (AtLabel "y")),
                  BiTau (AtAlpha 2) (ExDispatch (ExFormation []) (AtLabel "z")),
                  BiTau
                    (AtAlpha 3)
                    ( ExApplication
                        (ExDispatch ExGlobal (AtLabel "y"))
                        [ BiTau (AtAlpha 0) (ExDispatch ExThis AtRho),
                          BiTau (AtAlpha 1) (ExDispatch ExThis AtPhi)
                        ]
                    )
                ]
            )
        ),
        ( "5.plus(5.q(\"hello\".length))",
          Just
            ( ExApplication
                ( ExDispatch
                    ( ExApplication
                        (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "number"))
                        [ BiTau
                            (AtAlpha 0)
                            ( ExApplication
                                (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes"))
                                [ BiTau
                                    (AtAlpha 0)
                                    (ExFormation [BiDelta "40-14-00-00-00-00-00-00"])
                                ]
                            )
                        ]
                    )
                    (AtLabel "plus")
                )
                [ BiTau
                    (AtAlpha 0)
                    ( ExApplication
                        ( ExDispatch
                            ( ExApplication
                                (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "number"))
                                [ BiTau
                                    (AtAlpha 0)
                                    ( ExApplication
                                        (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes"))
                                        [ BiTau
                                            (AtAlpha 0)
                                            (ExFormation [BiDelta "40-14-00-00-00-00-00-00"])
                                        ]
                                    )
                                ]
                            )
                            (AtLabel "q")
                        )
                        [ BiTau
                            (AtAlpha 0)
                            ( ExDispatch
                                ( ExApplication
                                    (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "string"))
                                    [ BiTau
                                        (AtAlpha 0)
                                        ( ExApplication
                                            (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes"))
                                            [ BiTau
                                                (AtAlpha 0)
                                                (ExFormation [BiDelta "68-65-6C-6C-6F"])
                                            ]
                                        )
                                    ]
                                )
                                (AtLabel "length")
                            )
                        ]
                    )
                ]
            )
        )
      ]

  describe "just parses" $
    forM_
      [ "[[x -> $, y -> ?]]",
        "[[x() -> [[]] ]]",
        "[[x(^, @, y) -> [[q -> QQ]] ]]",
        "Q.x(y() -> [[]])",
        "Q.x(y(q) -> [[w -> !e]])",
        "Q.x(~1(^,@) -> [[]])",
        "Q.x.~1.^.@.!a0",
        "[[x -> y.z]]",
        "[[x -> ~1]]",
        "[[x -> ^, y -> @, z -> !a]]",
        "Q.x(a.b.c, Q.a(b), [[]])",
        "Q.x(~1, y, [[]].z, Q.y(^,@))",
        "[[x -> 5.plus(5), y -> \"hello\", z -> 42.5]]",
        "[[w -> x(~1)]]",
        "[[\n  x -> \"Hi\",\n  y -> 42\n]]",
        "[[x -> -42, y -> +34]]",
        "⟦x ↦ Φ.org.eolang(z ↦ ξ.f, x ↦ α0, φ ↦ ρ, t ↦ φ, first ↦ ⟦ λ ⤍ Function_name, Δ ⤍ 42- ⟧)⟧",
        "[[x -> 1.00e+3, y -> 2.32e-4]]"
      ]
      (\expr -> it expr (parseExpression expr `shouldSatisfy` isRight))

  describe "prohibits" $
    test
      parseExpression
      ( map
          (\input -> (input, Nothing))
          [ "Q.x()",
            "Q * !t1 * !t2",
            "Q(x -> [[]])",
            "$(x -> [[]])",
            "Q.x(x -> ?)",
            "Q.x(L> Func)",
            "Q.x(D> --)",
            "Q.x(!B)",
            "Q.x(~1 -> ?)",
            "Q.x(L> !F)",
            "Q.x(D> !b)",
            "[[~0 -> Q.x]]",
            "[[x(~1) -> [[]] ]]",
            "[[y(!e) -> [[]] ]]",
            "[[z(w) -> Q.x]]",
            "Q.x(y(~1) -> [[]])"
          ]
      )

  describe "parse packs" $ do
    packs <- runIO (allPathsIn "test/resources/parser-packs")
    forM_
      packs
      ( \pack -> do
          content <- runIO (readFile pack)
          it (takeBaseName pack) (parseProgram content `shouldSatisfy` isRight)
      )