CanonicaliseLazy

Text.XML.HaXml.Combinators

Contents

- The content filter type.
- Simple filters.
  - Selection filters.
  - Predicate filters.
  - Search filters.
- Filter combinators
  - Basic combinators.
  - Recursive search.
  - Interior editing.
  - Constructive filters.
- C-like conditionals.
- Filters with labelled results.
  - Using and combining labelled filters.
  - Some label-generating filters.

Description

This module defines the notion of filters and filter combinators for processing XML documents.

These XML transformation combinators are described in the paper ``Haskell and XML: Generic Combinators or Type-Based Translation?'' Malcolm Wallace and Colin Runciman, Proceedings ICFP'99.

Synopsis

- type CFilter i = Content i -> [Content i]
- keep :: a -> [a]
- none :: a -> [b]
- children :: CFilter i
- childrenBy :: CFilter i -> CFilter i
- position :: Int -> CFilter i -> CFilter i
- elm :: CFilter i
- txt :: CFilter i
- tag :: String -> CFilter i
- attr :: String -> CFilter i
- attrval :: Attribute -> CFilter i
- tagWith :: (String -> Bool) -> CFilter i
- find :: String -> (String -> CFilter i) -> CFilter i
- iffind :: String -> (String -> CFilter i) -> CFilter i -> CFilter i
- ifTxt :: (String -> CFilter i) -> CFilter i -> CFilter i
- o :: CFilter i -> CFilter i -> CFilter i
- union :: (a -> [b]) -> (a -> [b]) -> a -> [b]
- cat :: [a -> [b]] -> a -> [b]
- andThen :: (a -> c) -> (c -> a -> b) -> a -> b
- (|>|) :: (a -> [b]) -> (a -> [b]) -> a -> [b]
- with :: CFilter i -> CFilter i -> CFilter i
- without :: CFilter i -> CFilter i -> CFilter i
- (/>) :: CFilter i -> CFilter i -> CFilter i
- (</) :: CFilter i -> CFilter i -> CFilter i
- et :: (String -> CFilter i) -> CFilter i -> CFilter i
- path :: [CFilter i] -> CFilter i
- deep :: CFilter i -> CFilter i
- deepest :: CFilter i -> CFilter i
- multi :: CFilter i -> CFilter i
- when :: CFilter i -> CFilter i -> CFilter i
- guards :: CFilter i -> CFilter i -> CFilter i
- chip :: CFilter i -> CFilter i
- inplace :: CFilter i -> CFilter i
- foldXml :: CFilter i -> CFilter i
- mkElem :: String -> [CFilter i] -> CFilter i
- mkElemAttr :: String -> [(String, CFilter i)] -> [CFilter i] -> CFilter i
- literal :: String -> CFilter i
- cdata :: String -> CFilter i
- replaceTag :: String -> CFilter i
- replaceAttrs :: [(String, String)] -> CFilter i
- data ThenElse a = a :> a
- (?>) :: (a -> [b]) -> ThenElse (a -> [b]) -> a -> [b]
- type LabelFilter i a = Content i -> [(a, Content i)]
- oo :: (a -> CFilter i) -> LabelFilter i a -> CFilter i
- x :: (CFilter i -> LabelFilter i a) -> (CFilter i -> LabelFilter i b) -> CFilter i -> LabelFilter i (a, b)
- numbered :: CFilter i -> LabelFilter i Int
- interspersed :: String -> CFilter i -> String -> LabelFilter i String
- tagged :: CFilter i -> LabelFilter i String
- attributed :: String -> CFilter i -> LabelFilter i String
- textlabelled :: CFilter i -> LabelFilter i (Maybe String)
- extracted :: (Content i -> a) -> CFilter i -> LabelFilter i a

# The content filter type.

type CFilter i = Content i -> [Content i]

All document transformations are content filters. A filter takes a single XML `Content` value and returns a sequence of `Content` values, possibly empty.

# Simple filters.

## Selection filters.

In the algebra of combinators, `none` is the zero, and `keep` the identity. (They have a more general type than just CFilter.)

keep :: a -> [a]

none :: a -> [b]

children :: CFilter i

Throw away current node, keep just the (unprocessed) children.

childrenBy :: CFilter i -> CFilter i

Process children using specified filters.

position :: Int -> CFilter i -> CFilter i

Select the `n`'th positional result of a filter.

## Predicate filters.

These filters either keep or throw away some content based on a simple test. For instance, `elm` keeps only a tagged element, `txt` keeps only non-element text, `tag` keeps only an element with the named tag, `attr` keeps only an element with the named attribute, `attrval` keeps only an element with the given attribute value, `tagWith` keeps only an element whose tag name satisfies the given predicate.

elm :: CFilter i

txt :: CFilter i

tag :: String -> CFilter i

attr :: String -> CFilter i

attrval :: Attribute -> CFilter i

tagWith :: (String -> Bool) -> CFilter i

## Search filters.

find :: String -> (String -> CFilter i) -> CFilter i

For a mandatory attribute field, `find key cont` looks up the value of the attribute name `key`, and applies the continuation `cont` to the value.

iffind :: String -> (String -> CFilter i) -> CFilter i -> CFilter i

When an attribute field may be absent, use `iffind key yes no` to lookup its value. If the attribute is absent, it acts as the `no` filter, otherwise it applies the `yes` filter.

