packages feed

babynf-0.1.0.2: lib/Data/BAByNF/ABNF/Rules/Repeat.hs

module Data.BAByNF.ABNF.Rules.Repeat
    ( ref
    , rule
    , fromTree
    ) where

import Data.Functor ((<&>))
import Data.Maybe qualified as Maybe
import Data.List qualified as List

import Data.ByteString qualified as ByteString
import Data.ByteString.Char8 qualified as ByteString.Char8

import Data.BAByNF.Core.Tree (Tree)
import Data.BAByNF.Core.Tree qualified as Tree
import Data.BAByNF.ABNF.Core qualified as Core
import Data.BAByNF.Util.Stream qualified as Stream
import Data.BAByNF.Util.Ascii qualified as Ascii
import Data.BAByNF.Core.Ref qualified as Ref
import Data.BAByNF.ABNF.Model qualified as Model

ref :: Model.Rulename
ref = Model.Rulename (Ascii.stringAsBytesUnsafe "repeat")

rule :: Model.Rule
rule = Model.Rule ref Model.BasicDefinition
    . Model.Elements
    . Model.Alternation
    $ 
        [ Model.Concatenation
            . List.singleton
            . Model.Repetition (Model.RangedRepeat (Model.FixedBound 1) Model.UnBound)
            $ Model.RulenameElement Core.digitRef 
        , Model.Concatenation
            . List.singleton
            . Model.Repetition Model.NoRepeat
            . Model.GroupElement
            . Model.Group
            . Model.Alternation
            . List.singleton
            . Model.Concatenation
            $  
                [ Model.Repetition (Model.RangedRepeat Model.UnBound Model.UnBound)
                    $ Model.RulenameElement Core.digitRef
                , Model.Repetition Model.NoRepeat
                    . Model.CharValElement 
                    . Model.CaseInsensitiveCharVal
                    . Model.CaseInsensitiveString
                    . Model.QuotedString
                    $ Ascii.stringAsBytesUnsafe "*"
                , Model.Repetition (Model.RangedRepeat Model.UnBound Model.UnBound)
                    $ Model.RulenameElement Core.digitRef 
                ]
        ]

fromTree :: Tree Model.Rulename -> Either String Model.Repeat
fromTree tree =
    let stream = do
            mnOpt <- takeDigits
            hasStar <- Stream.take <&> Maybe.isJust
            mxOpt <- if hasStar then takeDigits else return Nothing
            case (mnOpt, hasStar, mxOpt) of
                (Just mns, False, _) -> return $ tryToInteger mns >>= \mn -> return $ Model.FixedRepeat mn
                (_, True, _) -> return $
                    let toBound = Maybe.maybe (Right Model.UnBound) (\x -> tryToInteger x >>= return . Model.FixedBound)
                     in do 
                        lo <- toBound mnOpt
                        hi <- toBound mxOpt
                        Right (Model.RangedRepeat lo hi)
                _ -> return $ Left "structural mismatch for <repeat>"
     in Stream.runStream_ stream (Tree.nodes tree)
    where takeDigits = Stream.takeWhileMap (\e ->
            case e of
                Tree.RefNode r subtree ->
                    if Ref.eq r Core.digitRef
                        then Just $ Tree.stringify subtree
                        else Nothing
                _ -> Nothing
            ) <&> \bs -> case bs of [] -> Nothing; _ -> Just . ByteString.concat $ bs
          tryToInteger bs =
              case ByteString.Char8.readInteger bs of
                  Nothing -> Left "not integer"
                  Just (no, rest) | ByteString.null rest  -> Right no
                                  | otherwise -> Left "more than an integer read"