summary refs log tree commit diff stats
path: root/doc/trmacros.txt
blob: d5ad74e6eea02fafe5c78eeb55db4ab51821a7fe (plain) (blame)
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
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800
=========================================================
   Term rewriting macros for Nimrod
=========================================================

:Author: Andreas Rumpf

Term rewriting macros are macros or templates that have not only a *name* but
also a *pattern* that is searched for after the semantic checking phase of
the compiler: This means they provide an easy way to enhance the compilation
pipeline with user defined optimizations:

.. code-block:: nimrod
  template optMul{`*`(a, 2)}(a: int): int = a+a
  
  let x = 3
  echo x * 2

The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the
curlies is the pattern to match against. The operators ``*``,  ``**``,
``|``, ``~`` have a special meaning in patterns if they are written in infix 
notation, so to match verbatim against ``*`` the ordinary function call syntax
needs to be used.


Unfortunately optimizations are hard to get right and even the tiny example
is **wrong**: 

.. code-block:: nimrod
  template optMul{`*`(a, 2)}(a: int): int = a+a
  
  proc f(): int =
    echo "side effect!"
    result = 55
  
  echo f() * 2

We cannot duplicate 'a' if it denotes an expression that has a side effect!
Fortunately Nimrod supports side effect analysis:

.. code-block:: nimrod
  template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a
  
  proc f(): int =
    echo "side effect!"
    result = 55
  
  echo f() * 2 # not optimized ;-)

So what about ``2 * a``? We should tell the compiler ``*`` is commutative. We
cannot really do that however as the following code only swaps arguments
blindly:

.. code-block:: nimrod
  template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a
  
What optimizers really need to do is a *canonicalization*:

.. code-block:: nimrod
  template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a

The ``int{lit}`` parameter pattern matches against an expression of 
type ``int``, but only if it's a literal.



Parameter constraints
=====================

The parameter constraint expression can use the operators ``|`` (or), 
``&`` (and) and ``~`` (not) and the following predicates:

===================      =====================================================
Predicate                Meaning
===================      =====================================================
``atom``                 The matching node has no children.
``lit``                  The matching node is a literal like "abc", 12.
``sym``                  The matching node must be a symbol (a bound 
                         identifier).
``ident``                The matching node must be an identifier (an unbound
                         identifier).
``call``                 The matching AST must be a call/apply expression.
``lvalue``               The matching AST must be an lvalue.
``sideeffect``           The matching AST must have a side effect.
``nosideeffect``         The matching AST must have no side effect.
``param``                A symbol which is a parameter.
``genericparam``         A symbol which is a generic parameter.
``module``               A symbol which is a module.
``type``                 A symbol which is a type.
``var``                  A symbol which is a variable.
``let``                  A symbol which is a ``let`` variable.
``const``                A symbol which is a constant.
``result``               The special ``result`` variable.
``proc``                 A symbol which is a proc.
``method``               A symbol which is a method.
``iterator``             A symbol which is an iterator.
``converter``            A symbol which is a converter.
``macro``                A symbol which is a macro.
``template``             A symbol which is a template.
``field``                A symbol which is a field in a tuple or an object.
``enumfield``            A symbol which is a field in an enumeration.
``forvar``               A for loop variable.
``label``                A label (used in ``block`` statements).
``nk*``                  The matching AST must have the specified kind. 
                         (Example: ``nkIfStmt`` denotes an ``if`` statement.)
``alias``                States that the marked parameter needs to alias 
                         with *some* other parameter.
``noalias``              States that *every* other parameter must not alias
                         with the marked parameter.
===================      =====================================================

The ``alias`` and ``noalias`` predicates refer not only to the matching AST,
but also to every other bound parameter; syntactially they need to occur after
the ordinary AST predicates:

.. code-block:: nimrod
  template ex{a = b + c}(a: int{noalias}, b, c: int) =
    # this transformation is only valid if 'b' and 'c' do not alias 'a':
    a = b
    inc a, b


Pattern operators
=================

The operators ``*``,  ``**``, ``|``, ``~`` have a special meaning in patterns
if they are written in infix notation.


The ``|`` operator
------------------

The ``|`` operator if used as infix operator creates an ordered choice:

.. code-block:: nimrod
  template t{0|1}(): expr = 3
  let a = 1
  # outputs 3:
  echo a

The matching is performed after the compiler performed some optimizations like
constant folding, so the following does not work:

.. code-block:: nimrod
  template t{0|1}(): expr = 3
  # outputs 1:
  echo 1

