1.3. Creating New Filters


1.3.1. Using Event Processing

SDF's event processing feature makes it easy to create a new filter from an existing one. For example, the following lines make grid a variation of the table filter:

!on filter 'grid';; \
    $name='table'; $params.="; style='grid'"

1.3.2. Using Perl

Perl subroutines can be used to implement filter logic. An SDF filter called xxx is mapped to a Perl subroutine called xxx_Filter within the SDF_USER package. The subroutine has the following interface:

       xxx_Filter(*text, %param);

where:

For example, the Perl implementation of the appendix filter is:

sub appendix_Filter {
    local(*text, %param) = @_;

    # change headings
    unshift(@text, '!on paragraph \'H\d\'; __appendix; $style =~ s/H/A/');
    push   (@text, '!off paragraph __appendix');
}

The perllib/sdf/filters.pl file within the SDF distribution contains lots of examples of filters implemented as Perl subroutines.


1.3.3. Adding Parameters

Parameters can be declared in a Perl array called _xxx_FilterParams. The array is a table in TBL format which contains the following fields:

Field Description
Name The parameter name
Type The parameter type
Rule The parameter validation rule, if any

Parameter types and rules follow the same conventions as macro argument types and rules. Refer to Creating New Macros for details.

For example, the parameters for the example filter are declared like this:

@_example_FilterParams = (
    'Name       Type        Rule',
    'skipheader boolean',
    'lang       string',
    'wide       boolean',
    'listitem   integer',
    'pure       boolean',
);

1.3.4. Arbitary Parameters

Occasionally, it is useful to support arbitary parameters. To do this, specify ('ANY') as the value of the parameter table. For example, the sdf filter can be used to apply an arbitary set of attributes to a block of text. Its parameter table is:

@_sdf_FilterParams = ('ANY');

1.3.5. Data Models

If the text is a table, the filter typically has a data model (i.e. validation table) called xxx_FilterModel. To speed up processing, model validation is only done in verbose mode. For example, the define filter is defined as follows:

# define - variable definitions
@_define_FilterParams = (
    'Name       Type        Rule',
    'family     string      <\w+>',
    'export     boolean',
);
@_define_FilterModel = (
    'Field      Category    Rule',
    'Name       key         <\w+>',
    'Value      optional',
);
sub define_Filter {
    local(*text, %param) = @_;
    local(@tbl, @flds, $rec, %values);
    local($family, $export, $name);

    # Parse and validate the data
    @tbl = &'TableParse(@text);
    @text = ();
    &_FilterValidate(*tbl, *_define_FilterModel) if $validate;

    # Process the data
    $family = $param{'family'};
    $family .= '_' if $family ne '';
    $export = $param{'export'};
    (@flds) = &'TableFields(shift @tbl);
    for $rec (@tbl) {
        %values = &'TableRecSplit(*flds, $rec);
        $name = $family . $values{'Name'};
        push(@text, "!define $name $values{'Value'}");
        push(@text, "!export $name") if $export;
    }
}

1.3.6. Dynamic Filter Loading

Most filters are either built-in or defined in a library or module which is loaded at the top of a document. The script filter makes it easy to embed Perl code within an SDF file, and library and module files are simply normal SDF files.

However, if SDF is asked to apply a filter which it does not know about, it looks for a file called filter_name.sdp and dynamically loads the filter from that file if it can. This feature is useful for complex filters which are rarely used.

See the stdlib/module.sdp file for an example.