packages feed

hydra-kernel-0.18.0: src/main/haskell/Hydra/Core/Print/Posix/Regex.hs

-- Note: this is an automatically generated file. Do not edit.

-- | Per-dialect printer rendering the hydra.core.regex AST into POSIX ERE syntax (Haskell Text.Regex.TDFA, Scheme Guile (ice-9 regex)). Differs from the canonical hydra.core.print.regex in three places, each probe-verified against BOTH TDFA and Guile (docs/specification/regex.md and issue #567/#603/#729-land): (1) Hydra's . (any character INCLUDING newline) renders as (.|<newline>), a grouped alternation, because POSIX ERE's native . excludes newline, does not support the \s / \S shorthands, and (on Guile specifically) rejects a literal NUL byte inside a bracket expression, ruling out an explicit [<U+0000>-<U+10FFFF>] range class; (2) anchors ^ and $ (whole-STRING boundaries in Hydra) render as the GNU/glibc whole-buffer anchors \` and \', because TDFA's native ^ and $ are line-oriented; (3) character-class metacharacters ] ^ - are disambiguated POSITIONALLY (] first, - last, ^ never first) rather than by backslash-escaping, because POSIX ERE bracket expressions have no escape mechanism at all (a backslash inside [...] is an ordinary character on TDFA). All other productions are native POSIX ERE.

module Hydra.Core.Print.Posix.Regex where

import qualified Hydra.Core.Ast as Ast
import qualified Hydra.Core.Coders as Coders
import qualified Hydra.Core.Diff as Diff
import qualified Hydra.Core.Docs as Docs
import qualified Hydra.Core.Error.Checking as Checking
import qualified Hydra.Core.Error.File as ErrorFile
import qualified Hydra.Core.Error.Model as ErrorModel
import qualified Hydra.Core.Error.Packaging as ErrorPackaging
import qualified Hydra.Core.Error.System as ErrorSystem
import qualified Hydra.Core.Errors as Errors
import qualified Hydra.Core.File as File
import qualified Hydra.Core.Graph as Graph
import qualified Hydra.Core.Json.Model as JsonModel
import qualified Hydra.Core.Overlay.Haskell.Lib.Equality as Equality
import qualified Hydra.Core.Overlay.Haskell.Lib.Lists as Lists
import qualified Hydra.Core.Overlay.Haskell.Lib.Literals as Literals
import qualified Hydra.Core.Overlay.Haskell.Lib.Logic as Logic
import qualified Hydra.Core.Overlay.Haskell.Lib.Pairs as Pairs
import qualified Hydra.Core.Overlay.Haskell.Lib.Strings as Strings
import qualified Hydra.Core.Markdown as Markdown
import qualified Hydra.Core.Model as Model
import qualified Hydra.Core.Packaging as Packaging
import qualified Hydra.Core.Parsing as Parsing
import qualified Hydra.Core.Paths as Paths
import qualified Hydra.Core.Query as Query
import qualified Hydra.Core.Regex as Regex
import qualified Hydra.Core.Relational as Relational
import qualified Hydra.Core.System as System
import qualified Hydra.Core.Tabular as Tabular
import qualified Hydra.Core.Testing as Testing
import qualified Hydra.Core.Time as Time
import qualified Hydra.Core.Topology as Topology
import qualified Hydra.Core.Typed as Typed
import qualified Hydra.Core.Typing as Typing
import qualified Hydra.Core.Util as Util
import qualified Hydra.Core.Validation as Validation
import qualified Hydra.Core.Variants as Variants
import Prelude hiding  (Enum, Ordering, decodeFloat, encodeFloat, fail, lines, map, pure, sum, unlines)
import qualified Data.Scientific as Sci
import Data.Void

-- | Render an alternation, joining its branches with the | operator.
alternation :: [[Regex.Quantified]] -> String
alternation alt = Strings.join "|" (Lists.map regexSequence alt)

-- | The POSIX ERE rendering of Hydra's . (any character incl. newline): '.' alternated with an explicit newline, grouped — portable across POSIX ERE engines including Guile, which rejects a literal NUL byte in a bracket expression.
anyClass :: String
anyClass =
    Strings.concat [
      "(.|",
      (printCodePoint 10),
      ")"]

-- | Render a single atom; . renders as (.|<newline>), portable across POSIX ERE engines.
atom :: Regex.Atom -> String
atom a =
    case a of
      Regex.AtomLiteral v0 -> escapeLiteral v0
      Regex.AtomAny -> anyClass
      Regex.AtomAnchorStart -> "\\`"
      Regex.AtomAnchorEnd -> "\\'"
      Regex.AtomGroup v0 -> Strings.concat [
        "(",
        (alternation v0),
        ")"]
      Regex.AtomClass v0 -> characterClass v0

-- | Render a character class, including the leading ^ for a negated class. Reorders items positionally for POSIX ERE (which has no bracket-expression escaping): a literal ] is moved to the front (immediately after the optional negating ^), a literal - is moved to the back, and (only when there is no literal ] item to occupy the first slot) a literal ^ is moved after the first non-^ item -- POSIX treats a leading ^ as the negation marker regardless of Hydra's negated flag. The one residual gap: a class whose only member is a literal ^ has no safe position on POSIX ERE; this is an extreme corner case (single-item classes are otherwise unremarkable) and is left unhandled here.
characterClass :: Regex.CharacterClass -> String
characterClass cc =

      let items = Regex.characterClassItems cc
          bracketAndRest = Lists.partition (classItemIsLiteral 93) items
          bracketItems = Pairs.first bracketAndRest
          afterBracket = Pairs.second bracketAndRest
          dashAndRest = Lists.partition (classItemIsLiteral 45) afterBracket
          dashItems = Pairs.first dashAndRest
          rest = Pairs.second dashAndRest
          caretAndNonCaret = Lists.partition (classItemIsLiteral 94) rest
          caretItems = Pairs.first caretAndNonCaret
          nonCaretItems = Pairs.second caretAndNonCaret
          safeRest =
                  Logic.ifElse (Logic.or (Logic.not (Lists.isEmpty bracketItems)) (Lists.isEmpty nonCaretItems)) rest (Lists.concat2 nonCaretItems caretItems)
          ordered =
                  Lists.concat [
                    bracketItems,
                    safeRest,
                    dashItems]
      in (Strings.concat [
        "[",
        (Logic.ifElse (Regex.characterClassNegated cc) "^" ""),
        (Strings.concat (Lists.map classItem ordered)),
        "]"])

