Skip to content

Commit 574bbd6

Browse files
knothedEricson2314
authored andcommitted
Extra tabular and grammar stuff
Fix appveyor Create happy-grammar package - Split Grammar into Grammar, Mangler and Tables - Move ErrorHandlerType from AbsSyn to Grammar where it belongs - Bump version to 1.21.0 Create happy-tabular package - Move things out of Main - call `runTabular` from Main Remove code changes Move GenUtils out of happy-grammar - Move mapDollarDollar to Grammar: the $$ feature is on grammar level - Move mkClosure into tabular as it is only used there Update haskell-ci.yml
1 parent 3c014cb commit 574bbd6

12 files changed

+261
-180
lines changed

packages/tabular/happy-tabular.cabal

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ library
4343
Happy.Tabular.Info,
4444
Happy.Tabular.LALR,
4545
Happy.Tabular.NameSet
46+
Happy.Tabular.Main
47+
Happy.Tabular.FindRedundancies
48+
Happy.Tabular.Tables
4649
build-depends: base < 5,
4750
array,
4851
containers >= 0.4.2,

packages/tabular/src/Happy/Tabular.lhs

+13-54
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
> select_first_reduction
77
> ) where
88

9-
> import Happy.Grammar
9+
> import Happy.Tabular.NameSet ( NameSet )
1010
> import Happy.Tabular.First
11+
> import qualified Happy.Tabular.LALR as LALR
1112
> import Happy.Tabular.LALR
12-
> import Happy.Tabular.NameSet (NameSet)
13+
> ( Lr0Item, Lr1Item, ItemSetWithGotos
14+
> , precalcClosure0, propLookaheads, calcLookaheads, mergeLookaheadInfo
15+
> )
16+
> import Happy.Tabular.Tables
17+
> import Happy.Grammar
18+
> import Happy.Tabular.FindRedundancies
1319

14-
> import Data.Array( Array, assocs, elems, (!) )
15-
> import Data.List ( nub )
20+
> import Data.Array ( Array )
1621

