Skip to content

Commit 3e060cf

Browse files
authored
=> supports pragmas & names (+ changed behavior) (#14200)
* => supports pragmas & names (+ changed behavior) (x, y: int) is now parsed as (x: int, y: int) instead of (x: auto, y: int) inside => and ->. * fix pragma check * fixes, use since & LHS of -> supports pragmas
1 parent b56432b commit 3e060cf

File tree

6 files changed

+96
-55
lines changed

6 files changed

+96
-55
lines changed

changelog.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,22 @@
6161
- `paramCount` & `paramStr` are now defined in os.nim instead of nimscript.nim for nimscript/nimble.
6262
- `dollars.$` now works for unsigned ints with `nim js`
6363

64+
- `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed
65+
into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` in consistency
66+
with regular proc definitions (although you cannot use semicolons).
67+
68+
Pragmas and using a name are now allowed on the lefthand side of `=>`. Here
69+
is an aggregate example of these changes:
70+
```nim
71+
import sugar
72+
73+
foo(x, y: int) {.noSideEffect.} => x + y
74+
75+
# is transformed into
76+
77+
proc foo(x: int, y: int): auto {.noSideEffect.} = x + y
78+
```
79+
6480
## Language changes
6581
- In newruntime it is now allowed to assign discriminator field without restrictions as long as case object doesn't have custom destructor. Discriminator value doesn't have to be a constant either. If you have custom destructor for case object and you do want to freely assign discriminator fields, it is recommended to refactor object into 2 objects like this:
6682
```nim

lib/pure/htmlgen.nim

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ proc getIdent(e: NimNode): string {.compileTime.} =
6464
result = getIdent(e[0])
6565
for i in 1 .. e.len-1:
6666
result.add getIdent(e[i])
67-
else: error("cannot extract identifier from node: " & toStrLit(e).strVal)
67+
else: error("cannot extract identifier from node: " & toStrLit(e).strVal, e)
6868

6969
proc delete[T](s: var seq[T], attr: T): bool =
7070
var idx = find(s, attr)
@@ -96,14 +96,14 @@ proc xmlCheckedTag*(argsList: NimNode, tag: string, optAttr = "", reqAttr = "",
9696
result.add(argsList[i][1])
9797
result.add(newStrLitNode("\""))
9898
else:
99-
error("invalid attribute for '" & tag & "' element: " & name)
99+
error("invalid attribute for '" & tag & "' element: " & name, argsList[i])
100100
# check each required attribute exists:
101101
if req.len > 0:
102-
error(req[0] & " attribute for '" & tag & "' element expected")
102+
error(req[0] & " attribute for '" & tag & "' element expected", argsList)
103103
if isLeaf:
104104
for i in 0 ..< argsList.len:
105105
if argsList[i].kind != nnkExprEqExpr:
106-
error("element " & tag & " cannot be nested")
106+
error("element " & tag & " cannot be nested", argsList[i])
107107
result.add(newStrLitNode(" />"))
108108
else:
109109
result.add(newStrLitNode(">"))

lib/pure/strutils.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,7 @@ macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped =
12951295
foundFields.add fStr
12961296
else:
12971297
error("Ambiguous enums cannot be parsed, field " & $fStr &
1298-
" appears multiple times!")
1298+
" appears multiple times!", f)
12991299
inc fNum
13001300
# finally add else branch to raise or use default
13011301
if default == nil:

lib/pure/sugar.nim

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,25 @@
1111
## macro system.
1212

1313
import std/private/since
14-
import macros
15-
import typetraits
14+
import macros, typetraits
15+
16+
proc checkPragma(ex, prag: var NimNode) =
17+
since (1, 3):
18+
if ex.kind == nnkPragmaExpr:
19+
prag = ex[1]
20+
if ex[0].kind == nnkPar and ex[0].len == 1:
21+
ex = ex[0][0]
22+
else:
23+
ex = ex[0]
1624

1725
proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
18-
#echo treeRepr(p)
19-
#echo treeRepr(b)
2026
result = newNimNode(nnkProcTy)
21-
var formalParams = newNimNode(nnkFormalParams)
27+
var
28+
formalParams = newNimNode(nnkFormalParams).add(b)
29+
p = p
30+
prag = newEmptyNode()
2231

23-
formalParams.add b
32+
checkPragma(p, prag)
2433

2534
case p.kind
2635
of nnkPar, nnkTupleConstr:
@@ -44,9 +53,7 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
4453
formalParams.add identDefs
4554

4655
result.add formalParams
47-
result.add newEmptyNode()
48-
#echo(treeRepr(result))
49-
#echo(result.toStrLit())
56+
result.add prag
5057

5158
macro `=>`*(p, b: untyped): untyped =
5259
## Syntax sugar for anonymous procedures.
@@ -58,56 +65,75 @@ macro `=>`*(p, b: untyped): untyped =
5865
##
5966
## passTwoAndTwo((x, y) => x + y) # 4
6067

61-
#echo treeRepr(p)
62-
#echo(treeRepr(b))
63-
var params: seq[NimNode] = @[newIdentNode("auto")]
68+
var
69+
params = @[ident"auto"]
70+
name = newEmptyNode()
71+
kind = nnkLambda
72+
pragma = newEmptyNode()
73+
p = p
74+
75+
checkPragma(p, pragma)
76+
77+
if p.kind == nnkInfix and p[0].kind == nnkIdent and p[0].eqIdent"->":
78+
params[0] = p[2]
79+
p = p[1]
80+
81+
checkPragma(p, pragma) # check again after -> transform
82+
83+
since (1, 3):
84+
if p.kind == nnkCall:
85+
# foo(x, y) => x + y
86+
kind = nnkProcDef
87+
name = p[0]
88+
let newP = newNimNode(nnkPar)
89+
for i in 1..<p.len:
90+
newP.add(p[i])
91+
p = newP
6492

6593
case p.kind
6694
of nnkPar, nnkTupleConstr:
67-
for c in children(p):
95+
var untypedBeforeColon = 0
96+
for i, c in p:
6897
var identDefs = newNimNode(nnkIdentDefs)
6998
case c.kind
7099
of nnkExprColonExpr:
100+
let t = c[1]
101+
since (1, 3):
102+
# + 1 here because of return type in params
103+
for j in (i - untypedBeforeColon + 1) .. i:
104+
params[j][1] = t
105+
untypedBeforeColon = 0
71106
identDefs.add(c[0])
72-
identDefs.add(c[1])
107+
identDefs.add(t)
73108
identDefs.add(newEmptyNode())
74109
of nnkIdent:
75110
identDefs.add(c)
76111
identDefs.add(newIdentNode("auto"))
77112
identDefs.add(newEmptyNode())
113+
inc untypedBeforeColon
78114
of nnkInfix:
79-
if c[0].kind == nnkIdent and c[0].ident == !"->":
115+
if c[0].kind == nnkIdent and c[0].eqIdent"->":
80116
var procTy = createProcType(c[1], c[2])
81117
params[0] = procTy[0][0]
82118
for i in 1 ..< procTy[0].len:
83119
params.add(procTy[0][i])
84120
else:
85-
error("Expected proc type (->) got (" & $c[0].ident & ").")
121+
error("Expected proc type (->) got (" & c[0].strVal & ").", c)
86122
break
87123
else:
88-
echo treeRepr c
89-
error("Incorrect procedure parameter list.")
124+
error("Incorrect procedure parameter list.", c)
90125
params.add(identDefs)
91126
of nnkIdent:
92127
var identDefs = newNimNode(nnkIdentDefs)
93128
identDefs.add(p)
94-
identDefs.add(newIdentNode("auto"))
129+
identDefs.add(ident"auto")
95130
identDefs.add(newEmptyNode())
96131
params.add(identDefs)
97-
of nnkInfix:
98-
if p[0].kind == nnkIdent and p[0].ident == !"->":
99-
var procTy = createProcType(p[1], p[2])
100-
params[0] = procTy[0][0]
101-
for i in 1 ..< procTy[0].len:
102-
params.add(procTy[0][i])
103-
else:
104-
error("Expected proc type (->) got (" & $p[0].ident & ").")
105132
else:
106-
error("Incorrect procedure parameter list.")
107-
result = newProc(params = params, body = b, procType = nnkLambda)
108-
#echo(result.treeRepr)
109-
#echo(result.toStrLit())
110-
#return result # TODO: Bug?
133+
error("Incorrect procedure parameter list.", p)
134+
result = newProc(body = b, params = params,
135+
pragmas = pragma, name = name,
136+
procType = kind)
111137

112138
macro `->`*(p, b: untyped): untyped =
113139
## Syntax sugar for procedure types.
@@ -190,7 +216,7 @@ macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).}
190216
result.add(newProc(newEmptyNode(), params, body, nnkProcDef))
191217
for arg in locals: result.add(arg)
192218

193-
when (NimMajor, NimMinor) >= (1, 1):
219+
since (1, 1):
194220
import std / private / underscored_calls
195221

196222
macro dup*[T](arg: T, calls: varargs[untyped]): T =

lib/pure/typetraits.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ macro genericParamsImpl(T: typedesc): untyped =
124124
result.add ret
125125
break
126126
else:
127-
error "wrong kind: " & $impl.kind
127+
error "wrong kind: " & $impl.kind, impl
128128

129129
since (1, 1):
130130
template genericParams*(T: typedesc): untyped =

tests/macros/tclosuremacro.nim

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
discard """
2-
output: '''10
3-
10
4-
10
5-
3
6-
3
2+
output: '''
73
noReturn
8-
6
94
calling mystuff
105
yes
116
calling mystuff
127
yes
8+
calling sugarWithPragma
9+
sugarWithPragma called
1310
'''
1411
"""
1512

16-
import future, macros
13+
import sugar, macros
1714

1815
proc twoParams(x: (int, int) -> int): int =
1916
result = x(5, 5)
@@ -30,23 +27,23 @@ proc noReturn(x: () -> void) =
3027
proc doWithOneAndTwo(f: (int, int) -> int): int =
3128
f(1,2)
3229

33-
echo twoParams(proc (a, b: auto): auto = a + b)
34-
echo twoParams((x, y) => x + y)
35-
36-
echo oneParam(x => x+5)
37-
38-
echo noParams(() => 3)
39-
40-
echo doWithOneAndTwo((x, y) => x + y)
30+
doAssert twoParams(proc (a, b: auto): auto = a + b) == 10
31+
doAssert twoParams((x, y) => x + y) == 10
32+
doAssert oneParam(x => x+5) == 10
33+
doAssert noParams(() => 3) == 3
34+
doAssert doWithOneAndTwo((x, y) => x + y) == 3
4135

4236
noReturn((() -> void) => echo("noReturn"))
4337

4438
proc pass2(f: (int, int) -> int): (int) -> int =
4539
((x: int) -> int) => f(2, x)
4640

47-
echo pass2((x, y) => x + y)(4)
41+
doAssert pass2((x, y) => x + y)(4) == 6
4842

43+
fun(x, y: int) {.noSideEffect.} => x + y
4944

45+
doAssert typeof(fun) is (proc (x, y: int): int {.nimcall.})
46+
doAssert fun(3, 4) == 7
5047

5148
proc register(name: string; x: proc()) =
5249
echo "calling ", name
@@ -72,3 +69,5 @@ macro m(x: untyped): untyped =
7269
m:
7370
proc mystuff() =
7471
echo "yes"
72+
73+
sugarWithPragma() {.m.} => echo "sugarWithPragma called"

0 commit comments

Comments
 (0)