echo.icn: Procedure to perform "variable interpolation" a la Perl

procedure echo:            interpolate variables and print

link echo
February 9, 2010; Charles L Hethcoat III and Carl Sturtivant
This file is in the public domain.

echo() substitutes global variables for occurrences of $name in &subject,
and writes the result to standard output.
____________________________________________________________

Background:

String "interpolation", as used in Perl, Tcl, Bash, and so on,
involves a special notation used within a string that causes the
value of a variable to be inserted into the string at runtime.  A
common notation for this is a dollar sign, e. g. "The price is
$price pfennig."  If a variable named "price" has the value 10, then
on output the string becomes "The price is 10 pfennig."

Interpolation is lacking in Icon, so we must use the fussier syntax
of an Icon write() procedure:  write("The price is ", price,
"pfennig.").  Here is a slightly more complex example, assuming
variables `price' = 10, `article' == "thimble", and `currency' ==
"pfennig":

   write("The price of a ", article, " is ", price, " ", currency, ".")

This can be annoying and error-prone if we must use many such
strings in a program.

The echo() procedure provides a very nice solution for Icon
programmers.  Compare the preceding write() call to this:

   "The price of a $article is $price $currency" ? echo()

Is this not much simpler?  Both examples will print out the string

   "The price of a thimble is 10 pfennig."

but interpolation with echo() greatly reduces the low-level
syntactic requirements (and reduces the number of characters to type
from 68 to 54).  It is much easier to write, read, and check.  If
many such lines of code are needed, the difference adds up.
Consider, for example, how this would pay off if your program needs
to generate hundreds of lines of HTML or PostScript.
____________________________________________________________

Usage:

A string to
be printed with interpolated values should be set up in a scanning
environment, using echo() as the scanning procedure, as in
"foo$variable" ? echo().  Here is an actual example for testing:

   link echo
   global month, day, year

   procedure main()
      month := "February"
      day := 30
      year := 2010
      "Free beer on $month $day, $year." ? echo()
   end

Assuming echo.icn has been compiled with the -c option beforehand,
compiling, linking, and running this program produces the string
"Free beer on February 30, 2010." on standard output.
____________________________________________________________

Notes:

Since there is no way for any Icon procedure to discover the values of
any another procedure's local variables, all variables to be used via
the echo() procedure must be global.  This restriction ought not to be
too serious for smaller programs, or even longer ones if they are of
simple construction.  But it may be a limitation for sophisticated
Icon programming projects.  You will have to be the judge.

If x is a global variable with value 10,

   "x" ? echo()         prints "x"
   "$x" ? echo()        prints "10"
   "$$x" ? echo()       prints "$x"
   "$$$x" ? echo()      prints "$10"
   "$$$$x" ? echo()     prints "$$x"
   "$$$$$x" ? echo()    prints "$$10"

and so on.  The rule is:  take dollar signs off in pairs from the
left.  Each pair prints ONE literal dollar sign on the output.

If there were an odd number of dollar signs to begin with, then one
will be left over, and this will print the value of the variable (10).

If there were an even number to begin with, then none are left, and a
literal "x" is printed.

There is an extended notation that helps disambiguate some usage
scenarios.  Here are some examples:

   "${x}" is the same as $x.
   "${x}:" is the same as $x:.
   "${x}${y}" is the same as $x$y.

However, "${x}avier" is NOT the same as $xavier!  Can you see why?

You may use any variable names you like.  There are no restrictions.
echo() uses no global variable names of its own, but receives the
string it interpolates in a string scanning environment.
____________________________________________________________

Using echo() on a larger scale , with input from a generator:

global time, date, save, wombats

link echo

procedure main()
  time := &clock
  date := &date
  save := ?100000
  wombats := 22
  "It is now $time on $date and you have savings of $$$save." |
     "The number of wombats is $wombats." |
     "It is now ${time} on ${date} and you have ${wombats} wombats." |
     "There is no global variable named \"$foo\"." |
     "This does not work:  It is now ${&clock}." |
     "" |
     "The previous input line printed an empty output line." ? echo()
end

Because echo() always fails (in the Icon sense), evaluation of

    a | b | c | d ? echo()

will group as

    (a | b | c | d) ? echo()

because of operator precedence, and the left-hand expression produces
_a_ first, which is assigned to &subject.  Then echo() is evaluated --
and fails.  This makes the whole expression fail, so Icon backtracks
to the first expression, resumes its evaluation to produce its second
value b, which is assigned to &subject and then echo() is called,
which fails, and so forth, until all possibilities are exhausted.
____________________________________________________________

Taking input from a template file:

You can create a template file (with $-strings in it) and use an Icon
program to read it and write it out to standard output.  Your main
Icon program will supply the needed variable values for the $-strings
in the template.

As an example, suppose your program will generate a hundred business
cards for you as a PostScript file.  You have a template file named
template.ps with $-strings such as $firstname, $lastname, $address,
$companyname, and so on --- all embedded in it at the proper places.
Your main program will read this template and substitute the actual
name and address information.

This is one way your program can read template.ps and pass it to
echo():

...
firstname := "Joe"
lastname := "Smith"
# ... etc. ...
reads("template.ps",1000000) ? echo()
...

When this is run, your customized business cards appear on standard
output.
____________________________________________________________

This trick relies upon concatenation having a higher precedence
than alternation:

    "................" ||
    "................" ||
    "................" |
    "................" ||
    "................" |
    "................" ||
    "................" ? echo()

This prints out three messages, one specified on three lines, one on
two, and one on two. The alternations fix the newlines provided at the
end of each message by echo().

 &subject is the empty string if it's unassigned.  So echo() called
without ? will under those circumstances print a blank line.

Source code | Program Library Page | Icon Home Page