Category : Dbase (Clipper, FoxBase, etc) Languages Source Code
Archive   : TN9011.ZIP
Filename : LIFNOMAC.TXT

 
Output of file : LIFNOMAC.TXT contained in archive : TN9011.ZIP

This article is reprinted from the November 1990 edition of
TechNotes/dBASE IV. Due to the limitations of this media, certain
graphic elements such as screen shots, illustrations and some tables
have been omitted. Where possible, reference to such items has been
deleted. As a result, continuity may be compromised.

TechNotes is a monthly publication from the Ashton-Tate Software
Support Center. For subscription information, call 800-545-9364.

Life Without the &
Alastair Dallas

Want to make your dBASE IV programs run faster? Of course you do.
Whether you're using code that was written for dBASE III PLUS or
whether you're writing new applications, we all want to maximize
performance by taking advantage of the latest dBASE IV features.
Here's a tip that will make your programs run faster: avoid & macros.

"Oh no," you say, "anything but that! The & macro is so useful, so
powerful." That it is. It is also much slower in dBASE IV than it
was in earlier versions of dBASE. And, to purists, anyway, wanton use
of the & macro is questionable programming practice, making your
programs harder to read and maintain.

So why was such a powerful feature slower for dBASE IV?

The & actually performs what we call a "run-time substitution." That
is, at run-time (when your .prg is executing, not when it is being
compiled), the value of the named character memvar is fetched and
substituted into the command line being executed. For example, say
your code included these common lines:

STORE "PAYROLL" TO Fname
STORE "C:\ACCTG\" TO Path
USE &Fname
USE &Path.&Fname

When these lines are interpreted by dBASE III PLUS, its somewhat
primitive parser takes each line and breaks out the first word. A
simple lookup in a verb table sends the interpreter to a parsing
routine for that command. As the command is parsed (in dBASE III) it
is executed immediately. By the time you get to the USE command, the
memvars Fname and Path already exist. It's a simple matter to lookup
their values. That's how dBASE III PLUS worksbefore each line is
interpreted it is scanned for the '&' character. For each '&' a
lookup and substitution are performed and then the whole line is
interpreted. So for this example, dBASE III PLUS sees:

STORE "PAYROLL" TO Fname
STORE "C:\ACCTG\" TO Path
USE PAYROLL
USE C:\ACCTG\PAYROLL

The lookup takes hardly any time, so using macros in dBASE III PLUS is
not noticeably slower than hard-coding literals.

However, dBASE IV compiles .prg files, creating .dbo files. It
converts dBASE keywords like "USE" into one-byte tokens. (The
compiler handles expressions and some commands, particularly
flow-of-control commands like "DO WHILE" in a more sophisticated
fashion.) The resulting dbo file executes considerably faster because
all the command parsing has already been done. When you say

DO MainMenu

the compiler is automatically invoked (if necessary) mainmenu.prg is
read and mainmenu.dbo is created. Lines entered at the dot prompt are
compiled to tokenized dbo code internally even though no .dbo file is
created. Although much of the speed advantage comes when you run an
existing .dbo file without recompiling it, the special handling of
expressions and flow-of-control statements improves performance
considerably over dBASE III PLUS.

But how is the & macro implemented in dBASE IV? If it were a simple
matter of looking up a character memvar, it could be tokenized at
compile time. Consider the example:

USE &Fname

If life were simple, this line might be compiled as a token for USE
followed by an indirect reference to a memvar called Fname. We could
do that (and several people thought we should), but imagine another
scenario:

A = "FirstName"
B = "ORDER "+A
C = IIF(gExclusive, "EXCLUSIVE", "")
TagExp = B+" "+C
USE PAYROLL &TagExp

In this case, the character memvar includes dBASE keywords (ORDER and
EXCLUSIVE) that the compiler needs to tokenize. We might have simply
outlawed all such uses of the & macro, but we suspected that a few
million existing programs would not work with dBASE IV if we did. In
some cases, the dBASE IV compiler optimizes across several lines, so
we could, in theory, detect code patterns like:

