packages feed

phino-0.0.157: test/Grown.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

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

module Grown (Grown (..)) where

import AST
import Bytes (numToBts, strToBts)
import Data.List (nub)
import Data.Text qualified as T
import Test.QuickCheck (Arbitrary (..), Gen, chooseInt, elements, frequency, shrinkList, shuffle, sized, suchThat, vectorOf)

newtype Grown = Grown Expression
  deriving (Show)

instance Arbitrary Grown where
  arbitrary = Grown <$> sized (expression . (`div` 20))
    where
      expression :: Int -> Gen Expression
      expression depth =
        frequency
          ( [ (2, pure ExXi)
            , (2, pure ExRoot)
            , (1, pure ExTermination)
            , (1, ExMeta <$> meta "enk")
            , (1, DataNumber . numToBts <$> arbitrary)
            , (1, DataString . strToBts <$> arbitrary)
            , (2, ExFormation <$> bindings 0)
            ]
              ++ [ (weight, grown)
                 | depth > 0
                 , (weight, grown) <-
                     [ (4, ExFormation <$> bindings depth)
                     , (3, ExDispatch <$> expression (depth - 1) <*> attribute)
                     , (3, (ExApplication <$> expression (depth - 1) <*> argument depth) `suchThat` spelled)
                     ]
                 ]
          )
      spelled :: Expression -> Bool
      spelled (ExApplication ExXi _) = False
      spelled (ExApplication ExRoot _) = False
      spelled (ExApplication (DataNumber _) (ArTau AtRho _)) = False
      spelled (ExApplication (DataString _) (ArTau AtRho _)) = False
      spelled _ = True
      argument :: Int -> Gen Argument
      argument depth = frequency [(3, ArTau <$> attribute <*> expression (depth - 1)), (2, ArAlpha <$> alpha <*> expression (depth - 1))]
      alpha :: Gen Alpha
      alpha = frequency [(4, Alpha <$> chooseInt (0, 3)), (1, AlMeta <$> meta "i")]
      bindings :: Int -> Gen [Binding]
      bindings depth = do
        attrs <- nub <$> (chooseInt (0, 4) >>= (`vectorOf` attribute))
        named <- mapM (\attr -> frequency [(1, pure (BiVoid attr)), (3, BiTau attr <$> expression (depth - 1))]) attrs
        wildcard <- frequency [(3, pure []), (1, pure . BiMeta <$> meta "B")]
        delta <- frequency [(2, pure []), (1, pure . BiDelta <$> bytes)]
        lambda <- frequency [(2, pure []), (1, pure . BiLambda <$> function)]
        (++ delta ++ lambda) <$> shuffle (named ++ wildcard)
      attribute :: Gen Attribute
      attribute = frequency [(6, AtLabel <$> label), (1, pure AtPhi), (1, pure AtRho), (1, AtMeta <$> meta "t")]
      label :: Gen T.Text
      label = T.pack <$> spelt ['a' .. 'z'] (['a' .. 'z'] ++ ['0' .. '9'] ++ "-_")
      function :: Gen Function
      function = frequency [(4, Function . T.pack <$> spelt ['A' .. 'Z'] (['a' .. 'z'] ++ ['0' .. '9'] ++ "_")), (1, FnMeta <$> meta "F"), (2, FnSymbol <$> chooseInt (1, 99))]
      bytes :: Gen Bytes
      bytes = frequency [(1, pure BtEmpty), (2, BtOne <$> byte), (3, BtMany <$> (chooseInt (2, 9) >>= (`vectorOf` byte))), (1, BtMeta <$> meta "d")]
      byte :: Gen String
      byte = vectorOf 2 (elements (['0' .. '9'] ++ ['A' .. 'F']))
      meta :: String -> Gen T.Text
      meta heads = T.pack <$> ((:) <$> elements heads <*> (chooseInt (1, 3) >>= (`vectorOf` elements (['1' .. '9'] ++ ['a' .. 'z']))))
      spelt :: String -> String -> Gen String
      spelt heads rest = (:) <$> elements heads <*> (chooseInt (0, 5) >>= (`vectorOf` elements rest))
  shrink (Grown expr) = Grown <$> smaller expr
    where
      smaller :: Expression -> [Expression]
      smaller = \case
        ExFormation bds -> [inner | BiTau _ inner <- bds] ++ (ExFormation <$> shrinkList binding bds)
        ExDispatch inner attr -> inner : (ExDispatch <$> smaller inner <*> pure attr)
        ExApplication inner arg -> inner : held arg : [ExApplication less arg | less <- smaller inner, less `notElem` [ExXi, ExRoot]] ++ (ExApplication inner <$> argued arg)
        _ -> []
      binding :: Binding -> [Binding]
      binding (BiTau attr inner) = BiTau attr <$> smaller inner
      binding _ = []
      held :: Argument -> Expression
      held (ArTau _ inner) = inner
      held (ArAlpha _ inner) = inner
      argued :: Argument -> [Argument]
      argued (ArTau attr inner) = ArTau attr <$> smaller inner
      argued (ArAlpha idx inner) = ArAlpha idx <$> smaller inner