Вчера решал одну тренировочную задачку с сайта Codewars -
Regular expression parser Собственно, задание не сложное:
Цитата:
Your task is to implement a simple regular expression parser. We will have a parser that outputs the following AST of a regular expression:
data RegExp = Normal Char -- ^ A single regular character
| Any -- ^ Any charater
| ZeroOrMore RegExp -- ^ Zero or more occurances of the same regexp
| Or RegExp RegExp -- ^ A choice between 2 regexps
| Str [RegExp] -- ^ A sequence of regexps.
Цитата:
As with the usual regular expressions, Any is denoted by the '.' character, ZeroOrMore is denoted by a subsequent '*' and Or is denoted by '|'. Brackets are allowed to group sequence of regular expressions into Str [RegExp].
Operator precedences from highest to lowest are: *, sequencing and |. Therefore the followings hold:
"ab*" -> Str [Normal 'a', ZeroOrMore (Normal 'b')]
"(ab)*" -> ZeroOrMore (Str [Normal 'a', Normal 'b'])
"ab|a" -> Or (Str [Normal 'a',Normal 'b']) (Normal 'a')
"a(b|a)" -> Str [Normal 'a',Or (Normal 'b') (Normal 'a')]
"a|b*" -> Or (Normal 'a') (ZeroOrMore (Normal 'b'))
"(a|b)*" -> ZeroOrMore (Or (Normal 'a') (Normal 'b'))
"a" -> Normal 'a'
"ab" -> Str [ Normal 'a', Normal 'b']
"a.*" -> Str [ Normal 'a', ZeroOrMore Any ]
"(a.*)|(bb)" -> Or (Str [Normal a, ZeroOrMore Any]) (Str [Normal 'b', Normal 'b'])
Цитата:
Feel free to use any parser combinator libraries available
Я написал простенькую программку в несколько строк безо всяких библиотек, она прошла тесты:
parseRegExp :: String -> Maybe RegExp
parseRegExp s = Just $ go s [] where
packlist [a] = a
packlist l = Str $ reverse l
spanclose s = (init l, drop (length l) s) where
l = head.dropWhile ((/=(-1)).sum.map f).inits $ s
f '(' = 1
f ')' = -1
f _ = 0
go [] l = packlist l
go (c:cs) l = case c of
'(' -> go sr $ go sl [] : l where (sl,sr) = spanclose cs
'|' -> Or (packlist l) $ go cs []
'*' -> go cs $ ZeroOrMore (head l) : (tail l)
'.' -> go cs $ Any : l
_ -> go cs $ Normal c : l
и это позволило мне посмотреть коды остальных участников. Начиная с кода автора задачи там были представлены обширные простыни со щедрым использованием монад/функторов/трансформеров да еще и со специализированными парсерными библиотеками нередко при этом (я не буду их приводить, но если попросите - скопипастю). И хотя я их детально не разбирал, но я примерно представляю себе их источники вдохновения, хотя я не разбираюсь в теории синтаксического разбора, порождающих грамматиках и прочему Хомскому и Брюне. Но тут на фоне всего этого великолепия я внезапно обнаружил код, который вызвал (и продолжает вызывать) у меня яркий эмоциональный отклик - вот он:
instance Eq RegExp where
_ == _ = True
parseRegExp _ = Just Any
и всё
Не могли бы уважаемые участники форума просветить меня, как и на каких принципах работает это волшебство?