diff options
Diffstat (limited to 'ipl/procs/echo.icn')
-rw-r--r-- | ipl/procs/echo.icn | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/ipl/procs/echo.icn b/ipl/procs/echo.icn new file mode 100644 index 0000000..5a90c97 --- /dev/null +++ b/ipl/procs/echo.icn @@ -0,0 +1,227 @@ +############################################################################ +# +# File: echo.icn +# +# Subject: Procedure to perform "variable interpolation" a la Perl +# +# Authors: Charles L Hethcoat III and Carl Sturtivant +# +# Date: February 9, 2010 +# +############################################################################ +# +# 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. +# +############################################################################ + +procedure echo() #: interpolate variables and print + + $define idchars 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_' + while writes(tab(find("$")) ) do { + move(1) + writes( ="$" | + variable(tab(many(idchars)) | + 2( ="{", tab(find("}")), ="}" ) + ) + ) | + tab(many(idchars)) | + ( ="{" & tab(find("}")) & ="}" ) + } + write(tab(0)) + $undef idchars + +end |