The reason is that the compiler already transformed the 1 into "1" for
the ``echo`` statement. However, a term rewriting macro should not change the
semantics anyway. In fact they can be deactived with the ``--patterns:off``
command line option or temporarily with the ``patterns`` pragma. 


The ``{}`` operator
-------------------

A pattern expression can be bound to a pattern parameter via the ``expr{param}``
notation: 

.. code-block:: nimrod
  template t{(0|1|2){x}}(x: expr): expr = x+1
  let a = 1
  # outputs 2:
  echo a


The ``~`` operator
------------------

The ``~`` operator is the **not** operator in patterns:

.. code-block:: nimrod
  template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt =
    x = y
    if x: x = z
  
  var
    a = false
    b = true
    c = false
  a = b and c
  echo a


The ``*`` operator
------------------

The ``*`` operator can *flatten* a nested binary expression like ``a & b & c``
to ``&(a, b, c)``: 

.. code-block:: nimrod
  var
    calls = 0
    
  proc `&&`(s: varargs[string]): string =
    result = s[0]
    for i in 1..len(s)-1: result.add s[i]
    inc calls

  template optConc{ `&&` * a }(a: string): expr = &&a

  let space = " "
  echo "my" && (space & "awe" && "some " ) && "concat"

  # check that it's been optimized properly:
  doAssert calls == 1


The second operator of `*` must be a parameter; it is used to gather all the
arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"``
is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``) 
which is flattened into a call expression; thus the invocation of ``optConc`` 
produces:

.. code-block:: nimrod
   `&&`("my", space & "awe", "some ", "concat")


The ``**`` operator
-------------------

The ``**`` is much like the ``*`` operator, except that it gathers not only
all the arguments, but also the matched operators in reverse polish notation:

.. code-block:: nimrod
  import macros

  type
    TMatrix = object
      dummy: int

  proc `*`(a, b: TMatrix): TMatrix = nil
  proc `+`(a, b: TMatrix): TMatrix = nil
  proc `-`(a, b: TMatrix): TMatrix = nil
  proc `$`(a: TMatrix): string = result = $a.dummy
  proc mat21(): TMatrix =
    result.dummy = 21

  macro optM{ (`+`|`-`|`*`) ** a }(a: TMatrix): expr =
    echo treeRepr(a)
    result = newCall(bindSym"mat21")

  var x, y, z: TMatrix

  echo x + y * z - x 

This passes the expression ``x + y * z - x`` to the ``optM`` macro as
an ``nnkArgList`` node containing::

  Arglist
    Sym "x"
    Sym "y"
    Sym "z"
    Sym "*"
    Sym "+"
    Sym "x"
    Sym "-"

(Which is the reverse polish notation of ``x + y * z - x``.)


Parameters
==========

Parameters in a pattern are type checked in the matching process. If a 
parameter is of the type ``varargs`` it is treated specially and it can match
0 or more arguments in the AST to be matched against:

.. code-block:: nimrod
  template optWrite{
    write(f, x)
    ((write|writeln){w})(f, y)
  }(x, y: varargs[expr], f: TFile, w: expr) =
    w(f, x, y)
  
