on master help sign in
Optimize custom syntax rules

Previously all custom syntax rules were tried one after the other with full rollback (via `try`). Now they are tried one after the other but with a minimal rollback. Introduces datatype SyntaxRules which contains a list of rules and 4 "precomputed" parsers (expr/simpleExpr * term/pattern). Every time new syntax rule is introduced parsers are rebuilt. Expression parsers use an appropriate parser. Example: for the following 5 rules syntax "[" [start] ".." ... syntax "[" [start] "," ... syntax [from] "={" ... syntax [expr] "QED" ... syntax "if" [test] ... previously resulting parser conceptually was choice [ try (symbol "[" >> expr >> symbol ".." >> ...) , try (symbol "[" >> expr >> symbol "," >> ...) , try (expr >> symbol "={" >> ...) , try (expr >> symbol "QED" >> ...) , try (symbol "if" >> expr >> ...) ] now it conceptually is choice [ try (symbol "[" >> choice [ try (expr >> choice [ try (symbol ".." >> ...) , try (symbol "," >> ...) ]) ]) , try (expr >> choice [ try (symbol "={" >> ...) , try (symbol "QED" >> ...) ]) , try (symbol "if" >> choice [ try (expr >> ...) ]) ] Profit is in transition between rules 1-2 (and rules 3-4): if rule 1 fails after `expr` was evaluated it would have been reevaluated again in the rule 2. Now it wouldn't.

