Skip to content

Commit 5d65f1c

Browse files
knothedEricson2314
authored andcommitted
Split out Tabular some more but also add Main
1 parent ede899b commit 5d65f1c

12 files changed

+254
-176
lines changed

packages/tabular/happy-tabular.cabal

+4-1
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,14 @@ library
4141
Happy.Tabular.Info,
4242
Happy.Tabular.LALR,
4343
Happy.Tabular.NameSet
44+
Happy.Tabular.Main
45+
Happy.Tabular.FindRedundancies
46+
Happy.Tabular.Tables
4447
build-depends: base < 5,
4548
array,
4649
containers >= 0.4.2,
4750
happy-grammar == 1.21.0
4851

4952
default-language: Haskell98
5053
default-extensions: CPP, MagicHash, FlexibleContexts, NamedFieldPuns
51-
ghc-options: -Wall
54+
ghc-options: -Wall

packages/tabular/src/Happy/Tabular.lhs

+3-48
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
> ) where
88

99
> import Happy.Grammar
10+
> import Happy.Tabular.FindRedundancies
1011
> import Happy.Tabular.First
1112
> import Happy.Tabular.LALR
13+
> import Happy.Tabular.Tables
1214
> import Happy.Tabular.NameSet (NameSet)
1315

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

1718
> data Tables =
1819
> Tables {
@@ -45,49 +46,3 @@
4546
> redundancies = find_redundancies select_reductions g actionTable
4647
> in Tables { lr0items, la_spont, la_prop, lookaheads, lr1items,
4748
> 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

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ 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

13+
> import Data.Set ( Set )
14+
> import qualified Data.Set as Set hiding ( Set )
1415
> import Data.Array
1516
> import Data.List (nub)
1617
> import Data.Version ( Version, showVersion )
@@ -227,4 +228,4 @@ Produce a file of parser information, useful for debugging the parser.
227228
> interleave :: String -> [String -> String] -> String -> String
228229
> interleave s = foldr (\a b -> a . str s . b) id
229230
> interleave' :: String -> [String -> String] -> String -> String
230-
> interleave' s = foldr1 (\a b -> a . str s . b)
231+
> 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
Token numbering in an array-based parser:
6552

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
import Data.Version (Version)
16+
17+
-------- Main entry point (runTabular) --------
18+
19+
data TabularArgs = TabularArgs {
20+
inFile :: String, -- printed to the info file, not used otherwise
21+
infoFile :: Maybe String,
22+
23+
dumpLR0 :: Bool,
24+
dumpLA :: Bool,
25+
dumpAction :: Bool,
26+
dumpGoto :: Bool
27+
}
28+
29+
type TabularResult = (ActionTable, GotoTable, [LALR.Lr1State], [Int])
30+
31+
runTabular :: Bool -> TabularArgs -> Grammar -> Version -> IO TabularResult
32+
runTabular glr args g version = do
33+
let select_reductions =
34+
if glr
35+
then select_all_reductions
36+
else select_first_reduction
37+
38+
let tables = genTables select_reductions g
39+
sets = lr0items tables
40+
la = lookaheads tables
41+
items2 = lr1items tables
42+
goto = gotoTable tables
43+
action = actionTable tables
44+
(conflictArray, (sr,rr)) = conflicts tables
45+
optPrint (dumpLR0 args) (print sets)
46+
optPrint (dumpLA args) (print la)
47+
optPrint (dumpAction args) (print action)
48+
optPrint (dumpGoto args) (print goto)
49+
reportUnusedRules tables
50+
let (unused_rules, unused_terminals) = redundancies tables
51+
writeInfoFile sets g action goto conflictArray (inFile args) (infoFile args) unused_rules unused_terminals version
52+
reportConflicts g sr rr
53+
return (action, goto, items2, unused_rules)
54+
where
55+
optPrint b io = when b (putStr "\n---------------------\n" >> io)
56+
57+
-------- Helpers --------
58+
59+
reportUnusedRules :: Tables -> IO ()
60+
reportUnusedRules tables = do
61+
let (unused_rules, unused_terminals) = redundancies tables
62+
when (not (null unused_rules)) $
63+
hPutStrLn stderr ("unused rules: " ++ show (length unused_rules))
64+
when (not (null unused_terminals)) $
65+
hPutStrLn stderr ("unused terminals: " ++ show (length unused_terminals))
66+
67+
reportConflicts :: Grammar -> Int -> Int -> IO ()
68+
reportConflicts g sr rr = case expect g of
69+
Just n | n == sr && rr == 0 -> return ()
70+
Just _ | rr > 0 ->
71+
die $ "The grammar has reduce/reduce conflicts.\n" ++
72+
"This is not allowed when an expect directive is given\n"
73+
Just _ ->
74+
die $ "The grammar has " ++ show sr ++ " shift/reduce conflicts.\n" ++
75+
"This is different from the number given in the expect directive\n"
76+
_ -> do
77+
if sr /= 0
78+
then hPutStrLn stderr ("shift/reduce conflicts: " ++ show sr)
79+
else return ()
80+
81+
if rr /= 0
82+
then hPutStrLn stderr ("reduce/reduce conflicts: " ++ show rr)
83+
else return ()
84+
85+
die :: String -> IO a
86+
die s = hPutStr stderr s >> exitWith (ExitFailure 1)
87+
88+
writeInfoFile
89+
:: [LALR.ItemSetWithGotos]
90+
-> Grammar
91+
-> ActionTable
92+
-> GotoTable
93+
-> Array Int (Int,Int)
94+
-> String
95+
-> Maybe String
96+
-> [Int]
97+
-> [String]
98+
-> Version
99+
-> IO ()
100+
writeInfoFile sets g action goto conflictArray file info_file unused_rules unused_terminals version =
101+
let info = genInfoFile
102+
(map fst sets)
103+
g
104+
action
105+
goto
106+
(token_specs g)
107+
conflictArray
108+
file
109+
unused_rules
110+
unused_terminals
111+
version
112+
in case info_file of
113+
Just s -> writeFile s info >> hPutStrLn stderr ("Grammar info written to: " ++ s)
114+
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)

src/GenUtils.lhs

+25-7
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@ All the code below is understood to be in the public domain.
55
-----------------------------------------------------------------------------
66

77
> module GenUtils (
8-
98
> combinePairs,
109
> str, char, nl, brack, brack',
1110
> interleave, interleave',
12-
> strspace, maybestr
11+
> strspace, maybestr,
12+
> die, dieHappy,
13+
> optPrint,
14+
> getProgramName
1315
> ) where
1416

15-
> import Data.Char (isAlphaNum)
1617
> import Data.Ord (comparing)
17-
> import Data.List (sortBy)
18-
19-
%------------------------------------------------------------------------------
18+
> import Data.List (sortBy, isSuffixOf)
19+
> import Control.Monad
20+
> import System.IO (stderr, hPutStr)
21+
> import System.Environment
22+
> import System.Exit (exitWith, ExitCode(..))
2023

2124
Gofer-like stuff:
2225

@@ -29,7 +32,6 @@ Gofer-like stuff:
2932
> combine (a:r) = a : combine r
3033
>
3134

32-
3335
%-------------------------------------------------------------------------------
3436
Fast string-building functions.
3537

@@ -55,3 +57,19 @@ Fast string-building functions.
5557
> brack s = str ('(' : s) . char ')'
5658
> brack' :: (String -> String) -> String -> String
5759
> brack' s = char '(' . s . char ')'
60+
61+
> die :: String -> IO a
62+
> die s = hPutStr stderr s >> exitWith (ExitFailure 1)
63+
64+
> dieHappy :: String -> IO a
65+
> dieHappy s = getProgramName >>= \prog -> die (prog ++ ": " ++ s)
66+
67+
> getProgramName :: IO String
68+
> getProgramName = liftM (`withoutSuffix` ".bin") getProgName
69+
> where str' `withoutSuffix` suff
70+
> | suff `isSuffixOf` str' = take (length str' - length suff) str'
71+
> | otherwise = str'
72+
73+
> optPrint :: Bool -> IO () -> IO ()
74+
> optPrint b io =
75+
> when b (putStr "\n---------------------\n" >> io)

0 commit comments

Comments
 (0)