Skip to content

Commit 7e11395

Browse files
committed
A correct CFG recognizer (least fixed point actually works)
1 parent 85f4c8b commit 7e11395

File tree

1 file changed

+184
-0
lines changed

1 file changed

+184
-0
lines changed

Gram.hs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
{-# LANGUAGE NoMonomorphismRestriction #-}
2+
module Gram where
3+
4+
import Control.Monad.State
5+
import qualified Data.Map as M
6+
import Data.Maybe
7+
import System.IO.Unsafe
8+
9+
data Term key tok = Fail
10+
| Eps
11+
| Conc (Term key tok) (Term key tok)
12+
| Alt (Term key tok) (Term key tok)
13+
| Ref key
14+
| One tok
15+
--deriving (Show)
16+
17+
instance (Show key, Show tok) => Show (Term key tok) where
18+
show Fail = "Fail"
19+
show Eps = "Eps"
20+
show (Conc lhs rhs) = show lhs ++ show rhs
21+
show (Alt lhs rhs) = "(" ++ show lhs ++ "|" ++ show rhs ++ ")"
22+
show (Ref key) = "<" ++ show key ++ ">"
23+
show (One tok) = show tok
24+
25+
type Rule key tok = (key, Term key tok)
26+
type Gram key tok = [Rule key tok]
27+
28+
{-conc = Conc-}
29+
{-alt = Alt-}
30+
31+
conc Eps t2 = t2
32+
conc t1 Eps = t1
33+
conc Fail t2 = Fail
34+
conc t1 Fail = Fail
35+
conc t1 t2 = Conc t1 t2
36+
37+
alt Fail t2 = t2
38+
alt t1 Fail = t1
39+
alt t1 t2 = Alt t1 t2
40+
41+
type BM key = M.Map key (Bool, Bool)
42+
type RM key tok = M.Map key (Term key tok)
43+
44+
data RuleSt key tok = RuleSt { rules :: RM key tok, nullables :: BM key, changed :: Bool }
45+
clearNullables = do
46+
st <- get
47+
put st{nullables=M.empty, changed=False}
48+
getSt proj key = get >>= return . M.lookup key . proj
49+
getRule = getSt rules
50+
getNullable key = do
51+
res <- getSt nullables key
52+
case res of
53+
Just res -> return res
54+
Nothing -> return (False, False)
55+
getChanged = get >>= return . changed
56+
57+
putNullable key val = do
58+
st <- get
59+
put st{nullables=M.insert key val $ nullables st}
60+
putRule key val = do
61+
st <- get
62+
put st{rules=M.insert key val $ rules st}
63+
putChanged ch = do
64+
st <- get
65+
put st{changed=ch}
66+
67+
{-fromBool False = 0-}
68+
{-fromBool True = 1-}
69+
{-countTrue xs = foldl (\cnt (_, bl) -> cnt + fromBool bl) 0 $ toList xs-}
70+
71+
--cmpNullables n0 n1 = countTrue n0 == countTrue n1
72+
--
73+
--bidiff ma mb = M.union (M.difference ma mb) $ M.difference mb ma
74+
75+
{-cmpNullables n0 n1 = shared && diff-}
76+
{-where shared = all (True ==) $ map snd . M.toList $ M.intersectionWith (==) n0 n1-}
77+
{-diff = all (False ==) $ map snd . M.toList $ bidiff n0 n1-}
78+
resetNullables = do
79+
st <- get
80+
let nls = nullables st
81+
let nls' = fmap (\(_, val) -> (False, val)) nls
82+
put $ st{nullables=nls'}
83+
84+
nullable' Fail = return False
85+
nullable' Eps = return True
86+
nullable' (Conc lhs rhs) = do
87+
nl <- nullable' lhs
88+
nr <- nullable' rhs
89+
return $ nl && nr
90+
nullable' (Alt lhs rhs) = do
91+
nl <- nullable' lhs
92+
nr <- nullable' rhs
93+
return $ nl || nr
94+
nullable' (Ref key) = do
95+
res <- getNullable key
96+
case res of
97+
(True, res) -> return res
98+
(False, old) -> do
99+
putNullable key (True, old)
100+
(Just term) <- getRule key
101+
res <- nullable' term
102+
when (res /= old) $ putChanged True
103+
putNullable key (True, res)
104+
return res
105+
nullable' (One tok) = return False
106+
107+
nullable rule = do
108+
res <- nullable' rule
109+
change <- getChanged
110+
putChanged False
111+
if change then do
112+
resetNullables
113+
nullable rule
114+
else return res
115+
116+
testNullable rule = fst $ runState (nullable rule) RuleSt { rules=M.empty, nullables=M.empty, changed=False }
117+
118+
mkKey nm = (nm, "")
119+
derivKey tok (nm, ts) = (nm, tok:ts)
120+
deriv _ Fail = return Fail
121+
deriv _ Eps = return Fail
122+
deriv tok (Conc lhs rhs) = do
123+
nl <- nullable lhs
124+
dl <- deriv tok lhs
125+
dr <- deriv tok rhs
126+
return $ if nl then alt (conc dl rhs) dr else conc dl rhs
127+
deriv tok (Alt lhs rhs) = do
128+
dl <- deriv tok lhs
129+
dr <- deriv tok rhs
130+
return $ alt dl dr
131+
deriv tok (Ref key) = do
132+
res <- getRule dkey
133+
case res of
134+
Just res -> return ()
135+
Nothing -> do
136+
putRule dkey Fail
137+
(Just term) <- getRule key
138+
dterm <- deriv tok term
139+
putRule dkey dterm
140+
return $ Ref dkey
141+
where dkey = derivKey tok key
142+
deriv t0 (One t1) = return $ if t0 == t1 then Eps else Fail
143+
144+
mkSt gram = RuleSt {rules=gram, nullables=M.empty, changed=False}
145+
146+
recognize gram start toks = fst $ runState (recog toks start) $ mkSt gram
147+
where recog [] term = nullable term
148+
recog (tok:ts) term = deriv tok term >>= recog ts
149+
150+
drv gram start toks = fst $ runState (drv' toks start) $ mkSt gram
151+
where drv' [] term = return term
152+
drv' (tok:ts) term = deriv tok term >>= drv' ts
153+
154+
-- todo: match/parse
155+
156+
-- S ::= S + S | 1
157+
kS = mkKey 'S'
158+
rsum = Ref $ kS
159+
tone = One '1'
160+
tsum = conc rsum $ conc (One '+') rsum
161+
tsum1 = alt tsum tone
162+
gsum = M.fromList [(kS, tsum1)]
163+
164+
td = drv gsum tsum1
165+
test = recognize gsum tsum1
166+
167+
easy = "1"
168+
good = "1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1"
169+
bad = good ++ "+"
170+
171+
-- P ::= P ( P ) ∪ ε
172+
kP = mkKey 'P'
173+
rparen = Ref $ kP
174+
tpl = One '('
175+
tpr = One ')'
176+
tparen = conc rparen $ conc tpl $ conc rparen $ tpr
177+
tparen1 = alt tparen Eps
178+
gparen = M.fromList [(kP, tparen1)]
179+
180+
tdp = drv gparen tparen1
181+
testp = recognize gparen tparen1
182+
183+
goodp = "(())()(()())((()))(()(()))((((((()))))))"
184+
badp = "(())()(()())((()))(()(()))(((((()))))))"

0 commit comments

Comments
 (0)