adp-multi-0.1.0: src/ADP/Multi/Combinators.hs
{-# LANGUAGE ImplicitParams #-}
module ADP.Multi.Combinators where
import Data.Maybe
import Data.Array
import qualified Control.Arrow as A
import ADP.Debug
import ADP.Multi.Parser
import ADP.Multi.Rewriting
{-
TODO
Weakening types:
The Subword in Parser could be made generic as a list. Then
the subword in Ranges would also be a list instead of a tuple. The simple parser would
then pattern match his accepting subword as e.g. [x1,x2,x3,x4] and this would happen for
every call to a parser. It's not clear how well GHC optimizes this, probably not as much as tuples.
We would loose some type-safety and (probably) performance but still have readable code.
=> This branch tries the above approach.
Using full type system without data-constructs:
If no data-constructs are used, then instead we need many combinations of overloads simulated with
type classes. Then the rewriting functions would also (need to) be type-safe, but it is not yet clear
how to do that.
-> Considering that this is a prototype and probably won't be used in this form, it might be too much effort
to get full type-safety.
-}
-- TODO use static info about min yield sizes for self-recursion
-- This is not easy to solve for indirect recursion like S -> a | aP, P -> aS | a
-- At the moment we would use S -> a | a ~~~| P and P -> a ~~~| S | a to prevent
-- endless recursion at yield size analysis. Therefore, as ~~~| isn't only used
-- for direct self-recursion, we would need to analyse the grammar in its whole to
-- detect cycles which seems impossible without creating a complete AST.
-- TODO define which grammars are not useable without a whole-grammar yield size analysis
infix 8 <<<
(<<<) :: Parseable p a b => (b -> c) -> p -> ([ParserInfo], [Ranges] -> Parser a c)
(<<<) f parseable =
let (info,parser) = toParser parseable
in (
[info],
\ [] z subword -> map f (parser z subword)
)
-- special version of <<< which ignores the first parser for determining the yield sizes
-- for dim1 parsers
infix 8 <<<|
(<<<|) :: Parseable p a b => (b -> c) -> p -> ([ParserInfo], [Ranges] -> Parser a c)
(<<<|) f parseable =
let (_,parser) = toParser parseable
info = ParserInfo1 { minYield = 0, maxYield = Nothing }
in (
[info],
\ [] z subword -> map f (parser z subword)
)
-- special version of <<< which ignores the first parser for determining the yield sizes
-- for dim2 parsers
infix 8 <<<||
(<<<||) :: Parseable p a b => (b -> c) -> p -> ([ParserInfo], [Ranges] -> Parser a c)
(<<<||) f parseable =
let (_,parser) = toParser parseable
info = ParserInfo2 { minYield2 = (0,0), maxYield2 = (Nothing,Nothing) }
in (
[info],
\ [] z subword -> map f (parser z subword)
)
infixl 7 ~~~
(~~~) :: Parseable p a b => ([ParserInfo], [Ranges] -> Parser a (b -> c)) -> p -> ([ParserInfo], [Ranges] -> Parser a c)
(~~~) (infos,leftParser) parseable =
let (info,rightParser) = toParser parseable
in (
info : infos,
\ ranges z subword ->
[ pr qr |
qr <- rightParser z subword
, RangeMap sub rest <- ranges
, pr <- leftParser rest z sub
]
)
-- special version of ~~~ which ignores the right parser for determining the yield sizes
-- this must be used for self-recursion, mutual recursion etc. There must be no cycles!
-- I guess this only works because of laziness (ignoring the info value of toParser).
-- for 1-dim parsers
infixl 7 ~~~|
(~~~|) :: Parseable p a b => ([ParserInfo], [Ranges] -> Parser a (b -> c)) -> p -> ([ParserInfo], [Ranges] -> Parser a c)
(~~~|) (infos,leftParser) parseable =
let (_,rightParser) = toParser parseable
info = ParserInfo1 { minYield = 0, maxYield = Nothing }
in (
info : infos,
\ ranges z subword ->
[ pr qr |
qr <- rightParser z subword
, RangeMap sub rest <- ranges
, pr <- leftParser rest z sub
]
)
-- for 2-dim parsers
infixl 7 ~~~||
(~~~||) :: Parseable p a b => ([ParserInfo], [Ranges] -> Parser a (b -> c)) -> p -> ([ParserInfo], [Ranges] -> Parser a c)
(~~~||) (infos,leftParser) parseable =
let (_,rightParser) = toParser parseable
info = ParserInfo2 { minYield2 = (0,0), maxYield2 = (Nothing,Nothing) }
in (
info : infos,
\ ranges z subword ->
[ pr qr |
qr <- rightParser z subword
, RangeMap sub rest <- ranges
, pr <- leftParser rest z sub
]
)
infix 6 >>>|
(>>>|) :: (?yieldAlg1 :: YieldAnalysisAlgorithm Dim1, ?rangeAlg1 :: RangeConstructionAlgorithm Dim1)
=> ([ParserInfo], [Ranges] -> Parser a b) -> Dim1 -> RichParser a b
(>>>|) = rewrite ?yieldAlg1 ?rangeAlg1
infix 6 >>>||
(>>>||) :: (?yieldAlg2 :: YieldAnalysisAlgorithm Dim2, ?rangeAlg2 :: RangeConstructionAlgorithm Dim2)
=> ([ParserInfo], [Ranges] -> Parser a b) -> Dim2 -> RichParser a b
(>>>||) = rewrite ?yieldAlg2 ?rangeAlg2
rewrite yieldAlg rangeAlg (infos,p) f =
let yieldSize = yieldAlg f infos
in trace (">>> yield size: " ++ show yieldSize) $
(
yieldSize,
\ z subword ->
let ranges = rangeAlg f infos subword
in trace (">>> " ++ show subword) $
trace ("ranges: " ++ show ranges) $
[ result |
RangeMap sub rest <- ranges
, result <- p rest z sub
]
)
infixr 5 |||
(|||) :: RichParser a b -> RichParser a b -> RichParser a b
(|||) (ParserInfo1 {minYield=minY1, maxYield=maxY1}, r) (ParserInfo1 {minYield=minY2, maxYield=maxY2}, q) =
(
ParserInfo1 {
minYield = min minY1 minY2,
maxYield = if isNothing maxY1 || isNothing maxY2 then Nothing else max maxY1 maxY2
},
\ z subword -> r z subword ++ q z subword
)
(|||) (ParserInfo2 {minYield2=minY1, maxYield2=maxY1}, r) (ParserInfo2 {minYield2=minY2, maxYield2=maxY2}, q) =
(
ParserInfo2 {
minYield2 = combineMinYields minY1 minY2,
maxYield2 = combineMaxYields maxY1 maxY2
},
\ z subword -> r z subword ++ q z subword
)
(|||) _ _ = error "Different parser dimensions can't be combined with ||| !"
combineMinYields :: (Int,Int) -> (Int,Int) -> (Int,Int)
combineMinYields (min11,min12) (min21,min22) = (min min11 min21, min min12 min22)
combineMaxYields :: (Maybe Int,Maybe Int) -> (Maybe Int,Maybe Int) -> (Maybe Int,Maybe Int)
combineMaxYields (a,b) (c,d) =
( if isNothing a || isNothing c then Nothing else max a c
, if isNothing b || isNothing d then Nothing else max b d
)
infix 4 ...
(...) :: RichParser a b -> ([b] -> [b]) -> RichParser a b
(...) (info,r) h = (info, \ z subword -> h (r z subword) )
--(...) richParser h = A.second (\ r z subword -> h (r z subword) ) richParser
type Filter a = Array Int a -> Subword -> Bool
with :: RichParser a b -> Filter a -> RichParser a b
with (info,q) c =
(
info,
\ z subword -> if c z subword then q z subword else []
)