aboutsummaryrefslogtreecommitdiff
path: root/docs/METAPROGRAMMING.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/METAPROGRAMMING.md')
-rw-r--r--docs/METAPROGRAMMING.md24
1 files changed, 13 insertions, 11 deletions
diff --git a/docs/METAPROGRAMMING.md b/docs/METAPROGRAMMING.md
index a8675b2..17e65a0 100644
--- a/docs/METAPROGRAMMING.md
+++ b/docs/METAPROGRAMMING.md
@@ -1,17 +1,19 @@
# Metaprogramming
-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 `?`, `std.fmt.print`, `async`/`await`) are instead implemented as macros within the standard library.
+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 `?`, `std.fmt.print`, `?`, `!`, `->` type sugar, `=>` closure sugar, `async`/`await`) are instead implemented as macros within the standard library.
+
+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.
-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.)
-Macros may not change Puck's syntax: the syntax is flexible enough. Code is syntactically checked (parsed), but *not* 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:
+Macros may not change Puck's syntax: the syntax is flexible enough. They have the same scope as other routines, that is:
**function scope**: takes the arguments within or following a function call
```puck
macro print(params: varargs) =
+ var res = Call("write", [stdout])
for param in params do
- result.add(quote(stdout.write(`params`.str)))
+ res.params.add(param)
print(1, 2, 3, 4)
print "hello", " ", "world", "!"
@@ -28,11 +30,11 @@ my_macro
4
```
-**operator scope**: takes one or two parameters either as a postfix (one parameter) or an infix (two parameters) operator
+**operator scope**: takes one or two parameters either as an infix (two parameters) or a postfix (one parameter) operator
```puck
+# operators are restricted to punctuation
macro +=(a, b) =
- quote
- `a` = `a` + `b`
+ Call("=", [a, Call("+", [a, b])])
a += b
```
@@ -43,12 +45,12 @@ Similarly, macros always return an `Expr` to be injected into the abstract synta
```puck
```
-As macros operate at compile time, they may not inspect the *values* that their parameters evaluate to. However, parameters may be marked with `static[T]`: in which case they will be treated like parameters in functions: as values. (note static parameters may be written as `static[T]` or `static T`.) There are many restrictions on what might be `static` parameters. Currently, it is constrained to literals i.e. `1`, `"hello"`, etc, though this will hopefully be expanded to any function that may be evaluated statically in the future.
+As macros operate at compile time, they may not inspect the *values* that their parameters evaluate to. However, parameters may be marked `const`: in which case they will be treated like parameters in functions: as values. (note constant parameters may be written as `const[T]` or `const T`.)
```puck
macro ?[T, E](self: Result[T, E]) =
quote
- match self
+ match `self`
of Okay(x) then x
of Error(e) then return Error(e)
@@ -56,7 +58,7 @@ func meow: Result[bool, ref Err] =
let a = stdin.get()?
```
-The `quote` macro is special. It takes in literal code and returns that code **as the AST**. 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 `Expr`. <!-- 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. -->
+The `quote` macro is special. It takes in literal code and returns that code **as the AST**. 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 `Expr`. Thus, quoting is *structured*: one cannot simply quote any arbitrary section. Quoting is very powerful: most macros are implemented using it.
```puck
```
@@ -66,4 +68,4 @@ The `Expr` type is available from `std.ast`, as are many helpers, and combined t
```puck
```
-Construction of macros can be difficult: and so several helpers are provided to ease debugging. The `Debug` and `Display` interfaces are implemented for abstract syntax trees: `dbg` will print a representation of the passed syntax tree as an object, and `print` will print a best-effort representation as literal code. Together with `quote` and optionally with `static`, these can be used to quickly get the representation of arbitrary code.
+Construction of macros can be difficult: and so several helpers are provided to ease debugging. The `Debug` and `Display` interfaces are implemented for abstract syntax trees: `dbg` will print a representation of the passed syntax tree as an object, and `print` will print a best-effort representation as literal code. Together with `quote` and optionally with `const`, these can be used to quickly get the representation of arbitrary code.