Name

WikiVar


Synopsis

WikiVar is a text formatter that provides roughly the same functionality as both Wiki variables and macros. That is, both simple text substitution and code invocation. Text substitutions (macros) can be defined programmatically or inline. Code invocations ("functions") can only be defined programmatically but used inline (thereby providing a mechanism for calling pre-existing Perl subroutines from text input).

Stylistically WikiVar is based on Wiki variables but extends the syntax to provide better function invocation capabilities. It supports both positional, named, and keyword arguments with robust quoting mechanisms.


Description

Background

WikiVar was originally intended as a module for an actual Wiki to improve variable handling, which for that Wiki code was scattered among numerous regular expressions in many modules, leading to a disturbing lack of consistency. Instead of that, WikiVar would provide a single place for registering all Wiki functions and, because it was easy and consistent to add, macros. Bad interactions between the variables would be minimized because they would all be processed in one pass, not each in its own separate pass, and much faster for the same reason. That project was sidelined but I kept WikiVar because it was useful in other circumstances.

Overview

WikiVar supports two types of usage, "macros" and "functions". Stylistically these are very similar, but the implementation / API differ.

Macros are simple text substitution. The invocation of a macro is replaced by the text for that macro. Macros can be defined inline or via the API. This makes them handy for predefined terms or local abbrevations.

Functions are calls to Perl subroutines. Arguments (both positional and named) are passed to the subroutines. The invocation is replaced by the output of the subroutine. Functions must be defined via the API, they cannot be assigned inline (only invoked).

Inline Usage

Inline usage is use of WikiVar by character sequences in the processed text.

Using a macro inline

The macro "name" is invoked by the character sequence $name$. This set of characters is replaced by the value of the macro. As a special case, two adjacent delimiters are converted to a single delimiter. That is, $$ is turned in to $.

Defining a macro inline

To define the macro "name" to have the value "value", use the text $name=@value@$ where @ is a &delimiter;. The delimiter can be any printable non-whitespace character that is not a valid macro name character except $, >, ) , ] , or } . The terminal delimiter is the same as the initial one, except for < , ( , [ , C < { >. In these cases the terminal delimiter is >, ) , ] , or } respectively. The value must not contain any instances of the delimiter. Because of the wide variety of delimiters that can be used, this is rarely a problem. Even when it is, there is a work around described after the following examples.

Examples:

$name="value"$

$name=(value)$

$name=|value|$

The name must start with a letter or an underscore, and can contain letters, underscores, numbers and dashes. It may not end with a dash.

Note that no white space is allowed in the syntax. The variable delimiter $ must immediately precede the initial name character and immediately follow the terminal value delimiter. This avoids problems with the use of the variable delimiter in normal text.

The actual text of the assignment (everything between the $ characters and those characters as well) is removed completely the output text.

The value itself is evaluated so that any variables in the value are evaluated. This is intended to allow defining a macro in terms of one or more other macros. However, it does mean that you can define new macros in the value part of a macro definition. This nested define must use a different delimiter than the outer define, limiting the possible recursion depth. However, such use is generally more trouble than benefit and is mentioned only to warn you to no do it.

There is no provision for escaping characters in the value. This is acceptable because of the ability to select from a larger number of delimiters. If that's not sufficient then another macro consisting of just the problematic character can be defined and used to put the delimiter in to the macro value.

$dq='"'$ $name="A double quote $dq$"$

Using a function inline

To invoke the function "func", use the text $func$. This calls the Perl subroutine associated with the name "func" and replaces the text with the output of the subroutine.

Arguments can be passed. Such arguments are of three types, positional, named, and keyword. In general, positional arguments are required and named / keyword arguments are optional, but this is by convention. Each argument value is delimited in the same way as macro text. Named arguments are indicated by putting the undelimited name in front of the argument value &without intervening whitespace;. Keyword arguments are names with a leading keyword mark character if keywords have been enabled (keyword arguments are disabled by default). Named arguments have values, keyword arguments don't, and positional arguments are just values.

Named and keyword arguments can be in any order, interspersed among the positional arguments in any fashion, but positional arguments are passed in the same order as they occur in the text. Named and keyword arguments are ignored for the purposes of positional arguments, so that the following are equivalent (presuming ':' has been set as the keyword marking character):

$func (arg1) 'arg2' <arg3> name1(value1) name2%value2% :key1 :key2$

$func :key2 {arg1} name2<value2> "arg2" :key1 name1[value1] (arg3)$

Conventionally the positional arguments are placed first, followed by named arguments, then keyword arguments. This is simply to reduce confusion, however, and you can use a different system if that works better for you.

Programmatic API

WikiVar supports two interfaces, object oriented and functional. These work the same way except that the functional interface uses a single pre-allocated global instance while the object oriented styles lets the client create and control multiple instances. Both of these are useful in different circumstances. The primary advantage of the OO interface is that the different instances can be set to have different processing styles (e.g. different variable delimiters) or different sets of defined macros / functions.

Text::WikiVar

Method new

Create a new instance of WikiVar. The argument list is treated as a hash with the following keys examined.

DELIMITER

The variable delimiter which must be a single printable non-alphanumeric/underscore/dash character. Default value is '$'. Setting this to '%' will make invocations look more like normal Wiki variables. Note that the value for this will be forbidden for use as a value delimiter (and if set to something other than '$', then '$' can be used for delimiting values).

KEYWORD_MARK

