Originally published on: Sun, 11 Oct 2009 01:33:22 +0000
Many programs contain embedded scripting languages that allow the user to write their own scripts to customize the program's behavior. Often, if the user does not write scripts themselves, they find themselves using scripts written by others.
I would like to embed a scripting language in some of my programs, but I haven't found anything that meets my needs. Most options available are larger than I'd like. I really want a simple command-processor shell that I can extend with custom commands specialized for a given piece of software.
I wrote a processor for a language that I call TAP. TAP is a recursive acronym that stands for "TAP Ain't Pretty." The name is derived from the spartan syntax and lack of expressiveness in the language.
In this post, I will describe the TAP syntax, the six core TAP commands, and the mechanisms used to extend TAP with new commands.
TAP is line-oriented. Its syntax is similar to that of a Windows batch file. The '#' character at the beginning of a line denotes a comment. The ':' character at the beginnng of a line denotes a label ( a target for the GOTO or CALL commands. ) Blank lines are ignored.
The general syntax for invoking a TAP command is simply the command name followed by any number of parameters separated by spaces.
If a parameter begins with a '$' character, it is treated as a variable. In this case, the variable's value is passed to the command in place of this parameter. If the variable has not been set to a specific value, an empty string is passed.
If a parameter begins with double quotation-marks, the string bounded by a closing double-quotation mark ( or the end of the line ) is passed to the command. A single quotation-mark may begin a string, but it must be terminated by another single quotation-mark.
All other words appearing on a line separated by spaces are passed as strings to the command.
TAP commands and variables are not case-sensitive.
TAP has six core commands:
Note that these commands don't really do much other than provide for control-flow and the ability to set variables. MSGBOX was added so that all programs using the core implementation would have a common mechanism for displaying a message.
The SET Command
The SET command sets a variable's value. You may specify multiple parameters to the right of the variable name. The current implementation of TAP allows up to 12 parameters to follow any command.
All of the paramters are then concatenated into a single value. The single value is then placed in the specified variable.
The GOTO Command
The GOTO command transfers control to the line containing a specified label. If the label is not found, an error message is displayed and further TAP processing ceases.
Note that a variable can be used in place of a literal label.
The CALL Command
The CALL command temporarily transfers control to the subroutine at the line containing a specified label. If the label is not found, an error message is displayed and further TAP processing ceases.
Note that a variable can also be used with CALL in place of a literal label.
The RETURN Command
When a RETURN command is encountered during a CALLed subroutine, processing continues at the line following the line originating the invocation of CALL.
The IF Command
The IF command performs a numeric comparison of parm1 and parm2 using the specified operator. Valid operators are:
If the numeric expression yields true, either a GOTO, a CALL, or a RETURN may be invoked.
The MSGBOX Command
The MSGBOX command is a thin wrapper for the Windows MessageBox API function. You may specify a message, a title, and a code that determines what kinds of buttons show up. Please consult other Windows API sources for the appropriate codes for the buttons. By default, a zero is passed ( which shows only the "OK" button. )
The message and title paramaters default to empty strings if omitted.
The TAP API library allows a host C program to augment TAP with new commands, load TAP scripts, execute TAP scripts, call labeled subroutines, and set/get variables.
The TAP API functions are as follows (excerpted from tap_core.h ):
We will now create our own interpreter host program for the TAP language called tap_test. We'll add two commmands to TAP:
tap_test.c
Note that command extension functions are not unlike the common C main function. Each receives a count of arguments passed and an array of those arguments. All arguments appear as evaluated text by the time the function is invoked. ( All variables are expanded, all appropriate quotation-marks are removed, ...etc. )
Here are two test scripts:
test1.txt
test2.txt
To run the first script, from a command-prompt enter:
tap_test test1.txt
You should first see a message box that says "Hello, there" with a title of "Here's a message". The first several lines of the script test1.txt concatenated the phrase "Hello, there" together into variable $c. Then, the MSGBOX command was invoked using $c as the message and "Here's a message" as the title.
Then, you should see a message box that says "Would you like to continue?" If you click "OK" a value of 1 will be left in the variable $retval. If you click "Cancel" a value of 2 will be left in $retval. We were presented with the ok/cancel options because we passed a 1 in as the code for the button parameter.
We then check $retval to see if it holds the value 1 indicating that "OK" had been pressed. If you click "OK", the continue subroutine will be invoked. That subroutine simply displays a message box that says "Continuing..."
The script then terminates.
To run the second script, from a command-prompt enter:
tap_test test2.txt
This script leverages the new commands in our host. The console output is:
Done.
Iteration #1
Iteration #2
Iteration #3
Iteration #4
Iteration #5
Iteration #6
Iteration #7
Iteration #8
Iteration #9
Iteration #10
The first executable line of code sets the variable $a to 1. The next executable line of code uses the new PRINTLN command to print "Iteration #" and the value of $a.
The line INC $a increments the value of $a leaving the result in the variable $retval Note that the value of $a itself doesn't change until we issue the command SET $a $retval.
It may appear as though INC is incrementing $a, but all variables are expanded to their values before they are passed to the command. So, the INC function only sees the value of $a. The only exception to this is the special case for the SET command. The argument immediately following the SET verb is known to be a variable. You might take a look at tap_core.c to see the special handler for this case.
An IF is then used to determine if $a is less than or equal to 10 by using the lt operator. If the expression is true, control is transferred to the line beginning with the label :loop via the GOTO command.
Otherwise, the script flows to the two ending PRINTLN statements.
In forthcoming articles, I'll provide a few more augmentations to TAP and will embed it in more practical code.
The source, executable sample file, and sample scripts for the TAP core can be downloaded in a single archive at: http://www.mailsend-online.com/wp/tapcore.zip
Unless otherwise noted, all code and text entries are Copyright ©2009 by James K. Lawless
Views expressed in this blog are those of the author and do not necessary reflect those of the author's employer. Views expressed in the comments are those of the responding individual.

Save to StumbleUpon
Digg it
Save to Reddit
Share on Facebook
Share on Twitter
More bookmarks
Click **here**
A JavaScript REPL for Android Devices
A Review of Kevin Mitnick's Book Ghost in the Wires
Play MP3 Files with Python on Windows
COM Scripting in C by way of JavaScript
Book Review : Using Google App Engine
An SMTP Server Simulator in Perl
Why Some Web Sites will go Dark on Jan 18th
Book Review : Paull Allen - Idea Man
A 90's Experiment in Online Systems - The U.S. West CommunityLink Service