Skip to content

Commit 5c50098

Browse files
committed
First
0 parents  commit 5c50098

File tree

5 files changed

+185
-0
lines changed

5 files changed

+185
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.o

LICENSE

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2017 Alexander Ivanov
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
this software and associated documentation files (the "Software"), to deal in
7+
the Software without restriction, including without limitation the rights to
8+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9+
the Software, and to permit persons to whom the Software is furnished to do so,
10+
subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# breeze
2+
3+
A dsl for writing macros in Nim
4+
5+
## rationale
6+
7+
I read a comment by Araq about how karax might be a good fit for the ast nodes in
8+
the compiler and I've always found building the nodes in `macro`-s in Nim tedious.
9+
`breeze` is a karax-like dsl for building nim nodes in macros:
10+
11+
```nim
12+
macro s(b: untyped): untyped =
13+
var e = newIdentNode(!"e")
14+
result = buildMacro:
15+
call:
16+
dotExpr(e, ident("f"))
17+
infix(ident("+"), 2, 3)
18+
```
19+
20+
expands to
21+
22+
```nim
23+
nnkCall.newTree(nnkDotExpr.newTree(e, newIdentNode(! "f")),
24+
nnkInfix.newTree(newIdentNode(! "+"), newLit(2), newLit(3)))
25+
```
26+
27+
## functionality
28+
29+
You use it invoking `buildMacro` with a singular child corresponding to the node you want to build
30+
31+
Each child is either of the form
32+
33+
```nim
34+
node:
35+
child
36+
child
37+
```
38+
39+
or
40+
41+
```nim
42+
node(child, child)
43+
```
44+
45+
They're equivalent in most cases and they usually expand to `nnk<capitalized_node>.newTree(<visited children>)`
46+
47+
Special cases are literals which are expanded to `newLit` and `ident(name)` which are expanded to `newIdentNode`.
48+
49+
You can build dynamically macros (with variables) just by passing them directly,
50+
e.g.
51+
52+
```nim
53+
var e = newIdentNode(!"f")
54+
result = buildMacro:
55+
call:
56+
e
57+
```
58+
59+
expands to
60+
61+
```nim
62+
Call
63+
Ident !"f"
64+
```
65+
66+
67+

breeze.nim

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import macros, strutils, sequtils, tables
2+
3+
const debugMacro = false
4+
5+
proc build(b: NimNode): NimNode
6+
7+
proc buildCall(args: NimNode): seq[NimNode] =
8+
result = args.mapIt(build(it))
9+
10+
proc buildInline(args: NimNode): seq[NimNode] =
11+
result = @[]
12+
var isArg = false
13+
for child in args:
14+
if isArg:
15+
result.add(build(child))
16+
else:
17+
isArg = true
18+
19+
proc labelOf(b: NimNode): string =
20+
assert b.kind in {nnkIdent, nnkAccQuoted}
21+
if b.kind == nnkAccQuoted:
22+
result = labelOf(b[0])
23+
else:
24+
result = $b.ident
25+
26+
proc buildIdent(b: NimNode): NimNode =
27+
result = nnkCall.newTree(
28+
newIdentNode(!"newIdentNode"),
29+
nnkPrefix.newTree(
30+
newIdentNode(!"!"),
31+
newLit($b)))
32+
33+
proc build(b: NimNode): NimNode =
34+
case b.kind:
35+
of nnkStmtList:
36+
result = build(b[0])
37+
of nnkCall:
38+
var label = labelOf(b[0])
39+
case label:
40+
of "ident":
41+
result = buildIdent(b[1])
42+
else:
43+
var args: seq[NimNode]
44+
if len(b) == 2:
45+
args = buildCall(b[1])
46+
else:
47+
args = buildInline(b)
48+
result = nnkCall.newTree(
49+
nnkDotExpr.newTree(
50+
newIdentNode(!("nnk$1" % capitalizeAscii(label))),
51+
newIdentNode(!"newTree")))
52+
for arg in args:
53+
result.add(arg)
54+
of nnkCharLit..nnkUInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit:
55+
result = nnkCall.newTree(
56+
newIdentNode(!"newLit"),
57+
b)
58+
of nnkIdent:
59+
if $b == "true" or $b == "false":
60+
result = nnkCall.newTree(
61+
newIdentNode(!"newLit"),
62+
b)
63+
else:
64+
result = b
65+
of nnkAccQuoted:
66+
result = build(b[0])
67+
of nnkNilLit:
68+
result = nil
69+
else:
70+
when debugMacro:
71+
echo treerepr(b)
72+
else:
73+
discard
74+
75+
macro buildMacro*(b: untyped): untyped =
76+
result = build(b)
77+
when debugMacro:
78+
echo "build: $1" % repr(result)
79+
80+
# macro s(b: untyped): untyped =
81+
# var e = newIdentNode(!"e")
82+
# result = buildMacro:
83+
# call:
84+
# dotExpr(e, ident("f"))
85+
# infix(ident("+"), 2, 3)
86+
# when debugMacro:
87+
# echo treerepr(result)
88+
89+
# var e = (f: (proc(x: int) = echo(x)))
90+
# s(2)

breeze.nimble

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version = "0.0.1"
2+
packageName = "breeze"
3+
author = "Alexander Ivanov"
4+
description = "macro dsl"
5+
license = "MIT"
6+
7+
requires "nim >= 0.16.1"

0 commit comments

Comments
 (0)