-
Notifications
You must be signed in to change notification settings - Fork 1
/
Parser.hs
101 lines (74 loc) · 2.72 KB
/
Parser.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
{-
Project: VUT FIT FLP 1. Project (Functional) - SIMPLIFY-BKG
Author: Dominik Harmim <[email protected]>
Year: 2020
Module: Parser
Description: An input parser. Parsing and validating an input grammar and
converting it to the internal representation.
-}
{-# LANGUAGE RecordWildCards #-}
module Parser (parseCFG, epsSymbol) where
import Control.Applicative ((<$>), (<*>), (<*), (<|>))
import Control.Arrow (left)
import Control.Monad ((<=<))
import Data.List (group, sort)
import Text.Parsec (char, count, endBy, eof, many1, newline, oneOf, parse,
sepBy, sepBy1, string)
import Text.Parsec.String (Parser)
import Types (CFG(..), Err, Rule, Rules, Symbol, Symbols)
-- Parses and validates an input grammar and converts it to the internal
-- representation. Returns an error message in case of invalid input grammar.
parseCFG :: String -> Err CFG
parseCFG = validate <=< left show . parse parser ""
-- Parses the entire context-free grammar.
parser :: Parser CFG
parser = CFG
<$> nonterminalsParser <* newline
<*> terminalsParser <* newline
<*> startingSymbolParser <* newline
<*> rulesParser <* eof
-- Parses a list of nonterminal symbols.
nonterminalsParser :: Parser Symbols
nonterminalsParser = sepBy1 (oneOf nonterminalSymbols) symbolSeparator
-- Parses a list of terminal symbols.
terminalsParser :: Parser Symbols
terminalsParser = sepBy (oneOf terminalSymbols) symbolSeparator
-- Parses the starting symbol.
startingSymbolParser :: Parser Symbol
startingSymbolParser = oneOf nonterminalSymbols
-- Parses a list of production rules.
rulesParser :: Parser Rules
rulesParser = endBy ruleParser newline
-- Parses single production rules.
ruleParser :: Parser Rule
ruleParser = (,)
<$> oneOf nonterminalSymbols <* string "->"
<*> ( count 1 (char epsSymbol)
<|> many1 (oneOf $ nonterminalSymbols ++ terminalSymbols) )
-- A symbol separator.
symbolSeparator :: Parser Char
symbolSeparator = char ','
-- A list of allowed nonterminal symbols.
nonterminalSymbols :: Symbols
nonterminalSymbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-- A list of allowed terminal symbols.
terminalSymbols :: Symbols
terminalSymbols = "abcdefghijklmnopqrstuvwxyz"
-- The empty string (epsilon) symbol.
epsSymbol :: Symbol
epsSymbol = '#'
-- Validates an input grammar.
validate :: CFG -> Err CFG
validate cfg@CFG{..} =
if isValid then Right cfg else Left "Invalid input context-free grammar."
where
allUnique l = all ((==) 1 . length) $ (group . sort) l
isValid =
allUnique nonterminals
&& allUnique terminals
&& elem startingSymbol nonterminals
&& allUnique rules
&& all ( \(l, r) ->
elem l nonterminals
&& all (`elem` nonterminals ++ terminals ++ [epsSymbol]) r
) rules