ifTxt :: (String -> CFilter i) -> CFilter i -> CFilter i

`ifTxt yes no` processes any textual content with the `yes` filter, but otherwise is the same as the `no` filter.

# Filter combinators

## Basic combinators.

o :: CFilter i -> CFilter i -> CFilter i

Sequential (Irish,backwards) composition

union :: (a -> [b]) -> (a -> [b]) -> a -> [b]

Binary parallel composition. Each filter uses a copy of the input, rather than one filter using the result of the other. (Has a more general type than just CFilter.)

cat :: [a -> [b]] -> a -> [b]

Glue a list of filters together. (A list version of union; also has a more general type than just CFilter.)

andThen :: (a -> c) -> (c -> a -> b) -> a -> b

A special form of filter composition where the second filter works over the same data as the first, but also uses the first's result.

(|>|) :: (a -> [b]) -> (a -> [b]) -> a -> [b]

Directional choice: in `f |>| g` give g-productions only if no f-productions

with :: CFilter i -> CFilter i -> CFilter i

Pruning: in `f `with` g`, keep only those f-productions which have at least one g-production

without :: CFilter i -> CFilter i -> CFilter i

Pruning: in `f `without` g`, keep only those f-productions which have no g-productions

(/>) :: CFilter i -> CFilter i -> CFilter i

Pronounced slash, `f /> g` means g inside f

(</) :: CFilter i -> CFilter i -> CFilter i

Pronounced outside, `f </ g` means f containing g

et :: (String -> CFilter i) -> CFilter i -> CFilter i

Join an element-matching filter with a text-only filter

path :: [CFilter i] -> CFilter i

Express a list of filters like an XPath query, e.g. `path [children, tag "name1", attr "attr1", children, tag "name2"]` is like the XPath query `/name1[@attr1]/name2`.

## Recursive search.

Recursive search has three variants: `deep` does a breadth-first search of the tree, `deepest` does a depth-first search, `multi` returns content at all tree-levels, even those strictly contained within results that have already been returned.

deep :: CFilter i -> CFilter i

deepest :: CFilter i -> CFilter i

multi :: CFilter i -> CFilter i

## Interior editing.

when :: CFilter i -> CFilter i -> CFilter i

Interior editing: `f `when` g` applies `f` only when the predicate `g` succeeds, otherwise the content is unchanged.

guards :: CFilter i -> CFilter i -> CFilter i

Interior editing: `g `guards` f` applies `f` only when the predicate `g` succeeds, otherwise the content is discarded.

chip :: CFilter i -> CFilter i

Process CHildren In Place. The filter is applied to any children of an element content, and the element rebuilt around the results.

inplace :: CFilter i -> CFilter i

Process an element In Place. The filter is applied to the element itself, and then the original element rebuilt around the results.

foldXml :: CFilter i -> CFilter i

Recursive application of filters: a fold-like operator. Defined as `f `o` chip (foldXml f)`.

## Constructive filters.

mkElem :: String -> [CFilter i] -> CFilter i

Build an element with the given tag name - its content is the results of the given list of filters.

mkElemAttr :: String -> [(String, CFilter i)] -> [CFilter i] -> CFilter i

Build an element with the given name, attributes, and content.

literal :: String -> CFilter i

Build some textual content.

cdata :: String -> CFilter i

Build some CDATA content.

replaceTag :: String -> CFilter i

Rename an element tag (leaving attributes in place).

replaceAttrs :: [(String, String)] -> CFilter i

Replace the attributes of an element (leaving tag the same).

# C-like conditionals.

These definitions provide C-like conditionals, lifted to the filter level.

The `(cond ? yes : no)` style in C becomes `(cond ?> yes :> no)` in Haskell.

data ThenElse a

Conjoin the two branches of a conditional.

Constructors

a :> a |

(?>) :: (a -> [b]) -> ThenElse (a -> [b]) -> a -> [b]

Select between the two branches of a joined conditional.

# Filters with labelled results.

type LabelFilter i a = Content i -> [(a, Content i)]

A LabelFilter is like a CFilter except that it pairs up a polymorphic value (label) with each of its results.

## Using and combining labelled filters.

oo :: (a -> CFilter i) -> LabelFilter i a -> CFilter i

Compose a label-processing filter with a label-generating filter.

x :: (CFilter i -> LabelFilter i a) -> (CFilter i -> LabelFilter i b) -> CFilter i -> LabelFilter i (a, b)

Combine labels. Think of this as a pair-wise zip on labels. e.g. `(numbered `x` tagged)`

## Some label-generating filters.

numbered :: CFilter i -> LabelFilter i Int

Number the results from 1 upwards.

interspersed :: String -> CFilter i -> String -> LabelFilter i String

In `interspersed a f b`, label each result of `f` with the string `a`, except for the last one which is labelled with the string `b`.

tagged :: CFilter i -> LabelFilter i String

Label each element in the result with its tag name. Non-element results get an empty string label.

attributed :: String -> CFilter i -> LabelFilter i String

Label each element in the result with the value of the named attribute. Elements without the attribute, and non-element results, get an empty string label.

textlabelled :: CFilter i -> LabelFilter i (Maybe String)

Label each textual part of the result with its text. Element results get an empty string label.

extracted :: (Content i -> a) -> CFilter i -> LabelFilter i a

Label each content with some information extracted from itself.