"nt">CODE>(like this)</CODE> and not <CODE>(&quot;like&quot; &quot;this&quot;)</CODE>. We've arranged that in most contexts symbols, strings, and numbers can be used interchangeably; our readers never see Scheme characters at all.<SUP>*</SUP> Although a word made of letters is represented internally as a symbol, while a word made of digits is represented as a number, above the abstraction line they're both words. (A word that standard Scheme won't accept as a symbol nor as a number is represented as a string.) <P><SMALL><BLOCKQUOTE><SMALL><SUP>*</SUP>Scheme's primitive I/O facility gives you the choice of expressions or characters. Instead of using <CODE>read-char</CODE>, we invent <CODE>read-line</CODE>, which reads a line as a sentence, and <CODE>read-string</CODE>, which returns the line as one long word.</SMALL></BLOCKQUOTE></SMALL><P>There is an efficiency cost to treating both words and sentences as abstract aggregates, since it's slow to disassemble a sentence from right to left and slow to disassemble a word in either direction. Many simple procedures that seem linear actually behave quadratically. Luckily, words aren't usually very long, and the applications we undertake in the early chapters don't use large amounts of data in any form. We write our large projects as efficiently as we can without making the programs unreadable, but we generally don't make a fuss about it. Near the end of the book we discuss explicitly the efficient use of data structures. <P><H2>Overloading in the Text Abstraction</H2> <P>Even though computers represent numbers internally in many different ways (fixed point, bignum, floating point, exact rational, complex), when people visit mathland, they expect to meet numbers there, and they expect that all the numbers will understand how to add, subtract, multiply, and divide with each other. (The exception is dividing by zero, but that's because of the inherent rules of mathematics, not because of the separation of numbers into categories by representation format.) <P>We feel the same way about visiting textland. We expect to meet English text there. It takes the form of words and sentences. The operations that text understands include <CODE>first</CODE>, <CODE>last</CODE>, <CODE>butfirst</CODE>, and <CODE> butlast</CODE> to divide the text into its component parts. You can't divide an empty word or sentence into parts, but it's just as natural to divide a word into letters as to divide a sentence into words. (The ideas of mathland and textland, as well as the details of the word and sentence procedures, come from Logo.) <P>Some people who are accustomed to Scheme's view of data types consider <CODE> first</CODE> to be badly &quot;overloaded&quot;; they feel that a procedure that selects an element from a list shouldn't also extract a letter from a symbol. Some of them would prefer that we use <CODE>car</CODE> for lists, use <CODE>substring</CODE> for strings, and not disassemble symbols at all. Others want us to define <CODE> word-first</CODE> and <CODE>sentence-first</CODE>. <P>To us, <CODE>word-first</CODE> and <CODE>sentence-first</CODE> sound no less awkward than <CODE>fixnum-+</CODE> and <CODE>bignum-+</CODE>. Everyone agrees that it's reasonable to overload the name <CODE>+</CODE> because the purposes are so similar. Our students find it just as reasonable that <CODE>first</CODE> works for words as well as for sentences; they don't get confused by this. <P>As for the inviolability of symbols--the wall between names and data--we are following an older Lisp tradition, in which it was commonplace to <CODE> explode</CODE> symbols and to construct new names within a program. Practically speaking, all that prevents us from representing words as strings is that Scheme requires quotation marks around them. But in any case, the abstraction we're presenting is that the data we're dissecting are neither strings nor symbols, but words. <P><H2>Higher-Order Procedures, Lambda, and Recursion</H2> <P>Scheme relies on procedure invocation as virtually its only control mechanism. In order to write interesting programs, a Scheme user must understand at least one of two hard ideas: recursion or procedure as object (in order to use higher-order procedures). We believe that higher-order procedures are easier to learn, especially because we begin in Chapter 8 by applying them only to named procedures. Using a named procedure as an argument to another procedure is the way to use procedures as objects that's least upsetting to a beginner. After the reader is comfortable with higher-order procedures, we introduce <CODE>lambda</CODE>; after that we introduce recursion. We do the tic-tac-toe example with higher-order procedures and <CODE>lambda</CODE>, but not recursion. <P>When we get to recursion, we begin with an example of embedded recursion. Many books begin with the simplest possible recursive procedure, which turns out to be a simple sequential recursion, or even a tail recursion. We feel that starting with such examples allows students to invent the &quot;go back&quot; model of recursion as looping. <P><H2>Mutators and Environments</H2> <P>One of the most unusual characteristics of this book is that there is no assignment to variables in it. The reason we avoid <CODE>set!</CODE> is that the environment model of evaluation is very hard for most students. We use a pure substitution model throughout most of the book. (With the background they get from this book, students should be ready for the environment model when they see a rigorous presentation, as they will, for example, in Chapter 3 of <EM>SICP.</EM>) <P>As the last topic in the book, we do introduce a form of mutation, namely <CODE>vector-set!</CODE>. Mutation of vectors is less problematic than mutation of lists, because lists naturally share storage. You really have to go out of your way to get two pointers to the same vector.<SUP>*</SUP> Mutation of data structures is less problematic than assignment to variables because it separates the issue of mutation from the issues of binding and scope. Using vectors raises no new questions about the evaluation process, so we present mutation without reference to any formal model of evaluation. We acknowledge that we're on thin ice here, but it seems to work for our students. <P><SMALL><BLOCKQUOTE><SMALL><SUP>*</SUP>We don't talk about <CODE>eq?</CODE> at all. We're careful to write our programs in such a way that the issue of identity doesn't arise for the reader.</SMALL></BLOCKQUOTE></SMALL><P>In effect, our model of mutation is the &quot;shoebox&quot; model that you'd find in a mainstream programming language text. Before we get to mutation, we use input/output programming to introduce the ideas of effect and sequence; assigning a value to a vector element introduces the important idea of state. We use the sequential model to write two more or less practical programs, a spreadsheet and a database system. A more traditional approach to assignment in Scheme would be to build an object-oriented language extension, but the use of local state variables would definitely force us to pay attention to environments. <P><A HREF="../simply-toc.html">(back to Table of Contents)</A> </HTML>