summaryrefslogtreecommitdiff
path: root/ipl/procs/echo.icn
blob: 5a90c97bbe78cb21e455a28c7a19fef305948222 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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