-- | Render one character-class member (a single character or an inclusive range). POSIX ERE bracket expressions do NOT support backslash escaping (confirmed by TDFA probe: \- and \] inside [...] are two literal characters, not an escape); the metacharacters ] ^ - are instead disambiguated positionally by characterClass (] first, - last, ^ never first), so a plain character or range here never needs an escape.
classItem :: Regex.ClassItem -> String
classItem item =
    case item of
      Regex.ClassItemCharacter v0 -> printCodePoint v0
      Regex.ClassItemRange v0 -> Strings.concat [
        printCodePoint (Regex.characterRangeFrom v0),
        "-",
        (printCodePoint (Regex.characterRangeTo v0))]

-- | True if a ClassItem is the single literal character code point c (not a range).
classItemIsLiteral :: Int -> Regex.ClassItem -> Bool
classItemIsLiteral c item =
    case item of
      Regex.ClassItemCharacter v0 -> Equality.equal v0 c
      _ -> False

-- | Render a literal code point in top-level context, backslash-escaping POSIX ERE metacharacters.
escapeLiteral :: Int -> String
escapeLiteral c =

      let isMeta =
              Lists.foldl (\acc -> \m -> Logic.or acc (Equality.equal c m)) False [
                46,
                94,
                36,
                42,
                43,
                63,
                40,
                41,
                91,
                93,
                123,
                125,
                124,
                92]
      in (Logic.ifElse isMeta (Strings.concat2 "\\" (printCodePoint c)) (printCodePoint c))

-- | Render a single Unicode code point as a one-character string.
printCodePoint :: Int -> String
printCodePoint c = Strings.fromList [
  c]

-- | Render a hydra.core.regex AST into POSIX ERE syntax. Identical to the canonical printer except that . (any incl. newline) becomes the grouped alternation (.|<newline>).
printRegex :: [[Regex.Quantified]] -> String
printRegex r = alternation r

-- | Render an atom followed by its quantifier suffix.
quantified :: Regex.Quantified -> String
quantified qa = Strings.concat2 (atom (Regex.quantifiedAtom qa)) (quantifier (Regex.quantifiedQuantifier qa))

-- | Render a quantifier suffix; the 'one' quantifier renders as the empty string.
quantifier :: Regex.Quantifier -> String
quantifier q =
    case q of
      Regex.QuantifierOne -> ""
      Regex.QuantifierZeroOrOne -> "?"
      Regex.QuantifierZeroOrMore -> "*"
      Regex.QuantifierOneOrMore -> "+"
      Regex.QuantifierExactly v0 -> Strings.concat [
        "{",
        (Literals.printInt32 v0),
        "}"]
      Regex.QuantifierAtLeast v0 -> Strings.concat [
        "{",
        (Literals.printInt32 v0),
        ",}"]
      Regex.QuantifierRange_ v0 -> Strings.concat [
        "{",
        (Literals.printInt32 (Regex.quantifierRangeMin v0)),
        ",",
        (Literals.printInt32 (Regex.quantifierRangeMax v0)),
        "}"]

-- | Render a sequence of quantified atoms by concatenation.
regexSequence :: [Regex.Quantified] -> String
regexSequence s = Strings.concat (Lists.map quantified s)