packages feed

screp-0.2.0.0: src/Scrappy/Grep/DSL.hs

{-# LANGUAGE DeriveGeneric #-}

module Scrappy.Grep.DSL
  ( ParserExpr(..)
  , MatchResult(..)
  , MatchContext(..)
  , containsRef
  , extractRefs
  ) where

import GHC.Generics (Generic)

-- | AST representation of the parsec-grep DSL
data ParserExpr
  -- Primitives
  = PChar Char              -- char 'x'
  | PString String          -- string "abc"
  | PAnyChar                -- anyChar
  | PDigit                  -- digit
  | PLetter                 -- letter
  | PAlphaNum               -- alphaNum
  | PSpace                  -- space
  | PSpaces                 -- spaces
  | PNewline                -- newline
  | POneOf String           -- oneOf "abc"
  | PNoneOf String          -- noneOf "abc"

  -- Combinators
  | PSeq ParserExpr ParserExpr       -- p1 >> p2 (sequence, return second)
  | PSeqConcat ParserExpr ParserExpr -- p1 <+> p2 (sequence, concatenate results)
  | PAlt ParserExpr ParserExpr       -- p1 <|> p2 (alternative)
  | PMany ParserExpr                 -- many p (zero or more)
  | PSome ParserExpr                 -- some p (one or more)
  | POptional ParserExpr             -- optional p (zero or one)
  | PTry ParserExpr                  -- try p (backtracking)
  | PBetween Char Char ParserExpr    -- between '(' ')' p
  | PCount Int ParserExpr            -- count 3 p
  | PManyTill ParserExpr ParserExpr  -- manyTill p end (non-greedy)

  -- References to named parsers (from config file)
  | PRef String                      -- ref "email"
  deriving (Show, Eq, Generic)

-- | A match result with position information
data MatchResult = MatchResult
  { mrFilePath  :: FilePath
  , mrLine      :: Int       -- Starting line (1-indexed)
  , mrCol       :: Int       -- Starting column (1-indexed)
  , mrMatchText :: String    -- The matched text (may contain newlines)
  , mrContext   :: Maybe MatchContext  -- Optional context lines
  } deriving (Show, Eq, Generic)

-- | Context lines around a match
data MatchContext = MatchContext
  { mcBefore :: [String]    -- Lines before the match
  , mcAfter  :: [String]    -- Lines after the match
  } deriving (Show, Eq, Generic)

-- | Check if a ParserExpr contains any PRef nodes
containsRef :: ParserExpr -> Bool
containsRef expr = case expr of
  PRef _           -> True
  PSeq a b         -> containsRef a || containsRef b
  PSeqConcat a b   -> containsRef a || containsRef b
  PAlt a b         -> containsRef a || containsRef b
  PMany a          -> containsRef a
  PSome a          -> containsRef a
  POptional a      -> containsRef a
  PTry a           -> containsRef a
  PBetween _ _ a   -> containsRef a
  PCount _ a       -> containsRef a
  PManyTill a b    -> containsRef a || containsRef b
  _                -> False

-- | Extract all ref names from a ParserExpr
extractRefs :: ParserExpr -> [String]
extractRefs expr = case expr of
  PRef name        -> [name]
  PSeq a b         -> extractRefs a ++ extractRefs b
  PSeqConcat a b   -> extractRefs a ++ extractRefs b
  PAlt a b         -> extractRefs a ++ extractRefs b
  PMany a          -> extractRefs a
  PSome a          -> extractRefs a
  POptional a      -> extractRefs a
  PTry a           -> extractRefs a
  PBetween _ _ a   -> extractRefs a
  PCount _ a       -> extractRefs a
  PManyTill a b    -> extractRefs a ++ extractRefs b
  _                -> []