commit9d804b5f3c7cf76a74d43d81ab6e83d174a30ea3 authorAlexander Shabalin <shabalyn.a@gmail.com> dateSun 16 Nov 2014, 13:44 (12 years ago) parentefd6f8a
5 files changed+114−33
src/Idris/AbsSyntaxTree.hs+7−3 src/Idris/IBC.hs+2−1 src/Idris/ParseExpr.hs+98−27 src/Idris/ParseExpr.hs-boot+5 src/Idris/Parser.hs+2−2
src/Idris/AbsSyntaxTree.hs+7−3
@@ -12,6 +12,7 @@ import IRTS.Lang
1212import IRTS.CodegenCommon
1313import Util.Pretty
1414import Util.DynamicLinker
15+import {-# SOURCE #-} Idris.ParseExpr (SyntaxRules, emptySyntaxRules)
1516
1617import Idris.Colours
1718
@@ -177,7 +178,7 @@ data IState = IState {
177178 idris_metavars :: [(Name, (Maybe Name, Int, Bool))], -- ^ The currently defined but not proven metavariables
178179 idris_coercions :: [Name],
179180 idris_errRev :: [(Term, Term)],
180− syntax_rules :: [Syntax],
181+ syntax_rules :: SyntaxRules,
181182 syntax_keywords :: [String],
182183 imported :: [FilePath], -- ^ The imported modules
183184 idris_scprims :: [(Name, (Int, PrimFn))],
@@ -290,7 +291,7 @@ idrisInit = IState initContext [] [] emptyContext emptyContext emptyContext
290291 emptyContext emptyContext emptyContext emptyContext
291292 emptyContext emptyContext emptyContext emptyContext
292293 emptyContext emptyContext emptyContext emptyContext
293− [] [] [] defaultOpts 6 [] [] [] [] [] [] [] [] [] [] [] []
294+ [] [] [] defaultOpts 6 [] [] [] [] emptySyntaxRules [] [] [] [] [] [] []
294295 [] [] Nothing [] Nothing [] [] Nothing Nothing [] Hidden False [] Nothing [] []
295296 (RawOutput stdout) True defaultTheme [] (0, emptyContext) emptyContext M.empty
296297 AutomaticWidth S.empty Nothing Nothing []
@@ -985,6 +986,9 @@ syntaxNames :: Syntax -> [Name]
985986syntaxNames (Rule syms _ _) = mapMaybe ename syms
986987 where ename (Keyword n) = Just n
987988 ename _ = Nothing
989+
990+syntaxSymbols :: Syntax -> [SSymbol]
991+syntaxSymbols (Rule ss _ _) = ss
988992{-!
989993deriving instance Binary Syntax
990994deriving instance NFData Syntax
@@ -995,7 +999,7 @@ data SSymbol = Keyword Name
995999 | Binding Name
9961000 | Expr Name
9971001 | SimpleExpr Name
998− deriving Show
1002+ deriving (Show, Eq)
9991003
10001004
10011005{-!
src/Idris/IBC.hs+2−1
@@ -12,6 +12,7 @@ import Idris.Error
1212import Idris.Delaborate
1313import qualified Idris.Docstrings as D
1414import Idris.Docstrings (Docstring)
15+import Idris.ParseExpr (updateSyntaxRules)
1516import Idris.Output
1617
1718import qualified Cheapskate.Types as CT
@@ -394,7 +395,7 @@ pOptimise cs = mapM_ (\ (n, c) ->
394395
395396pSyntax :: [Syntax] -> Idris ()
396397pSyntax s = do i <- getIState
397− putIState (i { syntax_rules = s ++ syntax_rules i })
398+ putIState (i { syntax_rules = updateSyntaxRules s (syntax_rules i) })
398399
399400pKeywords :: [String] -> Idris ()
400401pKeywords k = do i <- getIState
src/Idris/ParseExpr.hs+98−27
@@ -23,6 +23,7 @@ import Control.Applicative
2323import Control.Monad
2424import Control.Monad.State.Strict
2525
26+import Data.Function (on)
2627import Data.Maybe
2728import qualified Data.List.Split as Spl
2829import Data.List
@@ -34,6 +35,51 @@ import qualified Data.ByteString.UTF8 as UTF8
3435
3536import Debug.Trace
3637
38+data SyntaxRules = SyntaxRules
39+ { rules_rules :: [Syntax]
40+ , rules_parserPattern :: (SyntaxInfo -> IdrisParser PTerm)
41+ , rules_parserTerm :: (SyntaxInfo -> IdrisParser PTerm)
42+ , rules_parserSimplePattern :: (SyntaxInfo -> IdrisParser PTerm)
43+ , rules_parserSimpleTerm :: (SyntaxInfo -> IdrisParser PTerm)
44+ }
45+
46+emptySyntaxRules :: SyntaxRules
47+emptySyntaxRules = SyntaxRules
48+ { rules_rules = []
49+ , rules_parserPattern = const empty
50+ , rules_parserTerm = const empty
51+ , rules_parserSimplePattern = const empty
52+ , rules_parserSimpleTerm = const empty
53+ }
54+
55+updateSyntaxRules :: [Syntax] -> SyntaxRules -> SyntaxRules
56+updateSyntaxRules rules sr = sr
57+ { rules_rules = newRules
58+ , rules_parserPattern = \syn -> extensions syn $ filter isPattern newRules
59+ , rules_parserTerm = \syn -> extensions syn $ filter isTerm newRules
60+ , rules_parserSimplePattern = \syn ->
61+ extensions syn $ filter isPattern $ filter isSimple $ newRules
62+ , rules_parserSimpleTerm = \syn ->
63+ extensions syn $ filter isTerm $ filter isSimple $ newRules
64+ }
65+ where
66+ newRules = rules ++ rules_rules sr
67+ isSimple (Rule (Expr x:xs) _ _) = False
68+ isSimple (Rule (SimpleExpr x:xs) _ _) = False
69+ isSimple (Rule [Keyword _] _ _) = True
70+ isSimple (Rule [Symbol _] _ _) = True
71+ isSimple (Rule (_:xs) _ _) = case last xs of
72+ Keyword _ -> True
73+ Symbol _ -> True
74+ _ -> False
75+ isSimple _ = False
76+ isPattern (Rule _ _ AnySyntax) = True
77+ isPattern (Rule _ _ PatternSyntax) = True
78+ isPattern _ = False
79+ isTerm (Rule _ _ AnySyntax) = True
80+ isTerm (Rule _ _ TermSyntax) = True
81+ isTerm _ = False
82+
3783-- | Allow implicit type declarations
3884allowImp :: SyntaxInfo -> SyntaxInfo
3985allowImp syn = syn { implicitAllowed = True }
@@ -91,44 +137,69 @@ expr' syn = try (externalExpr syn)
91137{- | Parses a user-defined expression -}
92138externalExpr :: SyntaxInfo -> IdrisParser PTerm
93139externalExpr syn = do i <- get
94− extensions syn (syntax_rules i)
140+ if inPattern syn
141+ then rules_parserPattern (syntax_rules i) syn
142+ else rules_parserTerm (syntax_rules i) syn
95143 <?> "user-defined expression"
96144
97145{- | Parses a simple user-defined expression -}
98146simpleExternalExpr :: SyntaxInfo -> IdrisParser PTerm
99147simpleExternalExpr syn = do i <- get
100− extensions syn (filter isSimple (syntax_rules i))
101− where
102− isSimple (Rule (Expr x:xs) _ _) = False
103− isSimple (Rule (SimpleExpr x:xs) _ _) = False
104− isSimple (Rule [Keyword _] _ _) = True
105− isSimple (Rule [Symbol _] _ _) = True
106− isSimple (Rule (_:xs) _ _) = case last xs of
107− Keyword _ -> True
108− Symbol _ -> True
109− _ -> False
110− isSimple _ = False
148+ if inPattern syn
149+ then rules_parserSimplePattern (syntax_rules i) syn
150+ else rules_parserSimpleTerm (syntax_rules i) syn
151+
152+data SynMatch = SynTm PTerm | SynBind Name
111153
112154{- | Tries to parse a user-defined expression given a list of syntactic extensions -}
113155extensions :: SyntaxInfo -> [Syntax] -> IdrisParser PTerm
114−extensions syn rules = choice (map (try . extension syn) (filter isValid rules))
156+extensions syn rules = extension [] (sortBy (ruleSort `on` syntaxSymbols) rules)
115157 <?> "user-defined expression"
116158 where
117− isValid :: Syntax -> Bool
118− isValid (Rule _ _ AnySyntax) = True
119− isValid (Rule _ _ PatternSyntax) = inPattern syn
120− isValid (Rule _ _ TermSyntax) = not (inPattern syn)
121−
159+ extension ns rules =
160+ choice $ flip map (groupBy (ruleGroup `on` syntaxSymbols) rules) $ \rs ->
161+ case head rs of -- can never be []
162+ Rule (symb:_) _ _ -> try $ do
163+ n <- extensionSymbol symb
164+ extension (n : ns) [Rule ss t ctx | (Rule (_:ss) t ctx) <- rs]
165+ -- If we have more than one Rule in this bucket, our grammar is
166+ -- nondeterministic.
167+ Rule [] ptm _ -> return (flatten (update (mapMaybe id ns) ptm))
168+
169+ ruleGroup [] [] = True
170+ ruleGroup (s1:_) (s2:_) = s1 == s2
171+ ruleGroup _ _ = False
172+
173+ ruleSort [] [] = EQ
174+ ruleSort [] _ = LT
175+ ruleSort _ [] = GT
176+ ruleSort (s1:ss1) (s2:ss2) =
177+ case symCompare s1 s2 of
178+ EQ -> ruleSort ss1 ss2
179+ r -> r
180+
181+ -- Better than creating Ord instance for SSymbol since
182+ -- in general this ordering does not really make sense.
183+ symCompare (Keyword n1) (Keyword n2) = compare n1 n2
184+ symCompare (Keyword _) _ = LT
185+ symCompare (Symbol _) (Keyword _) = GT
186+ symCompare (Symbol s1) (Symbol s2) = compare s1 s2
187+ symCompare (Symbol _) _ = LT
188+ symCompare (Binding _) (Keyword _) = GT
189+ symCompare (Binding _) (Symbol _) = GT
190+ symCompare (Binding b1) (Binding b2) = compare b1 b2
191+ symCompare (Binding _) _ = LT
192+ symCompare (Expr _) (Keyword _) = GT
193+ symCompare (Expr _) (Symbol _) = GT
194+ symCompare (Expr _) (Binding _) = GT
195+ symCompare (Expr e1) (Expr e2) = compare e1 e2
196+ symCompare (Expr _) _ = LT
197+ symCompare (SimpleExpr _) (Keyword _) = GT
198+ symCompare (SimpleExpr _) (Symbol _) = GT
199+ symCompare (SimpleExpr _) (Binding _) = GT
200+ symCompare (SimpleExpr _) (Expr _) = GT
201+ symCompare (SimpleExpr e1) (SimpleExpr e2) = compare e1 e2
122202
123−data SynMatch = SynTm PTerm | SynBind Name
124−
125−{- | Tries to parse an expression given a user-defined rule -}
126−extension :: SyntaxInfo -> Syntax -> IdrisParser PTerm
127−extension syn (Rule ssym ptm _)
128− = do smap <- mapM extensionSymbol ssym
129− let ns = mapMaybe id smap
130− return (flatten (update ns ptm)) -- updated with smap
131− where
132203 extensionSymbol :: SSymbol -> IdrisParser (Maybe (Name, SynMatch))
133204 extensionSymbol (Keyword n) = do reserved (show n); return Nothing
134205 extensionSymbol (Expr n) = do tm <- expr syn
src/Idris/ParseExpr.hs-bootnew file+5
@@ -0,0 +1,5 @@
1+module Idris.ParseExpr where
2+
3+data SyntaxRules
4+
5+emptySyntaxRules :: SyntaxRules
src/Idris/Parser.hs+2−2
@@ -223,7 +223,7 @@ syntaxDecl syn = do s <- syntaxRule syn
223223
224224-- | Extend an 'IState' with a new syntax extension. See also 'addReplSyntax'.
225225addSyntax :: IState -> Syntax -> IState
226−addSyntax i s = i { syntax_rules = s : rs,
226+addSyntax i s = i { syntax_rules = updateSyntaxRules [s] rs,
227227 syntax_keywords = ks ++ ns,
228228 ibc_write = IBCSyntax s : map IBCKeyword ks ++ ibc }
229229 where rs = syntax_rules i
@@ -233,7 +233,7 @@ addSyntax i s = i { syntax_rules = s : rs,
233233
234234-- | Like 'addSyntax', but no effect on the IBC.
235235addReplSyntax :: IState -> Syntax -> IState
236−addReplSyntax i s = i { syntax_rules = s : rs,
236+addReplSyntax i s = i { syntax_rules = updateSyntaxRules [s] rs,
237237 syntax_keywords = ks ++ ns }
238238 where rs = syntax_rules i
239239 ns = syntax_keywords i