gll 0.2.0.3 → 0.3.0.0
raw patch · 17 files changed
+1495/−2071 lines, 17 filesdep +prettydep ~base
Dependencies added: pretty
Dependency ranges changed: base
Files
- gll.cabal +27/−38
- src/GLL/Combinators.hs +8/−0
- src/GLL/Combinators/BinInterface.hs +0/−239
- src/GLL/Combinators/Interface.hs +522/−230
- src/GLL/Combinators/MemBinInterface.hs +0/−264
- src/GLL/Combinators/MemInterface.hs +0/−309
- src/GLL/Combinators/Memoisation.hs +14/−4
- src/GLL/Combinators/Options.hs +74/−13
- src/GLL/Combinators/Test/BinInterface.hs +0/−187
- src/GLL/Combinators/Test/Interface.hs +124/−78
- src/GLL/Combinators/Test/MemBinInterface.hs +0/−196
- src/GLL/Combinators/Test/MemInterface.hs +0/−196
- src/GLL/Combinators/Visit/Grammar.hs +22/−0
- src/GLL/Combinators/Visit/Sem.hs +74/−0
- src/GLL/Parser.hs +418/−130
- src/GLL/Types/Abstract.hs +99/−23
- src/GLL/Types/Grammar.hs +113/−164
gll.cabal view
@@ -3,67 +3,56 @@ -- The name of the package. name: gll-version: 0.2.0.3+version: 0.3.0.0 synopsis: GLL parser with simple combinator interface license: BSD3 license-file: LICENSE author: L. Thomas van Binsbergen-maintainer: ltvanbinsbergen@acm.org+maintainer: L. Thomas van Binsbergen <ltvanbinsbergen@acm.org> category: Compilers build-type: Simple cabal-version: >=1.8 tested-with: GHC == 7.6.3+copyright: Copyright (C) 2015 L. Thomas van Binsbergen+stability: experimental description: - GLL is a parser combinator library for writing generalised parsers.- The user can write parsers for arbitrary context-free grammars, including - both non-determinism and all forms of left and right-recursion.- The underlying parsing algorithm is GLL (Scott and Johnstone 2013).- .- The library provides an interface in 'Control.Applicative' style: - it uses the combinators '<*>', '<|>', '<$>' and derivations. - With '<$>' arbitrary semantic actions are added to the parser. + The package gll provides generalised top-down parsing according to the GLL+ parsing algorithm [Scott and Johnstone 2010,2013]. .- Four functions can be used to run a parser: 'parse', 'parseString', - 'parseWithOptions' and 'parseStringWithOptions'. - Function 'parse' relies on the builtin 'Token' datatype, receiving a list of- 'Token' as an input string. User-defined token-types are currently not supported. - Function *parseString* enables parsing character-level parsing.- The result of aparse is a list of semantic results, one result for each derivation. - To avoid infinite recursion, only 'good parse trees' are considered (Ridge 2014).- To limit the number of accepted derivation, and therefore avoiding potential- exponential blow-up, 'GLL.Combinators.Options' are available to specify certain - disambiguation rules.+ The user can either invoke the GLL+ parser directly by importing "GLL.Parser" and providing a+ value of the Grammar datatype in (exported by "GLL.Parser"). + Alternatively, the user can import "GLL.Combinators" to write combinator expressions + from which a grammar of the required form is extracted.+ The combinators enable applying arbitrary semantic actions to parse results.+ The documentation of the respective packages provides more information. .- 'GLL.Combinators.MemInterface' is a memoised version of the library.- Memoisation is used to speed up the process of applying semantic actions,- it is not necessary for generalised parsing: - 'GLL.Combinators.Interface' and 'GLL.Combinators.MemInterface' are - equally general.- In the memoised version, parsers are no longer pure functions and must be - developed inside the IO monad.+ The main motivation for this package+ is the development of Domain Specific Languages (DSLs).+ More specifically: designing DSLs with minimal differences between+ between abstract and concrete syntax (abstract syntax is often ambiguous). .- Examples can be found in the 'GLL.Combinators.Test' directory.+ Please email any questions, comments and suggestions to the + maintainer. library hs-source-dirs : src- build-depends : base >=4.5 && <= 4.8.0.0+ build-depends : base >=4.3.1.0 && <= 4.8.0.0 , containers >= 0.4 , array , TypeCompose+ , pretty exposed-modules : GLL.Combinators.Interface- , GLL.Combinators.MemInterface- , GLL.Combinators.BinInterface- , GLL.Combinators.MemBinInterface , GLL.Combinators.Test.Interface- , GLL.Combinators.Test.MemInterface- , GLL.Combinators.Test.BinInterface- , GLL.Combinators.Test.MemBinInterface- other-modules : GLL.Types.Abstract- , GLL.Types.Grammar+ , GLL.Combinators , GLL.Parser+ other-modules : GLL.Types.Abstract , GLL.Combinators.Memoisation , GLL.Combinators.Options+ , GLL.Types.Grammar+ , GLL.Combinators.Visit.Grammar+ , GLL.Combinators.Visit.Sem extensions : TypeOperators, FlexibleInstances, ScopedTypeVariables, TypeSynonymInstances-+ ghc-options: -fwarn-incomplete-patterns -fwarn-monomorphism-restriction -fwarn-unused-imports
+ src/GLL/Combinators.hs view
@@ -0,0 +1,8 @@++-- |+-- "GLL.Combinators" imports "GLL.Combinators.Interface" as the default library.+module GLL.Combinators (+ module GLL.Combinators.Interface + ) where++import GLL.Combinators.Interface
− src/GLL/Combinators/BinInterface.hs
@@ -1,239 +0,0 @@-module GLL.Combinators.BinInterface (- Parser,- parse, parseString,- char, token, Token(..),- epsilon, satisfy,- many,some,optional,- (<::=>),(<:=>),- (<$>),- (<$),- (<*>),- (*>),- (<*),- (<|>),-) where--import Prelude hiding ((<*>), (<*), (<$>), (<$), (*>))--import GLL.Combinators.Options-import GLL.Types.Abstract-import GLL.Types.Grammar hiding (epsilon)-import GLL.Parser (gllSPPF,ParseResult(..))--import qualified Data.Array as A-import qualified Data.IntMap as IM-import qualified Data.Map as M-import qualified Data.Set as S--type Visit1 = Symbol -type Visit2 = M.Map Nt [Alt] -> M.Map Nt [Alt]-type Visit3 a = PCOptions -> A.Array Int Token -> ParseContext -> SPPF - -> Int -> Int -> Int -> S.Set a--type Parser a = (Visit1, Visit2, Visit3 a)--type ParseContext = IM.IntMap (IM.IntMap Nt)---- | Given a parser and a string of tokens, return:--- * The grammar (GLL.Types.Abstract)--- * a list of results, which are all semantic evaluations of 'good derivations'--- - semantic evaluations are specified by using <$> and satisfy--- - 'good derivations' as defined by by Tom Ridge-parse' :: PCOptions -> Parser a -> [Token] -> (Grammar, ParseResult, [a])-parse' opts (Nt start,rules,sem) str = - let cfg = Grammar start [] [ Rule x alts - | (x, alts) <- M.assocs (rules M.empty) ]- parse_r = gllSPPF cfg str- sppf = sppf_result parse_r- as = sem opts arr IM.empty sppf 0 m m- m = length str- arr = A.array (0,m) (zip [0..] str)- in (cfg,parse_r,S.toList as)---- | The grammar of a given parser-grammar :: Parser a -> Grammar-grammar p = (\(f,_,_) -> f) (parse' defaultOptions p [])---- | The semantic results of a parser, given a token string-parse :: Parser a -> [Token] -> [a]-parse = parseWithOptions defaultOptions ---- | The semantic results of a parser, given a token string --- and GLL.Combinator.Options-parseWithOptions :: PCOptions -> Parser a -> [Token] -> [a]-parseWithOptions opts p str = (\(_,_,t) -> t) (parse' opts p str)---- | Get the SPPF produced by parsing the given input with the given parser-sppf :: Parser a -> [Token] -> ParseResult-sppf p str = (\(_,s,_) -> s) (parse' defaultOptions p str)---- | Parse a given string of characters -parseString :: Parser a -> [Char] -> [a]-parseString p = parse p . charS---- | Parse a given string of characters and options -parseStringWithOptions :: PCOptions -> Parser a -> [Char] -> [a]-parseStringWithOptions opts p = parseWithOptions opts p . charS-infixl 3 <::=>--- | use <::=> to enforce using parse context (to handle left-recursion)-(<::=>) :: String -> Parser a -> Parser a-x <::=> _r = let (sym,_r_rules,_r_sem) = _r- alt = Alt x [sym] -- TODO indirection (extra alt)- rules m = case M.lookup x m of- Nothing -> _r_rules (M.insert x [alt] m)- Just _ -> m-- sem opts arr ctx sppf l r m- | (l,r,x) `inContext` ctx = S.empty- | otherwise = let ctx' = (l,r,x) `toContext` ctx- in _r_sem opts arr ctx' sppf l r m- in (Nt x,rules,sem)---- | useful for non-recursive definitions (only internally)-infixl 3 <:=>-(<:=>) :: String -> Parser a -> Parser a-x <:=> _r = let (sym,_r_rules,_r_sem) = _r- alt = Alt x [sym] -- TODO indirection (extra alt)- rules m = case M.lookup x m of- Nothing -> _r_rules (M.insert x [alt] m)- Just _ -> m- in (Nt x,rules,_r_sem)--infixl 5 <$>--- | Application of a semantic action. -(<$>) :: (Ord b, Ord a) => (a -> b) -> Parser a -> Parser b-f <$> _r = let (sym,rules,_r_sem) = _r- sem opts arr ctx sppf l r m = S.map f (_r_sem opts arr ctx sppf l r m)- in (sym,rules,sem)--infixl 6 <*>--- | Sequence two parsers, the results of the two parsers are tupled.-(<*>) :: (Ord a, Ord b) => Parser a -> Parser b -> Parser (a,b)-_l <*> _r = (Nt lhs_id,rules,sem)- where l_id = id_ _l- r_id = id_ _r- lhs_id = concat [l_id, "*", r_id]- - -- ** one can bind this parser and recurse on it + other duplicate work- alt = Alt lhs_id [sym_ _l, sym_ _r]- rules m = case M.lookup lhs_id m of -- necessary? **- Nothing -> rules_ _r (rules_ _l (M.insert lhs_id [alt] m))- Just _ -> m- - sem opts arr ctx sppf l r m = - let filter = maybe id id $ pivot_select opts in S.fromList- [ (a,b) | k <- filter ks- , a <- S.toList (sem_ _l opts arr ctx sppf l k m)- , b <- S.toList (sem_ _r opts arr ctx sppf k r m) ]- where ks = maybe [] id $ sppf `pNodeLookup` ((alt,2), l, r)--infixl 4 <|>--- | A choice between two parsers, results of the two are concatenated-(<|>) :: (Ord a) => Parser a -> Parser a -> Parser a-_l <|> _r = (Nt lhs_id,rules,sem)- where l_id = id_ _l- r_id = id_ _r- lhs_id = concat [l_id, "|", r_id]-- alts = [Alt lhs_id [sym_ _l], Alt lhs_id [sym_ _r]]- rules m = case M.lookup lhs_id m of- Nothing -> rules_ _r (rules_ _l (M.insert lhs_id alts m))- Just _ -> m-- sem opts arr ctx sppf l r m = - concatChoice opts (sem_ _l opts arr ctx sppf l r m)- (sem_ _r opts arr ctx sppf l r m)---- derived combinators-infixl 6 <*--- | Sequencing, ignoring the result to the right-(<*) :: (Ord a, Ord b) => Parser a -> Parser b -> Parser a-_l <* _r = (\(x,y) -> x) <$> _l <*> _r--infixl 6 *>--- | Sequencing, ignoring the result to the left -(*>) :: (Ord a, Ord b) => Parser a -> Parser b -> Parser b-_l *> _r = (\(x,y) -> y) <$> _l <*> _r--infixl 5 <$--- | Ignore all results and just return the given value-(<$) :: (Ord a, Ord b) => a -> Parser b -> Parser a-f <$ _r = const f <$> _r ---- elementary parsers-raw_parser :: String -> Token -> (Token -> a) -> Parser a-raw_parser str t f = (Nt str, rules, sem)- where alt = Alt str [Term t]- rules = M.insert str [alt] - sem _ arr ctx sppf l r m - | l + 1 == r && l < m && arr A.! l == t = S.singleton (f t)- | otherwise = S.empty---- | A parser that recognises a given character-char :: Char -> Parser Char-char c = raw_parser ([c]) (Char c) (\(Char c) -> c)---- | A parser that recognises a given token-token :: Token -> Parser Token-token t = raw_parser (show t) t id---- | A parser that always succeeds (and returns unit)-epsilon :: Parser ()-epsilon = (Nt x, rules, sem)- where x = "__eps"- alt = Alt x [Term Epsilon]- rules = M.insert x [alt]- sem _ arr ctx sppf l r m | l == r = S.singleton ()- | otherwise = S.empty---- | A parser that always succeeds and returns a given value-satisfy :: (Ord a) => a -> Parser a-satisfy a = a <$ epsilon---- helpers-sym_ :: Parser a -> Symbol-sym_ (f,_,_) = f--id_ :: Parser a -> Nt -id_ (Nt x,_,_) = x--rules_ :: Parser a -> Visit2-rules_ (_,f,_) = f--sem_ :: Parser a -> Visit3 a-sem_ (_,_,f) = f--mkNt :: String -> Char -> Nt-mkNt x c = concat ["(",x,")",[c]]--inContext :: (Int, Int, Nt) -> ParseContext -> Bool-inContext (l,r,x) = maybe False inner . IM.lookup l - where inner = maybe False ((==) x) . IM.lookup r--toContext :: (Int, Int, Nt) -> ParseContext -> ParseContext-toContext (l,r,x) = IM.insertWith IM.union l (IM.singleton r x)--concatChoice :: (Ord a) => PCOptions -> S.Set a -> S.Set a -> S.Set a-concatChoice opts ls rs = if left_biased_choice opts- then firstRes- else ls `S.union` rs- where firstRes | S.null ls = rs- | otherwise = ls---- higher level patterns---- | Optionally use the given parser-optional :: (Ord a) => Parser a -> Parser (Maybe a)-optional p@(Nt x,_,_) = (mkNt x '?') <:=> satisfy Nothing <|> Just <$> p---- | Apply the given parser many times, 0 or more times (Kleene closure)-many :: (Ord a) => Parser a -> Parser [a]-many p@(Nt x,_,_) = (mkNt x '^') <::=> satisfy [] - <|> uncurry (:) <$> p <*> many p---- | Apply the given parser some times, 1 or more times (positive closure)-some :: (Ord a) => Parser a -> Parser [a]-some p@(Nt x,_,_) = let rec = (mkNt x '+') <::=> (:[]) <$> p- <|> uncurry (:) <$> p <*> rec- in rec-
src/GLL/Combinators/Interface.hs view
@@ -1,289 +1,581 @@ {-# LANGUAGE TypeOperators, FlexibleInstances #-} -module GLL.Combinators.Interface (- SymbParser, IMParser, - HasAlts(..), IsSymbParser(..), IsIMParser(..),- parse, parseString, - char, token,Token(..),- epsilon, satisfy,- many, some, optional,- (<::=>),(<:=>),- (<$>),- (<$),- (<*>),- (<*),- (<|>),- (:.)- ) where+{-| +This module provides a combinator library in which combinators act as an interface+to a back-end parser. This technique is inspired by Tom Ridge (2014) and + Peter Ljunglöf (2002).+The library is fully general: it enables writing parsers for arbitrary context-free+grammars. -import Prelude hiding ((<*>), (<*), (<$>), (<$))+The user writes a combinator expression representing a grammar.+The represented grammar is extracted and given, together with an input string,+to a back-end parser.+The derivations constructed by the parser act as a guide in the "semantic phase"+in which the combinator expressions are evaluated to produce semantic results+for all derivations. Infinitely many derivations would result in a loop. +This problem is avoided by discarding the derivations that would arise from such a loop. -import GLL.Combinators.Options-import GLL.Types.Grammar hiding (epsilon)-import GLL.Types.Abstract-import GLL.Parser (gllSPPF, pNodeLookup, ParseResult(..))+This library uses "GLL.Parser" as the back-end parser and provides +"Control.Applicative"-like parser combinators: '<**>' for sequencing, '<||>' for +choice, '<$$>' for application, 'satisfy' instead of pure and derived +combinators '<**', '**>', '<$$', 'many', 'some' and 'optional'. -import Control.Compose ((:.)(..),unO)-import Control.Monad-import Data.List (unfoldr,intersperse)-import qualified Data.IntMap as IM-import qualified Data.Map as M-import qualified Data.Set as S+The semantic phase might benefit from memoisation which is provided in a separate (impure)+library "GLL.Combinators.MemInterface". --- | A parser expression representing a symbol.-data SymbParser b = SymbParser (SymbVisit1 b,SymbVisit2 b, SymbVisit3 b)--- | A parser expression representing an alternative (right-hand side).-data IMParser b = IMParser (IMVisit1 b, IMVisit2 b, IMVisit3 b)+=== Example usage+This library differs from parser combinator libraries in that combinator expressions+are used to describe a grammar rather than a parser. --- | The represented symbol.-type SymbVisit1 b = Symbol --- | Add the rules of this symbol to the given map--- If the symbol is a terminal, no rules will be added (identity function)-type SymbVisit2 b = M.Map Nt [Alt] -> M.Map Nt [Alt]-type SymbVisit3 b = PCOptions -> ParseContext -> SPPF -> Int -> Int -> [b]+A rule is considered to be of the form X ::= a | .. | z, and represented by the combinator+expression. -type IMVisit1 b = [Symbol] -type IMVisit2 b = M.Map Nt [Alt] -> M.Map Nt [Alt]-type IMVisit3 b = PCOptions -> (Alt,Int) -> ParseContext -> SPPF -> Int -> Int -> [b]+@+pX = "X" '<::=>' altA '<||>' ... '<||>' altZ+@ -type ParseContext = IM.IntMap (IM.IntMap (S.Set Nt))+Alternates ('a' ... 'z') start with the application of +a semantic action using '<$$>' (or variants '<$$' and 'satisfy'). +The alternate is extended with '<**>' (or variants '**>', '<**'). +@+altA = action1 '<$$>' 'keychar' \'a\' '<**>' pX+altZ = action2 '<$$>' 'keychar' \'z\'+@ -parse' :: (IsSymbParser s) => PCOptions -> s a -> [Token] -> (Grammar, ParseResult, [a])-parse' opts p' input' = - let input = input' ++ [Char 'z']- SymbParser (Nt start,vpa2,vpa3) = toSymb (id <$> p' <* char 'z')- snode = (start, 0, m)- m = length input- rules = vpa2 M.empty- as = vpa3 opts IM.empty sppf 0 m- grammar = Grammar start [] [ Rule x alts | (x, alts) <- M.assocs rules ]- parse_res = gllSPPF grammar input- sppf = sppf_result parse_res- in (grammar, parse_res, as)+Usability is improved by automatic lifting between expressions that represent symbols+and alternates. The main difference with "Control.Applicative" style parser combinator+libraries is that the user must use '<:=>' (or '<::=>') to represent all recursive +nonterminals and must use '<::=>' to represent all nonterminals that potentially lead+to an infinite number of derivations. It is, however, possible to represent left-recursive+nonterminals. --- | The grammar of a given parser-grammar :: (IsSymbParser s) => s a -> Grammar-grammar p = (\(f,_,_) -> f) (parse' defaultOptions p [])+=== Example --- | The semantic results of a parser, given a string of Tokens-parse :: (IsSymbParser s) => s a -> [Token] -> [a]-parse = parseWithOptions defaultOptions +In this example we define expressions for parsing (and evaluating) simple arithmetic +expressions for single digit integers. --- | Change the behaviour of the parse using GLL.Combinators.Options-parseWithOptions :: (IsSymbParser s) => PCOptions -> s a -> [Token] -> [a]-parseWithOptions opts p = (\(_,_,t) -> t) . parse' opts p+The library is capable of parsing arbitrary token types that are 'Parseable', orderable+and have a 'Show' instance.+This example demonstrates the usage of the builtin 'Token' datatype and uses the+elementary parsers 'keychar' and 'int_lit' to create parsers for character- and integer-terminals. --- | Parse a string of characters-parseString :: (IsSymbParser s) => s a -> String -> [a]-parseString = parseStringWithOptions defaultOptions+We define a very simpler lexer first. --- | Parse a string of characters using options-parseStringWithOptions :: (IsSymbParser s) => PCOptions -> s a -> String -> [a]-parseStringWithOptions opts p = parseWithOptions opts p . map Char+@+lexer :: String -> [Token]+lexer [] = []+lexer (x:xs)+ | isDigit x = 'IntLit' (Just (read [x])) : lexer xs+ | otherwise = 'Char' x : lexer xs+@ +Note that the Char constructor of the 'Token' type is used for character-level parsing.+Char contains no lexeme, unlike the Int constructor.++Consider the following (highly ambiguous and left-recursive) grammar:++@+Expr ::= Expr \'-\' Expr+ | Expr \'+\' Expr+ | Expr \'*\' Expr+ | Expr \'/\' Expr+ | INT+ | \'(\' Expr \')\'+@++The grammar is translated to the following combinator expression, adding the expected+evaluation functions as semantic actions.++@+pExpr :: Grammar Token Int+pExpr = \"Expr\" <::=> (-) '<$$>' pExpr '<**' 'keychar' \'-\' '<**>' pExpr+ '<||>' (+) '<$$>' pExpr '<**' 'keychar' \'+\' '<**>' pExpr+ '<||>' (*) '<$$>' pExpr '<**' 'keychar' \'*\' '<**>' pExpr+ '<||>' div '<$$>' pExpr '<**' 'keychar' \'/\' '<**>' pExpr+ '<||>' 'int_lit'+ '<||>' parens pExpr+@++Note that '<**' is used to ignore the parse result of the second argument and that '**>' +is used to ignore the parse result of the first argument. These combinators+help us to define the /derived combinator/s /within/ and /parens/.++@+within :: 'BNF' 'Token' a -> 'BNF' 'Token' b -> 'BNF' 'Token' a -> 'BNF' 'Token' b+within l p r = 'mkRule' $ l '**>' p '<**' r++parens :: 'BNF' 'Token' a -> 'BNF' 'Token' a+parens p = within ('keychar' '(') p ('keychar' ')')+@++All possible evaluations are obtained by invoking the 'parse' function.++@+run1 = 'parse' pExpr (lexer "1+2*2-5") -- [0,1,0,-5,-9] +run2 = 'parse' pExpr (lexer "((1+(2*2))-3)-5") -- [-3]+@++With every introduction of an operator '+', '-', '*' or '/' the number of ambiguities is +multiplied. The number of ambiguities behaves like the sequence https://oeis.org/A000108.++=== Simple disambiguation++This library offers simple disambiguation strategies that are applied post-parse +(the parser still faces the ambiguity, but the semantic evaluation only yields +the results according to the strategy). The disambiguations strategies are still+in the /experimental/ phase. ++We group the operators according to their priorities and use +'<::=' to turn the choice operator '<||>' into a left-biased operator locally+(use 'leftBiased' for the same effect globally).++@+pExpr1 :: Grammar Token Int+pExpr1 = \"Expr\" '<::=' ( (-) '<$$>' pExpr1 '<**' 'keychar' \'-\' '<**>' pExpr1+ '<||>' (+) '<$$>' pExpr1 '<**' 'keychar' \'+\' '<**>' pExpr1 )+ '<||>' ( (*) '<$$>' pExpr1 '<**' 'keychar' \'*\' '<**>' pExpr1+ '<||>' div '<$$>' pExpr1 '<**' 'keychar' \'/\' '<**>' pExpr1 )+ '<||>' ( 'int_lit'+ '<||>' 'parens' pExpr1 )++run3 = 'parseWithOptions' ['maximumPivotAtNt'] pExpr1 (lexer "1+2*2-5") -- [0]+@++The option 'maximumPivotAtNt' enables the 'longest-match' disambiguation strategy +and makes the arithmetic operators left-associative.++=== Grammar rewrites++To deal with the particular ambiguities associated with operators we can +rewrite the grammar to disambiguate pre-parse.++We define the /chainl/ combinator for parsing chains of left-associative operators.++@+chainl :: 'BNF' 'Token' a -> 'BNF' 'Token' (a -> a -> a) -> 'BNF' 'Token' a+chainl p s = 'mkRule' $+ foldl (flip ($)) '<$$>' p '<**>' many (flip '<$$>' s '<**>' p)+@++The expression parser is written with chainl as follows:++@+pExpr2 :: Grammar Token Int+pExpr2 = pE1+ where pE1 = chainl pE2 (\"E1\" '<::=>' (+) '<$$' 'keychar' \'+\' '<||>' (-) '<$$' 'keychar' \'-\')+ pE2 = chainl pE3 (\"E2\" '<::=>' (*) '<$$' 'keychar' \'*\' '<||>' div '<$$' 'keychar' \'/\')+ pE3 = \"E3\" '<::=>' 'int_lit' '<||>' parens pExpr2++run4 = 'parse' 'pExpr2' (lexer "1+2*2-5") -- [0]+@++Pre-parse disambiguation is desirable, as the parsing process could +speed up dramatically. In general however, it is not always possible to find +the appropriate grammar rewrite and implement it in a high-level combinator such +as chainl, /motivating the existence of this library/.++More simple examples can be found in "GLL.Combinators.Test.Interface".++-}+module GLL.Combinators.Interface (+ -- * Elementary parsers+ term_parser, satisfy,+ -- ** Elementary parsers using the 'Token' datatype + keychar, keyword, int_lit, bool_lit, string_lit, id_lit, token,+ -- ** Elementary character-level parsers+ char, + -- * Elementary combinators+ -- *** Sequencing+ (<**>),+ -- *** Choice+ (<||>),+ -- *** Semantic actions+ (<$$>),+ -- *** Nonterminal introduction+ (<:=>),(<::=>),+ -- * Types+ -- ** Grammar (combinator expression) types+ BNF, SymbExpr, AltExpr, AltExprs,+ -- ** Parseable token types + Token(..), Parseable(..), + -- * Running a parser + parse,+ -- ** Running a parser with disambiguation options+ parseWithOptions,+ -- *** Possible options+ Options, Option, leftBiased, maximumPivot, maximumPivotAtNt,+ GLL.Combinators.Options.maximumErrors, throwErrors,+ -- * Derived combinators+ mkNt, + -- *** Ignoring semantic results+ (<$$), (**>), (<**),+ -- *** Post-parse disambiguation+ (<::=),+ -- *** EBNF patterns+ optional, many, some,+ -- * Lifting+ HasAlts(..), IsSymbExpr(..), IsAltExpr(..),+ -- * Memoisation+ memo, newMemoTable, memClear, MemoTable, MemoRef, useMemoisation,+ ) where++import GLL.Combinators.Options+import GLL.Combinators.Visit.Sem+import GLL.Combinators.Visit.Grammar+import GLL.Combinators.Memoisation+import GLL.Types.Abstract+import GLL.Parser hiding (parse, parseWithOptions)+import qualified GLL.Parser as GLL++import Control.Arrow+import Control.Compose (OO(..),unOO)+import Data.List (intersperse)+import qualified Data.Array as A+import qualified Data.IntMap as IM+import qualified Data.Map as M+import Data.IORef +import System.IO.Unsafe++-- | A combinator expression representing a symbol.+-- A 'SymbExpr' either represents a terminal or a nonterminal.+-- In the latter case it is constructed with (a variant of) '<:=>' and +-- adds a rule to the grammar of which the represented symbol is the +-- left-hand side.+data SymbExpr t a = SymbExpr (Symbol t, Grammar_Expr t, Sem_Symb t a)+-- | A combinator expression representing a BNF-grammar. The terminals of+-- the grammar are of type 't'. When used to parse, the expression yields+-- semantic results of type 'a'. +type BNF t a = SymbExpr t a+-- | +-- A combinator expression representing an alternative: +-- the right-hand side of a production.+data AltExpr t a = AltExpr ([Symbol t], Grammar_Expr t, Sem_Alt t a)++-- | A list of alternatives represents the right-hand side of a rule.+type AltExprs = OO [] AltExpr++parse' :: (Show t, Parseable t, IsSymbExpr s) => + PCOptions -> s t a -> [t] -> (Grammar t, ParseResult t, Either String [a])+parse' opts p' input = + let SymbExpr (Nt start,vpa2,vpa3) = mkRule ("__Start" <:=> OO [id <$$> p'])+ snode = (start, 0, m)+ m = length input+ rules = vpa2 M.empty+ as = vpa3 opts IM.empty sppf arr 0 m+ grammar = (start, [ p | (_, alts) <- M.assocs rules, p <- alts ])+ max_err = max_errors opts+ parse_res = GLL.parseWithOptions [strictBinarisation, packedNodesOnly, GLL.maximumErrors max_err] grammar input+ sppf = sppf_result parse_res+ arr = A.array (0,m) (zip [0..] input)+ in (grammar, parse_res, if res_success parse_res + then Right $ unsafePerformIO as+ else Left (error_message parse_res) )++-- | The grammar of a given parser+grammar :: (Show t, Parseable t, IsSymbExpr s) => s t a -> Grammar t+grammar p = (\(f,_,_) -> f) (parse' defaultOptions p [])+ -- | Get the SPPF produced by parsing the given input with the given parser-sppf :: (IsSymbParser s) => s a -> [Token] -> ParseResult+sppf :: (Show t, Parseable t, IsSymbExpr s) => + s t a -> [t] -> ParseResult t sppf p str = (\(_,s,_) -> s) $ parse' defaultOptions p str -inParseContext :: ParseContext -> (Symbol, Int, Int) -> Bool-inParseContext ctx (Nt x, l, r) = maybe False inner $ IM.lookup l ctx- where inner = maybe False (S.member x) . IM.lookup r+-- | +-- Runs a parser given a string of 'Parseable's and returns a list of +-- semantic results, corresponding to all finitely many derivations.+parse :: (Show t, Parseable t, IsSymbExpr s) => s t a -> [t] -> [a]+parse = parseWithOptions [] -toParseContext :: ParseContext -> (Nt, Int, Int) -> ParseContext-toParseContext ctx (x, l, r) = IM.alter inner l ctx- where inner mm = case mm of - Nothing -> Just $ singleRX- Just m -> Just $ IM.insertWith (S.union) r singleX m- singleRX = IM.singleton r singleX- singleX = S.singleton x+-- | +-- Run the parser with some 'Options'.+parseWithOptions :: (Show t, Parseable t, IsSymbExpr s) => + Options -> s t a -> [t] -> [a]+parseWithOptions opts p ts = + case parseWithOptionsAndError opts p ts of+ Left str | throw_errors opts' -> error str+ | otherwise -> []+ Right as -> as+ where opts' = runOptions opts+-- | +-- Run the parser with some 'Options' and return either an error or the results.+-- Any returned results will be a list of length greater than 0.+parseWithOptionsAndError :: (Show t, Parseable t, IsSymbExpr s) => + Options -> s t a -> [t] -> Either String [a]+parseWithOptionsAndError opts p = (\(_,_,t) -> t) . parse' (runOptions opts) p +infixl 2 <:=>+-- | +-- Form a rule by giving the name of the left-hand side of the new rule.+-- Use this combinator on recursive non-terminals.+(<:=>) :: (Show t, Ord t, HasAlts b) => Nt -> b t a -> SymbExpr t a +x <:=> altPs = mkNtRule False False x altPs infixl 2 <::=>--- | Use this combinator on all combinators that might have an infinite--- number of derivations for some input string. A non-terminal has--- this property if and only if it is left-recursive and would be++-- | +-- Variant of '<:=>' for recursive non-terminals that have a potentially infinite+-- number of derivations for some input string.+--+-- A non-terminal yields infinitely many derivations +-- if and only if it is left-recursive and would be -- left-recursive if all the right-hand sides of the productions of the -- grammar are reversed.-(<::=>) :: (HasAlts b) => String -> b a -> SymbParser a -x <::=> altPs' =- let vas1 = [ va1 | va1 <- map (\(IMParser (f,_,_)) -> f) altPs ]- alts = map (Alt x) vas1 - altPs = unO $ altsOf altPs' in SymbParser- (Nt x- ,\rules ->- if x `M.member` rules - then rules - else foldr ($) (M.insert x alts rules) $ (map (\(IMParser (_,s,_)) -> s) altPs)- ,\opts ctx sppf l r -> - let ctx' = ctx `toParseContext` (x,l,r)- vas2 = [ va3 opts (alt,length rhs) ctx' sppf l r - | (alt@(Alt _ rhs), va3) <- zip alts (map (\(IMParser (_,_,t)) -> t) altPs) ]- in if ctx `inParseContext` (Nt x, l, r) - then []- else concatChoice opts vas2- )--infixl 2 <:=>--- | Use this combinator on all recursive non-terminals-(<:=>) :: (HasAlts b) => String -> b a -> SymbParser a -x <:=> altPs' =- let vas1 = [ va1 | va1 <- map (\(IMParser (f,_,_)) -> f) altPs ]- alts = map (Alt x) vas1 - altPs = unO $ altsOf altPs' in SymbParser- (Nt x- ,\rules ->- if x `M.member` rules - then rules - else foldr ($) (M.insert x alts rules) $ (map (\(IMParser (_,s,_)) -> s) altPs)- ,\opts ctx sppf l r -> - let vas2 = [ va3 opts (alt,length rhs) ctx sppf l r - | (alt@(Alt _ rhs), va3) <- zip alts (map (\(IMParser (_,_,t)) -> t) altPs) ]- in concatChoice opts vas2- )+(<::=>) :: (Show t, Ord t, HasAlts b) => Nt -> b t a -> SymbExpr t a +x <::=> altPs = mkNtRule True False x altPs -concatChoice :: PCOptions -> [[a]] -> [a]-concatChoice opts ress = if left_biased_choice opts- then firstRes ress- else concat ress- where firstRes [] = []- firstRes ([]:ress) = firstRes ress- firstRes (res:_) = res+mkNtRule :: (Show t, Ord t, HasAlts b) => Bool -> Bool -> Nt -> b t a -> SymbExpr t a+mkNtRule use_ctx left_biased x altPs' =+ let vas1 = map (\(AltExpr (f,_,_)) -> f) altPs + vas2 = map (\(AltExpr (_,s,_)) -> s) altPs+ vas3 = map (\(AltExpr (_,_,t)) -> t) altPs+ alts = map (Prod x) vas1 + altPs = altsOf altPs'+ in SymbExpr (Nt x, grammar_nterm x alts vas2, sem_nterm use_ctx left_biased x alts vas3) -infixl 4 <*>-(<*>) :: (IsIMParser i, IsSymbParser s) => i (a -> b) -> s a -> IMParser b-pl' <*> pr' = - let IMParser (vimp1,vimp2,vimp3) = toImp pl'- SymbParser (vpa1,vpa2,vpa3) = toSymb pr' in IMParser- (vimp1++[vpa1]- ,\rules -> let rules1 = vpa2 rules- rules2 = vimp2 rules1 in rules2- ,\opts (alt@(Alt x rhs),j) ctx sppf l r ->- let ks = maybe [] id $ sppf `pNodeLookup` ((alt,j), l, r)- filter = maybe id id $ pivot_select opts- in [ a2b a | k <- (filter ks) , a <- vpa3 opts ctx sppf k r- , a2b <- vimp3 opts(alt,j-1) ctx sppf l k ]- )+infixl 4 <$$>+-- |+-- Form an 'AltExpr' by mapping some semantic action overy the result+-- of the second argument.+(<$$>) :: (Show t, Ord t, IsSymbExpr s) => (a -> b) -> s t a -> AltExpr t b+f <$$> p' = + let SymbExpr (vpa1,vpa2,vpa3) = mkRule p' in AltExpr+ ([vpa1],grammar_apply vpa2, sem_apply f vpa3) +infixl 4 <**>+-- | +-- Add a 'SymbExpr' to the right-hand side represented by an 'AltExpr'+-- creating a new 'AltExpr'. +-- The semantic result of the first argument is applied to the second +-- as a cross-product. +(<**>) :: (Show t, Ord t, IsAltExpr i, IsSymbExpr s) => + i t (a -> b) -> s t a -> AltExpr t b+pl' <**> pr' = + let AltExpr (vimp1,vimp2,vimp3) = toAlt pl'+ SymbExpr (vpa1,vpa2,vpa3) = mkRule pr' in AltExpr+ (vimp1++[vpa1], grammar_seq vimp2 vpa2, sem_seq vimp3 vpa3) -infixl 4 <*-(<*) :: (IsIMParser i, IsSymbParser s) => i b -> s a -> IMParser b+infixr 3 <||>+-- |+-- Add an 'AltExpr' to a list of 'AltExpr'+-- The resuling '[] :. AltExpr' forms the right-hand side of a rule.+(<||>) :: (Show t, Ord t, IsAltExpr i, HasAlts b) => i t a -> b t a -> AltExprs t a+l' <||> r' = let l = toAlt l'+ r = altsOf r'+ in OO (l : r) -pl' <* pr' = - let IMParser (vimp1,vimp2,vimp3) = toImp pl'- SymbParser (vpa1,vpa2,vpa3) = toSymb pr' in IMParser- (vimp1++[vpa1]- ,\rules ->- let rules1 = vpa2 rules- rules2 = vimp2 rules1- in rules2- ,\opts (alt@(Alt x rhs),j) ctx sppf l r ->- let ks = maybe [] id $ sppf `pNodeLookup` ((alt,j), l, r)- filter = maybe id id $ pivot_select opts- in [ b | k <- (filter ks) , a <- vpa3 opts ctx sppf k r- , b <- vimp3 opts (alt,j-1) ctx sppf l k ]- )+-- | Create a symbol-parse for a terminal given:+--+-- * The 'Parseable' token represented by the terminal.+-- * A function from that 'Parseable' to a semantic result.+term_parser :: t -> (t -> a) -> SymbExpr t a +term_parser t f = SymbExpr (Term t, id,\_ _ _ arr l _ -> return [f (arr A.! l)]) -infixl 4 <$>-(<$>) :: (IsSymbParser s) => (a -> b) -> s a -> IMParser b-f <$> p' = - let SymbParser (vpa1,vpa2,vpa3) = toSymb p' in IMParser- ([vpa1]- ,\rules -> - vpa2 rules- ,\opts (alt,j) ctx sppf l r ->- let a = vpa3 opts ctx sppf l r- in maybe [] (const (map f a)) $ sppf `pNodeLookup` ((alt,1),l,r)- )+-- | Parse a single character.+--+-- @+-- char c = term_parser c id+-- @+--+-- Currently, this is the only character-level combinator exported+-- by this module. Please use token-level combinators for practical parsing.+-- Might change in the future.+char :: Char -> SymbExpr Char Char+char c = term_parser c id+{-+-- | Parse a list of characters.+string :: [Char] -> SymbExpr Char [Char]+string [] = mkRule $ satisfy []+string (c:cs) = mkRule $ (:) <$$> char c <**> string cs -infixl 4 <$-(<$) :: (IsSymbParser s) => b -> s a -> IMParser b-f <$ p' = - let SymbParser (vpa1,vpa2,vpa3) = toSymb p' in IMParser - ([vpa1]- ,\rules -> - vpa2 rules- ,\opts (alt,j) ctx sppf l r ->- let a = vpa3 opts ctx sppf l r- in maybe [] (const (map (const f) a)) $ sppf `pNodeLookup` ((alt,1),l,r)- )+-- | +-- Apply a parser within two other parsers.+within :: BNF Char a -> BNF Char b -> BNF Char c -> BNF Char b+within l p r = mkRule $ l *> p <* r -infixr 3 <|>-(<|>) :: (IsIMParser i, HasAlts b) => i a -> b a -> ([] :. IMParser) a-l' <|> r' = let l = toImp l'- r = altsOf r'- in O (l : unO r)+-- | +-- Apply a parser within parentheses.+-- parens p = within (char '(') p (char ')')+parens :: BNF Char a -> BNF Char a +parens p = within (char '(') p (char ')')+-} -raw_parser :: Token -> (Token -> a) -> SymbParser a -raw_parser t f = SymbParser (Term t, id,\_ _ _ _ _ -> [f t])+-- | Parse a single character, using the 'Token' datatype.+keychar :: Char -> SymbExpr Token Char+keychar c = term_parser (Char c) (const c) -- helper for Char tokens -token :: Token -> SymbParser Token-token t = raw_parser t id +-- | Parse a single character, using the 'Token' datatype.+keyword :: String -> SymbExpr Token String+keyword k = term_parser (Keyword k) (const k) -- helper for Char tokens -char :: Char -> SymbParser Char-char c = raw_parser (Char c) (\(Char c) -> c) +-- | Parse a single integer, using the 'Token' datatype.+-- Returns the lexeme interpreted as an integer.+int_lit :: SymbExpr Token Int+int_lit = term_parser (IntLit Nothing) unwrap+ where unwrap (IntLit (Just i)) = i+ unwrap _ = error "int_lit: the token must store lexeme" -epsilon :: SymbParser ()-epsilon = raw_parser (Epsilon) (\_ -> ()) +-- | Parse a single Boolean, using the 'Token' datatype.+-- Returns the lexeme interpreter as a Boolean.+bool_lit :: SymbExpr Token Bool+bool_lit = term_parser (BoolLit Nothing) unwrap+ where unwrap (BoolLit (Just b)) = b+ unwrap _ = error "bool_lit: the token must store lexeme" -satisfy :: a -> IMParser a-satisfy a = a <$ epsilon+-- | Parse a single String literal, using the 'Token' datatype.+-- Returns the lexeme interpreted as a String literal.+string_lit :: SymbExpr Token String+string_lit = term_parser (StringLit Nothing) unwrap+ where unwrap (StringLit (Just i)) = i+ unwrap _ = error "string_lit: the token must store lexeme" -many :: SymbParser a -> SymbParser [a]-many p = SymbParser f- where SymbParser (myx,_,_) = p- SymbParser f = many_ ("(" ++ show myx ++ ")^") p+-- | Parse a single identifier, using the 'Token' datatype.+-- Returns the lexeme as a String+id_lit :: SymbExpr Token String+id_lit = term_parser (IDLit Nothing) unwrap+ where unwrap (IDLit (Just i)) = i+ unwrap _ = error "id_lit: the token must store lexeme" -many_ x p = x <:=> (:) <$> p <*> many_ x p <|> [] <$ epsilon+-- | Parse a single arbitrary token, using the 'Token' datatype.+-- Returns the lexeme.+token :: String -> SymbExpr Token String+token name = term_parser (Token name Nothing) unwrap+ where unwrap (Token name' (Just i)) | name == name' = i+ unwrap _ = error "tokenT: the token must store the lexeme" -some :: SymbParser a -> SymbParser [a]-some p = SymbParser f- where SymbParser (myx,_, _) = p- SymbParser f = some_ ("(" ++ show myx ++ ")+") p +epsilon :: (Show t, Ord t) => AltExpr t ()+epsilon = AltExpr ([], M.insert x [Prod x []],\_ _ _ _ _ l r -> + if l == r then return [(l,())] else return [] )+ where x = "__eps" -some_ x p = x <:=> (:) <$> p <*> some_ x p <|> (:[]) <$> p+-- | The empty right-hand side that yields its +-- first argument as a semantic result.+satisfy :: (Show t, Ord t ) => a -> AltExpr t a+satisfy a = a <$$ epsilon -optional :: SymbParser a -> SymbParser (Maybe a)-optional p = SymbParser f- where SymbParser (myx, _, _) = p - SymbParser f = optional_ ("(" ++ show myx ++ ")?") p+-- | +-- This function memoises a parser, given:+--+-- * A 'MemoRef' pointing to a fresh 'MemoTable', created using 'newMemoTable'.+-- * The 'SymbExpr' to memoise.+--+-- Use 'memo' on all parsers that are expected to derive the same +-- substring multiple times. If the same combinator expression is used+-- to parse multiple times the 'MemoRef' needs to be cleared using 'memClear'.+--+-- 'memo' relies on 'unsafePerformIO' and is therefore potentially unsafe.+-- The option 'useMemoisation' enables memoisation.+-- It is off by default, even if 'memo' is used in a combinator expression.+memo :: (Ord t, Show t, IsSymbExpr s) => MemoRef [a] -> s t a -> SymbExpr t a+memo ref p' = let SymbExpr (sym,rules,sem) = toSymb p'+ lhs_sem opts ctx sppf arr l r + | not (do_memo opts) = sem opts ctx sppf arr l r+ | otherwise = do+ tab <- readIORef ref+ case memLookup (l,r) tab of+ Just as -> return as+ Nothing -> do as <- sem opts ctx sppf arr l r+ modifyIORef ref (memInsert (l,r) as)+ return as+ in SymbExpr (sym, rules, lhs_sem) -optional_ x p = x <:=> Just <$> p <|> (Nothing <$ epsilon)+-- | +-- Helper function for defining new combinators.+-- Use 'mkNt' to form a new unique non-terminal name based on+-- the symbol of a given 'SymbExpr' and a 'String' that is unique to+-- the newly defined combinator.+mkNt :: (Show t, Ord t, IsSymbExpr s) => s t a -> String -> Nt+mkNt p str = let SymbExpr (myx,_,_) = mkRule p+ in "_(" ++ show myx ++ ")" ++ str -class HasAlts a where- altsOf :: a b -> ([] :. IMParser) b+-- | Specialised fmap for altparsers+(.$.) :: (Show t, Ord t, IsAltExpr i) => (a -> b) -> i t a -> AltExpr t b+f .$. i = let AltExpr (s,r,sem) = toAlt i+ in AltExpr (s,r,\opts slot ctx sppf arr l r -> + do as <- sem opts slot ctx sppf arr l r+ return $ map (id *** f) as ) -instance HasAlts IMParser where- altsOf = O . (:[])+-- | +-- Version of '<$$>' that ignores the semantic result of its second argument. +(<$$) :: (Show t, Ord t, IsSymbExpr s) => b -> s t a -> AltExpr t b+f <$$ p = const f <$$> p+infixl 4 <$$ -instance HasAlts SymbParser where- altsOf = altsOf . toImp+-- | +-- Version of '<**>' that ignores the semantic result of the first argument.+(**>) :: (Show t, Ord t, IsAltExpr i, IsSymbExpr s) => i t a -> s t b -> AltExpr t b+l **>r = flip const .$. l <**> r+infixl 4 **> -instance HasAlts ([] :. IMParser) where- altsOf = id+-- | +-- Version of '<**>' that ignores the semantic result of the second argument.+(<**) :: (Show t, Ord t, IsAltExpr i, IsSymbExpr s) => i t a -> s t b -> AltExpr t a+l <** r = const .$. l <**> r +infixl 4 <** -class IsIMParser a where- toImp :: a b -> IMParser b+-- | +-- Left-biased version of '<::='+x <::= altPs = mkNtRule True True x altPs +infixl 2 <::= -instance IsIMParser IMParser where- toImp = id+-- | Try to apply a parser `many' (0 or more) times. The results are returned in a list.+many :: (Show t, Ord t, IsSymbExpr s) => s t a -> SymbExpr t [a]+many p = let fresh = mkNt p "*" --TODO using <:=> or <::=> ? + in fresh <::=> (:) <$$> p <**> many p <||> satisfy [] -instance IsIMParser SymbParser where- toImp p = id <$> p+-- | Try to apply a parser `some' (1 or more) times. The results are returned in a list.+some :: (Show t, Ord t, IsSymbExpr s) => s t a -> SymbExpr t [a]+some p = let fresh = mkNt p "+"+ in fresh <::=> (:) <$$> p <**> many p -instance IsIMParser ([] :. IMParser) where- toImp = toImp . toSymb+-- | Try to apply a parser once, but proceed if unsuccessful +-- (yielding 'Nothing' as a result in that case).+optional :: (Show t, Ord t, IsSymbExpr s) => s t a -> SymbExpr t (Maybe a)+optional p = let fresh = mkNt p "?"+ in fresh <::=> Just <$$> p <||> satisfy Nothing -class IsSymbParser a where- toSymb :: a b -> SymbParser b+-- | +-- Class for lifting to 'SymbExpr'.+class IsSymbExpr a where+ toSymb :: (Show t, Ord t) => a t b -> SymbExpr t b+ -- | Synonym of 'toSymb' for creating /derived combinators/. + mkRule :: (Show t, Ord t) => a t b -> BNF t b+ mkRule = toSymb -instance IsSymbParser IMParser where- toSymb = toSymb . O . (:[]) +instance IsSymbExpr AltExpr where+ toSymb = toSymb . OO . (:[]) -instance IsSymbParser SymbParser where+instance IsSymbExpr SymbExpr where toSymb = id -instance IsSymbParser ([] :. IMParser) where+instance IsSymbExpr AltExprs where toSymb a = mkName <:=> a - where mkName = "_" ++ concat (intersperse "|" (map op (unO a)))- where op (IMParser (rhs,_,_)) = concat (intersperse "*" (map show rhs))+ where mkName = "_(" ++ concat (intersperse "|" (map op (unOO a))) ++ ")_"+ where op (AltExpr (rhs,_,_)) = concat (intersperse "*" (map show rhs))+-- | +-- Class for lifting to 'AltExprs'. +class HasAlts a where+ altsOf :: (Show t, Ord t) => a t b -> [AltExpr t b]++instance HasAlts AltExpr where+ altsOf = (:[])++instance HasAlts SymbExpr where+ altsOf = altsOf . toAlt++instance HasAlts AltExprs where+ altsOf = unOO ++-- | +-- Class for lifting to 'AltExpr'. +class IsAltExpr a where+ toAlt :: (Show t, Ord t) => a t b -> AltExpr t b++instance IsAltExpr AltExpr where+ toAlt = id++instance IsAltExpr SymbExpr where+ toAlt p = id <$$> p++instance IsAltExpr AltExprs where+ toAlt = toAlt . mkRule
− src/GLL/Combinators/MemBinInterface.hs
@@ -1,264 +0,0 @@---- | Parser Combinators for GLL parsing inspired by Tom Ridge's P3 OCaml library-module GLL.Combinators.MemBinInterface (- Parser,- parse, parseString,- (<::=>),(<:=>),- (<$>),- (<$),- (<*>),- (*>),- (<*),- (<|>),- char, token, Token(..),- epsilon,satisfy,- optional, many, some,- memo, newMemoTable, MemoRef, MemoTable- ) where--import Prelude hiding ((<*>), (<*), (<$>), (<$), (*>))--import GLL.Combinators.Options-import GLL.Combinators.Memoisation-import GLL.Types.Abstract-import GLL.Types.Grammar hiding (epsilon)-import GLL.Parser (gllSPPF,ParseResult(..))--import Control.Monad-import qualified Data.Array as A-import qualified Data.Map as M-import Data.IORef-import qualified Data.IntMap as IM-import qualified Data.Set as S--type Visit1 = Symbol -type Visit2 = M.Map Nt [Alt] -> M.Map Nt [Alt]-type Visit3 a = PCOptions -> A.Array Int Token -> ParseContext -> SPPF - -> Int -> Int -> Int -> IO (S.Set a)--type Parser a = (Visit1, Visit2, Visit3 a)--type ParseContext = IM.IntMap (IM.IntMap Nt)---- | Given a parser and a string of tokens, return:--- * The grammar (GLL.Types.Abstract)--- * a list of results, which are all semantic evaluations of 'good derivations'--- - semantic evaluations are specified by using <$> and satisfy--- - 'good derivations' as defined by by Tom Ridge-parse' :: PCOptions -> Parser a -> [Token] -> (Grammar, ParseResult, IO [a])-parse' opts (Nt start,rules,sem) str = - let cfg = Grammar start [] [ Rule x alts - | (x, alts) <- M.assocs (rules M.empty) ]- parse_r = gllSPPF cfg str- sppf = sppf_result parse_r- as = sem opts arr IM.empty sppf 0 m m- m = length str- arr = A.array (0,m) (zip [0..] str)- in (cfg,parse_r,as >>= return . S.toList)---- | The grammar of a given parser-grammar :: Parser a -> Grammar-grammar p = (\(f,_,_) -> f) (parse' defaultOptions p [])---- | The semantic results of a parser, given a token string-parse :: Parser a -> [Token] -> IO [a]-parse = parseWithOptions defaultOptions ---- | The semantic results of a parser, given a token string --- and GLL.Combinator.Options-parseWithOptions :: PCOptions -> Parser a -> [Token] -> IO [a]-parseWithOptions opts p str = (\(_,_,t) -> t) (parse' opts p str)---- | Get the SPPF produced by parsing the given input with the given parser-sppf :: Parser a -> [Token] -> ParseResult-sppf p str = (\(_,s,_) -> s) (parse' defaultOptions p str)---- | Parse a given string of characters -parseString :: Parser a -> [Char] -> IO [a]-parseString p = parse p . charS---- | Parse a given string of characters and options -parseStringWithOptions :: PCOptions -> Parser a -> [Char] -> IO [a]-parseStringWithOptions opts p = parseWithOptions opts p . charS--infixl 3 <::=>-(<::=>) :: String -> Parser a -> Parser a-x <::=> _r = let (sym,_r_rules,_r_sem) = _r- alt = Alt x [sym] -- TODO indirection (extra alt)- rules m = case M.lookup x m of- Nothing -> _r_rules (M.insert x [alt] m)- Just _ -> m-- sem opts arr ctx sppf l r m- | (l,r,x) `inContext` ctx = return S.empty- | otherwise = let ctx' = (l,r,x) `toContext` ctx- in _r_sem opts arr ctx' sppf l r m- in (Nt x,rules,sem)---- | useful for non-recursive definitions (only internally)-infixl 3 <:=>-(<:=>) :: String -> Parser a -> Parser a-x <:=> _r = let (sym,_r_rules,_r_sem) = _r- alt = Alt x [sym] -- TODO indirection (extra alt)- rules m = case M.lookup x m of- Nothing -> _r_rules (M.insert x [alt] m)- Just _ -> m- in (Nt x,rules,_r_sem)--infixl 5 <$>--- | Application of a semantic action. -(<$>) :: (Ord b, Ord a) => (a -> b) -> Parser a -> Parser b-f <$> _r = let (sym,rules,_r_sem) = _r- sem opts arr ctx sppf l r m = - do as <- _r_sem opts arr ctx sppf l r m- return (S.map f as)- in (sym,rules,sem)--infixl 6 <*>--- | Sequence two parsers, the results of the two parsers are tupled.-(<*>) :: (Ord a, Ord b) => Parser a -> Parser b -> Parser (a,b)-_l <*> _r = (Nt lhs_id,rules,sem)- where l_id = id_ _l- r_id = id_ _r- lhs_id = concat [l_id, "*", r_id]- - -- ** one can bind this parser and recurse on it + other duplicate work- alt = Alt lhs_id [sym_ _l, sym_ _r]- rules m = case M.lookup lhs_id m of -- necessary? **- Nothing -> rules_ _r (rules_ _l (M.insert lhs_id [alt] m))- Just _ -> m- - sem opts arr ctx sppf l r m = do ass <- forM (filter ks) seq- return (S.unions ass)- where ks = maybe [] id $ sppf `pNodeLookup` ((alt,2), l, r)- filter = maybe id id $ pivot_select opts- seq k = do as <- sem_ _l opts arr ctx sppf l k m- bs <- sem_ _r opts arr ctx sppf k r m- return $ S.fromList [ (a,b) | a <- S.toList as- , b <- S.toList bs ]--infixl 4 <|>--- | A choice between two parsers, results of the two are concatenated-(<|>) :: (Ord a) => Parser a -> Parser a -> Parser a-_l <|> _r = (Nt lhs_id,rules,sem)- where l_id = id_ _l- r_id = id_ _r- lhs_id = concat [l_id, "|", r_id]-- alts = [Alt lhs_id [sym_ _l], Alt lhs_id [sym_ _r]]- rules m = case M.lookup lhs_id m of- Nothing -> rules_ _r (rules_ _l (M.insert lhs_id alts m))- Just _ -> m-- sem opts arr ctx sppf l r m = - do as1 <- sem_ _l opts arr ctx sppf l r m- as2 <- sem_ _r opts arr ctx sppf l r m - return (concatChoice opts as1 as2)---- | Use this function on a parser to memoise the semantic phase of the parser--- It is advised to only use 'memo' on a parser whose symbol occurs many times--- in a highly ambiguous grammar--- Every symbol on which 'memo' is used should have its own table.-memo :: MemoRef (S.Set a) -> Parser a -> Parser a-memo ref (sym@(Nt x),rules,sem) = (sym, rules, lhs_sem)- where lhs_sem opts arr ctx sppf l r m = do - tab <- readIORef ref- case memLookup (l,r) tab of- Just as -> return as- Nothing -> do as <- sem opts arr ctx sppf l r m- modifyIORef ref (memInsert (l,r) as)- return as--- derived combinators-infixl 6 <*--- | Sequencing, ignoring the result to the right-(<*) :: (Ord a, Ord b) => Parser a -> Parser b -> Parser a-_l <* _r = (\(x,y) -> x) <$> _l <*> _r--infixl 6 *>--- | Sequencing, ignoring the result to the left -(*>) :: (Ord a, Ord b) => Parser a -> Parser b -> Parser b-_l *> _r = (\(x,y) -> y) <$> _l <*> _r--infixl 5 <$--- | Ignore all results and just return the given value-(<$) :: (Ord a, Ord b) => a -> Parser b -> Parser a-f <$ _r = const f <$> _r ---- elementary parsers-raw_parser :: String -> Token -> (Token -> a) -> Parser a-raw_parser str t f = (Nt str, rules, sem)- where alt = Alt str [Term t]- rules = M.insert str [alt] - sem _ arr ctx sppf l r m - | l + 1 == r && l < m && arr A.! l == t - = return $ S.singleton (f t)- | otherwise = return $ S.empty---- | A parser that recognises a given character-char :: Char -> Parser Char-char c = raw_parser ([c]) (Char c) (\(Char c) -> c)---- | A parser that recognises a given token-token :: Token -> Parser Token-token t = raw_parser (show t) t id---- | A parser that always succeeds (and returns unit)-epsilon :: Parser ()-epsilon = (Nt x, rules, sem)- where x = "__eps"- alt = Alt x [Term Epsilon]- rules = M.insert x [alt]- sem _ arr ctx sppf l r m | l == r = return $ S.singleton ()- | otherwise = return $ S.empty---- | A parser that always succeeds and returns a given value-satisfy :: (Ord a) => a -> Parser a-satisfy a = a <$ epsilon---- helpers-sym_ :: Parser a -> Symbol-sym_ (f,_,_) = f--id_ :: Parser a -> Nt -id_ (Nt x,_,_) = x--rules_ :: Parser a -> Visit2-rules_ (_,f,_) = f--sem_ :: Parser a -> Visit3 a-sem_ (_,_,f) = f--mkNt :: String -> Char -> Nt-mkNt x c = concat ["(",x,")",[c]]--inContext :: (Int, Int, Nt) -> ParseContext -> Bool-inContext (l,r,x) = maybe False inner . IM.lookup l - where inner = maybe False ((==) x) . IM.lookup r--toContext :: (Int, Int, Nt) -> ParseContext -> ParseContext-toContext (l,r,x) = IM.insertWith IM.union l (IM.singleton r x)--concatChoice :: (Ord a) => PCOptions -> S.Set a -> S.Set a -> S.Set a-concatChoice opts ls rs = if left_biased_choice opts- then firstRes- else ls `S.union` rs- where firstRes | S.null ls = rs- | otherwise = ls---- higher level patterns---- | Optionally use the given parser-optional :: (Ord a) => Parser a -> Parser (Maybe a)-optional p@(Nt x,_,_) = (mkNt x '?') <:=> satisfy Nothing <|> Just <$> p---- | Apply the given parser many times, 0 or more times (Kleene closure)-many :: (Ord a) => Parser a -> Parser [a]-many p@(Nt x,_,_) = (mkNt x '^') <::=> satisfy [] - <|> uncurry (:) <$> p <*> many p---- | Apply the given parser some times, 1 or more times (positive closure)-some :: (Ord a) => Parser a -> Parser [a]-some p@(Nt x,_,_) = let rec = (mkNt x '+') <::=> (:[]) <$> p- <|> uncurry (:) <$> p <*> rec- in rec-
− src/GLL/Combinators/MemInterface.hs
@@ -1,309 +0,0 @@-{-# LANGUAGE TypeOperators, FlexibleInstances #-}--module GLL.Combinators.MemInterface (- SymbParser, IMParser,- HasAlts(..), IsSymbParser(..), IsIMParser(..),- parse, parseString,- char, token, Token(..),- epsilon, satisfy,- many, some, optional,- (<::=>),(<:=>),- (<$>),- (<$),- (<*>),- (<*),- (<|>),- (:.),- memo, newMemoTable, MemoRef, MemoTable- ) where--import Prelude hiding ((<*>), (<*), (<$>), (<$))--import GLL.Combinators.Options-import GLL.Combinators.Memoisation-import GLL.Types.Grammar hiding (epsilon)-import GLL.Types.Abstract-import GLL.Parser (gllSPPF, pNodeLookup, ParseResult(..))--import Control.Compose-import Control.Monad-import Data.List (unfoldr,intersperse)-import Data.IORef-import qualified Data.IntMap as IM-import qualified Data.Map as M-import qualified Data.Set as S--type SymbVisit1 b = Symbol -type SymbVisit2 b = M.Map Nt [Alt] -> M.Map Nt [Alt]-type SymbVisit3 b = PCOptions -> ParseContext -> SPPF -> Int -> Int -> IO [b]--type IMVisit1 b = [Symbol] -type IMVisit2 b = M.Map Nt [Alt] -> M.Map Nt [Alt]-type IMVisit3 b = PCOptions -> (Alt,Int) -> ParseContext -> SPPF -> Int -> Int -> IO [b]--type ParseContext = IM.IntMap (IM.IntMap (S.Set Nt))--data SymbParser b = SymbParser (SymbVisit1 b,SymbVisit2 b, SymbVisit3 b)-data IMParser b = IMParser (IMVisit1 b, IMVisit2 b, IMVisit3 b)--parse' :: (IsSymbParser s) => PCOptions -> s a -> [Token] -> (Grammar, ParseResult, IO [a])-parse' opts p' input' = - let input = input' ++ [Char 'z']- SymbParser (Nt start,vpa2,vpa3) = toSymb (id <$> p' <* char 'z')- snode = (start, 0, m)- m = length input- rules = vpa2 M.empty- as = vpa3 opts IM.empty sppf 0 m- grammar = Grammar start [] [ Rule x alts | (x, alts) <- M.assocs rules ]- parse_res = gllSPPF grammar input- sppf = sppf_result parse_res- in (grammar, parse_res, as)---- | The grammar of a given parser-grammar :: (IsSymbParser s) => s a -> Grammar-grammar p = (\(f,_,_) -> f) (parse' defaultOptions p [])---- | The semantic results of a parser, given a string of Tokens-parse :: (IsSymbParser s) => s a -> [Token] -> IO [a]-parse = parseWithOptions defaultOptions ---- | Change the behaviour of the parse using GLL.Combinators.Options-parseWithOptions :: (IsSymbParser s) => PCOptions -> s a -> [Token] -> IO [a]-parseWithOptions opts p = (\(_,_,t) -> t) . parse' opts p---- | Parse a string of characters-parseString :: (IsSymbParser s) => s a -> String -> IO [a]-parseString = parseStringWithOptions defaultOptions---- | Parse a string of characters using options-parseStringWithOptions :: (IsSymbParser s) => PCOptions -> s a -> String -> IO [a]-parseStringWithOptions opts p = parseWithOptions opts p . map Char---- | Get the SPPF produced by parsing the given input with the given parser-sppf :: (IsSymbParser s) => s a -> [Token] -> ParseResult-sppf p str = (\(_,s,_) -> s) $ parse' defaultOptions p str--inParseContext :: ParseContext -> (Symbol, Int, Int) -> Bool-inParseContext ctx (Nt x, l, r) = maybe False inner $ IM.lookup l ctx- where inner = maybe False (S.member x) . IM.lookup r--toParseContext :: ParseContext -> (Nt, Int, Int) -> ParseContext-toParseContext ctx (x, l, r) = IM.alter inner l ctx- where inner mm = case mm of - Nothing -> Just $ singleRX- Just m -> Just $ IM.insertWith (S.union) r singleX m- singleRX = IM.singleton r singleX- singleX = S.singleton x--infixl 2 <::=>--- | Use this combinator on all combinators that might have an infinite--- number of derivations for some input string. A non-terminal has--- this property if and only if it is left-recursive and would be--- left-recursive if all the right-hand sides of the productions of the--- grammar are reversed.-(<::=>) :: (HasAlts b) => String -> b a -> SymbParser a -x <::=> altPs' =- let vas1 = [ va1 | va1 <- map (\(IMParser (f,_,_)) -> f) altPs ]- alts = map (Alt x) vas1 - altPs = unO $ altsOf altPs' in SymbParser- (Nt x- ,\rules ->- if x `M.member` rules - then rules - else foldr ($) (M.insert x alts rules) $ (map (\(IMParser (_,s,_)) -> s) altPs)- ,\opts ctx sppf l r -> - let ctx' = ctx `toParseContext` (x,l,r)- sems = zip alts (map (\(IMParser (_,_,t)) -> t) altPs) - seq (alt@(Alt _ rhs), va3) = va3 opts (alt,length rhs) ctx' sppf l r - in if ctx `inParseContext` (Nt x, l, r) - then return []- else do ass <- forM sems seq- return (concatChoice opts ass)- )--infixl 2 <:=>--- | Use this combinator on all recursive non-terminals-(<:=>) :: (HasAlts b) => String -> b a -> SymbParser a -x <:=> altPs' =- let vas1 = [ va1 | va1 <- map (\(IMParser (f,_,_)) -> f) altPs ]- alts = map (Alt x) vas1 - altPs = unO $ altsOf altPs' in SymbParser- (Nt x- ,\rules ->- if x `M.member` rules - then rules - else foldr ($) (M.insert x alts rules) $ (map (\(IMParser (_,s,_)) -> s) altPs)- ,\opts ctx sppf l r -> - let sems = zip alts (map (\(IMParser (_,_,t)) -> t) altPs)- seq (alt@(Alt _ rhs), va3) = va3 opts (alt,length rhs) ctx sppf l r - in do ass <- forM sems seq- return (concatChoice opts ass)- )--concatChoice :: PCOptions -> [[a]] -> [a]-concatChoice opts ress = if left_biased_choice opts- then firstRes ress- else concat ress- where firstRes [] = []- firstRes ([]:ress) = firstRes ress- firstRes (res:_) = res--infixl 4 <*>-(<*>) :: (IsIMParser i, IsSymbParser s) => i (a -> b) -> s a -> IMParser b-pl' <*> pr' = - let IMParser (vimp1,vimp2,vimp3) = toImp pl'- SymbParser (vpa1,vpa2,vpa3) = toSymb pr' in IMParser- (vimp1++[vpa1]- ,\rules -> let rules1 = vpa2 rules- rules2 = vimp2 rules1 in rules2- ,\opts (alt@(Alt x rhs),j) ctx sppf l r ->- let ks = maybe [] id $ sppf `pNodeLookup` ((alt,j), l, r)- filter = maybe id id $ pivot_select opts- seq k = do as <- vpa3 opts ctx sppf k r- a2bs <- vimp3 opts(alt,j-1) ctx sppf l k- return [ a2b a | a2b <- a2bs, a <- as ]- in do ass <- forM (filter ks) seq- return (concat ass)- )---infixl 4 <*-(<*) :: (IsIMParser i, IsSymbParser s) => i b -> s a -> IMParser b--pl' <* pr' = - let IMParser (vimp1,vimp2,vimp3) = toImp pl'- SymbParser (vpa1,vpa2,vpa3) = toSymb pr' in IMParser- (vimp1++[vpa1]- ,\rules ->- let rules1 = vpa2 rules- rules2 = vimp2 rules1- in rules2- ,\opts (alt@(Alt x rhs),j) ctx sppf l r ->- let ks = maybe [] id $ sppf `pNodeLookup` ((alt,j), l, r)- filter = maybe id id $ pivot_select opts- seq k = do as <- vpa3 opts ctx sppf k r- bs <- vimp3 opts (alt,j-1) ctx sppf l k- return [ b | b <- bs, a <- as ]- in do ass <- forM (filter ks) seq- return (concat ass)- )--infixl 4 <$>-(<$>) :: (IsSymbParser s) => (a -> b) -> s a -> IMParser b-f <$> p' = - let SymbParser (vpa1,vpa2,vpa3) = toSymb p' in IMParser- ([vpa1]- ,\rules -> - vpa2 rules- ,\opts (alt,j) ctx sppf l r ->- let a = vpa3 opts ctx sppf l r- ks = maybe [] id $ sppf `pNodeLookup` ((alt,1),l,r)- in if null ks then return [] else do res <- a- return (map f res)- )--infixl 4 <$-(<$) :: (IsSymbParser s) => b -> s a -> IMParser b-f <$ p' = - let SymbParser (vpa1,vpa2,vpa3) = toSymb p' in IMParser - ([vpa1]- ,\rules -> - vpa2 rules- ,\opts (alt,j) ctx sppf l r ->- let a = vpa3 opts ctx sppf l r- ks = maybe [] id $ sppf `pNodeLookup` ((alt,1),l,r)- in if null ks then return [] else do res <- a- return (map (const f) res)- )--infixr 3 <|>-(<|>) :: (IsIMParser i, HasAlts b) => i a -> b a -> ([] :. IMParser) a-l' <|> r' = let l = toImp l'- r = altsOf r'- in O (l : unO r)--memo :: (IsSymbParser s) => MemoRef [a] -> s a -> SymbParser a-memo ref p' = let SymbParser (sym,rules,sem) = toSymb p' - lhs_sem opts ctx sppf l r = do- tab <- readIORef ref- case memLookup (l,r) tab of- Just as -> return as- Nothing -> do as <- sem opts ctx sppf l r- modifyIORef ref (memInsert (l,r) as)- return as- in SymbParser (sym, rules, lhs_sem)--raw_parser :: Token -> (Token -> a) -> SymbParser a -raw_parser t f = SymbParser (Term t, id,\_ _ _ _ _ -> return [f t])--token :: Token -> SymbParser Token-token t = raw_parser t id --char :: Char -> SymbParser Char-char c = raw_parser (Char c) (\(Char c) -> c) --epsilon :: SymbParser ()-epsilon = raw_parser (Epsilon) (\_ -> ()) --satisfy :: a -> IMParser a-satisfy a = a <$ epsilon--many :: SymbParser a -> SymbParser [a]-many p = SymbParser f- where SymbParser (myx,_,_) = p- SymbParser f = many_ ("(" ++ show myx ++ ")^") p--many_ x p = x <:=> (:) <$> p <*> many_ x p <|> [] <$ epsilon--some :: SymbParser a -> SymbParser [a]-some p = SymbParser f- where SymbParser (myx,_, _) = p- SymbParser f = some_ ("(" ++ show myx ++ ")+") p --some_ x p = x <:=> (:) <$> p <*> some_ x p <|> (:[]) <$> p--optional :: SymbParser a -> SymbParser (Maybe a)-optional p = SymbParser f- where SymbParser (myx, _, _) = p - SymbParser f = optional_ ("(" ++ show myx ++ ")?") p--optional_ x p = x <:=> Just <$> p <|> (Nothing <$ epsilon)--class HasAlts a where- altsOf :: a b -> ([] :. IMParser) b--instance HasAlts IMParser where- altsOf = O . (:[])--instance HasAlts SymbParser where- altsOf = altsOf . toImp--instance HasAlts ([] :. IMParser) where- altsOf = id--class IsIMParser a where- toImp :: a b -> IMParser b--instance IsIMParser IMParser where- toImp = id--instance IsIMParser SymbParser where- toImp p = id <$> p--instance IsIMParser ([] :. IMParser) where- toImp = toImp . toSymb--class IsSymbParser a where- toSymb :: a b -> SymbParser b--instance IsSymbParser IMParser where- toSymb = toSymb . O . (:[]) --instance IsSymbParser SymbParser where- toSymb = id --instance IsSymbParser ([] :. IMParser) where- toSymb a = mkName <:=> a - where mkName = "_" ++ concat (intersperse "|" (map op (unO a)))- where op (IMParser (rhs,_,_)) = concat (intersperse "*" (map show rhs))-
src/GLL/Combinators/Memoisation.hs view
@@ -2,9 +2,14 @@ import Data.IORef import qualified Data.IntMap as IM+import System.IO.Unsafe --- | use <::=> to enforce using parse context (to handle left-recursion)+-- | +-- A 'MemoTable' maps left-extent /l/ to right-extent /r/ to some results /a/+-- indicating the the substring ranging from /l/ to /r/ is derived with parse result /a/. type MemoTable a = IM.IntMap (IM.IntMap a)++-- | An impure reference to a 'MemoTable'. type MemoRef a = IORef (MemoTable a) memLookup :: (Int, Int) -> MemoTable a -> Maybe a@@ -17,8 +22,13 @@ Nothing -> Just $ IM.singleton r as Just m -> Just $ IM.insert r as m --- create a new memo-table to use with the memo function-newMemoTable :: IO (MemoRef a)-newMemoTable = newIORef IM.empty+-- |+-- Clears the 'MemoTable' to which the given reference refers.+memClear :: MemoRef a -> IO ()+memClear ref = modifyIORef ref (const IM.empty) +-- | +-- Create a reference to a fresh 'MemoTable'.+newMemoTable :: MemoRef a+newMemoTable = unsafePerformIO $ newIORef IM.empty
src/GLL/Combinators/Options.hs view
@@ -1,26 +1,87 @@ module GLL.Combinators.Options where +import Data.Function (on)+ -- | Options datatype -- * left_biased_choice: see function leftBiased -- * pivot_select: provide a filtering function on `pivots' data PCOptions = PCOptions { left_biased_choice :: Bool- , pivot_select :: Maybe ([Int] -> [Int])+ , pivot_select :: Maybe (Int -> Int -> Ordering)+ , pivot_select_nt :: Bool+ , max_errors :: Int+ , throw_errors :: Bool+ , do_memo :: Bool } --- | The default options: no disambiguation+-- | A list of 'Option's for evaluating combinator expressions.+type Options = [Option]++-- | A single option.+type Option = PCOptions -> PCOptions++runOptions :: [Option] -> PCOptions+runOptions = foldr ($) defaultOptions++-- | The default options: no disambiguation. defaultOptions :: PCOptions-defaultOptions = PCOptions False Nothing+defaultOptions = PCOptions False Nothing False 3 False False --- | Perform a disambiguation similar to 'longest-match'-maximumPivot :: PCOptions -> PCOptions-maximumPivot opts = opts {pivot_select = Just op}- where op [] = []- op xs = (:[]) $ maximum xs+-- | Enables a 'longest-match' at production level.+maximumPivot :: Option+maximumPivot opts = opts {pivot_select = Just compare} --- | Make the <|> combinator left-biased such that it--- only returns results of the right child if the left--- child does not has any results.-leftBiased :: PCOptions-leftBiased = defaultOptions { left_biased_choice = True }+-- | Enables a 'shortest-match' at production level.+minimumPivot :: Option+minimumPivot opts = opts {pivot_select = Just (flip compare)} +-- | Enables 'longest-match' at non-terminal level. +maximumPivotAtNt :: Option+maximumPivotAtNt opts = opts {pivot_select_nt = True, pivot_select = Just compare}++-- | +-- Set the maximum number of errors shown in case of an unsuccessful parse.+maximumErrors :: Int -> Option+maximumErrors n flags = flags {max_errors = n}++-- | +-- If there are no parse results, the default behaviour is to return an empty list.+-- If this option is used, a runtime error will be reported, with debugging information.+throwErrors :: Option+throwErrors opts = opts{throw_errors = True}+++-- | +-- Turns all occurrences of '<||>' into a 'left biased' variant:+-- only return results of the second alternate if the first alternate+-- does not have any results.+leftBiased :: Option+leftBiased opts = opts { left_biased_choice = True }++-- | +-- Whether to use unsafe memoisation to speed up the enumeration of parse results+useMemoisation :: Option+useMemoisation opts = opts { do_memo = True }++-- | Filter a list such that the only remaining elements are equal to+-- the maximum element, given an ordering operator.+maximumsWith :: (a -> a -> Ordering) -> [a] -> [a]+maximumsWith compare xs = + case xs of+ [] -> []+ [x] -> [x]+ x:xs -> maxx xs x []+ where maxx [] x acc = x : acc+ maxx (y:ys) x acc = case y `compare` x of+ LT -> maxx ys x acc+ GT -> maxx ys y []+ EQ -> maxx ys y (x:acc)++-- assumes every sub-list contains only maximums already+maintainWith :: (Eq k) => (k -> k -> Ordering) -> [[(k,a)]] -> [[(k,a)]]+maintainWith compare = + maintain .+ filter (not . null)+ where maintain xss = + let (max,_):_ = maximumsWith (compare `on` fst) $ map head xss+ in (filter ((== max) . fst . head) xss)
− src/GLL/Combinators/Test/BinInterface.hs
@@ -1,187 +0,0 @@-{-| This model contains unit-tests for 'GLL.Combinators.BinInterface'--= Included examples-- * Elementary parsers- * Sequencing- * Alternatives- * Simple binding- * Binding with alternatives- * Recursion (non-left)-- * Higher-order patterns:-- * Optional- * Kleene-closure / positive closure- * Seperator- * Inline choice-- * Ambiguities:-- * "aaa"- * longambig- * aho_s- * EEE-- * Left recursion- * Hidden left-recursion--}-module GLL.Combinators.Test.BinInterface where--import Prelude hiding ((<*>), (<*), (<$>), (<$), (*>))--import Control.Compose-import Control.Monad-import Data.Char (ord)-import Data.List (sort)-import Data.IORef-import qualified Data.Map as M--import GLL.Combinators.BinInterface---- | Defines and executes some unit-tests -main = do- count <- newIORef 1- let test name p arg_pairs = do- i <- readIORef count- modifyIORef count succ- subcount <- newIORef 'a'- putStrLn (">> testing " ++ show i ++ " (" ++ name ++ ")")- forM_ arg_pairs $ \(str,res) -> do- j <- readIORef subcount- modifyIORef subcount succ- let parse_res = parseString p str- norm = sort . take 100- b = norm parse_res == norm res- putStrLn (" >> " ++ [j,')',' '] ++ show b)- unless b (putStrLn (" >> " ++ show parse_res))-- -- Elementary parsers- test "eps1" (satisfy 0) [("", [0])]- test "eps2" (0 <$ epsilon) [("", [0]), ("111", [])]- test "single" (char 'a') [("a", ['a'])- ,("abc", [])]- test "semfun1" (1 <$ char 'a') [("a", [1])]-- -- Elementary combinators- test "<*>" ((\b -> ['1',b]) <$> char 'a' *> char 'b')- [("ab", ["1b"])- ,("b", [])]- - -- Alternation- test "<|>" (ord <$> char 'a' *> char 'b' <|> ord <$> char 'c')- [("a", []), ("ab", [98]), ("c", [99]), ("cab", [])]-- -- Simple binding- let pX = "X" <::=> ord <$> char 'a' <* char 'b'- test "<::=>" pX [("ab",[97]),("a",[])]-- let pX = "X" <::=> uncurry (flip (:)) <$> pY <*> char 'a'- pY = "Y" <::=> uncurry (\x y -> [x,y]) <$> char 'b' <*> char 'c'- test "<::=> 2" pX [("bca", ["abc"]), ("cba", [])]-- -- Binding with alternatives- let pX = "X" <::=> pY <* char 'c'- pY = "Y" <::=> char 'a' <|> char 'b'- test "<::=> <|>" pX [("ac", "a"), ("bc", "b")]-- -- (Right) Recursion- let pX = "X" <::=> (+1) <$> char 'a' *> pX <|> 0 <$ epsilon- test "rec1" pX [("", [0]), ("aa",[2]), (replicate 42 'a', [42]), ("bbb", [])]-- -- EBNF- let pX = "X" <::=> id <$> char 'a' *> char 'b' *> optional (char 'z')- test "optional" pX [("abz", [Just 'z']), ("abab", []), ("ab", [Nothing])]-- let pX = "X" <::=> (char 'a' <|> char 'b')- test "<|> optional" (pX <* optional (char 'z'))- [("az", "a"), ("bz", "b"), ("z", []), ("b", "b"), ("a", "a")]-- let pX = "X" <::=> (1 <$ optional (char 'a') <|> 2 <$ optional (char 'b'))- test "optional-ambig" (pX <* optional (char 'z'))- [("az", [1]), ("bz", [2]), ("z", [1,2]), ("b", [2]), ("a", [1])]-- let pX = "X" <::=> id <$> char 'a' *> (char 'b' <|> char 'c')- test "inline choice (1)" pX- [("ab", "b"), ("ac", "c"), ("a", []), ("b", [])]-- let pX = "X" <::=> length <$> many (char '1')- test "many" pX [("", [0]), ("11", [2]), (replicate 12 '1', [12])]-- let pX = "X" <::=> length <$> some (char '1')- test "some" pX [("", []), ("11", [2]), (replicate 12 '1', [12])]-- let pX = "X" <::=> 1 <$ many (char 'a') <|> 2 <$ many (char 'b')- test "(many <|> many) <*> optional" (pX <* optional (char 'z'))- [("az", [1]), ("bz", [2]), ("z", [1,2])- ,("", [1,2]), ("b", [2]), ("a", [1])]-- let pX = "X" <::=> pY <* optional (char 'z')- where pY = "Y" <::=> length <$> many (char 'a')- <|> length <$> some (char 'b') <* char 'e'- test "many & some & optional" - pX [("aaaz", [3]), ("bbbez", [3]), ("ez", []), ("z", [0])- ,("aa", [2]), ("bbe", [2]) - ]-- -- Simple ambiguities- let pX = uncurry (++) <$> pA <*> pB- pA = "a" <$ char 'a' <|> "aa" <$ char 'a' <* char 'a'- pB = "b" <$ char 'a' <|> "bb" <$ char 'a' <* char 'a' - test "aaa" pX [("aaa", ["aab", "abb"])- ,("aa", ["ab"])]-- let pX = (\(x,y) -> [x,y]) <$> char 'a' *> pL <*> pL <* char 'e'- pL = 1 <$ char 'b'- <|> 2 <$ char 'b' <* char 'c'- <|> 3 <$ char 'c' <* char 'd'- <|> 4 <$ char 'd'- test "longambig" pX [("abcde", [[1,3],[2,4]]), ("abcdd", [])]-- let pX = "X" <::=> (1 <$ some (char 'a') <|> 2 <$ many (char 'b'))- pY = "Y" <::=> uncurry (+) <$> pX <*> pY- <|> satisfy 0- test "some & many & recursion + ambiguities" pY- [("ab", [3]),("aa", [1,2]), (replicate 10 'a', [1..10])]-- let pX = "X" <::=> 1 <$ char 'a' <|> satisfy 0- pY = "Y" <::=> uncurry (+) <$> pX <*> pY- -- shouldn't this be 1 + infinite 0's?- test "no parse infinite rec?" pY - [("a", [])]-- let pS = "S" <::=> ((\(x,y) -> x+y+1) <$> char '1' *> pS <*> pS) <|> satisfy 0 - test "aho_S" pS [("", [0]), ("1", [1]), (replicate 5 '1', [5])]--- let pS = "S" <::=> ((\(x,y) -> '1':x++y) <$> char '1' *> pS <*> pS) <|> satisfy "0"- test "aho_S" pS [("", ["0"]), ("1", ["100"]), ("11", ["10100", "11000"])- ,(replicate 5 '1', aho_S_5)]-- let pE = "E" <::=> (\((x,y),z) -> x+y+z) <$> pE <*> pE <*> pE - <|> 1 <$ char '1'- <|> satisfy 0- test "EEE" pE [("", [0]), ("1", [1]), ("11", [2])- ,(replicate 5 '1', [5]), ("112", [])]-- let pE = "E" <::=> (\((x,y),z) -> x++y++z) <$> pE <*> pE <*> pE - <|> "1" <$ char '1'- <|> satisfy "0"- test "EEE ambig" pE [("", ["0"]), ("1", ["1"])- ,("11", ["110", "011", "101"]), ("111", _EEE_3)]-- let pX = "X" <::=> maybe 0 (const 1) <$> optional (char 'z') - <|> (+1) <$> pX <* char '1'- test "simple left-recursion" pX [("", [0]), ("z11", [3]), ("z", [1])- ,(replicate 100 '1', [100])]-- let pX = "X" <::=> satisfy 0 - <|> (+1) <$> pB *> pX <* char '1'- pB = maybe 0 (const 0) <$> optional (char 'z')- test "hidden left-recursion" pX - [("", [0]), ("zz11", [2]), ("z11", [2]), ("11", [2])- ,(replicate 100 '1', [100])]- where- aho_S_5 = ["10101010100","10101011000","10101100100","10101101000","10101110000","10110010100","10110011000","10110100100","10110101000","10110110000","10111000100","10111001000","10111010000","10111100000","11001010100","11001011000","11001100100","11001101000","11001110000","11010010100","11010011000","11010100100","11010101000","11010110000","11011000100","11011001000","11011010000","11011100000","11100010100","11100011000","11100100100","11100101000","11100110000","11101000100","11101001000","11101010000","11101100000","11110000100","11110001000","11110010000","11110100000","11111000000"]-- _EEE_3 = ["00111","01011","01101","01110","10011","10101","10110","11001","11010","111","11100"]
src/GLL/Combinators/Test/Interface.hs view
@@ -1,4 +1,4 @@-{-| This model contains unit-tests for 'GLL.Combinators.Interface'+{-| This model contains unit-tests for 'GLL.Combinators.Interface'. = Included examples @@ -28,29 +28,28 @@ -} module GLL.Combinators.Test.Interface where -import Prelude hiding ((<$>),(<*>),(<*),(<$))--import Control.Compose import Control.Monad import Data.Char (ord) import Data.List (sort, nub) import Data.IORef-import qualified Data.Map as M import GLL.Combinators.Interface -- | Defines and executes some unit-tests main = do count <- newIORef 1- let test name p arg_pairs = do+ let test mref name p arg_pairs = do i <- readIORef count modifyIORef count succ subcount <- newIORef 'a' putStrLn (">> testing " ++ show i ++ " (" ++ name ++ ")") forM_ arg_pairs $ \(str,res) -> do+ case mref of -- empty memtable between parses+ Nothing -> return ()+ Just ref -> memClear ref j <- readIORef subcount modifyIORef subcount succ- let parse_res = parseString p str+ let parse_res = parseWithOptions[useMemoisation] p str norm = take 100 . sort . nub norm_p_res = norm parse_res b = norm_p_res == norm res@@ -58,135 +57,182 @@ unless b (putStrLn (" >> " ++ show norm_p_res)) -- Elementary parsers- test "eps1" (satisfy 0) [("", [0])]- test "eps2" (0 <$ epsilon) [("", [0]), ("111", [])]- test "single" (char 'a') [("a", ['a'])+ test Nothing "eps1" (satisfy 0) [("", [0])]+ test Nothing "eps2" (satisfy 0) [("", [0]), ("111", [])]+ test Nothing "single" (char 'a') [("a", ['a']) ,("abc", [])]- test "semfun1" (1 <$ char 'a') [("a", [1])]+ test Nothing "semfun1" (1 <$$ char 'a') [("a", [1])] -- Elementary combinators- test "<*>" ((\b -> ['1',b]) <$ char 'a' <*> char 'b')+ test Nothing "<**>" ((\b -> ['1',b]) <$$ char 'a' <**> char 'b') [("ab", ["1b"]) ,("b", [])] -- Alternation- test "<|>" (ord <$ char 'a' <*> char 'b' <|> ord <$> char 'c')+ test Nothing "<||>" (ord <$$ char 'a' <**> char 'b' <||> ord <$$> char 'c') [("a", []), ("ab", [98]), ("c", [99]), ("cab", [])] -- Simple binding- let pX = "X" <:=> ord <$> char 'a' <* char 'b'- test "<:=>" pX [("ab",[97]),("a",[])]+ let pX = "X" <:=> ord <$$> char 'a' <** char 'b'+ test Nothing "<:=>" pX [("ab",[97]),("a",[])] -- Simple binding- let pX = "X" <::=> ord <$> char 'a' <* char 'b'- test "<::=>" pX [("ab",[97]),("a",[])]+ let pX = "X" <::=> ord <$$> char 'a' <** char 'b'+ test Nothing "<::=>" pX [("ab",[97]),("a",[])] - let pX = "X" <:=> flip (:) <$> pY <*> char 'a'- pY = "Y" <:=> (\x y -> [x,y]) <$> char 'b' <*> char 'c'- test "<::=> 2" pX [("bca", ["abc"]), ("cba", [])]+ let pX = "X" <:=> flip (:) <$$> pY <**> char 'a'+ pY = "Y" <:=> (\x y -> [x,y]) <$$> char 'b' <**> char 'c'+ test Nothing "<::=> 2" pX [("bca", ["abc"]), ("cba", [])] -- Binding with alternatives- let pX = "X" <::=> pY <* char 'c'- pY = "Y" <::=> char 'a' <|> char 'b'- test "<::=> <|>" pX [("ac", "a"), ("bc", "b")]+ let pX = "X" <::=> pY <** char 'c'+ pY = "Y" <::=> char 'a' <||> char 'b'+ test Nothing "<::=> <||>" pX [("ac", "a"), ("bc", "b")] -- (Right) Recursion- let pX = "X" <::=> (+1) <$ char 'a' <*> pX <|> 0 <$ epsilon- test "rec1" pX [("", [0]), ("aa",[2]), (replicate 42 'a', [42]), ("bbb", [])]+ let pX = "X" <::=> (+1) <$$ char 'a' <**> pX <||> satisfy 0 + test Nothing "rec1" pX [("", [0]), ("aa",[2]), (replicate 42 'a', [42]), ("bbb", [])] -- EBNF- let pX = "X" <::=> id <$ char 'a' <* char 'b' <*> optional (char 'z')- test "optional" pX [("abz", [Just 'z']), ("abab", []), ("ab", [Nothing])]+ let pX = "X" <::=> id <$$ char 'a' <** char 'b' <**> optional (char 'z')+ test Nothing "optional" pX [("abz", [Just 'z']), ("abab", []), ("ab", [Nothing])] - let pX = "X" <::=> (char 'a' <|> char 'b')- test "<|> optional" (pX <* optional (char 'z'))+ let pX = "X" <::=> (char 'a' <||> char 'b')+ test Nothing "<||> optional" (pX <** optional (char 'z')) [("az", "a"), ("bz", "b"), ("z", []), ("b", "b"), ("a", "a")] - let pX = "X" <::=> (1 <$ optional (char 'a') <|> 2 <$ optional (char 'b'))- test "optional-ambig" (pX <* optional (char 'z'))+ let pX = "X" <::=> (1 <$$ optional (char 'a') <||> 2 <$$ optional (char 'b'))+ test Nothing "optional-ambig" (pX <** optional (char 'z')) [("az", [1]), ("bz", [2]), ("z", [1,2]), ("b", [2]), ("a", [1])] - let pX = "X" <::=> id <$ char 'a' <*> (char 'b' <|> char 'c')- test "inline choice (1)" pX+ let pX = "X" <::=> id <$$ char 'a' <**> (char 'b' <||> char 'c')+ test Nothing "inline choice (1)" pX [("ab", "b"), ("ac", "c"), ("a", []), ("b", [])] - let pX = "X" <::=> length <$> many (char '1')- test "many" pX [("", [0]), ("11", [2]), (replicate 12 '1', [12])]+ let pX = "X" <::=> length <$$> many (char '1')+ test Nothing "many" pX [("", [0]), ("11", [2]), (replicate 12 '1', [12])] - let pX = "X" <::=> length <$> some (char '1')- test "some" pX [("", []), ("11", [2]), (replicate 12 '1', [12])]+ let pX = "X" <::=> length <$$> some (char '1')+ test Nothing "some" pX [("", []), ("11", [2]), (replicate 12 '1', [12])] - let pX = "X" <::=> 1 <$ many (char 'a') <|> 2 <$ many (char 'b')- test "(many <|> many) <*> optional" (pX <* optional (char 'z'))+ let pX = "X" <::=> 1 <$$ many (char 'a') <||> 2 <$$ many (char 'b')+ test Nothing "(many <||> many) <**> optional" (pX <** optional (char 'z')) [("az", [1]), ("bz", [2]), ("z", [1,2]) ,("", [1,2]), ("b", [2]), ("a", [1])] - let pX = "X" <::=> pY <* optional (char 'z')- where pY = "Y" <::=> length <$> many (char 'a')- <|> length <$> some (char 'b') <* char 'e'- test "many & some & optional" + let pX = "X" <::=> pY <** optional (char 'z')+ where pY = "Y" <::=> length <$$> many (char 'a')+ <||> length <$$> some (char 'b') <** char 'e'+ test Nothing "many & some & optional" pX [("aaaz", [3]), ("bbbez", [3]), ("ez", []), ("z", [0]) ,("aa", [2]), ("bbe", [2]) ] + -- many with nullable argument+ let pX = 1 <$$ char '1' <||> satisfy 0+ test Nothing "many (nullable arg)" + (many pX) [("11", [[1,1]]), ("",[[]]), ("e", [])]+ -- Simple ambiguities- let pX = (++) <$> pA <*> pB- pA = "a" <$ char 'a' <|> "aa" <$ char 'a' <* char 'a'- pB = "b" <$ char 'a' <|> "bb" <$ char 'a' <* char 'a' - test "aaa" pX [("aaa", ["aab", "abb"])+ let pX = (++) <$$> pA <**> pB+ pA = "a" <$$ char 'a' <||> "aa" <$$ char 'a' <** char 'a'+ pB = "b" <$$ char 'a' <||> "bb" <$$ char 'a' <** char 'a' + test Nothing "aaa" pX [("aaa", ["aab", "abb"]) ,("aa", ["ab"])] - let pX = (\x y -> [x,y]) <$ char 'a' <*> pL <*> pL <* char 'e'- pL = 1 <$ char 'b'- <|> 2 <$ char 'b' <* char 'c'- <|> 3 <$ char 'c' <* char 'd'- <|> 4 <$ char 'd'- test "longambig" pX [("abcde", [[1,3],[2,4]]), ("abcdd", [])]+ let pX = (\x y -> [x,y]) <$$ char 'a' <**> pL <**> pL <** char 'e'+ pL = 1 <$$ char 'b'+ <||> 2 <$$ char 'b' <** char 'c'+ <||> 3 <$$ char 'c' <** char 'd'+ <||> 4 <$$ char 'd'+ test Nothing "longambig" pX [("abcde", [[1,3],[2,4]]), ("abcdd", [])] - let pX = "X" <::=> (1 <$ some (char 'a') <|> 2 <$ many (char 'b'))- pY = "Y" <::=> (+) <$> pX <*> pY- <|> satisfy 0- test "some & many & recursion + ambiguities" pY+ let pX = "X" <::=> (1 <$$ some (char 'a') <||> 2 <$$ many (char 'b'))+ pY = "Y" <::=> (+) <$$> pX <**> pY+ <||> satisfy 0+ test Nothing "some & many & recursion + ambiguities" pY [("ab", [3]),("aa", [1,2]), (replicate 10 'a', [1..10])] - let pX = "X" <::=> 1 <$ char 'a' <|> satisfy 0- pY = "Y" <::=> (+) <$> pX <*> pY+ let pX = "X" <::=> 1 <$$ char 'a' <||> satisfy 0+ pY = "Y" <::=> (+) <$$> pX <**> pY -- shouldn't this be 1 + infinite 0's?- test "no parse infinite rec?" pY + test Nothing "no parse infinite rec?" pY [("a", [])] - let pS = "S" <::=> ((\x y -> x+y+1) <$ char '1' <*> pS <*> pS) <|> satisfy 0 - test "aho_S" pS [("", [0]), ("1", [1]), (replicate 5 '1', [5])]+ let pS = "S" <::=> ((\x y -> x+y+1) <$$ char '1' <**> pS <**> pS) <||> satisfy 0 + test Nothing "aho_S" pS [("", [0]), ("1", [1]), (replicate 5 '1', [5])] - let pS = "S" <::=> ((\x y -> '1':x++y) <$ char '1' <*> pS <*> pS) <|> satisfy "0"- test "aho_S" pS [("", ["0"]), ("1", ["100"]), ("11", ["10100", "11000"])+ let pS = "S" <::=> ((\x y -> '1':x++y) <$$ char '1' <**> pS <**> pS) <||> satisfy "0"+ test Nothing "aho_S" pS [("", ["0"]), ("1", ["100"]), ("11", ["10100", "11000"]) ,(replicate 5 '1', aho_S_5)] - let pE = "E" <::=> (\x y z -> x+y+z) <$> pE <*> pE <*> pE - <|> 1 <$ char '1'- <|> satisfy 0- test "EEE" pE [("", [0]), ("1", [1]), ("11", [2])+ let pE = "E" <::=> (\x y z -> x+y+z) <$$> pE <**> pE <**> pE + <||> 1 <$$ char '1'+ <||> satisfy 0+ test Nothing "EEE" pE [("", [0]), ("1", [1]), ("11", [2]) ,(replicate 5 '1', [5]), ("112", [])] - let pE = "E" <::=> (\x y z -> x++y++z) <$> pE <*> pE <*> pE - <|> "1" <$ char '1'- <|> satisfy "0"- test "EEE ambig" pE [("", ["0"]), ("1", ["1"])+ let pE = "E" <::=> (\x y z -> x++y++z) <$$> pE <**> pE <**> pE + <||> "1" <$$ char '1'+ <||> satisfy "0"+ test Nothing "EEE ambig" pE [("", ["0"]), ("1", ["1"]) ,("11", ["110", "011", "101"]), ("111", _EEE_3)] - let pX = "X" <::=> maybe 0 (const 1) <$> optional (char 'z') - <|> (+1) <$> pX <* char '1'- test "simple left-recursion" pX [("", [0]), ("z11", [3]), ("z", [1])+ let pX = "X" <::=> maybe 0 (const 1) <$$> optional (char 'z') + <||> (+1) <$$> pX <** char '1'+ test Nothing "simple left-recursion" pX [("", [0]), ("z11", [3]), ("z", [1]) ,(replicate 100 '1', [100])] let pX = "X" <::=> satisfy 0 - <|> (+1) <$ pB <*> pX <* char '1'- pB = maybe 0 (const 0) <$> optional (char 'z')- test "hidden left-recursion" pX + <||> (+1) <$$ pB <**> pX <** char '1'+ pB = maybe 0 (const 0) <$$> optional (char 'z')+ test Nothing "hidden left-recursion" pX [("", [0]), ("zz11", [2]), ("z11", [2]), ("11", [2]) ,(replicate 100 '1', [100])]++ let pX = "X" <::=> (+) <$$> pY <**> pA+ pA = 1 <$$ char 'a' <** char 'b' <||> satisfy 0+ pY = "Y" <::=> satisfy 0 <||> pX + test Nothing "hidden left-recursion + infinite derivations" pX+ [("", [0]), ("ab", [1]), ("ababab", [3])]++ putStrLn "Tests that use memoisation"++ let tab = newMemoTable+ pX = "X" <::=> (1 <$$ some (char 'a') <||> 2 <$$ many (char 'b'))+ pY = memo tab ("Y" <::=> (+) <$$> pX <**> pY+ <||> satisfy 0)+ test (Just tab) "some & many & recursion + ambiguities" pY+ [("ab", [3]),("aa", [1,2]), (replicate 10 'a', [1..10])]++ let tab = newMemoTable + pX = "X" <::=> 1 <$$ char 'a' <||> satisfy 0+ pY = memo tab ("Y" <::=> (+) <$$> pX <**> pY)+ -- shouldn't this be 1 + infinite 0's?+ test (Just tab) "no parse infinite rec?" pY + [("a", [])]++ -- Higher ambiguities+ let tab = newMemoTable+ pE = memo tab ("E" <::=> (\x y z -> x+y+z) <$$> pE <**> pE <**> pE + <||> 1 <$$ char '1'+ <||> satisfy 0)+ test (Just tab) "EEE" pE [("", [0]), ("1", [1]), ("11", [2])+ ,(replicate 5 '1', [5]), ("112", [])]++ let tab = newMemoTable+ pX = "X" <::=> (+) <$$> pY <**> pA+ pA = 1 <$$ char 'a' <** char 'b' <||> satisfy 0+ pY = memo tab ("Y" <::=> satisfy 0 <||> pX)+ test (Just tab) "hidden left-recursion + infinite derivations" pX+ [("", [0]), ("ab", [1]), ("ababab", [3])]+ where aho_S_5 = ["10101010100","10101011000","10101100100","10101101000","10101110000","10110010100","10110011000","10110100100","10110101000","10110110000","10111000100","10111001000","10111010000","10111100000","11001010100","11001011000","11001100100","11001101000","11001110000","11010010100","11010011000","11010100100","11010101000","11010110000","11011000100","11011001000","11011010000","11011100000","11100010100","11100011000","11100100100","11100101000","11100110000","11101000100","11101001000","11101010000","11101100000","11110000100","11110001000","11110010000","11110100000","11111000000"] _EEE_3 = ["00111","01011","01101","01110","10011","10101","10110","11001","11010","111","11100"]++instance Parseable Char where+ eos = '$'+ eps = '#'
− src/GLL/Combinators/Test/MemBinInterface.hs
@@ -1,196 +0,0 @@-{-| This model contains unit-tests for 'GLL.Combinators.MemBinInterface'--= Included examples-- * Elementary parsers- * Sequencing- * Alternatives- * Simple binding- * Binding with alternatives- * Recursion (non-left)-- * Higher-order patterns:-- * Optional- * Kleene-closure / positive closure- * Seperator- * Inline choice-- * Ambiguities:-- * "aaa"- * longambig- * aho_s- * EEE-- * Left recursion- * Hidden left-recursion--}-module GLL.Combinators.Test.MemBinInterface where--import Prelude hiding ((<*>), (<*), (<$>), (<$), (*>))--import Control.Compose-import Control.Monad-import Data.Char (ord)-import Data.List (sort)-import Data.IORef-import qualified Data.Map as M-import qualified Data.IntMap as IM--import GLL.Combinators.MemBinInterface---- | Defines and executes some unit-tests -main = do- count <- newIORef 1- let test mref name p arg_pairs = do- i <- readIORef count- modifyIORef count succ- subcount <- newIORef 'a'- putStrLn (">> testing " ++ show i ++ " (" ++ name ++ ")")- forM_ arg_pairs $ \(str,res) -> do- case mref of -- empty memtable between parses- Nothing -> return ()- Just ref -> modifyIORef ref (const IM.empty)- j <- readIORef subcount- modifyIORef subcount succ- parse_res <- parseString p str- let norm = sort . take 100- b = norm parse_res == norm res- putStrLn (" >> " ++ [j,')',' '] ++ show b)- unless b (putStrLn (" >> " ++ show parse_res))-- -- Elementary parsers- test Nothing "eps1" (satisfy 0) [("", [0])]- test Nothing "eps2" (0 <$ epsilon) [("", [0]), ("111", [])]- test Nothing "single" (char 'a') [("a", ['a'])- ,("abc", [])]- test Nothing "semfun1" (1 <$ char 'a') [("a", [1])]-- -- Elementary combinators- test Nothing "<*>" ((\b -> ['1',b]) <$> char 'a' *> char 'b')- [("ab", ["1b"])- ,("b", [])]- - -- Alternation- test Nothing "<|>" (ord <$> char 'a' *> char 'b' <|> ord <$> char 'c')- [("a", []), ("ab", [98]), ("c", [99]), ("cab", [])]-- -- Simple binding- let pX = "X" <::=> ord <$> char 'a' <* char 'b'- test Nothing "<::=>" pX [("ab",[97]),("a",[])]-- let pX = "X" <::=> uncurry (flip (:)) <$> pY <*> char 'a'- pY = "Y" <::=> uncurry (\x y -> [x,y]) <$> char 'b' <*> char 'c'- test Nothing "<::=> 2" pX [("bca", ["abc"]), ("cba", [])]-- -- Binding with alternatives- let pX = "X" <::=> pY <* char 'c'- pY = "Y" <::=> char 'a' <|> char 'b'- test Nothing "<::=> <|>" pX [("ac", "a"), ("bc", "b")]-- -- (Right) Recursion- let pX = "X" <::=> (+1) <$> char 'a' *> pX <|> 0 <$ epsilon- test Nothing "rec1" pX [("", [0]), ("aa",[2]), (replicate 42 'a', [42]), ("bbb", [])]-- -- EBNF- let pX = "X" <::=> id <$> char 'a' *> char 'b' *> optional (char 'z')- test Nothing "optional" pX [("abz", [Just 'z']), ("abab", []), ("ab", [Nothing])]-- let pX = "X" <::=> (char 'a' <|> char 'b')- test Nothing "<|> optional" (pX <* optional (char 'z'))- [("az", "a"), ("bz", "b"), ("z", []), ("b", "b"), ("a", "a")]-- let pX = "X" <::=> (1 <$ optional (char 'a') <|> 2 <$ optional (char 'b'))- test Nothing "optional-ambig" (pX <* optional (char 'z'))- [("az", [1]), ("bz", [2]), ("z", [1,2]), ("b", [2]), ("a", [1])]-- let pX = "X" <::=> id <$> char 'a' *> (char 'b' <|> char 'c')- test Nothing "inline choice (1)" pX- [("ab", "b"), ("ac", "c"), ("a", []), ("b", [])]-- let pX = "X" <::=> length <$> many (char '1')- test Nothing "many" pX [("", [0]), ("11", [2]), (replicate 12 '1', [12])]-- let pX = "X" <::=> length <$> some (char '1')- test Nothing "some" pX [("", []), ("11", [2]), (replicate 12 '1', [12])]-- let pX = "X" <::=> (1 <$ many (char 'a') <|> 2 <$ many (char 'b'))- test Nothing "(many <|> many) <*> optional" (pX <* optional (char 'z'))- [("az", [1]), ("bz", [2]), ("z", [1,2])- ,("", [1,2]), ("b", [2]), ("a", [1])]-- let pX = "X" <::=> pY <* optional (char 'z')- where pY = "Y" <::=> length <$> many (char 'a')- <|> length <$> some (char 'b') <* char 'e'- test Nothing "many & some & optional" - pX [("aaaz", [3]), ("bbbez", [3]), ("ez", []), ("z", [0])- ,("aa", [2]), ("bbe", [2]) - ]-- -- Simple ambiguities- let pX = uncurry (++) <$> pA <*> pB- pA = "a" <$ char 'a' <|> "aa" <$ char 'a' <* char 'a'- pB = "b" <$ char 'a' <|> "bb" <$ char 'a' <* char 'a' - test Nothing "aaa" pX [("aaa", ["aab", "abb"])- ,("aa", ["ab"])]-- let pX = (\(x,y) -> [x,y]) <$> char 'a' *> pL <*> pL <* char 'e'- pL = 1 <$ char 'b'- <|> 2 <$ char 'b' <* char 'c'- <|> 3 <$ char 'c' <* char 'd'- <|> 4 <$ char 'd'- test Nothing "longambig" pX [("abcde", [[1,3],[2,4]]), ("abcdd", [])]-- tab1 <- newMemoTable- let pX = "X" <::=> (1 <$ some (char 'a') <|> 2 <$ many (char 'b'))- pY = memo tab1 ("Y" <::=> uncurry (+) <$> pX <*> pY- <|> satisfy 0)- test (Just tab1) "some & many & recursion + ambiguities" pY- [("ab", [3]),("aa", [1,2]), (replicate 10 'a', [1..10])]-- tab <- newMemoTable- let pX = "X" <::=> 1 <$ char 'a' <|> satisfy 0- pY = memo tab ("Y" <::=> uncurry (+) <$> pX <*> pY)- -- shouldn't this be 1 + infinite 0's?- test (Just tab) "no parse infinite rec?" pY - [("a", [])]-- -- Higher ambiguities- let pS = "S" <::=> ((\(x,y) -> x+y+1) <$> char '1' *> pS <*> pS) <|> satisfy 0 - test Nothing "aho_S" pS [("", [0]), ("1", [1]), (replicate 5 '1', [5])]--- let pS = "S" <::=> ((\(x,y) -> '1':x++y) <$> char '1' *> pS <*> pS) <|> satisfy "0"- test Nothing "aho_S" pS [("", ["0"]), ("1", ["100"]), ("11", ["10100", "11000"])- ,(replicate 5 '1', aho_S_5)]--- tab <- newMemoTable- let pE = memo tab ("E" <::=> (\((x,y),z) -> x+y+z) <$> pE <*> pE <*> pE - <|> 1 <$ char '1'- <|> satisfy 0)- test (Just tab) "EEE" pE [("", [0]), ("1", [1]), ("11", [2])- ,(replicate 5 '1', [5]), ("112", [])]-- let pE = "E" <::=> (\((x,y),z) -> x++y++z) <$> pE <*> pE <*> pE - <|> "1" <$ char '1'- <|> satisfy "0"- test Nothing "EEE ambig" pE [("", ["0"]), ("1", ["1"])- ,("11", ["110", "011", "101"]), ("111", _EEE_3)]-- let pX = "X" <::=> maybe 0 (const 1) <$> optional (char 'z') - <|> (+1) <$> pX <* char '1'- test Nothing "simple left-recursion" pX [("", [0]), ("z11", [3]), ("z", [1])- ,(replicate 100 '1', [100])]-- let pX = "X" <::=> satisfy 0 - <|> (+1) <$> pB *> pX <* char '1'- pB = maybe 0 (const 0) <$> optional (char 'z')- test Nothing "hidden left-recursion" pX - [("", [0]), ("zz11", [2]), ("z11", [2]), ("11", [2])- ,(replicate 100 '1', [100])]- where- aho_S_5 = ["10101010100","10101011000","10101100100","10101101000","10101110000","10110010100","10110011000","10110100100","10110101000","10110110000","10111000100","10111001000","10111010000","10111100000","11001010100","11001011000","11001100100","11001101000","11001110000","11010010100","11010011000","11010100100","11010101000","11010110000","11011000100","11011001000","11011010000","11011100000","11100010100","11100011000","11100100100","11100101000","11100110000","11101000100","11101001000","11101010000","11101100000","11110000100","11110001000","11110010000","11110100000","11111000000"]-- _EEE_3 = ["00111","01011","01101","01110","10011","10101","10110","11001","11010","111","11100"]
− src/GLL/Combinators/Test/MemInterface.hs
@@ -1,196 +0,0 @@-{-| This model contains unit-tests for 'GLL.Combinators.MemInterface'--= Included examples-- * Elementary parsers- * Sequencing- * Alternatives- * Simple binding- * Binding with alternatives- * Recursion (non-left)-- * Higher-order patterns:-- * Optional- * Kleene-closure / positive closure- * Seperator- * Inline choice-- * Ambiguities:-- * "aaa"- * longambig- * aho_s- * EEE-- * Left recursion- * Hidden left-recursion--}-module GLL.Combinators.Test.MemInterface where--import Prelude hiding ((<$>),(<*>),(<*),(<$))--import Control.Compose-import Control.Monad-import Data.Char (ord)-import Data.List (sort,nub)-import Data.IORef-import qualified Data.Map as M-import qualified Data.IntMap as IM--import GLL.Combinators.MemInterface---- | Defines and executes some unit-tests -main = do- count <- newIORef 1- let test mref name p arg_pairs = do- i <- readIORef count- modifyIORef count succ- subcount <- newIORef 'a'- putStrLn (">> testing " ++ show i ++ " (" ++ name ++ ")")- forM_ arg_pairs $ \(str,res) -> do- case mref of -- empty memtable between parses- Nothing -> return ()- Just ref -> modifyIORef ref (const IM.empty)- j <- readIORef subcount- modifyIORef subcount succ- parse_res <- parseString p str- let norm = take 100 . sort . nub- b = norm parse_res == norm res- putStrLn (" >> " ++ [j,')',' '] ++ show b)- unless b (putStrLn (" >> " ++ show parse_res))-- -- Elementary parsers- test Nothing "eps1" (satisfy 0) [("", [0])]- test Nothing "eps2" (0 <$ epsilon) [("", [0]), ("111", [])]- test Nothing "single" (char 'a') [("a", ['a'])- ,("abc", [])]- test Nothing "semfun1" (1 <$ char 'a') [("a", [1])]-- -- Elementary combinators- test Nothing "<*>" ((\b -> ['1',b]) <$ char 'a' <*> char 'b')- [("ab", ["1b"])- ,("b", [])]- - -- Alternation- test Nothing "<|>" (ord <$ char 'a' <*> char 'b' <|> ord <$> char 'c')- [("a", []), ("ab", [98]), ("c", [99]), ("cab", [])]-- -- Simple binding- let pX = "X" <::=> ord <$> char 'a' <* char 'b'- test Nothing "<::=>" pX [("ab",[97]),("a",[])]-- let pX = "X" <::=> (flip (:)) <$> pY <*> char 'a'- pY = "Y" <::=> (\x y -> [x,y]) <$> char 'b' <*> char 'c'- test Nothing "<::=> 2" pX [("bca", ["abc"]), ("cba", [])]-- -- Binding with alternatives- let pX = "X" <::=> pY <* char 'c'- pY = "Y" <::=> char 'a' <|> char 'b'- test Nothing "<::=> <|>" pX [("ac", "a"), ("bc", "b")]-- -- (Right) Recursion- let pX = "X" <::=> (+1) <$ char 'a' <*> pX <|> 0 <$ epsilon- test Nothing "rec1" pX [("", [0]), ("aa",[2]), (replicate 42 'a', [42]), ("bbb", [])]-- -- EBNF- let pX = "X" <::=> id <$ char 'a' <* char 'b' <*> optional (char 'z')- test Nothing "optional" pX [("abz", [Just 'z']), ("abab", []), ("ab", [Nothing])]-- let pX = "X" <::=> (char 'a' <|> char 'b')- test Nothing "<|> optional" (pX <* optional (char 'z'))- [("az", "a"), ("bz", "b"), ("z", []), ("b", "b"), ("a", "a")]-- let pX = "X" <::=> (1 <$ optional (char 'a') <|> 2 <$ optional (char 'b'))- test Nothing "optional-ambig" (pX <* optional (char 'z'))- [("az", [1]), ("bz", [2]), ("z", [1,2]), ("b", [2]), ("a", [1])]-- let pX = "X" <::=> id <$ char 'a' <*> (char 'b' <|> char 'c')- test Nothing "inline choice (1)" pX- [("ab", "b"), ("ac", "c"), ("a", []), ("b", [])]-- let pX = "X" <::=> length <$> many (char '1')- test Nothing "many" pX [("", [0]), ("11", [2]), (replicate 12 '1', [12])]-- let pX = "X" <::=> length <$> some (char '1')- test Nothing "some" pX [("", []), ("11", [2]), (replicate 12 '1', [12])]-- let pX = "X" <::=> (1 <$ many (char 'a') <|> 2 <$ many (char 'b'))- test Nothing "(many <|> many) <*> optional" (pX <* optional (char 'z'))- [("az", [1]), ("bz", [2]), ("z", [1,2])- ,("", [1,2]), ("b", [2]), ("a", [1])]-- let pX = "X" <::=> pY <* optional (char 'z')- where pY = "Y" <::=> length <$> many (char 'a')- <|> length <$> some (char 'b') <* char 'e'- test Nothing "many & some & optional" - pX [("aaaz", [3]), ("bbbez", [3]), ("ez", []), ("z", [0])- ,("aa", [2]), ("bbe", [2]) - ]-- -- Simple ambiguities- let pX = (++) <$> pA <*> pB- pA = "a" <$ char 'a' <|> "aa" <$ char 'a' <* char 'a'- pB = "b" <$ char 'a' <|> "bb" <$ char 'a' <* char 'a' - test Nothing "aaa" pX [("aaa", ["aab", "abb"])- ,("aa", ["ab"])]-- let pX = (\x y -> [x,y]) <$ char 'a' <*> pL <*> pL <* char 'e'- pL = 1 <$ char 'b'- <|> 2 <$ char 'b' <* char 'c'- <|> 3 <$ char 'c' <* char 'd'- <|> 4 <$ char 'd'- test Nothing "longambig" pX [("abcde", [[1,3],[2,4]]), ("abcdd", [])]-- tab1 <- newMemoTable- let pX = "X" <::=> (1 <$ some (char 'a') <|> 2 <$ many (char 'b'))- pY = memo tab1 ("Y" <::=> (+) <$> pX <*> pY- <|> satisfy 0)- test (Just tab1) "some & many & recursion + ambiguities" pY- [("ab", [3]),("aa", [1,2]), (replicate 10 'a', [1..10])]-- tab <- newMemoTable- let pX = "X" <::=> 1 <$ char 'a' <|> satisfy 0- pY = memo tab ("Y" <::=> (+) <$> pX <*> pY)- -- shouldn't this be 1 + infinite 0's?- test (Just tab) "no parse infinite rec?" pY - [("a", [])]-- -- Higher ambiguities- let pS = "S" <::=> ((\x y -> x+y+1) <$ char '1' <*> pS <*> pS) <|> satisfy 0 - test Nothing "aho_S" pS [("", [0]), ("1", [1]), (replicate 5 '1', [5])]--- let pS = "S" <::=> ((\x y -> '1':x++y) <$ char '1' <*> pS <*> pS) <|> satisfy "0"- test Nothing "aho_S" pS [("", ["0"]), ("1", ["100"]), ("11", ["10100", "11000"])- ,(replicate 5 '1', aho_S_5 )]--- tab <- newMemoTable- let pE = memo tab ("E" <::=> (\x y z -> x+y+z) <$> pE <*> pE <*> pE - <|> 1 <$ char '1'- <|> satisfy 0)- test (Just tab) "EEE" pE [("", [0]), ("1", [1]), ("11", [2])- ,(replicate 5 '1', [5]), ("112", [])]-- let pE = "E" <::=> (\x y z -> x++y++z) <$> pE <*> pE <*> pE - <|> "1" <$ char '1'- <|> satisfy "0"- test Nothing "EEE ambig" pE [("", ["0"]), ("1", ["1"])- ,("11", ["110", "011", "101"]), ("111", _EEE_3)]-- let pX = "X" <::=> maybe 0 (const 1) <$> optional (char 'z') - <|> (+1) <$> pX <* char '1'- test Nothing "simple left-recursion" pX [("", [0]), ("z11", [3]), ("z", [1])- ,(replicate 100 '1', [100])]-- let pX = "X" <::=> satisfy 0 - <|> (+1) <$ pB <*> pX <* char '1'- pB = maybe 0 (const 0) <$> optional (char 'z')- test Nothing "hidden left-recursion" pX - [("", [0]), ("zz11", [2]), ("z11", [2]), ("11", [2])- ,(replicate 100 '1', [100])]- where- aho_S_5 = ["10101010100","10101011000","10101100100","10101101000","10101110000","10110010100","10110011000","10110100100","10110101000","10110110000","10111000100","10111001000","10111010000","10111100000","11001010100","11001011000","11001100100","11001101000","11001110000","11010010100","11010011000","11010100100","11010101000","11010110000","11011000100","11011001000","11011010000","11011100000","11100010100","11100011000","11100100100","11100101000","11100110000","11101000100","11101001000","11101010000","11101100000","11110000100","11110001000","11110010000","11110100000","11111000000"]-- _EEE_3 = ["00111","01011","01101","01110","10011","10101","10110","11001","11010","111","11100"]
+ src/GLL/Combinators/Visit/Grammar.hs view
@@ -0,0 +1,22 @@++module GLL.Combinators.Visit.Grammar where++import GLL.Types.Abstract++import qualified Data.Map as M++type Grammar_Expr t = M.Map Nt [Prod t] -> M.Map Nt [Prod t]++grammar_nterm :: Nt -> [Prod t] -> [Grammar_Expr t] -> Grammar_Expr t+grammar_nterm x alts ps rules + | x `M.member` rules = rules+ | otherwise = foldr ($) (M.insert x alts rules) $ ps ++grammar_apply :: Grammar_Expr t -> Grammar_Expr t+grammar_apply = id++grammar_seq :: Grammar_Expr t -> Grammar_Expr t -> Grammar_Expr t+grammar_seq p q rules =+ let rules1 = q rules+ rules2 = p rules1 in rules2+
+ src/GLL/Combinators/Visit/Sem.hs view
@@ -0,0 +1,74 @@++module GLL.Combinators.Visit.Sem where++import GLL.Combinators.Options+import GLL.Types.Abstract+import GLL.Types.Grammar++import Control.Monad (forM)+import qualified Data.Array as A+import qualified Data.IntMap as IM+import qualified Data.Set as S++type Sem_Symb t a = PCOptions -> Ancestors t + -> SPPF t -> A.Array Int t -> Int -> Int -> IO [a]+type Sem_Alt t a = PCOptions -> (Prod t,Int) -> Ancestors t + -> SPPF t -> A.Array Int t -> Int -> Int -> IO [(Int,a)]++sem_nterm :: Bool -> Bool -> Nt -> [Prod t] -> [Sem_Alt t a] -> Sem_Symb t a+sem_nterm use_ctx left_biased x alts ps opts ctx sppf arr l r =+ let ctx' = ctx `toAncestors` (x,l,r)+ sems = zip alts ps + seq (alt@(Prod _ rhs), va3) = va3 opts (alt,length rhs) ctx' sppf arr l r + in if use_ctx && ctx `inAncestors` (Nt x, l, r) + then return []+ else do ass <- forM sems seq+ let choices = case (pivot_select_nt opts, pivot_select opts) of+ (True,Just compare) -> maintainWith compare ass+ _ -> ass+ return (concatChoice left_biased opts (map (map snd) choices))+ where+ concatChoice :: Bool -> PCOptions -> [[a]] -> [a]+ concatChoice left_biased opts ress = + if left_biased || left_biased_choice opts+ then firstRes ress+ else concat ress+ where firstRes [] = []+ firstRes ([]:ress) = firstRes ress+ firstRes (res:_) = res++sem_apply :: Ord t => (a -> b) -> Sem_Symb t a -> Sem_Alt t b+sem_apply f p opts (alt,j) ctx sppf arr l r = + let op f a = (r,f a)+ in do as <- p opts ctx sppf arr l r+ return (maybe [] (const (map (op f) as)) $ sppf `pNodeLookup` ((alt,1),l,r))++sem_seq :: Ord t => Sem_Alt t (a -> b) -> Sem_Symb t a -> Sem_Alt t b +sem_seq p q opts (alt@(Prod x rhs),j) ctx sppf arr l r = + let ks = maybe [] id $ sppf `pNodeLookup` ((alt,j), l, r)+ choices = case pivot_select opts of+ Nothing -> ks+ Just compare -> maximumsWith compare ks+ seq k = do as <- q opts ctx sppf arr k r+ a2bs <- p opts(alt,j-1) ctx sppf arr l k+ return [ (k,a2b a) | (_,a2b) <- a2bs, a <- as ]+ in do ass <- forM choices seq+ return (concat ass)++--- contexts+type Ancestors t = IM.IntMap (IM.IntMap (S.Set Nt))++inAncestors :: Ancestors t -> (Symbol t, Int, Int) -> Bool+inAncestors ctx (Term _, _, _) = False+inAncestors ctx (Nt x, l, r) = maybe False inner $ IM.lookup l ctx+ where inner = maybe False (S.member x) . IM.lookup r++toAncestors :: Ancestors t -> (Nt, Int, Int) -> Ancestors t+toAncestors ctx (x, l, r) = IM.alter inner l ctx+ where inner mm = case mm of + Nothing -> Just $ singleRX+ Just m -> Just $ IM.insertWith (S.union) r singleX m+ singleRX = IM.singleton r singleX+ singleX = S.singleton x++
src/GLL/Parser.hs view
@@ -1,150 +1,378 @@++{-|+Implementation of the GLL parsing algorithm [Scott and Johnstone 2010,2013]+with the grammar as an explicit parameter.++Function 'parse' receives a 'Grammar' as input together with a +list of tokens (the input string).++The type of token is chosen arbitrarily, but the type should be 'Parseable' and orderable.+To be 'Parseable' a type must have two distinct values, 'eos' (end-of-string)+and 'eps' (epsilon). The user must ensure that these two values will never occur+as part of the input string.++== GLL Parsing+=== Recursive Descent+GLL parsing is a generalisation of recursive descent parsing (RD parsing).+A RD parser (RDP), for some grammar 'G' , consists of a set of parse +functions 'f_X', one for every nonterminal 'X', and a main function which +calls 'f_S', where 'S' is the start symbol. +The parse function 'f_X' take an integer 'l' as an argument and produces an +integer 'r', indicating that nonterminal 'X' derives 's_l_r', +where 's_i_j' is the substring of the input string 's' ranging from+'i' to 'j'. We call 'l' and 'r' the right- and left-extent, respectively.++The parse function 'f_X'+has a branch for every production X ::= s_1 ... s_k in 'G', guarded+by a look-ahead test, and every+branch has 'k' code fragments, one for every symbol 's_i', +with 1 <= i <= k.+A RDP matches grammar positions, represented by /slots/ of the form+X ::= a . b, with (input) string positions.+The dot in a slot tells us how much of the production's symbols have been +matched (the symbols before the dot) and which symbols still need to +be matched (the symbols after the dot). The symbol immediately after the dot+is the next symbol to be match and is either:++* A terminal token, matched directly with the token at the current+ string position.+* A nonterminal 'Y', for which 'f_Y' is called. In the case of+ LL(1) deterministic parsing, only one (or none) of the productions+ of 'Y' passes the lookahead-test, say "Y ::= c", and its branch + will be executed: the next grammar position is "Y ::= .c".+* No further symbol, represented by "X ::= d." (all + symbols have been processed). In this case a return call is made+ to the caller of 'f_X' (relying on a function call stack).++=== Handling function/return calls+GLL handles its own function calls and return calls, instead of relying on an +underlying mechanism. This form of low-level control allows+GLL to avoid much duplicate work, not only for function calls (as in classical+memoisation) but also for return calls. The underlying observation is that+both return calls and function calls continue matching grammar slots. +In non-deterministic RDP, every function call leads to a slot of the+form "X ::= . a" being processed, while every return call +leads to a slot of the form "X ::= aY.b" being processed,+where 'Y' is some nonterminal. GLL uses /descriptors/, containing+a slot of one of these forms, to uniquely identify the computation that+processes the slot. The descriptor therefore also needs to contain+the initial values of the local variables used in that computation. ++A generated GLL parser (Scott and Johnstone 2013) has a code fragment for +every nonterminal 'X' (labelled 'L_X') and slot (labelled "L_{X ::= a.b}"). +This Haskell implementation abstracts over the grammar and has a function for+executing 'L_X', for a given 'X', and a function for executing +"L_{X ::= a.b}", for a given "X ::= a.b".++=== Generalisation+GLL parsing generalises RD parsing by allowing non-determinism:+when processing "X ::= a.Yb", all productions of 'Y', that pass +the lookahead test, are considered. A slot is considered, by adding a +descriptor for it to the /worklist/ 'R'. +Duplicates in the worklist are avoided by maintaining a separate descriptor-set+'U' containing all descriptors added to the worklist before.++The result of a parse function 'f_X' is no longer a single right extent 'r'.+Instead, it is a list of right extents 'rs', indicating that 'X' derives+'s_l_r' for all 'r' in 'rs' and integer input 'l' (left extent).+Every discovered right extent is stored in the /pop-set/ 'P'.++When a descriptors for a function call is a duplicate, it is not added to the+worklist, but we have to make sure that the corresponding+return call is still made. Note that a function call to 'f_Y', with +the same parameters, can be made from multiple right-hand side occurrences+of 'Y'. It might be the case that:++* The original descriptors is still being processed. + Once finished, a descriptor must be added for all return calls + corresponding to function calls that lead to duplicates of + this descriptor. + GLL uses a Graph-Structured Stack (GSS) to efficiently maintain multiple + such continuations.+* The original descriptors has already been processed. In this+ case, one or more right extents 'rs' are stored in 'P' for the + corresponding function call. A descriptor for the return call must be + added for all 'r' in 'rs'. The descriptor for the return call must + be added to the GSS in this case as well, as other right extents might + be found in the future.+ ++== Usage+This module provides generalised parsing to other applications that work with +BNF grammars. ++The user should provide a 'Grammar' and an input string as arguments+to top-level functions 'parse' or 'parseWithOptions'.++=== Example+This example shows simple character level parsing.+First we must make 'Char' and instance of 'Parseable'.++@+instance Parseable Char where+ eos = \'$\'+ eps = '#'+@++This instance mandates that \'$\' and '#' are 'reserved tokens' +and not part of the input string.++"GLL.Parser" exports 'Prod' for constructing 'Grammar's.++@+grammar1 = (\"X\", [Prod \"X\" [Nt \"A\", Nt \"A\"]+ ,Prod \"A\" [Term \'a\']+ ,Prod \"A\" [Term \'a\', Term \'a\']+ ] )++fail1 = "a"+success1 = "aa"+success2 = "aaa"+fail2 = "aaaaa"+@+Note that there are two possible derivations of 'success2'.++The parser can be accessed through 'parse' or 'parseWithOptions'.++@+run1 = parse grammar1 success1+run2 = parseWithOptions [fullSPPF, strictBinarisation] grammar1 success2+@ ++The options 'fullSPPF', 'allNodes', 'packedNodesOnly', decide whether all SPPF nodes and +edges are inserted into the resulting value of the 'SPPF' type.+Packed nodes are enough to fully represent an SPPF, as the parent and children+of a packed node can be computed from the packed nodes' information (given that the +packed node stores the left and right extent of its parent).+For efficiency the 'SPPF' is not strictly binarised by default: a packed+node might have a symbol node as a left child. In a strictly binarised 'SPPF'+a packed node has an intermediate node as a left child, or no left child at all.+To create a strictly binarised 'SPPF' (necessary for "GLL.Combinators") to option+'strictBinarisation' is available.++=== Combinator interface+Module "GLL.Combinators.Interface" provides a combinator interface to access+"GLL.Parser". Applicative-like combinators are used to extract a 'Grammar' and+call 'parse'. The 'SPPF' is then used to produce semantic results.++-} module GLL.Parser (- gllSPPF -- run the parser- , charS, charT, nT, epsilon -- create terminals- , pNodeLookup, ParseResult(..)+ -- * Grammar+ Grammar(..), Prods(..), Prod(..), Symbols(..), Symbol(..), Slot(..),+ -- ** Parseable tokens + Parseable(..),+ -- * Run the GLL parser+ parse, + -- ** Run the GLL parser with options+ parseWithOptions,+ -- *** Options+ strictBinarisation, fullSPPF, allNodes, packedNodesOnly, maximumErrors,+ -- ** Result+ ParseResult(..), SPPF(..), SPPFNode(..), SymbMap, ImdMap, PackMap, EdgeMap, ) where import Data.Foldable hiding (forM_, toList, sum) import Prelude hiding (lookup, foldr, fmap, foldl, elem) import Control.Monad-import Control.Applicative hiding (empty)-import Data.Map (Map(..), empty, insertWith, (!), toList, lookup)-import Data.Set (member, Set(..)) import qualified Data.IntMap as IM import qualified Data.Map as M import qualified Data.Array as A import qualified Data.Set as S import qualified Data.IntSet as IS+import Text.PrettyPrint.HughesPJ as PP import GLL.Types.Abstract import GLL.Types.Grammar -- | Representation of the input string-type Input = A.Array Int Token+type Input t = A.Array Int t -- | Types for -type LhsParams = (Nt , Int , GSSNode)-type RhsParams = (Slot , Int , GSSNode)+type LhsParams t = (Nt , Int , GSSNode t)+type RhsParams t = (Slot t , Int , GSSNode t) -- | The worklist and descriptor set-type Rcal = [(RhsParams,SPPFNode)]-type Ucal = IM.IntMap (IM.IntMap (S.Set (Slot, GSlot)))+type Rcal t = [(RhsParams t, SPPFNode t)]+type Ucal t = IM.IntMap (IM.IntMap (S.Set (Slot t, GSlot t))) -- | GSS representation-type GSS = IM.IntMap (M.Map GSlot [GSSEdge])-type GSSEdge = (GSSNode, SPPFNode)-type GSSNode = (GSlot, Int)-data GSlot = GSlot Slot+type GSS t = IM.IntMap (M.Map (GSlot t) [GSSEdge t])+type GSSEdge t = (GSSNode t, SPPFNode t)+type GSSNode t = (GSlot t, Int)+data GSlot t = GSlot (Slot t) | U0 deriving (Ord, Eq) +type MisMatches t = IM.IntMap (S.Set t) -- | Pop-set-type Pcal = IM.IntMap (Map GSlot [Int])+type Pcal t = IM.IntMap (M.Map (GSlot t) [Int]) -- | Connecting it all-type Mutable = (SPPF,Rcal, Ucal, GSS, Pcal)+data Mutable t = Mutable { mut_success :: Bool+ , mut_sppf :: SPPF t+ , mut_worklist :: Rcal t+ , mut_descriptors :: Ucal t+ , mut_gss :: GSS t+ , mut_popset :: Pcal t + , mut_mismatches :: MisMatches t + } -- | Monad for implicitly passing around 'context'-data GLL a = GLL (Mutable -> (a, Mutable))+data GLL t a = GLL (Flags -> Mutable t -> (a, Mutable t)) -addDescr :: SPPFNode -> RhsParams -> GLL ()-getDescr :: GLL (Maybe (RhsParams,SPPFNode))-addSPPFEdge :: SPPFNode -> SPPFNode -> GLL ()-addPop :: GSSNode -> Int -> GLL () -getChildren :: GSSNode -> GLL [GSSEdge]-addGSSEdge :: GSSNode -> GSSEdge -> GLL ()-getPops :: GSSNode -> GLL [Int]-joinSPPFs :: Slot -> SPPFNode -> Int -> Int -> Int - -> GLL SPPFNode-runGLL :: GLL a -> Mutable -> Mutable-runGLL (GLL f) p = snd $ f p-addSPPFEdge f t = GLL $ \(sppf,r,u,gss,p) -> - ((), - (- pMapInsert f t $- sppf- ,r,u,gss,p))+runGLL :: GLL t a -> Flags -> Mutable t -> Mutable t+runGLL (GLL f) o p = snd $ f o p -addDescr sppf alt@(slot,i,(gs,l)) = GLL $ \(dv,r,u,gss,p) -> - let new = maybe True inner $ IM.lookup i u- where inner m = maybe True (not . ((slot,gs) `S.member`)) $ IM.lookup l m - newU = IM.alter inner i u+addSPPFEdge f t = GLL $ \flags mut -> + let sppf' = (if symbol_nodes flags then sNodeInsert f t else id) $+ (if intermediate_nodes flags then iNodeInsert f t else id) $+ (if edges flags then eMapInsert f t else id) $ + pMapInsert f t (mut_sppf mut)+ in ((),mut{mut_sppf = sppf'})++addDescr sppf alt@(slot,i,(gs,l)) = GLL $ \_ mut -> + let new = maybe True inner $ IM.lookup i (mut_descriptors mut)+ where inner m = maybe True (not . ((slot,gs) `S.member`)) $ IM.lookup l m+ newU = IM.alter inner i (mut_descriptors mut) where inner mm = case mm of Nothing -> Just $ IM.singleton l single Just m -> Just $ IM.insertWith (S.union) l single m single = S.singleton (slot,gs)- in if new then ((), (dv, (alt,sppf):r, newU, gss , p))- else ((), (dv, r, u, gss, p))+ in if new then ((), mut{mut_worklist = (alt,sppf):(mut_worklist mut)+ ,mut_descriptors = newU})+ else ((), mut) -getDescr = GLL $ \(dv,r,u,gss,p) -> - case r of - [] -> (Nothing, (dv,r,u,gss,p))- (next@(alt,sppf):rest) -> - let res = (Just next, (dv,rest,u,gss,p))- in res+getDescr = GLL $ \_ mut -> + case mut_worklist mut of + [] -> (Nothing, mut)+ (next@(alt,sppf):rest) -> (Just next, mut{mut_worklist = rest}) -addPop (gs,l) i = GLL $ \(dv,r,u,gss,p) ->- let newP = IM.alter inner l p+addPop (gs,l) i = GLL $ \_ mut ->+ let newP = IM.alter inner l (mut_popset mut) where inner mm = case mm of Nothing -> Just $ M.singleton gs [i] Just m -> Just $ M.insertWith (++) gs [i] m- in ((), (dv,r,u,gss,newP))+ in ((), mut{mut_popset = newP}) -getChildren (gs,l) = GLL $ \(dv,r,u,gss,p) ->- let res = maybe [] inner $ IM.lookup l gss+getChildren (gs,l) = GLL $ \_ mut ->+ let res = maybe [] inner $ IM.lookup l (mut_gss mut) where inner m = maybe [] id $ M.lookup gs m- in (res, (dv,r,u,gss,p))+ in (res, mut) -addGSSEdge f@(gs,i) t = GLL $ \(dv,r,u,gss,p) -> - let newGSS = IM.alter inner i gss+addGSSEdge f@(gs,i) t = GLL $ \_ mut -> + let newGSS = IM.alter inner i (mut_gss mut) where inner mm = case mm of Nothing -> Just $ M.singleton gs [t] Just m -> Just $ M.insertWith (++) gs [t] m- in ((), (dv,r,u,newGSS,p))+ in ((), mut{mut_gss = newGSS}) -getPops (gs,l) = GLL $ \ctx@(dv,r,u,gss,p) -> - let res = maybe [] inner $ IM.lookup l p+getPops (gs,l) = GLL $ \_ mut -> + let res = maybe [] inner $ IM.lookup l (mut_popset mut) where inner = maybe [] id . M.lookup gs- in (res, ctx)+ in (res, mut) -instance Show GSlot where+addSuccess = GLL $ \_ mut -> ((),mut{mut_success = True})++getFlags = GLL $ \fs ctx -> (fs, ctx)++addMisMatch :: (Ord t) => Int -> S.Set t -> GLL t ()+addMisMatch k ts = GLL $ \flags mut -> + let newM = IM.insertWith S.union k ts (mut_mismatches mut)+ newM' | length (IM.keys newM) > max_errors flags = IM.deleteMin newM+ | otherwise = newM+ in ((), mut{mut_mismatches = newM'})++instance (Show t) => Show (GSlot t) where show (U0) = "u0" show (GSlot gn) = show gn -instance Show SPPFNode where+instance (Show t) => Show (SPPFNode t) where show (SNode (s, l, r)) = "(s: " ++ show s ++ ", " ++ show l ++ ", " ++ show r ++ ")" show (INode (s, l, r)) = "(i: " ++ show s ++ ", " ++ show l ++ ", " ++ show r ++ ")" show (PNode (p, l, k, r)) = "(p: " ++ show p ++ ", " ++ show l ++ ", " ++ show k ++ ", " ++ show r ++ ")" show Dummy = "$" -instance Applicative GLL where+instance Applicative (GLL t) where (<*>) = ap pure = return-instance Functor GLL where+instance Functor (GLL t) where fmap = liftM-instance Monad GLL where- return a = GLL $ \p -> (a, p)- (GLL m) >>= f = GLL $ \p -> let (a, p') = m p- (GLL m') = f a- in m' p'-gllSPPF :: Grammar -> [Token] -> ParseResult -gllSPPF grammar@(Grammar start _ _ ) input = - let (mutable,_,_,_) = gll m False grammar input- m = length input- in resultFromMutable mutable (Nt start, 0, m)+instance Monad (GLL t) where+ return a = GLL $ \_ p -> (a, p)+ (GLL m) >>= f = GLL $ \o p -> let (a, p') = m o p+ (GLL m') = f a+ in m' o p' -gll :: Int -> Bool -> Grammar -> [Token] -> (Mutable, [Alt], SelectMap, FollowMap)-gll m debug (Grammar start _ rules) input' = - (runGLL (pLhs (start, 0, (U0,0))) context, prs, selects, follows)- where - prs = [ alt | Rule _ alts <- rules, alt <- (reverse alts) ]- context = (emptySPPF, [], IM.empty, IM.empty, IM.empty)- input = A.array (0,m) $ zip [0..] $ input' ++ [EOS]+data Flags = Flags { symbol_nodes :: Bool+ , intermediate_nodes :: Bool+ , edges :: Bool+ , flexible_binarisation :: Bool+ , max_errors :: Int+ }+defaultOptions = Flags False False False True 3+runOptions = foldr ($) defaultOptions - dispatch :: GLL ()- pLhs :: LhsParams -> GLL () - pRhs :: RhsParams -> SPPFNode -> GLL ()+type Option = Flags -> Flags+type Options = [Option] +-- | +-- Create the 'SPPF' with all nodes and edges, not necessarily strictly binarised.+fullSPPF :: Option+fullSPPF flags = flags{symbol_nodes = True, intermediate_nodes = True, edges = True}++-- |+-- Create all nodes, but no edges between nodes.+allNodes :: Option+allNodes flags = flags{symbol_nodes = True, intermediate_nodes = True}++-- | +-- Create packed-nodes only.+packedNodesOnly :: Option+packedNodesOnly flags = flags{symbol_nodes = False, intermediate_nodes = False, edges = False}++-- | +-- Fully binarise the SPPF, resulting in a larger 'SPPF' and possibly slower runtimes.+-- When this flag is on, packed nodes can only have a single symbol node child +-- or one intermediate node child and one symbol node child.+-- With the flag disabled a packed node can have two symbol node children.+strictBinarisation :: Option+strictBinarisation flags = flags{flexible_binarisation = False}++-- | +-- Set the maximum number of errors shown in case of an unsuccessful parse.+maximumErrors :: Int -> Option+maximumErrors n flags = flags {max_errors = n}++-- | +-- Run the GLL parser given a 'Grammar' 't' and a list of 't's, +-- where 't' is an arbitrary token-type.+-- All token-types must be 'Parseable'.+parse :: (Parseable t) => Grammar t -> [t] -> ParseResult t+parse = parseWithOptions [] ++-- | +-- Run the GLL parser given some options, a 'Grammar' 't' and a list of 't's.+--+-- If no options are given a minimal 'SPPF' will be created:+--+-- * only packed nodes are created+-- * the resulting 'SPPF' is not strictly binarised+parseWithOptions :: Parseable t => [Option] -> Grammar t -> [t] -> ParseResult t+parseWithOptions opts grammar@(start,_) str = + let flags = runOptions opts+ (mutable,_,_,_) = gll flags m False grammar input+ m = length str + input = A.array (0,m) $ zip [0..] $ str ++ [eos]+ in resultFromMutable input flags mutable (Nt start, 0, m)++gll :: Parseable t => Flags -> Int -> Bool -> Grammar t -> Input t -> + (Mutable t, [Prod t], SelectMap t, FollowMap t)+gll flags m debug (start, prods) input = + (runGLL (pLhs (start, 0, (U0,0))) flags context, prs, selects, follows)+ where + prs = [ alt | alt <- (reverse prods) ]+ context = Mutable False emptySPPF [] IM.empty IM.empty IM.empty IM.empty+ dispatch = do mnext <- getDescr case mnext of@@ -152,28 +380,30 @@ Just (next,sppf) -> pRhs next sppf pLhs (bigx, i, gn) = do - let alts = [ (Slot bigx [] beta, i, gn) | (Alt bigx beta) <- altsOf bigx- , select (input A.! i) beta bigx+ let alts = [ ((Slot bigx [] beta, i, gn), first_ts) + | Prod bigx beta <- altsOf bigx+ , let first_ts = select beta bigx ]- forM_ alts (addDescr Dummy)+ first_ts = S.unions (map snd alts)+ cands = [ descr | (descr, first_ts) <- alts+ , any (matches (input A.! i)) first_ts ]+ if null cands+ then addMisMatch i first_ts+ else forM_ cands (addDescr Dummy) dispatch - pRhs (Slot bigx [] [Term Epsilon], i, (gs,l)) _ = do- root <- joinSPPFs slot Dummy l i i- pRhs (slot, i, (gs,l)) root- where slot = Slot bigx [Term Epsilon] []- pRhs (Slot bigx alpha ((Term tau):beta), i, (gs,l)) sppf = - if (input A.! i == tau) + if (input A.! i `matches` tau) then do -- token test root <- joinSPPFs slot sppf l i (i+1) pRhs (slot, i+1, (gs,l)) root - else+ else do+ addMisMatch i (S.singleton tau) dispatch where slot = Slot bigx (alpha++[Term tau]) beta pRhs (Slot bigx alpha ((Nt bigy):beta), i, (gs, l)) sppf = - if (select (input A.! i) ((Nt bigy):beta) bigx) + if any (matches (input A.! i)) first_ts then do addGSSEdge ret ((gs,l), sppf) rs <- getPops ret -- has ret been popped?@@ -181,13 +411,24 @@ root <- joinSPPFs slot sppf l i r addDescr root (slot, r, (gs,l)) pLhs (bigy, i, ret)- else+ else do+ addMisMatch i first_ts dispatch where ret = (GSlot slot, i) slot = Slot bigx (alpha++[Nt bigy]) beta+ first_ts = select ((Nt bigy):beta) bigx - pRhs (Slot bigy alpha [], i, (U0,0)) sppf = dispatch + pRhs (Slot bigy alpha [], i, (U0,_)) sppf = do+ when (bigy /= start) (error "assert: start symbol with U0") + if i == m + then addSuccess >> dispatch + else addMisMatch i (S.singleton eos) >> dispatch + pRhs (Slot bigx alpha [], i, (gs,l)) Dummy = do+ root <- joinSPPFs slot Dummy l i i+ pRhs (slot, i, (gs,l)) root+ where slot = Slot bigx [] []+ pRhs (Slot bigy alpha [], i, gn@(GSlot slot,l)) ynode = do addPop gn i returns <- getChildren gn@@ -197,54 +438,101 @@ dispatch (prodMap,_,_,follows,selects) = fixedMaps start prs- follow x = follows ! x- select t rhs x = t `member` (selects ! (x,rhs))- altsOf x = prodMap ! x+ follow x = follows M.! x+ select rhs x = selects M.! (x,rhs)+ altsOf x = prodMap M.! x merge m1 m2 = IM.unionWith inner m1 m2 where inner = IM.unionWith S.union -joinSPPFs (Slot bigx alpha beta) sppf l k r =- case (sppf, beta) of--- (Dummy, _:_) -> return snode- (Dummy, []) -> do addSPPFEdge xnode pnode- addSPPFEdge pnode snode- return xnode- (_, []) -> do addSPPFEdge xnode pnode- addSPPFEdge pnode sppf- addSPPFEdge pnode snode- return xnode- _ -> do addSPPFEdge inode pnode- addSPPFEdge pnode sppf- addSPPFEdge pnode snode- return inode+joinSPPFs (Slot bigx alpha beta) sppf l k r = do+ flags <- getFlags+ case (flexible_binarisation flags, sppf, beta) of+ (True,Dummy, _:_) -> return snode+ (_,Dummy, []) -> do addSPPFEdge xnode pnode+ addSPPFEdge pnode snode+ return xnode+ (_,_, []) -> do addSPPFEdge xnode pnode+ addSPPFEdge pnode sppf+ addSPPFEdge pnode snode+ return xnode+ _ -> do addSPPFEdge inode pnode+ addSPPFEdge pnode sppf+ addSPPFEdge pnode snode+ return inode where x = last alpha -- symbol before the dot snode = SNode (x, k, r) xnode = SNode (Nt bigx, l, r) inode = INode ((Slot bigx alpha beta), l, r) pnode = PNode ((Slot bigx alpha beta), l, k, r) -data ParseResult = ParseResult { sppf_result :: SPPF- , success :: Bool- , nr_descriptors :: Int- , nr_sppf_edges :: Int- , nr_gss_nodes :: Int+-- | +-- The "ParseResult" datatype contains the "SPPF" and some other +-- information about the parse:+--+-- * 'SPPF'+-- * Whether the parse was successful+-- * The number of descriptors that have been processed+-- * The number of symbol nodes (nonterminal and terminal)+-- * The number of intermediate noes+-- * The number of packed nodes+-- * The number of GSS nodes+-- * The number of GSS edges+data ParseResult t = ParseResult{ sppf_result :: SPPF t+ , res_success :: Bool+ , nr_descriptors :: Int+ , nr_symbol_nodes :: Int+ , nr_intermediate_nodes :: Int+ , nr_packed_nodes :: Int+ , nr_sppf_edges :: Int+ , nr_gss_nodes :: Int+ , nr_gss_edges :: Int+ , error_message :: String } -resultFromMutable :: Mutable -> SNode -> ParseResult-resultFromMutable (sppf@(_,_,_,eMap,_),_,u,gss,_) s_node =- ParseResult sppf success usize sppf_edges gsssize- where success = sppf `sNodeLookup` s_node+resultFromMutable :: (Show t, Ord t) => Input t -> Flags -> Mutable t -> SNode t -> ParseResult t+resultFromMutable inp flags mutable s_node@(s, l, m) =+ let u = mut_descriptors mutable+ gss = mut_gss mutable usize = sum [ S.size s | (l, r2s) <- IM.assocs u , (r,s) <- IM.assocs r2s ]+ s_nodes = sum [ S.size s | (l, r2s) <- IM.assocs sMap+ , (r, s) <- IM.assocs r2s ] + m+ i_nodes = sum [ S.size s | (l, r2s) <- IM.assocs iMap+ , (r, s) <- IM.assocs r2s ]+ p_nodes = sum [ IS.size ks | (l, r2j) <- IM.assocs pMap+ , (r, j2s) <- IM.assocs r2j+ , (j, s2k) <- IM.assocs j2s+ , (s, ks) <- M.assocs s2k ] sppf_edges = sum [ S.size ts | (_, ts) <- M.assocs eMap ]- gsssize = 1 + sum [ length $ M.keys x2s| (l,x2s) <- IM.assocs gss] + gss_nodes = 1 + sum [ length $ M.keys x2s| (l,x2s) <- IM.assocs gss] + gss_edges = 1 + sum [ length s | (l,x2s) <- IM.assocs gss+ , (x,s) <- M.assocs x2s ]+ sppf@(sMap, iMap, pMap, eMap) = mut_sppf mutable+ in ParseResult sppf (mut_success mutable) usize s_nodes i_nodes p_nodes sppf_edges gss_nodes gss_edges (renderErrors inp flags (mut_mismatches mutable)) -instance Show ParseResult where- show res = unlines $- [ "Success: " ++ show (success res)- , "Descriptors: " ++ show (nr_descriptors res)- , "SPPFEdges: " ++ show (nr_sppf_edges res)- , "GSSNodes: " ++ show (nr_gss_nodes res)- ]+renderErrors :: Show t => Input t -> Flags -> MisMatches t -> String+renderErrors inp flags mm = render doc + where n = max_errors flags+ locs = reverse (IM.assocs mm)+ doc = text ("Unsuccessful parse, showing "++ show n ++ " furthest matches") $+$+ foldr (\loc -> (ppLoc loc $+$)) PP.empty locs + ppLoc (k, ts) = text ("did not match at position " ++ show k ++ ":") $+$+ nest 4 (text (show token)) $+$+ nest 4 (text "expected:") $+$+ nest 8 (vcat (map ppExp (S.toList ts)))+ where token = inp A.! k+ ppExp = text . show++instance Show (ParseResult t) where+ show res | res_success res = unlines $+ [ "Descriptors: " ++ show (nr_descriptors res)+ , "Symbol nodes: " ++ show (nr_symbol_nodes res)+ , "Intermediate nodes: " ++ show (nr_intermediate_nodes res)+ , "Packed nodes: " ++ show (nr_packed_nodes res)+ , "SPPF edges: " ++ show (nr_sppf_edges res)+ , "GSS nodes: " ++ show (nr_gss_nodes res)+ , "GSS edges: " ++ show (nr_gss_edges res)+ ]+ | otherwise = error_message res
src/GLL/Types/Abstract.hs view
@@ -1,41 +1,117 @@+{-# LANGUAGE StandaloneDeriving #-} -- UUAGC 0.9.52.1 (src/GLL/Types/Abstract.ag) module GLL.Types.Abstract where {-# LINE 1 "src/GLL/Types/Abstract.ag" #-} -import qualified Data.Map as M-import qualified Data.Set as S -import Data.List (delete, (\\), elemIndices, findIndices) {-# LINE 12 "dist/build/GLL/Types/Abstract.hs" #-} --- | Identifier for non-terminals+-- | Identifier for nonterminals. type Nt = String--- Alt ----------------------------------------------------------data Alt = Alt (Nt) (Symbols)--- Alts ---------------------------------------------------------type Alts = [Alt]+-- Prod ---------------------------------------------------------++-- | +-- A production binds a nonterminal identifier (left-hand side) to a list of symbols +--(the right-hand side of the production).+data Prod t = Prod (Nt) (Symbols t)+-- Prods --------------------------------------------------------+-- | A list of 'Prod's.+type Prods t = [Prod t] -- Grammar ------------------------------------------------------data Grammar = Grammar (Nt) (([(String,String)])) (Rules)--- Rule ---------------------------------------------------------data Rule = Rule (Nt) (Alts)--- Rules --------------------------------------------------------type Rules = [Rule]+-- |+-- A grammar is a start symbol and a list of productions. +type Grammar t = (Nt, Prods t) -- Slot ---------------------------------------------------------data Slot = Slot (Nt) (([Symbol])) (([Symbol]))+-- | +-- A grammar slot acts as a label to identify progress of matching a production.+-- As such, a slot is a "Prod" with its right-hand side split in two: +-- a part before and a part after 'the dot'.+-- The dot indicates which part of the right-hand side has been processed thus far.+data Slot t = Slot (Nt) (([Symbol t])) (([Symbol t])) -- Symbol -------------------------------------------------------data Symbol = Nt (String)- | Term (Token)- | Error (Token) (Token)++-- | +-- A 'Symbol' is either a nonterminal or a terminal,+-- where a terminal contains some arbitrary token.+data Symbol t = Nt Nt+ | Term t+-- | Error (Token) (Token) -- Symbols ------------------------------------------------------type Symbols = [Symbol]++-- | +-- A list of 'Symbol's+type Symbols t = [Symbol t] -- Token --------------------------------------------------------data Token = Char (Char)++-- |+-- A datatype for representing tokens with some builtins +-- and an aribitrary Token constructor.+-- This datatype stores (optional) lexemes.+data Token = Char Char+ | Keyword String | EOS | Epsilon- | Int (((Maybe Int)))- | Bool (((Maybe Bool)))- | String (((Maybe String)))- | Token (String) (((Maybe String)))+ | IntLit (Maybe Int)+ | BoolLit (Maybe Bool)+ | StringLit (Maybe String)+ | IDLit (Maybe String)+ | Token String (Maybe String) -- Tokens ------------------------------------------------------+-- | +-- A list of 'Token's type Tokens = [Token]++-- | Class that captures elements of an input string (tokens).+-- +-- * 'eos' is the end-of-string symbol +-- * 'eps' is the empty-string symbol+--+-- Both 'eos' and 'eps' must be distinct from eachother and from all +-- tokens in the input string.+-- The show instance is required to throw error messages.+class (Ord a, Eq a, Show a) => Parseable a where+ eos :: a+ eps :: a ++ -- | This function is used for matching grammar tokens and input tokens.+ -- Override this method if, for example, your input tokens store lexemes+ -- while the grammar tokens do not+ matches :: a -> a -> Bool+ matches = (==)++deriving instance Ord Token+deriving instance Eq Token++instance Show Token where+ show (Char c) = "char('" ++ [c] ++ "')"+ show (Keyword s) = "keyword(\"" ++ s ++ "\")"+ show (EOS) = "<end-of-string>"+ show (Epsilon) = "<epsilon>"+ show (IntLit (Just i)) = "int(" ++ show i ++ ")"+ show (IntLit _) = "<int>"+ show (BoolLit (Just b)) = "bool(" ++ show b ++ ")"+ show (BoolLit _) = "<bool>"+ show (StringLit (Just s)) = "string(\"" ++ s ++ "\")"+ show (StringLit _) = "<string>"+ show (IDLit (Just id)) = "id(\"" ++ id ++ "\")"+ show (IDLit Nothing) = "<id>"+ show (Token nm (Just s)) = nm ++ "(\"" ++ s ++ "\")"+ show (Token nm _) = "<" ++ nm ++ ">"+ +instance Parseable Token where+ eos = EOS+ eps = Epsilon++ Token k _ `matches` Token k' _ = k' == k+ Char c `matches` Char c' = c' == c+ Keyword k `matches` Keyword k' = k' == k+ EOS `matches` EOS = True+ Epsilon `matches` Epsilon = True+ StringLit _ `matches` StringLit _ = True+ IntLit _ `matches` IntLit _ = True+ BoolLit _ `matches` BoolLit _ = True+ IDLit _ `matches` IDLit _ = True+ _ `matches` _ = False++
src/GLL/Types/Grammar.hs view
@@ -6,59 +6,79 @@ import qualified Data.IntMap as IM import qualified Data.Set as S import qualified Data.IntSet as IS -import Data.List (delete, (\\), elemIndices, findIndices)+import Data.List (elemIndices, findIndices) import GLL.Types.Abstract -token_length :: Token -> Int-token_length (Char _) = 1-token_length (EOS) = 1-token_length (Epsilon)= 0-token_length (Int _) = error "find out nr of digits in int"-token_length (Bool b) = maybe (error "no length for bool tokens") - (\b -> if b then 4 else 5) b-- supposing "True" and "False"-token_length (String s) = maybe (error "no length for string tokens") length s-token_length (Token _ str) = maybe (error "no length of tokens") length str- -- make sure that tokens are equal independent of their character level value-type SlotL = (Slot, Int) -- slot with left extent-type PrL = (Alt, Int) -- Production rule with left extent+type SlotL t = (Slot t, Int) -- slot with left extent+type PrL t = (Prod t, Int) -- Production rule with left extent type NtL = (Nt, Int) -- Nonterminal with left extent -- SPPF-type SPPF = (SymbMap, ImdMap, PackMap, EdgeMap, IDMap)-type PackMap = IM.IntMap (IM.IntMap (IM.IntMap (M.Map Alt IS.IntSet)))-type SymbMap = IM.IntMap (IM.IntMap (S.Set Symbol))-type ImdMap = IM.IntMap (IM.IntMap (S.Set Slot))-type EdgeMap = M.Map SPPFNode (S.Set SPPFNode)-type IDMap = (IDFMap,IDTMap)-type IDFMap = IM.IntMap SPPFNode-type IDTMap = M.Map SPPFNode Int-data SPPFNode = SNode (Symbol, Int, Int) - | INode (Slot, Int, Int)- | PNode (Slot, Int, Int, Int)++-- | +-- An 'SPPF' contains symbol nodes, intermediate nodes, packed nodes and edges between them.+-- See Scott and Johnstone (2013) for an explanation of the 'SPPF'.+type SPPF t = (SymbMap t, ImdMap t, PackMap t, EdgeMap t)++-- | +-- Stores packed nodes using nested "Data.IntMap"s, nesting is as follows:+--+-- * left extent+-- * right extent+-- * dot position (from left to right)+-- * mapping from productions to set of pivots+type PackMap t = IM.IntMap (IM.IntMap (IM.IntMap (M.Map (Prod t) IS.IntSet)))++-- | +-- Stores symbol nodes using nested "Data.IntMap"s, nesting is as follows:+--+-- * left extent+-- * right extent+-- * set of symbols+type SymbMap t = IM.IntMap (IM.IntMap (S.Set (Symbol t)))++-- | +-- Stores intermediate nodes using nested "Data.IntMap"s, nesting is as follows:+--+-- * left extent+-- * right extent+-- * set of slots +type ImdMap t = IM.IntMap (IM.IntMap (S.Set (Slot t)))++-- | +-- Stores edges, potentially costly.+type EdgeMap t = M.Map (SPPFNode t) (S.Set (SPPFNode t))++-- | +-- An "SPPFNode" is either a symbol node, an intermediate node, a packed node or a dummy.+data SPPFNode t = SNode (Symbol t, Int, Int) + | INode (Slot t, Int, Int)+ | PNode (Slot t, Int, Int, Int) | Dummy deriving (Ord, Eq)-type SNode = (Symbol, Int, Int)-type PNode = (Alt, [Int])-type SEdge = M.Map SNode (S.Set PNode)-type PEdge = M.Map PNode (S.Set SNode) -emptySPPF :: SPPF-emptySPPF = (IM.empty, IM.empty, IM.empty, M.empty, (IM.empty, M.empty))+type SNode t = (Symbol t, Int, Int)+type PNode t = (Prod t, [Int])+type SEdge t = M.Map (SNode t)(S.Set (PNode t))+type PEdge t = M.Map (PNode t) (S.Set (SNode t)) -pNodeLookup :: SPPF -> ((Alt, Int), Int, Int) -> Maybe [Int]-pNodeLookup (_,_,pMap,_,_) ((alt,j),l,r) = maybe Nothing inner $ IM.lookup l pMap+emptySPPF :: (Ord t) => SPPF t+emptySPPF = (IM.empty, IM.empty, IM.empty, M.empty)++pNodeLookup :: (Ord t) => SPPF t -> ((Prod t, Int), Int, Int) -> Maybe [Int]+pNodeLookup (_,_,pMap,_) ((alt,j),l,r) = maybe Nothing inner $ IM.lookup l pMap where inner = maybe Nothing inner2 . IM.lookup r inner2 = maybe Nothing inner3 . IM.lookup j inner3 = maybe Nothing (Just . IS.toList) . M.lookup alt -pMapInsert :: SPPFNode -> SPPFNode -> SPPF -> SPPF-pMapInsert f t (sMap,iMap,pMap,eMap,idMap) = +pMapInsert :: (Ord t) => SPPFNode t -> SPPFNode t -> SPPF t -> SPPF t+pMapInsert f t (sMap,iMap,pMap,eMap) = let pMap' = case f of PNode ((Slot x alpha beta), l, k, r) -> - add (Alt x (alpha++beta)) (length alpha) l r k+ add (Prod x (alpha++beta)) (length alpha) l r k _ -> pMap- in (sMap,iMap,pMap',eMap,idMap)+ in (sMap,iMap,pMap',eMap) where add alt j l r k = IM.alter addInnerL l pMap where addInnerL mm = case mm of Nothing -> Just singleRJAK@@ -75,16 +95,16 @@ singleK = IS.singleton k -sNodeLookup :: SPPF -> (Symbol, Int, Int) -> Bool -sNodeLookup (sm,_,_,_,_) (s,l,r) = maybe False inner $ IM.lookup l sm+sNodeLookup :: (Ord t) => SPPF t -> (Symbol t, Int, Int) -> Bool +sNodeLookup (sm,_,_,_) (s,l,r) = maybe False inner $ IM.lookup l sm where inner = maybe False (S.member s) . IM.lookup r -sNodeInsert :: SPPFNode -> SPPFNode -> SPPF -> SPPF-sNodeInsert f t (sMap,iMap,pMap,eMap,idMap) = +sNodeInsert :: (Ord t) => SPPFNode t -> SPPFNode t -> SPPF t -> SPPF t+sNodeInsert f t (sMap,iMap,pMap,eMap) = let sMap' = case f of SNode (s, l, r) -> newt (add s l r sMap) _ -> newt sMap- in (sMap',iMap,pMap,eMap,idMap)+ in (sMap',iMap,pMap,eMap) where newt sMap = case t of (SNode (s, l, r)) -> add s l r sMap _ -> sMap@@ -95,21 +115,21 @@ singleRS = IM.fromList [(r, singleS)] singleS = S.singleton s -sNodeRemove :: SPPF -> (Symbol, Int, Int) -> SPPF -sNodeRemove (sm,iMap,pMap,eMap,idMap) (s,l,r) = - (IM.adjust inner l sm, iMap,pMap,eMap,idMap)+sNodeRemove :: (Ord t) => SPPF t -> (Symbol t, Int, Int) -> SPPF t+sNodeRemove (sm,iMap,pMap,eMap) (s,l,r) = + (IM.adjust inner l sm, iMap,pMap,eMap) where inner = IM.adjust ((s `S.delete`)) r -iNodeLookup :: SPPF -> (Slot, Int, Int) -> Bool -iNodeLookup (_,iMap,_,_,_) (s,l,r) = maybe False inner $ IM.lookup l iMap+iNodeLookup :: (Ord t) => SPPF t -> (Slot t, Int, Int) -> Bool +iNodeLookup (_,iMap,_,_) (s,l,r) = maybe False inner $ IM.lookup l iMap where inner = maybe False (S.member s) . IM.lookup r -iNodeInsert :: SPPFNode -> SPPFNode -> SPPF -> SPPF-iNodeInsert f t (sMap,iMap,pMap,eMap,idMap) = +iNodeInsert :: (Ord t) => SPPFNode t -> SPPFNode t -> SPPF t -> SPPF t+iNodeInsert f t (sMap,iMap,pMap,eMap) = let iMap' = case f of INode (s, l, r) -> newt (add s l r iMap) _ -> newt iMap- in (sMap,iMap',pMap,eMap,idMap)+ in (sMap,iMap',pMap,eMap) where newt iMap = case t of (INode (s, l, r)) -> add s l r iMap _ -> iMap@@ -120,26 +140,15 @@ singleRS = IM.fromList [(r, singleS)] singleS = S.singleton s -iNodeRemove :: SPPF -> (Slot, Int, Int) -> SPPF -iNodeRemove (sMap,iMap,pMap,eMap,idMap) (s,l,r) = - (sMap,IM.adjust inner l iMap,pMap,eMap,idMap)+iNodeRemove :: (Ord t) => SPPF t -> (Slot t, Int, Int) -> SPPF t+iNodeRemove (sMap,iMap,pMap,eMap) (s,l,r) = + (sMap,IM.adjust inner l iMap,pMap,eMap) where inner = IM.adjust ((s `S.delete`)) r -eMapInsert :: SPPFNode -> SPPFNode -> SPPF -> SPPF-eMapInsert f t (sMap,iMap,pMap,eMap,idMap) = - (sMap,iMap,pMap,M.insertWith (S.union) f (S.singleton t) eMap,idMap)+eMapInsert :: (Ord t) => SPPFNode t -> SPPFNode t -> SPPF t -> SPPF t+eMapInsert f t (sMap,iMap,pMap,eMap) = + (sMap,iMap,pMap,M.insertWith (S.union) f (S.singleton t) eMap) -idMapInsert :: SPPFNode -> SPPFNode -> SPPF -> (SPPF, Int, Int)-idMapInsert f t (sMap,iMap,pMap,eMap,(idfMap,idtMap)) =- ((sMap,iMap,pMap,eMap,(idfMap'',idtMap'')),fkey,tkey)- where idx | IM.null idfMap = 0- | otherwise = fst (IM.findMax idfMap)- (fkey,idfMap',idtMap') = newKey f (idx+1) idfMap idtMap- (tkey,idfMap'',idtMap'') = newKey t (idx+2) idfMap' idtMap'- newKey :: SPPFNode -> Int -> IDFMap -> IDTMap -> (Int,IDFMap,IDTMap)- newKey n i mf mt = case M.lookup n mt of- Nothing -> (i,IM.insert i n mf,M.insert n i mt)- Just j -> (j,mf,mt) -- helpers for Ucal inU (slot,l,i) u = maybe False inner $ IM.lookup l u where inner = maybe False (S.member slot) . IM.lookup i@@ -160,52 +169,43 @@ showS sMap = unlines [ show (l,r) ++ " --> " ++ show (sset) | (l,r2s) <- IM.assocs sMap, (r,sset) <- IM.assocs r2s] -- TODO change to Map-showSPPF :: ([(SNode,PNode)],[(PNode,SNode)]) -> String+showSPPF :: (Show t) => ([(SNode t,PNode t)],[(PNode t,SNode t)]) -> String showSPPF (se,pe) = "\n"++ (unlines $ map ppPn $ pe) ++ "\n" ++ (unlines $ map ppSn $ se)- where ppPn ((Alt x alpha, rs), sn) = ppRhs (x,alpha,rs) ++ " --> " ++ show sn- ppSn (sn, (Alt x alpha, rs)) = show sn ++ " --> " ++ ppRhs (x,alpha,rs)+ where ppPn ((Prod x alpha, rs), sn) = ppRhs (x,alpha,rs) ++ " --> " ++ show sn+ ppSn (sn, (Prod x alpha, rs)) = show sn ++ " --> " ++ ppRhs (x,alpha,rs) ppRhs (x, alpha, rs) = "(" ++ x ++ " ::= "++ (foldr ((++) . ppS) "" alpha) ++ foldr (\i -> (("," ++ show i) ++)) "" rs ++ ")" ppS (Nt s) = s- ppS (Term Epsilon) = "''"- ppS (Term (Char c)) = [c]- ppS (Term (Token t _)) = t- ppS (Term (Int i)) = maybe "Int" show i- ppS (Term (Bool b)) = maybe "Bool" show b- ppS (Term (String s)) = maybe "String" id s+ ppS (Term t) = show t -- smart constructors-tokenT :: Token -> Symbol-tokenT t = Term $ t-charT c = Term $ Char c nT x = Nt x-charS = map Char -epsilon = [Term Epsilon]+epsilon = [] -type ProdMap = M.Map Nt [Alt]-type PrefixMap = M.Map (Alt,Int) ([Token], Maybe Nt)-type SelectMap = M.Map (Nt, [Symbol]) (S.Set Token)-type FirstMap = M.Map Nt (S.Set Token)-type FollowMap = M.Map Nt (S.Set Token)+type ProdMap t = M.Map Nt [Prod t]+type PrefixMap t = M.Map (Prod t,Int) ([t], Maybe Nt)+type SelectMap t = M.Map (Nt, [Symbol t]) (S.Set t)+type FirstMap t = M.Map Nt (S.Set t)+type FollowMap t = M.Map Nt (S.Set t) -fixedMaps :: Nt -> [Alt] -> (ProdMap, PrefixMap, FirstMap, FollowMap, SelectMap) +fixedMaps :: (Eq t, Ord t, Parseable t) => Nt -> [Prod t] -> + (ProdMap t, PrefixMap t, FirstMap t, FollowMap t, SelectMap t) fixedMaps s prs = let f = (prodMap, prefixMap, firstMap, followMap, selectMap)- in f `seq` f+ in f `seq` f where- prodMap = M.fromListWith (++) [ (x,[pr]) | pr@(Alt x _) <- prs ]+ prodMap = M.fromListWith (++) [ (x,[pr]) | pr@(Prod x _) <- prs ] - prefixMap :: PrefixMap prefixMap = M.fromList - [ ((pr,j), (tokens,msymb)) | pr@(Alt x alpha) <- prs+ [ ((pr,j), (tokens,msymb)) | pr@(Prod x alpha) <- prs , (j,tokens,msymb) <- prefix x alpha ] where prefix x alpha = map rangePrefix ranges where js = (map ((+) 1) (findIndices isNt alpha)) ranges = zip (0:js) (js ++ [length alpha]) rangePrefix (a,z) | a >= z = (a,[],Nothing)- | a < z = + rangePrefix (a,z) = let init = map ((\(Term t) -> t) . (alpha !!)) [a .. (z-2)] last = alpha !! (z-1) in case last of @@ -214,11 +214,9 @@ firstMap = M.fromList [ (x, first_x [] x) | x <- M.keys prodMap ] - first_x :: [Nt] -> Nt -> (S.Set Token) -- filter prevents self-calls- first_x ys x = S.unions [ first_alpha (x:ys) rhs | Alt _ rhs <- prodMap M.! x ]+ first_x ys x = S.unions [ first_alpha (x:ys) rhs | Prod _ rhs <- prodMap M.! x ] - selectMap :: SelectMap - selectMap = M.fromList [ ((x,alpha), select alpha x) | Alt x rhs <- prs+ selectMap = M.fromList [ ((x,alpha), select alpha x) | Prod x rhs <- prs , alpha <- split rhs ] where split rhs = foldr op [] js@@ -226,43 +224,38 @@ js = 0 : findIndices isNt rhs -- TODO store intermediate results- select :: [Symbol] -> Nt -> (S.Set Token) select alpha x = res where firsts = first_alpha [] alpha- res | Epsilon `S.member` firsts = S.delete Epsilon firsts `S.union` (followMap M.! x)+ res | eps `S.member` firsts = S.delete eps firsts `S.union` (followMap M.! x) | otherwise = firsts -- list of symbols to get firsts from + non-terminal to ignore -- TODO store in map- first_alpha :: [Nt] -> [Symbol] -> (S.Set Token)- first_alpha ys [] = S.singleton Epsilon+ first_alpha ys [] = S.singleton eps first_alpha ys (x:xs) = case x of- Term Epsilon -> first_alpha ys xs- Term tau -> S.singleton tau+ Term tau -> if tau == eps then first_alpha ys xs+ else S.singleton tau Nt x -> let fs | x `elem` ys = S.empty | otherwise = first_x (x:ys) x in if x `S.member` nullableSet- then (S.delete Epsilon fs) `S.union` first_alpha (x:ys) xs + then S.delete eps fs `S.union` first_alpha (x:ys) xs else fs - followMap :: M.Map Nt (S.Set Token) followMap = M.fromList [ (x, follow [] x) | x <- M.keys prodMap ] - follow :: [Nt] -> Nt -> (S.Set Token) follow ys x = S.unions (map fw (maybe [] id $ M.lookup x localMap))- `S.union` (if x == s then S.singleton EOS else S.empty)+ `S.union` (if x == s then S.singleton eos else S.empty) where fw (y,ss) = - let ts = S.delete Epsilon (first_alpha [] ss)+ let ts = S.delete eps (first_alpha [] ss) fs = follow (x:ys) y in if nullable_alpha [] ss && not (x `elem` (y:ys)) then ts `S.union` fs else ts - localMap = M.fromListWith (++)- [ (x,[(y,tail)]) | x <- M.keys prodMap, (Alt y rhs) <- prs+ [ (x,[(y,tail)]) | x <- M.keys prodMap, (Prod y rhs) <- prs , tail <- tails x rhs ] where tails x symbs = [ drop (index + 1) symbs | index <- indices ]@@ -274,17 +267,16 @@ -- a nonterminal is nullable if any of its alternatives is empty nullable_x :: [Nt] -> Nt -> Bool nullable_x ys x = or [ nullable_alpha (x:ys) rhs - | (Alt _ rhs) <- prodMap M.! x ] + | (Prod _ rhs) <- prodMap M.! x ] -- TODO store in map- nullable_alpha :: [Nt] -> [Symbol] -> Bool+ nullable_alpha :: [Nt] -> [Symbol t] -> Bool nullable_alpha ys [] = True nullable_alpha ys (s:ss) = case s of Nt nt -> if nt `elem` ys then False --nullable only if some other alternative is nullable else nullable_x ys nt && nullable_alpha (nt:ys) ss- Term Epsilon -> True otherwise -> False -- some helpers@@ -297,16 +289,13 @@ isChar (Char _) = True isChar _ = False -deriving instance Show Grammar -deriving instance Ord Slot-deriving instance Eq Slot-deriving instance Show Rule-deriving instance Show Alt-deriving instance Ord Alt-deriving instance Eq Alt-deriving instance Eq Symbol-deriving instance Ord Symbol-+deriving instance (Ord t) => Ord (Slot t)+deriving instance (Eq t) => Eq (Slot t)+deriving instance (Show t) => Show (Prod t)+deriving instance (Ord t) => Ord (Prod t)+deriving instance (Eq t) => Eq (Prod t)+deriving instance (Eq t) => Eq (Symbol t)+deriving instance (Ord t) => Ord (Symbol t) {- instance Show Symbol where show (Nt nt) = "Nt " ++ show nt@@ -328,53 +317,13 @@ (Error t1 t2) `compare` (Error t1' t2') = (t1,t2) `compare` (t1',t2') -} -instance Eq Token where- Token k _ == Token k' _ = k' == k- Char c == Char c' = c' == c- EOS == EOS = True- Epsilon == Epsilon = True- String _ == String _ = True- Int _ == Int _ = True- Bool _ == Bool _ = True- _ == _ = False--instance Ord Token where- EOS `compare` EOS = EQ - EOS `compare` _ = LT- _ `compare` EOS = GT- Epsilon `compare` Epsilon = EQ- Epsilon `compare` _ = LT- _ `compare` Epsilon = GT- String _ `compare` String _ = EQ- String _ `compare` _ = LT- _ `compare` String _ = GT- Int _ `compare` Int _ = EQ- Int _ `compare` _ = LT- _ `compare` Int _ = GT- Bool _ `compare` Bool _ = EQ- Bool _ `compare` _ = LT- _ `compare` Bool _ = GT- Char c `compare` Char c2 = c `compare` c2- Char _ `compare` _ = LT- _ `compare` Char c = GT- Token k _ `compare` Token k2 _ = k `compare` k2--instance Show Token where- show (Char c) = ['\'',c,'\'']- show (EOS) = "$"- show Epsilon = "#"- show (Int mi) = "int" - show (Bool mb)= "bool"- show (String ms) = "string"- show (Token t ms) = t --instance Show Slot where+instance (Show t) => Show (Slot t) where show (Slot x alpha beta) = x ++ " ::= " ++ showRhs alpha ++ "." ++ showRhs beta where showRhs [] = "" showRhs ((Term t):rhs) = show t ++ showRhs rhs showRhs ((Nt x):rhs) = x ++ showRhs rhs -instance Show Symbol where+instance (Show t) => Show (Symbol t) where show (Nt s) = s show (Term t) = show t- show (Error e _) = error ("show Error symbol")+