aboutsummaryrefslogtreecommitdiff
path: root/docs/book/METAPROGRAMMING.html
diff options
context:
space:
mode:
Diffstat (limited to 'docs/book/METAPROGRAMMING.html')
-rw-r--r--docs/book/METAPROGRAMMING.html57
1 files changed, 21 insertions, 36 deletions
diff --git a/docs/book/METAPROGRAMMING.html b/docs/book/METAPROGRAMMING.html
index 92bb4cb..2bb9333 100644
--- a/docs/book/METAPROGRAMMING.html
+++ b/docs/book/METAPROGRAMMING.html
@@ -7,7 +7,7 @@
<!-- Custom HTML head -->
-
+
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
@@ -174,52 +174,53 @@
<div id="content" class="content">
<main>
<h1 id="metaprogramming"><a class="header" href="#metaprogramming">Metaprogramming</a></h1>
-<p>Puck has rich metaprogramming support, heavily inspired by Nim. Many features that would have to be at the compiler level in most languages (error propagation <code>?</code>, <code>std.fmt.print</code>, <code>async</code>/<code>await</code>) are instead implemented as macros within the standard library.</p>
-<p>Macros take in fragments of the AST within their scope, transform them with arbitrary compile-time code, and spit back out transformed AST fragments to be injected and checked for validity. This is similar to what Nim and the Lisp family of languages do.
-By keeping an intentionally minimal AST, some things not possible to express in literal code may be expressible in the AST: in particular, bindings can be injected in many places they could not be injected in ordinarily. (A minimal AST also has the benefit of being quite predictable.)</p>
-<p>Macros may not change Puck's syntax: the syntax is flexible enough. Code is syntactically checked (parsed), but <em>not</em> semantically checked (typechecked) before being passed to macros. This may change in the future<!-- (to require arguments to be semantically correct)-->. Macros have the same scope as other routines, that is:</p>
+<p>Puck has rich metaprogramming support, heavily inspired by Nim. Many features that would have to be at the compiler level in most languages (error propagation <code>?</code>, <code>std.fmt.print</code>, <code>?</code>, <code>!</code>, <code>-&gt;</code> type sugar, <code>=&gt;</code> closure sugar, <code>async</code>/<code>await</code>) are instead implemented as macros within the standard library.</p>
+<p>Macros take in fragments of the AST within their scope, transform them with arbitrary compile-time code, and spit back out transformed AST fragments to be injected and checked for validity. This is similar to what the Lisp family of languages do. It has a number of benefits: there is no separate metaprogramming language, it is syntactically and semantically hygienic, and the underlying framework can be reused for all kinds of compile-time code execution.</p>
+<p>By keeping an intentionally minimal AST, some things not possible to express in literal code may be expressible in the AST: in particular, bindings can be injected in many places they could not be injected in ordinarily. (A minimal AST also has the benefit of being quite predictable.)</p>
+<p>Macros may not change Puck's syntax: the syntax is flexible enough. They have the same scope as other routines, that is:</p>
<p><strong>function scope</strong>: takes the arguments within or following a function call</p>
<pre><code class="language-puck">macro print(params: varargs) =
- for param in params:
- result.add(quote(stdout.write(`params`.str)))
+ var res = Call("write", [stdout])
+ for param in params do
+ res.params.add(param)
print(1, 2, 3, 4)
-print &quot;hello&quot;, &quot; &quot;, &quot;world&quot;, &quot;!&quot;
+print "hello", " ", "world", "!"
</code></pre>
<p><strong>block scope</strong>: takes the expression following a colon as a single argument</p>
<pre><code class="language-puck">macro my_macro(body)
-my_macro:
+my_macro
1
2
3
4
</code></pre>
-<p><strong>operator scope</strong>: takes one or two parameters either as a postfix (one parameter) or an infix (two parameters) operator</p>
-<pre><code class="language-puck">macro +=(a, b) =
- quote:
- `a` = `a` + `b`
+<p><strong>operator scope</strong>: takes one or two parameters either as an infix (two parameters) or a postfix (one parameter) operator</p>
+<pre><code class="language-puck"># operators are restricted to punctuation
+macro +=(a, b) =
+ Call("=", [a, Call("+", [a, b])])
a += b
</code></pre>
<p>Macros typically take a list of parameters <em>without</em> types, but they optionally may be given a type to constrain the usage of a macro. Regardless: as macros operate at compile time, their parameters are not instances of a type, but rather an <code>Expr</code> expression representing a portion of the <em>abstract syntax tree</em>.
Similarly, macros always return an <code>Expr</code> to be injected into the abstract syntax tree despite the usual absence of an explicit return type, but the return type may be specified to additionally typecheck the returned <code>Expr</code>.</p>
<pre><code class="language-puck"></code></pre>
-<p>As macros operate at compile time, they may not inspect the <em>values</em> that their parameters evaluate to. However, parameters may be marked with <code>static[T]</code>: in which case they will be treated like parameters in functions: as values. (note static parameters may be written as <code>static[T]</code> or <code>static T</code>.) There are many restrictions on what might be <code>static</code> parameters. Currently, it is constrained to literals i.e. <code>1</code>, <code>&quot;hello&quot;</code>, etc, though this will hopefully be expanded to any function that may be evaluated statically in the future.</p>
+<p>As macros operate at compile time, they may not inspect the <em>values</em> that their parameters evaluate to. However, parameters may be marked <code>const</code>: in which case they will be treated like parameters in functions: as values. (note constant parameters may be written as <code>const[T]</code> or <code>const T</code>.)</p>
<pre><code class="language-puck">macro ?[T, E](self: Result[T, E]) =
- quote:
- match self
- of Okay(x): x
- of Error(e): return Error(e)
+ quote
+ match `self`
+ of Okay(x) then x
+ of Error(e) then return Error(e)
func meow: Result[bool, ref Err] =
let a = stdin.get()?
</code></pre>
-<p>The <code>quote</code> macro is special. It takes in literal code and returns that code <strong>as the AST</strong>. Within quoted data, backticks may be used to break out in order to evaluate and inject arbitrary code: though the code must evaluate to an expression of type <code>Expr</code>. <!-- Variables (of type `Expr`) may be *injected* into the literal code by wrapping them in backticks. This reuse of backticks does mean that defining new operators is impossible within quoted code. --></p>
+<p>The <code>quote</code> macro is special. It takes in literal code and returns that code <strong>as the AST</strong>. Within quoted data, backticks may be used to break out in order to evaluate and inject arbitrary code: though the code must evaluate to an expression of type <code>Expr</code>. Thus, quoting is <em>structured</em>: one cannot simply quote any arbitrary section. Quoting is very powerful: most macros are implemented using it.</p>
<pre><code class="language-puck"></code></pre>
<p>The <code>Expr</code> type is available from <code>std.ast</code>, as are many helpers, and combined they provide the construction of arbitrary syntax trees (indeed, <code>quote</code> relies on and emits types of it). It is a <code>union</code> type with its variants directly corresponding to the variants of the internal AST of Puck.</p>
<pre><code class="language-puck"></code></pre>
-<p>Construction of macros can be difficult: and so several helpers are provided to ease debugging. The <code>Debug</code> and <code>Display</code> interfaces are implemented for abstract syntax trees: <code>dbg</code> will print a representation of the passed syntax tree as an object, and <code>print</code> will print a best-effort representation as literal code. Together with <code>quote</code> and optionally with <code>static</code>, these can be used to quickly get the representation of arbitrary code.</p>
+<p>Construction of macros can be difficult: and so several helpers are provided to ease debugging. The <code>Debug</code> and <code>Display</code> interfaces are implemented for abstract syntax trees: <code>dbg</code> will print a representation of the passed syntax tree as an object, and <code>print</code> will print a best-effort representation as literal code. Together with <code>quote</code> and optionally with <code>const</code>, these can be used to quickly get the representation of arbitrary code.</p>
</main>
@@ -250,22 +251,6 @@ func meow: Result[bool, ref Err] =
</div>
- <!-- Livereload script (if served using the cli tool) -->
- <script>
- const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
- const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
- const socket = new WebSocket(wsAddress);
- socket.onmessage = function (event) {
- if (event.data === "reload") {
- socket.close();
- location.reload();
- }
- };
-
- window.onbeforeunload = function() {
- socket.close();
- }
- </script>