1722
> data Tables =
1823
> Tables {
@@ -34,60 +39,14 @@
3439
> genTables select_reductions g =
3540
> let first = {-# SCC "First" #-} (mkFirst g)
3641
> closures = {-# SCC "Closures" #-} (precalcClosure0 g)
37-
> lr0items = {-# SCC "LR0_Sets" #-} (genLR0items g closures)
42+
> lr0items = {-# SCC "LR0_Sets" #-} (LALR.genLR0items g closures)
3843
> (la_spont, la_prop)
3944
> = {-# SCC "Prop" #-} (propLookaheads g lr0items first)
4045
> lookaheads = {-# SCC "Calc" #-} (calcLookaheads (length lr0items) la_spont la_prop)
4146
> lr1items = {-# SCC "Merge" #-} (mergeLookaheadInfo lookaheads lr0items)
42-
> gotoTable = {-# SCC "Goto" #-} (genGotoTable g lr0items)
43-
> actionTable = {-# SCC "Action" #-} (genActionTable g first lr1items)
44-
> conflicts = {-# SCC "Conflict" #-} (countConflicts actionTable)
47+
> gotoTable = {-# SCC "Goto" #-} (LALR.genGotoTable g lr0items)
48+
> actionTable = {-# SCC "Action" #-} (LALR.genActionTable g first lr1items)
49+
> conflicts = {-# SCC "Conflict" #-} (LALR.countConflicts actionTable)
4550
> redundancies = find_redundancies select_reductions g actionTable
4651
> in Tables { lr0items, la_spont, la_prop, lookaheads, lr1items,
4752
> gotoTable, actionTable, conflicts, redundancies }
48-
49-
-----------------------------------------------------------------------------
50-
Find unused rules and tokens
51-
52-
> find_redundancies
53-
> :: SelectReductions -> Grammar -> ActionTable -> ([Int], [String])
54-
> find_redundancies extract_reductions g action_table =
55-
> (unused_rules, map (env !) unused_terminals)
56-
> where
57-
> Grammar { terminals = terms,
58-
> token_names = env,
59-
> eof_term = eof,
60-
> starts = starts',
61-
> productions = productions'
62-
> } = g
63-
> actions = concat (map assocs (elems action_table))
64-
> start_rules = [ 0 .. (length starts' - 1) ]
65-
> used_rules = start_rules ++
66-
> nub [ r | (_,a) <- actions, r <- extract_reductions a ]
67-
> used_tokens = errorTok : eof :
68-
> nub [ t | (t,a) <- actions, is_shift a ]
69-
> n_prods = length productions'
70-
> unused_terminals = filter (`notElem` used_tokens) terms
71-
> unused_rules = filter (`notElem` used_rules ) [0..n_prods-1]
72-
73-
> is_shift :: LRAction -> Bool
74-
> is_shift (LR'Shift _ _) = True
75-
> is_shift (LR'Multiple _ LR'Shift{}) = True
76-
> is_shift _ = False
77-
78-
---
79-
selects what counts as a reduction when calculating used/unused
80-
81-
> type SelectReductions = LRAction -> [Int]
82-
83-
> select_all_reductions :: SelectReductions
84-
> select_all_reductions = go
85-
> where go (LR'Reduce r _) = [r]
86-
> go (LR'Multiple as a) = concatMap go (a : as)
87-
> go _ = []
88-
89-
> select_first_reduction :: SelectReductions
90-
> select_first_reduction = go
91-
> where go (LR'Reduce r _) = [r]
92-
> go (LR'Multiple _ a) = go a -- eg R/R conflict
93-
> go _ = []
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
> module Happy.Tabular.FindRedundancies where
2+
3+
> import Happy.Grammar
4+
> import Happy.Tabular.Tables
5+
> import Data.Array( assocs, elems, (!) )
6+
> import Data.List
7+
8+
Find unused rules and tokens
9+
10+
> find_redundancies
11+
> :: SelectReductions -> Grammar -> ActionTable -> ([Int], [String])
12+
> find_redundancies extract_reductions g action_table =
13+
> (unused_rules, map (env !) unused_terminals)
14+
> where
15+
> Grammar { terminals = terms,
16+
> token_names = env,
17+
> eof_term = eof,
18+
> starts = starts',
19+
> productions = productions'
20+
> } = g
21+
22+
> actions = concat (map assocs (elems action_table))
23+
> start_rules = [ 0 .. (length starts' - 1) ]
24+
> used_rules = start_rules ++
25+
> nub [ r | (_,a) <- actions, r <- extract_reductions a ]
26+
> used_tokens = errorTok : eof :
27+
> nub [ t | (t,a) <- actions, is_shift a ]
28+
> n_prods = length productions'
29+
> unused_terminals = filter (`notElem` used_tokens) terms
30+
> unused_rules = filter (`notElem` used_rules ) [0..n_prods-1]
31+
32+
> is_shift :: LRAction -> Bool
33+
> is_shift (LR'Shift _ _) = True
34+
> is_shift (LR'Multiple _ LR'Shift{}) = True
35+
> is_shift _ = False
36+
37+
---
38+
selects what counts as a reduction when calculating used/unused
39+
40+
> type SelectReductions = LRAction -> [Int]
41+
42+
> select_all_reductions :: SelectReductions
43+
> select_all_reductions = go
44+
> where go (LR'Reduce r _) = [r]
45+
> go (LR'Multiple as a) = concatMap go (a : as)
46+
> go _ = []
47+
48+
> select_first_reduction :: SelectReductions
49+
> select_first_reduction = go
50+
> where go (LR'Reduce r _) = [r]
51+
> go (LR'Multiple _ a) = go a -- eg R/R conflict
52+
> go _ = []

packages/tabular/src/Happy/Tabular/Info.lhs

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ Generating info files.
66

77
> module Happy.Tabular.Info (genInfoFile) where
88

9-
> import Data.Set ( Set )
10-
> import qualified Data.Set as Set hiding ( Set )
119
> import Happy.Grammar
12-
> import Happy.Tabular.LALR ( Lr0Item(..), LRAction(..), Goto(..), GotoTable, ActionTable )
10+
> import Happy.Tabular.Tables
11+
> import Happy.Tabular.LALR ( Lr0Item(..) )
1312
> import Paths_happy_tabular ( version )
13+
> import Data.Set ( Set )
14+
> import qualified Data.Set as Set hiding ( Set )
15+
1416

1517
> import Data.Array
1618
> import Data.List (nub)
@@ -227,4 +229,4 @@ Produce a file of parser information, useful for debugging the parser.
227229
> interleave :: String -> [String -> String] -> String -> String
228230
> interleave s = foldr (\a b -> a . str s . b) id
229231
> interleave' :: String -> [String -> String] -> String -> String
230-
> interleave' s = foldr1 (\a b -> a . str s . b)
232+
> interleave' s = foldr1 (\a b -> a . str s . b)

packages/tabular/src/Happy/Tabular/LALR.lhs

+2-15
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ Generation of LALR parsing tables.
88
> module Happy.Tabular.LALR
99
> (genActionTable, genGotoTable, genLR0items, precalcClosure0,
1010
> propLookaheads, calcLookaheads, mergeLookaheadInfo, countConflicts,
11-
> Lr0Item(..), Lr1Item(..), ItemSetWithGotos, LRAction(..), Lr1State,
12-
> ActionTable, GotoTable, Goto(..))
11+
> Lr0Item(..), Lr1Item, ItemSetWithGotos, Lr1State)
1312
> where
1413

1514
> import Happy.Tabular.First ( mkClosure )
1615
> import Happy.Tabular.NameSet ( NameSet )
1716
> import qualified Happy.Tabular.NameSet as NameSet
1817
> import Happy.Grammar
18+
> import Happy.Tabular.Tables
1919
> import qualified Data.Set as Set hiding ( Set )
2020
> import Data.Set ( Set )
2121

@@ -47,19 +47,6 @@ This means rule $a$, with dot at $b$ (all starting at 0)
4747

4848
> type ItemSetWithGotos = (Set Lr0Item, [(Name,Int)])
4949

50-
> data LRAction = LR'Shift Int Priority -- state number and priority
51-
> | LR'Reduce Int Priority-- rule no and priority
52-
> | LR'Accept -- :-)
53-
> | LR'Fail -- :-(
54-
> | LR'MustFail -- :-(
55-
> | LR'Multiple [LRAction] LRAction -- conflict
56-
> deriving (Eq,Show)
57-
58-
> type ActionTable = Array Int{-state-} (Array Int{-terminal#-} LRAction)
59-
> type GotoTable = Array Int{-state-} (Array Int{-nonterminal #-} Goto)
60-
> data Goto = Goto Int | NoGoto
61-
> deriving (Eq, Show)
62-
6350
-----------------------------------------------------------------------------
6451
Generating the closure of a set of LR(0) items
6552

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
module Happy.Tabular.Main (
2+
TabularArgs(..), TabularResult, runTabular
3+
) where
4+
5+
import Happy.Grammar
6+
import Happy.Tabular.Tables
7+
import qualified Happy.Tabular.LALR as LALR
8+
import Happy.Tabular.FindRedundancies
9+
import Happy.Tabular.Info
10+
import Happy.Tabular
11+
import Data.Array (Array)
12+
import System.IO
13+
import System.Exit (exitWith, ExitCode(..))
14+
import Control.Monad
15+
16+
-------- Main entry point (runTabular) --------
17+
18+
data TabularArgs = TabularArgs {
19+
inFile :: String, -- printed to the info file, not used otherwise
20+
infoFile :: Maybe String,
21+
22+
dumpLR0 :: Bool,
23+
dumpLA :: Bool,
24+
dumpAction :: Bool,
25+
dumpGoto :: Bool
26+
}
27+
28+
type TabularResult = (ActionTable, GotoTable, [LALR.Lr1State], [Int])
29+
30+
runTabular :: Bool -> TabularArgs -> Grammar -> IO TabularResult
31+
runTabular glr args g = do
32+
let select_reductions =
33+
if glr
34+
then select_all_reductions
35+
else select_first_reduction
36+
37+
let tables = genTables select_reductions g
38+
sets = lr0items tables
39+
la = lookaheads tables
40+
items2 = lr1items tables
41+
goto = gotoTable tables
42+
action = actionTable tables
43+
(conflictArray, (sr,rr)) = conflicts tables
44+
optPrint (dumpLR0 args) (print sets)
45+
optPrint (dumpLA args) (print la)
46+
optPrint (dumpAction args) (print action)
47+
optPrint (dumpGoto args) (print goto)
48+
reportUnusedRules tables
49+
let (unused_rules, unused_terminals) = redundancies tables
50+
writeInfoFile sets g action goto conflictArray (inFile args) (infoFile args) unused_rules unused_terminals
51+
reportConflicts g sr rr
52+
return (action, goto, items2, unused_rules)
53+
where
54+
optPrint b io = when b (putStr "\n---------------------\n" >> io)
55+
56+
-------- Helpers --------
57+
58+
reportUnusedRules :: Tables -> IO ()
59+
reportUnusedRules tables = do
60+
let (unused_rules, unused_terminals) = redundancies tables
61+
when (not (null unused_rules)) $
62+
hPutStrLn stderr ("unused rules: " ++ show (length unused_rules))
63+
when (not (null unused_terminals)) $
64+
hPutStrLn stderr ("unused terminals: " ++ show (length unused_terminals))
65+
66+
reportConflicts :: Grammar -> Int -> Int -> IO ()
67+
reportConflicts g sr rr = case expect g of
68+
Just n | n == sr && rr == 0 -> return ()
69+
Just _ | rr > 0 ->
70+
die $ "The grammar has reduce/reduce conflicts.\n" ++
71+
"This is not allowed when an expect directive is given\n"
72+
Just _ ->
73+
die $ "The grammar has " ++ show sr ++ " shift/reduce conflicts.\n" ++
74+
"This is different from the number given in the expect directive\n"
75+
_ -> do
76+
if sr /= 0
77+
then hPutStrLn stderr ("shift/reduce conflicts: " ++ show sr)
78+
else return ()
79+
80+
if rr /= 0
81+
then hPutStrLn stderr ("reduce/reduce conflicts: " ++ show rr)
82+
else return ()
83+
84+
die :: String -> IO a
85+
die s = hPutStr stderr s >> exitWith (ExitFailure 1)
86+
87+
writeInfoFile
88+
:: [LALR.ItemSetWithGotos]
89+
-> Grammar
90+
-> ActionTable
91+
-> GotoTable
92+
-> Array Int (Int,Int)
93+
-> String
94+
-> Maybe String
95+
-> [Int]
96+
-> [String]
97+
-> IO ()
98+
writeInfoFile sets g action goto conflictArray file info_file unused_rules unused_terminals =
99+
let info = genInfoFile
100+
(map fst sets)
101+
g
102+
action
103+
goto
104+
(token_specs g)
105+
conflictArray
106+
file
107+
unused_rules
108+
unused_terminals
109+
in case info_file of
110+
Just s -> writeFile s info >> hPutStrLn stderr ("Grammar info written to: " ++ s)
111+
Nothing -> return ()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Datatypes for goto and action tables which are consumed by happy-backend.
2+
3+
> module Happy.Tabular.Tables (
4+
> LRAction(..), ActionTable, Goto(..), GotoTable
5+
> ) where
6+
7+
> import Happy.Grammar
8+
9+
> import Data.Array
10+
11+
> data LRAction = LR'Shift Int Priority -- state number and priority
12+
> | LR'Reduce Int Priority-- rule no and priority
13+
> | LR'Accept -- :-)
14+
> | LR'Fail -- :-(
15+
> | LR'MustFail -- :-(
16+
> | LR'Multiple [LRAction] LRAction -- conflict
17+
> deriving (Eq, Show)
18+
19+
> type ActionTable = Array Int{-state-} (Array Int{-terminal#-} LRAction)
20+
21+
> data Goto = Goto Int | NoGoto
22+
> deriving(Eq, Show)
23+
24+
> type GotoTable = Array Int{-state-} (Array Int{-nonterminal #-} Goto)

0 commit comments

Comments
 (0)