Match.icl 3.2 KB
Newer Older
Camil Staps's avatar
Camil Staps committed
1 2
implementation module Regex.Match

3
import _SystemStrictLists
Camil Staps's avatar
Camil Staps committed
4
import StdBool
5
from StdFunc import flip, id, o
6 7
import qualified StdOverloadedList
from StdOverloadedList import class List, ++|, Length
8
import StdTuple
Camil Staps's avatar
Camil Staps committed
9 10

from Data.Func import $
11
import Data.Functor
Camil Staps's avatar
Camil Staps committed
12
import Data.List
13
import Data.Maybe
Camil Staps's avatar
Camil Staps committed
14 15 16 17

import Regex

:: MatchStatus
18 19 20 21 22
	= { skipped  :: ![Char]
	  , matched  :: ![Char]
	  , unseen   :: ![Char]
	  , can_skip :: !Bool
	  , groups   :: ![(GroupId, [Char])]
Camil Staps's avatar
Camil Staps committed
23 24 25
	  }

instance zero MatchStatus
Camil Staps's avatar
Camil Staps committed
26
where zero = {skipped=[], matched=[], unseen=[], can_skip=True, groups=[]}
Camil Staps's avatar
Camil Staps committed
27

28
match :: !Regex ![Char] -> [Match]
Camil Staps's avatar
Camil Staps committed
29 30
match r s = [(length st.skipped, st.matched, st.groups) \\ st <- match` r stat]
where stat = {zero & unseen=s}
Camil Staps's avatar
Camil Staps committed
31

32
match` :: !Regex !MatchStatus -> [MatchStatus]
Camil Staps's avatar
Camil Staps committed
33
match` r=:(Literal cs) st
Camil Staps's avatar
Camil Staps committed
34
	= matchOrSkip r st $
35 36 37 38 39 40 41
		if (isPrefixOf cs st.unseen) [eat (Length cs) st] []
where
	isPrefixOf :: [!Char!] [Char] -> Bool
	isPrefixOf [!c:cs!] xs = case xs of
		[x:xs] -> c==x && isPrefixOf cs xs
		[]     -> False
	isPrefixOf [!!]     _  = True
42
match` (CharacterClass _ _) {unseen=[]}
Camil Staps's avatar
Camil Staps committed
43
	= []
44
match` r=:(CharacterClass n cs) st=:{matched,unseen=[u:us]}
Camil Staps's avatar
Camil Staps committed
45
	= matchOrSkip r st $
46 47
		if (if n not id $ 'StdOverloadedList'.Any (\(f,t) -> f <= u && u <= t) cs) [eat 1 st] []
match` (Concat [|]) st
48
	= [st]
49
match` (Concat [|r]) st
50
	= match` r st
51
match` (Concat [|r:rs]) st
52
	= [st`` \\ st` <- match` r st, st`` <- match` (Concat rs) st`]
Camil Staps's avatar
Camil Staps committed
53
match` (Any rs) st
54
	= flatten [match` r st \\ r <|- rs]
55 56 57 58
match` (Repeated _ 0 _ _) st=:{unseen=[]}
	= [st]
match` tr=:(Repeated True 0 Nothing r) st
	= flatten [match` tr st` \\ st` <- match` r st | st.matched <> st`.matched] ++ [st]
59 60 61 62
match` (Repeated g f (Just 0) r) st
	= [st]
match` (Repeated g f t r) st
| isJust t && fromJust t <= f
63 64 65
	= match` (Concat [|r \\ _ <- [1..f]]) st
	= match` (Concat $ [|r \\ _ <- [1..f]] ++| [|Any [|r \\ r <- if g id reverse opts]]) st
where opts = [Concat [|r, Repeated g 0 (flip (-) (f+1) <$> t) r], Concat [|]]
Camil Staps's avatar
Camil Staps committed
66
match` (Group id r) st
67
	= [{st` & groups=put id (drop (length st.matched) st`.matched) st`.groups}
Camil Staps's avatar
Camil Staps committed
68
		\\ st` <- match` r st]
69
where put id m gs = [(id,m):filter ((<>) id o fst) gs]
Camil Staps's avatar
Camil Staps committed
70
match` r=:StartOfString st = if (isEmpty st.matched) [{st & can_skip=False}] []
Camil Staps's avatar
Camil Staps committed
71 72
match` r=:EndOfString st
	= matchOrSkip r st $ if (isEmpty st.unseen) [st] []
Camil Staps's avatar
Camil Staps committed
73
match` r=:(WordBreak n) st
Camil Staps's avatar
Camil Staps committed
74
	= matchOrSkip r st $ if (if n not id atWordBreak) [st`] []
Camil Staps's avatar
Camil Staps committed
75 76 77 78
where
	st` = {st & can_skip=False}
	atWordBreak
	| isEmpty st.skipped && isEmpty st.matched = nextIsWord
79
	| isEmpty st.matched = isWordChar (hd st.skipped) <> nextIsWord
Camil Staps's avatar
Camil Staps committed
80 81 82 83 84 85
	| otherwise = isWordChar (last st.matched) <> nextIsWord
	nextIsWord = not (isEmpty st.unseen) && isWordChar (hd st.unseen)
	isWordChar c = ('A' <= c && c <= 'Z')
		|| ('a' <= c && c <= 'z')
		|| ('0' <= c && c <= '9')
		|| c == '_'
Camil Staps's avatar
Camil Staps committed
86

87
skip :: !Int !MatchStatus -> MatchStatus
Camil Staps's avatar
Camil Staps committed
88
skip n st
89 90 91
	= {st & skipped=reverse skip ++ st.skipped, unseen=rest}
where
	(skip,rest) = splitAt n st.unseen
Camil Staps's avatar
Camil Staps committed
92

93
eat :: !Int !MatchStatus -> MatchStatus
Camil Staps's avatar
Camil Staps committed
94 95
eat n st
	= {st & matched=st.matched ++ take n st.unseen, unseen=drop n st.unseen}
Camil Staps's avatar
Camil Staps committed
96

97
matchOrSkip :: !Regex !MatchStatus ![MatchStatus] -> [MatchStatus]
Camil Staps's avatar
Camil Staps committed
98
matchOrSkip r st sts = sts ++
Camil Staps's avatar
Camil Staps committed
99 100 101
	if can_skip (match` r $ skip 1 st) []
where
	can_skip = st.can_skip && not (isEmpty st.unseen) && isEmpty st.matched