packages feed

platinum-parsing-0.1.0.0: test/PPTest/Builders/Lalr.hs

module PPTest.Builders.Lalr (specs) where

import qualified Data.Map.Strict  as Map
import qualified Data.Set         as Set
import qualified Data.Vector      as Vector
import           PP
import           PP.Builders.Lalr
import           Test.Hspec

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

  it "should build the LALR items set collection" $ do
    -- Dragon Book (2nd edition, fr), page 240, example 4.54
    let rs = ruleSet [Rule "__start" [NonTerm "S", Empty],
                      Rule "S" [NonTerm "C", NonTerm "C", Empty],
                      Rule "C" [Term 'c', NonTerm "C", Empty],
                      Rule "C" [Term 'd', Empty]]
    let fs = firstSet rs
    let c = collection rs fs :: LrCollection LalrItem
    let e0 = [LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 0 (Term 'c'),
              LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 0 (Term 'd'),
              LalrItem (Rule "C" [Term 'd',Empty]) 0 (Term 'c'),
              LalrItem (Rule "C" [Term 'd',Empty]) 0 (Term 'd'),
              LalrItem (Rule "S" [NonTerm "C",NonTerm "C",Empty]) 0 Empty,
              LalrItem (Rule "__start" [NonTerm "S",Empty]) 0 Empty]
    let e1 = [LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 0 (Term 'c'),
              LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 0 (Term 'd'),
              LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 0 Empty,
              LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 1 (Term 'c'),
              LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 1 (Term 'd'),
              LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 1 Empty,
              LalrItem (Rule "C" [Term 'd',Empty]) 0 (Term 'c'),
              LalrItem (Rule "C" [Term 'd',Empty]) 0 (Term 'd'),
              LalrItem (Rule "C" [Term 'd',Empty]) 0 Empty]
    let e2 = [LalrItem (Rule "C" [Term 'd',Empty]) 1 (Term 'c'),
              LalrItem (Rule "C" [Term 'd',Empty]) 1 (Term 'd'),
              LalrItem (Rule "C" [Term 'd',Empty]) 1 Empty]
    let e3 = [LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 0 Empty,
              LalrItem (Rule "C" [Term 'd',Empty]) 0 Empty,
              LalrItem (Rule "S" [NonTerm "C",NonTerm "C",Empty]) 1 Empty]
    let e4 = [LalrItem (Rule "__start" [NonTerm "S",Empty]) 1 Empty]
    let e5 = [LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 2 (Term 'c'),
              LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 2 (Term 'd'),
              LalrItem (Rule "C" [Term 'c',NonTerm "C",Empty]) 2 Empty]
    let e6 = [LalrItem (Rule "S" [NonTerm "C",NonTerm "C",Empty]) 2 Empty]
    Vector.length c `shouldBe` 7
    Set.toList (c Vector.! 0) `shouldBe` e0
    Set.toList (c Vector.! 1) `shouldBe` e1
    Set.toList (c Vector.! 2) `shouldBe` e2
    Set.toList (c Vector.! 3) `shouldBe` e3
    Set.toList (c Vector.! 4) `shouldBe` e4
    Set.toList (c Vector.! 5) `shouldBe` e5
    Set.toList (c Vector.! 6) `shouldBe` e6

  it "should build the LALR parsing table" $ do
    -- Dragon Book (2nd edition, fr), page 247, figure 4.43
    let r0 = Rule "S" [NonTerm "C", NonTerm "C", Empty]
    let r1 = Rule "C" [Term 'c', NonTerm "C", Empty]
    let r2 = Rule "C" [Term 'd', Empty]
    let rs = ruleSet [Rule "__start" [NonTerm "S", Empty], r0, r1, r2]
    let fs = firstSet rs
    let c = collection rs fs :: LrCollection LalrItem
    case table c of
      Left err -> show err `shouldBe` "not an error"
      Right t -> do
        Map.size t `shouldBe` 18
        action t 0 (Term 'c') `shouldBe` LrShift 1
        action t 0 (Term 'd') `shouldBe` LrShift 2
        action t 0 (NonTerm "S") `shouldBe` LrGoto 4
        action t 0 (NonTerm "C") `shouldBe` LrGoto 3
        action t 1 (Term 'c') `shouldBe` LrShift 1
        action t 1 (Term 'd') `shouldBe` LrShift 2
        action t 1 (NonTerm "C") `shouldBe` LrGoto 5
        action t 2 (Term 'c') `shouldBe` LrReduce r2
        action t 2 (Term 'd') `shouldBe` LrReduce r2
        action t 2 Empty `shouldBe` LrReduce r2
        action t 3 (Term 'c') `shouldBe` LrShift 1
        action t 3 (Term 'd') `shouldBe` LrShift 2
        action t 3 (NonTerm "C") `shouldBe` LrGoto 6
        action t 4 Empty `shouldBe` LrAccept
        action t 5 (Term 'c') `shouldBe` LrReduce r1
        action t 5 (Term 'd') `shouldBe` LrReduce r1
        action t 5 Empty `shouldBe` LrReduce r1
        action t 6 Empty `shouldBe` LrReduce r0

  it "should detect conflicts during the table generation" $ do
    let r0 = Rule "__start" [NonTerm "S", Empty]
    let r1 = Rule "S" [Term 'a', NonTerm "A", Term 'd', Empty]
    let r2 = Rule "S" [Term 'b', NonTerm "B", Term 'd', Empty]
    let r3 = Rule "S" [Term 'a', NonTerm "B", Term 'e', Empty]
    let r4 = Rule "S" [Term 'b', NonTerm "A", Term 'e', Empty]
    let r5 = Rule "A" [Term 'c', Empty]
    let r6 = Rule "B" [Term 'c', Empty]
    let rs = ruleSet [r0, r1, r2, r3, r4, r5, r6]
    let fs = firstSet rs
    let c = collection rs fs :: LrCollection LalrItem
    let e = ["(4,'e') conflict: reduce B -> 'c',$ with reduce A -> 'c',$",
             "(4,'d') conflict: reduce B -> 'c',$ with reduce A -> 'c',$"]
    case table c of
      Left err -> err `shouldBe` e
      Right t  -> show t `shouldBe` "an error"