module PPTest.Builders.Nfa (specs) where

import qualified Data.Char                  as C
import qualified Data.Graph.Inductive.Graph as Gr
import           PP
import           PP.Builders.Nfa
import           PP.Grammars.Lexical
import           Test.Hspec

-- Utilities
getNfa expr = let Right ast = (parseAst expr :: To RegExpr) in buildNfa ast
isValue (_, _, NfaValue _) = True
isValue _                  = False

specs = describe "PPTest.Builders.Nfa" $ do

  it "should build the correct automaton ((a|b)*abb)" $ do
    -- Dragon Book (2nd edition, fr), page 142, figure 3.34
    let expr = "(a|b)*abb"
    let e = Gr.mkGraph [(0,NfaInitial),(1,NfaNode),(2,NfaNode),(3,NfaNode),
                        (4,NfaNode),(5,NfaNode),(6,NfaNode),(7,NfaNode),
                        (8,NfaNode),(9,NfaNode),(10,NfaFinal expr)]
                       [(0,1,NfaEmpty),(0,7,NfaEmpty),(1,2,NfaEmpty),
                        (1,4,NfaEmpty),(2,3,NfaValue 'a'),(3,6,NfaEmpty),
                        (4,5,NfaValue 'b'),(5,6,NfaEmpty),(6,1,NfaEmpty),
                        (6,7,NfaEmpty),(7,8,NfaValue 'a'),(8,9,NfaValue 'b'),
                        (9,10,NfaValue 'b')]
    getNfa expr `shouldBe` e

  it "should build the correct automaton (a+)" $ do
    let expr = "a+"
    let e = Gr.mkGraph [(0,NfaInitial),(1,NfaNode),(2,NfaNode),(3,NfaFinal expr)]
                       [(0,1,NfaEmpty),(1,2,NfaValue 'a'),
                        (2,1,NfaEmpty),(2,3,NfaEmpty)]
    getNfa expr `shouldBe` e

  it "should build the correct automaton (a?)" $ do
    let expr = "a?"
    let e = Gr.mkGraph [(0,NfaInitial),(1,NfaNode),(2,NfaNode),(3,NfaFinal expr)]
                       [(0,1,NfaEmpty),(0,3,NfaEmpty),
                        (1,2,NfaValue 'a'),(2,3,NfaEmpty)]
    getNfa expr `shouldBe` e

  it "should build the correct automaton ([a-c])" $ do
    let expr = "[a-c]"
    let e = Gr.mkGraph [(0,NfaInitial),(1,NfaNode),(2,NfaNode),(3,NfaNode),
                        (4,NfaNode),(5,NfaNode),(6,NfaNode),(7,NfaFinal expr)]
                       [(0,1,NfaEmpty),(0,3,NfaEmpty),(0,5,NfaEmpty),
                        (1,2,NfaValue 'a'),(2,7,NfaEmpty),(3,4,NfaValue 'b'),
                        (4,7,NfaEmpty),(5,6,NfaValue 'c'),(6,7,NfaEmpty)]
    getNfa expr `shouldBe` e

  it "should build the correct automaton ([a-c0-2.-])" $ do
    let expr = "[a-c0-2.-]"
    let e = Gr.mkGraph [(0,NfaInitial),(1,NfaNode),(2,NfaNode),(3,NfaNode),
                        (4,NfaNode),(5,NfaNode),(6,NfaNode),(7,NfaNode),
                        (8,NfaNode),(9,NfaNode),(10,NfaNode),(11,NfaNode),
                        (12,NfaNode),(13,NfaNode),(14,NfaNode),(15,NfaNode),
                        (16,NfaNode),(17,NfaFinal expr)]
                       [(0,1,NfaEmpty),(0,3,NfaEmpty),(0,5,NfaEmpty),
                        (0,7,NfaEmpty),(0,9,NfaEmpty),(0,11,NfaEmpty),
                        (0,13,NfaEmpty),(0,15,NfaEmpty),(1,2,NfaValue 'a'),
                        (2,17,NfaEmpty),(3,4,NfaValue 'b'),(4,17,NfaEmpty),
                        (5,6,NfaValue 'c'),(6,17,NfaEmpty),(7,8,NfaValue '0'),
                        (8,17,NfaEmpty),(9,10,NfaValue '1'),(10,17,NfaEmpty),
                        (11,12,NfaValue '2'),(12,17,NfaEmpty),(13,14,NfaValue '.'),
                        (14,17,NfaEmpty),(15,16,NfaValue '-'),(16,17,NfaEmpty)]
    getNfa expr `shouldBe` e

  it "should build the correct automaton (.)" $ do
    let expr = "."
    let e = [c | c <- [minBound..maxBound], C.isAscii c]
    let values = map (\(_, _, NfaValue c) -> c) $ filter isValue $ Gr.labEdges $ getNfa expr
    values `shouldBe` e

  it "should combine multiple NFA in one correctly" $ do
    let a = Gr.mkGraph [(0,NfaInitial),(1,NfaFinal "a")]
                       [(0,1,NfaValue 'a')]
    let b = Gr.mkGraph [(0,NfaInitial),(1,NfaNode),(2,NfaFinal "bc")]
                       [(0,1,NfaValue 'b'),(1,2,NfaValue 'c')]
    let e = Gr.mkGraph [(0,NfaInitial),(1,NfaNode),(2,NfaFinal "a"),(3,NfaNode),
                        (4,NfaNode),(5,NfaFinal "bc")]
                       [(0,1,NfaEmpty),(0,3,NfaEmpty),(1,2,NfaValue 'a'),
                        (3,4,NfaValue 'b'),(4,5,NfaValue 'c')]
    combineNfa [a,b] `shouldBe` e
