Fix typo
[rofl0r-order-pp.git] / example / bottles.c
blobba586dd81b2981aeeb97db756f5ce5486e45668c
1 // (C) Copyright Vesa Karvonen 2004.
2 //
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE.)
6 // \section{A Taste of Order} %*************************************
7 //
8 // The following chapters gradually introduce and formally define
9 // the elements of the Order language, but in this section we will
10 // take a brief informal look at a concrete example. The code
11 // snippets that we will show in this section may look ``funny'' and
12 // may be difficult to understand on first reading, but you
13 // shouldn't worry about it. You may want to read this section again
14 // after you've finished a few more chapters.
16 // As we said, Order is a C preprocessor metalanguage that can be
17 // used to generate sequences of preprocessing tokens. Normally, the
18 // generated preprocessing tokens would eventually be converted to C
19 // or C++ tokens and would constitute program code. To avoid
20 // motivating and defining an actual code generation problem, we
21 // will show two ways to use Order to generate the complete lyrics
22 // to the song ``99 Bottles of Beer on the Wall''. While the actual
23 // logic required to generate the lyrics is quite trivial, this
24 // example is sufficient to give an idea how Order can actually be
25 // used.
27 // Let's first consider the song, which can be thought of as a
28 // countdown from 99 to 1. Each phrase, between 99 and 3, looks like
29 // this:
30 #if 0
31 N bottles of beer on the wall,
32 N bottles of beer, take one down, pass it around,
33 N-1 bottles of beer on the wall.
34 #endif//0
35 // Boring, isn't it? Well, things get a bit more interesting in the
36 // last two phrases. When there is just 1 bottle of beer we must
37 // drop the plural. When there are no more bottles, we must say so.
38 // The last two phrases look like this:
39 #if 0
40 2 bottles of beer on the wall,
41 2 bottles of beer, take one down, pass it around,
42 1 bottle of beer on the wall.
44 1 bottle of beer on the wall,
45 1 bottle of beer, take one down, pass it around,
46 no more bottles of beer on the wall.
47 #endif//0
48 // So, the key is to generate the correct phrase for referring to
49 // the number of bottles. We can express the rule in Order code
50 // using a conditional expression with three clauses:
51 #if 0
52 8cond((8greater(8N, 1),
53 8separate(8N, 8quote(bottles)))
54 (8equal(8N, 1),
55 8quote(1 bottle))
56 (8else,
57 8quote(no more bottles)))
58 #endif//0
59 // Undoubtedly the first thing to notice was the prefix `8'. It
60 // is a prefix of \emph{all} Order expressions and its purpose is to
61 // prevent \emph{unintended macro replacement} of Order expressions.
62 // A token that starts with a digit, like `8cond', is called a
63 // \emph{pp-number} and because it isn't an \emph{identifier} it
64 // isn't subject to macro replacement. The use of pp-numbers is
65 // admittedly an ugly detail, but it is absolutely necessary,
66 // because otherwise an Order expression might get macro replaced by
67 // a user or standard defined macro, like the \emph{abominable}
68 // macro `I' incredibly defined by the C standard\footnote{There
69 // aren't words strong enough that I could credibly use here to
70 // describe what I think about the quality of certain parts of the C
71 // standard \cite{c:1999}.}, before the Order interpreter gets to
72 // analyze the expression. Please note that unintended macro
73 // replacement isn't a novel problem introduced by the Order
74 // interpreter. It is a well known problem of the C preprocessor
75 // \cite{stroustrup:1994}. It should be noted that \emph{all} macros
76 // defined by the implementation of the Order interpreter have the
77 // prefix `ORDER_PP'. This makes it very unlikely that the Order
78 // interpreter would cause unintended macro replacements.
80 // The second thing to notice in the above expression is the
81 // conditional expression `8cond(...)'.\footnote{Borrowed from
82 // Scheme.} A conditional expression consists of a sequence of
83 // clauses. Each clause is a parenthesized list of comma separated
84 // expressions. The first expression of each clause must be a
85 // boolean expression or `8else', which can only be used in the
86 // last clause. Roughly speaking, a conditional expression is
87 // evaluated by evaluating the boolean expressions, starting at the
88 // first clause, until a boolean expression is found that evaluates
89 // to true. Then the rest of the expressions of the true clause are
90 // evaluated.
92 // The `8quote' syntactic form is used to quote values that must not
93 // be evaluated by the interpreter. All values manipulated by the
94 // Order interpreter are sequences of preprocessing tokens. A value
95 // manipulated by the Order interpreter must be eligible as a single
96 // macro argument. We use the term \emph{pp-arg} to refer to such
97 // preprocessing token sequences. A \emph{pp-arg} must not contain,
98 // nor macro replace to a token sequence containing, unparenthesized
99 // commas nor unbalanced parentheses.
101 // The binary function `8separate', given two arguments, evaluates
102 // to a new token sequence that consists of the tokens of the two
103 // arguments separated by a space. The expression `8N' is a variable
104 // reference. By default, variable identifiers in the Order
105 // interpreter are limited to the tokens `8[A-Z]', meaning the digit
106 // `8' followed by a capital letter.\footnote{The interpreter can be
107 // extended to be able to use additional variable symbols by
108 // defining suitable macros.}
110 // Let's then continue with the example. To make the above
111 // conditional expression really useful, we can wrap it inside a
112 // function that we can apply to any number of bottles:
113 #if 0
114 8fn(8N,
115 8cond((8greater(8N, 1),
117 8quote(no more bottles))))
118 #endif//0
119 // The `8fn(...)' syntactic form defines an anonymous
120 // first-class function. The above function binds the variable
121 // `8N', which is referred to in the conditional expression.
123 // Given the number of bottles bound to the variable `8N' and
124 // the above auxiliary function bound to the variable `8B', we
125 // can now output one phrase of the song using a `8print'
126 // expression:
127 #if 0
128 8print(8ap(8B, 8N) (of beer on the wall,) 8space
129 8ap(8B, 8N) (of beer,) 8space
130 (take one down, pass it around,) 8space
131 8ap(8B, 8dec(8N)) (of beer on the wall.))
132 #endif//0
133 // A `8print' expression is evaluated by implicitly outputing the
134 // value of any unparenthesized Order expression and outputing any
135 // parenthesized sequence of tokens verbatim. The `8ap' syntactic
136 // form is the function application expression. \emph{Top-level}
137 // functions, like the prelude function `8greater', but also
138 // \emph{user-defined} top-level functions, can be applied without
139 // using `8ap', but `8ap' needs to be used otherwise. The `8space'
140 // syntactic form can only be used inside `8print' and causes a
141 // space to be output.\footnote{In other words, the keyword `8space'
142 // is part of the syntax of `8print' expressions.} Ordinarily, while
143 // generating program code, \emph{whitespace separations} in the
144 // output are insignificant, and it doesn't matter that heading and
145 // trailing whitespace is implicitly removed from the output, but in
146 // this case we'd like to have a space after each comma. The
147 // `8print' form can actually be used to output arbitrary sequences
148 // of preprocessing tokens, including unbalanced parentheses using
149 // the syntactic forms `8lparen' and `8rparen' that output a left
150 // and a right paren, respectively.
152 // To execute the above `8print' expression for all numbers from 99
153 // to 1 we can wrap it in an anonymous function and use the
154 // \emph{higher-order} function `8for_each_in_range', conveniently
155 // provided by the Order prelude, to make the function applications:
156 #if 0
157 8for_each_in_range
158 (8fn(8N,
159 8print(8ap(8B, 8N) (of beer on the wall,) 8space
161 8ap(8B, 8dec(8N)) (of beer on the wall.))),
162 100, 1)
163 #endif//0
164 // You may wonder about the above upper bound of 100.
165 // `8for_each_in_range' always considers the upper bound open and
166 // the lower bound closed regardless of the direction, ascending or
167 // descending, in which the range is specified. This minimizes
168 // special cases.
170 // To complete the program, we use a `8let' expression to bind the
171 // auxiliary function to `8B', include the Order interpreter header,
172 // and invoke the Order interpreter using the `ORDER_PP' macro:
174 #include "order/interpreter.h"
176 ORDER_PP
177 (8let((8B, 8fn(8N,
178 8cond((8greater(8N, 1),
179 8separate(8N, 8quote(bottles)))
180 (8equal(8N, 1),
181 8quote(1 bottle))
182 (8else,
183 8quote(no more bottles))))),
184 8for_each_in_range
185 (8fn(8N,
186 8print(8ap(8B, 8N) (of beer on the wall,) 8space
187 8ap(8B, 8N) (of beer,) 8space
188 (take one down, pass it around,) 8space
189 8ap(8B, 8dec(8N)) (of beer on the wall.))),
190 100, 1)))
192 // The above program, when preprocessed, generates some 2773 tokens
193 // on one line containing the desired lyrics. As you can see, we
194 // didn't need to define any macros to generate the desired output.
195 // In fact, the Order intepreter is complete in the sense that it
196 // can \emph{theoretically} generate any finite sequence of
197 // preprocessing tokens without requiring the definition of
198 // additional macros. In practice, however, it usually makes sense
199 // to use additional macros in the form of \emph{ad hoc code
200 // generation macros} and Order \emph{top-level definitions} to
201 // both simplify and optimize a generator. Let's see how to do it.
203 // First we'll write an ad hoc macro for generating one phrase of
204 // the song:
206 #define GEN_phrase(N_bottles, N_minus_1_bottles) \
207 N_bottles of beer on the wall, \
208 N_bottles of beer, take one down, pass it around, \
209 N_minus_1_bottles of beer on the wall.
211 // The above function-like macro, named `GEN_phrase', takes two
212 // arguments, `N_bottles' and `N_minus_1_bottles', and expands to a
213 // single phrase of the song. The idea is to use the above macro to
214 // generate all the phrases of the song by outputing a sequence of
215 // 99 macro invocations. In general, a viable design heuristic is to
216 // completely parameterize any ad hoc code generation macro and then
217 // use the Order interpreter to implement any non-trivial logic to
218 // compute the parameters. This minimizes the need to implement
219 // complex logic using only the low-level macro mechanism.
221 // To compute the arguments to the `GEN_phrase' macro, we give a
222 // top-level definition, named `8bottles', for the previously used
223 // auxiliary function for referring to the number of bottles:
225 #define ORDER_PP_DEF_8bottles \
226 ORDER_PP_FN(8fn(8N, \
227 8cond((8greater(8N, 1), \
228 8separate(8N, 8quote(bottles))) \
229 (8equal(8N, 1), \
230 8quote(1 bottle)) \
231 (8else, \
232 8quote(no more bottles)))))
234 // The above object-like macro definition is an Order top-level
235 // definition. Order top-level definition macros use the prefix
236 // `ORDER_PP_DEF_', which makes accidental macro replacement
237 // unlikely and allows the Order interpreter to deconstruct
238 // expressions using concatenation. The `ORDER_PP_FN' tagging macro
239 // effectively tells the Order interpreter that `8bottles' defines a
240 // function. Order also has other forms of top-level definitions
241 // such as constant and macro definitions.
243 // To actually output an invocation of the `GEN_phrase' macro, we
244 // use the `8emit' procedure:
245 #if 0
246 8emit(8quote(GEN_phrase),
247 8tuple(8bottles(8N),
248 8bottles(8dec(8N))))
249 #endif//0
250 // Given two arguments, the `8emit' procedure outputs both of the
251 // arguments separated by a space. Above, `8emit' is used to output
252 // the identifier of a function-like macro and a tuple\footnote{In
253 // Order, a tuple is a parenthesized and comma separated list of
254 // elements.} that matches the formal parameters of the
255 // function-like macro `GEN_phrase'. As one could expect, it will
256 // eventually result in expanding the function-like macro generating
257 // one phrase of the song.
259 // The complete program now looks like this:
261 #include "order/interpreter.h"
263 #define GEN_phrase(N_bottles, N_minus_1_bottles) \
264 N_bottles of beer on the wall, \
265 N_bottles of beer, take one down, pass it around, \
266 N_minus_1_bottles of beer on the wall.
268 #define ORDER_PP_DEF_8bottles \
269 ORDER_PP_FN(8fn(8N, \
270 8cond((8greater(8N, 1), \
271 8separate(8N, 8quote(bottles))) \
272 (8equal(8N, 1), \
273 8quote(1 bottle)) \
274 (8else, \
275 8quote(no more bottles)))))
277 ORDER_PP(8for_each_in_range
278 (8fn(8N,
279 8emit(8quote(GEN_phrase),
280 8tuple(8bottles(8N),
281 8bottles(8dec(8N))))),
282 100, 1))
284 #undef GEN_phrase
286 // Depending on the preprocessor, the above program can be
287 // preprocessed significantly faster than the previous program using
288 // `8print'. This is because the state of the program being
289 // evaluated is smaller and the program performs fewer steps. The C
290 // preprocessor has some interesting performance characteristics,
291 // which we will investigate later.
293 // All that is left to do is to preprocess this example, direct the
294 // output to the printer, get 99 bottles of beer and a few friends
295 // to sing the song\ldots{} On second thought, you could also just
296 // continue reading.