=
... &

We would still have to parse the to solve the TagExp
case, and we thought of yet another doomsday scenario:

A = "FirstName"
DO Subroutine
USE &A

Does the subroutine alter the value of A or not? To know for sure, we
would have to parse the subroutine (and any procedures or functions
that it refers to) as well. To top it off, subroutine.prg need not
exist when the main program is compiled. The subroutine may be
compiled during the execution of the main program.

So, what does the dBASE IV development team do when faced with such
obstacles?

We punt.

Our top priority was compatibility with existing application programs,
so we took the long way home. When we compile a line that includes an
& macro, we tag it as such with a token and when we go to execute that
line, we expand the line in memory, perform the character string
substitution and then we invoke the compiler again for that one line
(just as if you had typed it at the dot prompt). This fulfills the
promise that the & macro provides a "run-time substitution" facility.

This is not slower than dBASE III PLUS, but its not much faster,
either. And, as you've surely figured out, it is not as fast as it
could be. If you could find a way to avoid using the &,, your
programs would run faster because they wouldn't have to invoke the
compiler at run-time.

We're here to help. We analyzed a few real-world dBASE III PLUS
programs and we concluded that there are two main reasons that real
programmers use the & macro. The first is to simulate arrays and the
second is to specify a filename. We implemented real arrays in dBASE
IV, so the first case no longer mattered. We wanted to address the
following situation, however:

ACCEPT "Enter filename: " TO A
USE &A
The solution was to change the syntax from:

USE
to:

USE

That solved all the problems except one. Now you can get really
fancy and your programs will still be readable. For example:

ACCEPT "Enter filename: " TO A
USE DefPath+IIF(""=A, "Default.dbf", A)

You've probably guessed the remaining problem. Five million programs
for which the line:

USE Payroll

would now return Variable Not Found. So the clever folks at
Ashton-Tate, your humble servant included, devised an exception. The
syntax for USE in dBASE IV is:

USE

An is defined as an with one exception. When the
consists of a single memvar name, the parser "pretends" that it
saw quotes around the memvar, converting it into a string literal.
This has been a surprisingly difficult concept to explain. In the
following examples, only the first receives implied quotation marks:

USE A
USE "A"
USE A+""
USE UPPER(A)
USE (A)

You can quote the literal name yourself, or you can make the memvar
name something other than a "single memvar name" by using operators
(such as "+"), functions (such as UPPER()), or by enclosing the name
in parentheses. This last case has caused the most confusionÄpeople
seem to think that parentheses now replace the & macro, which is not
so. Filenames are now character expressions () except that a
single memvar name is treated as a quoted string literal. Therefore,
if your character expression happens to be a single memvar name, you
must use parentheses (or +"") to make your meaning clear. If your
character expression is more than a single memvar name already, extra
parentheses aren't needed (but they won't hurt).

USE A && supported for backward-compatibility only
&& (parsed as USE "A")
USE "A" && specifies a hard-coded filename
USE A+B && refers to memvars A and B
USE (A+B) && extra parentheses don't hurt
USE (A) && refers to the value of memvar A
USE UPPER(A) && refers to memvar A
USE MyUDF() && calls a user-defined function

Filenames throughout the dBASE IV programming language, not just the
USE command, support . To pick a few examples:

APPEND FROM
COPY TO
INDEX ON TO
SET ALTERNATE TO
LIST TO FILE
USE [INDEX ]

Dozens of programmers worked on dBASE IV version 1.0 and almost as
many produced version 1.1, which was just released. Not to ignore the
work of the others, but I'd like to take this opportunity to applaud
the work of Chip Morton, Bob Dvorak and Jeb Long for inventing and
implementing the . In my opinion, the future of the dBASE
language depends upon just this kind of ingenuity in reconciling
backward compatibility with future technologies.