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