packages feed

distributors-0.2.0.0: test/Spec.hs

module Main (main) where

import Data.Char
import Data.Foldable
import Data.List (nub)
import Text.Grammar.Distributor
import Test.Hspec

expectedRegexGrammar :: [(String, RegEx)]
expectedRegexGrammar =
  [("start",NonTerminal "regex")
  ,("alternate",Sequence (NonTerminal "sequence") (KleeneStar (Sequence (Terminal "|") (NonTerminal "sequence"))))
  ,("any",Terminal ".")
  ,("atom",Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (NonTerminal "nonterminal") (NonTerminal "fail")) (NonTerminal "class-in")) (NonTerminal "class-not-in")) (NonTerminal "category-in")) (NonTerminal "category-not-in")) (NonTerminal "char")) (NonTerminal "any")) (NonTerminal "parenthesized"))
  ,("category",Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Alternate (Terminal "Ll") (Terminal "Lu")) (Terminal "Lt")) (Terminal "Lm")) (Terminal "Lo")) (Terminal "Mn")) (Terminal "Mc")) (Terminal "Me")) (Terminal "Nd")) (Terminal "Nl")) (Terminal "No")) (Terminal "Pc")) (Terminal "Pd")) (Terminal "Ps")) (Terminal "Pe")) (Terminal "Pi")) (Terminal "Pf")) (Terminal "Po")) (Terminal "Sm")) (Terminal "Sc")) (Terminal "Sk")) (Terminal "So")) (Terminal "Zs")) (Terminal "Zl")) (Terminal "Zp")) (Terminal "Cc")) (Terminal "Cf")) (Terminal "Cs")) (Terminal "Co")) (Terminal "Cn"))
  ,("category-in",Sequence (Sequence (Terminal "\\p{") (NonTerminal "category")) (Terminal "}"))
  ,("category-not-in",Sequence (Sequence (Terminal "\\P{") (NonTerminal "category")) (Terminal "}"))
  ,("char",Alternate (NonTerminal "char-literal") (NonTerminal "char-escaped"))
  ,("char-escaped",Sequence (Terminal "\\") (InClass "$()*+.?[\\]^{|}"))
  ,("char-literal",NotInClass "$()*+.?[\\]^{|}")
  ,("class-in",Sequence (Sequence (Terminal "[") (KleeneStar (NonTerminal "char"))) (Terminal "]"))
  ,("class-not-in",Sequence (Sequence (Terminal "[^") (KleeneStar (NonTerminal "char"))) (Terminal "]"))
  ,("expression",Alternate (Alternate (Alternate (Alternate (NonTerminal "terminal") (NonTerminal "kleene-optional")) (NonTerminal "kleene-star")) (NonTerminal "kleene-plus")) (NonTerminal "atom"))
  ,("fail",Terminal "\\q")
  ,("kleene-optional",Sequence (NonTerminal "atom") (Terminal "?"))
  ,("kleene-plus",Sequence (NonTerminal "atom") (Terminal "+"))
  ,("kleene-star",Sequence (NonTerminal "atom") (Terminal "*"))
  ,("nonterminal",Sequence (Sequence (Terminal "\\q{") (KleeneStar (NonTerminal "char"))) (Terminal "}"))
  ,("parenthesized",Sequence (Sequence (Terminal "(") (NonTerminal "regex")) (Terminal ")"))
  ,("regex",NonTerminal "alternate")
  ,("sequence",KleeneStar (NonTerminal "expression"))
  ,("terminal",KleenePlus (NonTerminal "char"))
  ]

regexExamples :: [(RegEx, String)]
regexExamples =
  [ (Terminal "abc123etc.", "abc123etc\\.")
  , (Sequence (Terminal "x") (Terminal "y"), "xy")
  , (Fail, "\\q")
  , (Alternate (Terminal "x") (Terminal "y"), "x|y")
  , (KleeneOpt (Terminal "x"), "x?")
  , (KleeneStar (Terminal "x"), "x*")
  , (KleenePlus (Terminal "x"), "x+")
  , (AnyChar, ".")
  , (InClass "abc", "[abc]")
  , (NotInClass "abc", "[^abc]")
  , (InCategory UppercaseLetter, "\\p{Lu}")
  , (NotInCategory LowercaseLetter, "\\P{Ll}")
  , (NonTerminal "rule-name", "\\q{rule-name}")
  , (Terminal "", "")
  ]

main :: IO ()
main = hspec $ do
  describe "regexGrammar" $ do
    it "should generate a correct grammar" $
      genGrammar regexGrammar `shouldBe` expectedRegexGrammar
    for_ regexExamples $ \(rex, str) -> do
      it ("should print " <> show rex <> " correctly") $
        showGrammar regexGrammar rex `shouldBe` Just str
      it ("should parse " <> str <> " correctly") $ do
        let parses = readGrammar regexGrammar str
        parses `shouldSatisfy` elem rex
        length (nub (map regexNorm parses)) `shouldBe` 1