Character mark for keywords. This has the same limitations as DELIMITER and is similarly removed from the pool of valid value delimiters. The default is the empty string which disables keyword arguments. If set, keyword arguments will be processed. Of course, this and DELIMITER must be different characters.

FALLBACK

This is used when text contains what looks like a WikiVar invocation but the name is not defined. If this is a string, then that string is used as the value of the variable. If FALLBACK is a CODE reference then it is called as if the undefined name was a function and FALLBACK the code for that function. Note that as with AUTOLOAD, this function can change the set of defined variable names. If an undefined name is use and FALLBACK is not set (the default) then an error message is generated.

This can be changed later using the fallback method.

PRE_ERROR_TEXT, POST_ERROR_TEXT

If an error occurs, an error message is generated and used as the value of the variable causing the error. These values are prepended and appended to that message. The default is <!-- WikiVar Error: and --> respectively, so that if used to process HTML the errors are findable but not visible.

Method assign

This defines a macro or a function. The arguments are

name

The name of the variable. This must consist only of alphanumeric, dash or underscore characters. The name must start with a letter or an underscore, and must not end with a dash.

value

The value of the variable. This should be one of

Method fallback

This has the same semantics as assign except that it stores the variable in a special slot in the WikiVar instance and invokes it whenever a WikiWord is used that is not defined. The default behavior in such a case is to generate an error message as the output text but if the fallback is set then it is called instead and its return value used.

Method invoke

This performs the substitution for the variable. The return value is the substitution string for the variable. The arguments are

name

The name of the variable.

arguments

If name is a not a function then arguments is ignored and may be omitted. Otherwise the arguments are passed on to the subroutine as is. Since the function is likely to be expecting a Text::WikiVar::Arguments object you may want to use the string_to_args method.

Method string_to_args

Convert an argument string to an instance Text::WikiVar::Arguments. The string format is as described for inline invocation. The argument list can be passed directly to a subroutine or passed, along with a name, to invoke. Note that argument values are processed for further WikiVar substitutions.

process

This transforms a string, performing all macro substitutions and updating the context for any new macro definitions. It returns the transformed string. It takes two arguments.

text

The text string to transform.

flags

A reference to a hash containing processing flags. The currently defined flags are

scope

If set to a true value, a scope is entered before processing and left after processing, isolating any inline definitions in the text.

Method enter_scope

Creates a new scope for definitions. Any definitions made in this scope can be reverted with leave_scope. This is handy when processing multiple chunks of user text so that inline definitions in one chunk do not propagate to other chunks.

Method leave_scope

Destroys the current definition scope. All definitions (both inline and via assign) made in the scope are reverted to their state before the scope was entered.

Text::WikiVar::CodeRef

As noted previously, this class is useful when intialization ordering means that a subroutine to be used as a function may not be loaded when assign is called. In that case, a CodeRef wrapper can be used to delay access to the CODE reference until the function is invoked during text processing.

Method new

Text::WikiVar::Arguments

An instance of this class is passed to the subroutine for a function. It contains all of the arguments supplied in the text that invoked the function. It can be treated as an array reference for positional arguments or a hash reference for named and keyword arguments. If $args is an instance of this class passed to a function, then

An instance is also a class with various utility methods.

Example

Here is some example text illustrating the basic usage.

 $Bob='<a href="mailto:bob@nowhere.com">Bob</a>'$
 $BobHomePage=(<a href="http://nowhere.com/~bob">Home Page</a>)$
 $Bobco="Bob's Organic Software and Vegetables"$
 I first met $Bob$ a about 10 years ago. You can check out his biography at his
 $BobHomePage$. Shortly after I met him he started $Bobco$ to combine his talent
 for software development with his wife's green thumb. $Bobco$ has been a rousing
 success, their "free onion with every download" marketing scheme taking the
 market by storm. I see a good future for $Bob$ and $Bobco$.

After processing, this yields

 I first met <a href="mailto:bob@nowhere.com">Bob</a> a about 10 years ago. You
 can check out his biography at his
 <a href="http://nowhere.com/~bob">Home Page</a>. Shortly after I met him he
 started Bob's Organic Software and Vegetables to combine his talent for software
 development with his wife's green thumb. Bob's Organic Software and Vegetables
 has been a rousing success, their "free onion with every download" marketing
 scheme taking the market by storm. I see a good future for <a
 href="mailto:bob@nowhere.com">Bob</a> and Bob's Organic Software and Vegetables.


Version History

    0.1: 10 Oct 2003
    1.0: 19 Nov 2004
    1.1 : 16 May 2008
    1.2 : 26 Oct 2008
    1.2.1 : 04 Dec 2008


Author

    Alan M. Carroll
    https://network-geographics.com


Additional Credits

The original goal of this project was to extend Textile so that I could do inline text macros (particularly, predefined ones for links). Originally I had hacked the Textile source to define the macros, which made maintenance difficult. WikiVar was started to generalize that. Eventually most of that functionality was provided by Webiki, but I still use WikiVar for inline macros and certain Perl based functionality.

While doing the design I started working with Wiki and decided to expand the functionality enough to handle Wiki variables as well. This was part of an effort to support Textile formatting in a Wiki. The base logic for handling Wiki variables was a complete hack, not generalized and scattered about the source code. WikiVar supports all of the required utility in one place, in an easily customized, more consistent, and more efficient way.


Copyright and License

    Copyright (c) 2008 Network Geographics Inc.
    (https://network-geographics.com/)
    All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name "WikiVar" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.