WikiVar
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.
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.
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 is use of WikiVar by character sequences in the processed text.
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 $.
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$"$
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.
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.
Create a new instance of WikiVar. The argument list is treated as a hash with the following keys examined.
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).
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.
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.
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.
This defines a macro or a function. The arguments are
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.
The value of the variable. This should be one of
A scalar. The result is a macro and the printable version of the value is used as the substitution text for the macro.
A reference to a subroutine. The result is a function. When the function is
invoked the subroutine is called with the arguments provided by the inline
invocation as an instance of the Text::WikiVar::Arguments class.
An instance of Text::WikiVar::CodeRef. This allows a subroutine to be
specified by name. The actual code reference is not accessed until the function
is invoked. This is useful if there are initialization ordering problems and the
module with the subroutine may not be loaded when assign is called. If a
subroutine is defined at the point of assignment, it is generally preferable to
just pass a reference to it instead of using CodeRef.
undef. This will erase the definition of the variable.
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.
This performs the substitution for the variable. The return value is the substitution string for the variable. The arguments are
The name of the variable.
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.
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.
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.
The text string to transform.
A reference to a hash containing processing flags. The currently defined flags are
If set to a true value, a scope is entered before processing and
left after processing, isolating any inline definitions in the text.
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.
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.
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.
name The fully qualified name of the subroutine.
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
$args->[0] is the name of the function.
$args->[$n] is the nth positional argument.
$args->{name} The value for the named or keyword argument name. The value
for a keyword argument is a true value if the keyword was present, and a false
value if not. The value for a named argument is undef if the argument was not
present, the value if it was. The exists keyword can also be used to check if
a named or keyword argument was present.
An instance is also a class with various utility methods.
argc Return the number of positional arguments.
argv Identical to $args->[]. Gotta give my fellow C/C++ programmers some love.
value The same as $args->{} except that the argument is not implicitly
quoted. Useful when the argument name is in a variable and not a literal.
word The WikiVar variable name invoked. Identical to $args->[0] but
a bit clearer in context.
force_array Force values for named arguments to be ARRAY references. The
arguments to force_array are names that name named arguments. If the value
for one of the names is already an ARRAY reference it is unchanged. If it is
not, it is converted to a reference to an ARRAY of length 1.
required_args Return a reference to an array of the positional arguments. The invoking word is not included. Cached so it's very low marginal cost to call multiple times.
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.
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
Alan M. Carroll
https://network-geographics.com
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 (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.