=> 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
This commit is contained in:
hlaaftana 2020-05-03 11:22:49 +03:00 • committed by GitHub
commit 3e060cfb0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 55 deletions

View file

@ -61,6 +61,22 @@
- `paramCount` & `paramStr` are now defined in os.nim instead of nimscript.nim for nimscript/nimble. - `paramCount` & `paramStr` are now defined in os.nim instead of nimscript.nim for nimscript/nimble.
- `dollars.$` now works for unsigned ints with `nim js` - `dollars.$` now works for unsigned ints with `nim js`
- `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed
into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` in consistency
with regular proc definitions (although you cannot use semicolons).
Pragmas and using a name are now allowed on the lefthand side of `=>`. Here
is an aggregate example of these changes:
```nim
import sugar
foo(x, y: int) {.noSideEffect.} => x + y
# is transformed into
proc foo(x: int, y: int): auto {.noSideEffect.} = x + y
```
## Language changes ## Language changes
- 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: - 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:
```nim ```nim

View file

@ -64,7 +64,7 @@ proc getIdent(e: NimNode): string {.compileTime.} =
result = getIdent(e[0]) result = getIdent(e[0])
for i in 1 .. e.len-1: for i in 1 .. e.len-1:
result.add getIdent(e[i]) result.add getIdent(e[i])
else: error("cannot extract identifier from node: " & toStrLit(e).strVal) else: error("cannot extract identifier from node: " & toStrLit(e).strVal, e)
proc delete[T](s: var seq[T], attr: T): bool = proc delete[T](s: var seq[T], attr: T): bool =
var idx = find(s, attr) var idx = find(s, attr)
@ -96,14 +96,14 @@ proc xmlCheckedTag*(argsList: NimNode, tag: string, optAttr = "", reqAttr = "",
result.add(argsList[i][1]) result.add(argsList[i][1])
result.add(newStrLitNode("\"")) result.add(newStrLitNode("\""))
else: else:
error("invalid attribute for '" & tag & "' element: " & name) error("invalid attribute for '" & tag & "' element: " & name, argsList[i])
# check each required attribute exists: # check each required attribute exists:
if req.len > 0: if req.len > 0:
error(req[0] & " attribute for '" & tag & "' element expected") error(req[0] & " attribute for '" & tag & "' element expected", argsList)
if isLeaf: if isLeaf:
for i in 0 ..< argsList.len: for i in 0 ..< argsList.len:
if argsList[i].kind != nnkExprEqExpr: if argsList[i].kind != nnkExprEqExpr:
error("element " & tag & " cannot be nested") error("element " & tag & " cannot be nested", argsList[i])
result.add(newStrLitNode(" />")) result.add(newStrLitNode(" />"))
else: else:
result.add(newStrLitNode(">")) result.add(newStrLitNode(">"))

View file

@ -1295,7 +1295,7 @@ macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped =
foundFields.add fStr foundFields.add fStr
else: else:
error("Ambiguous enums cannot be parsed, field " & $fStr & error("Ambiguous enums cannot be parsed, field " & $fStr &
" appears multiple times!") " appears multiple times!", f)
inc fNum inc fNum
# finally add else branch to raise or use default # finally add else branch to raise or use default
if default == nil: if default == nil:

View file

@ -11,16 +11,25 @@
## macro system. ## macro system.
import std/private/since import std/private/since
import macros import macros, typetraits
import typetraits
proc checkPragma(ex, prag: var NimNode) =
since (1, 3):
if ex.kind == nnkPragmaExpr:
prag = ex[1]
if ex[0].kind == nnkPar and ex[0].len == 1:
ex = ex[0][0]
else:
ex = ex[0]
proc createProcType(p, b: NimNode): NimNode {.compileTime.} = proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
#echo treeRepr(p)
#echo treeRepr(b)
result = newNimNode(nnkProcTy) result = newNimNode(nnkProcTy)
var formalParams = newNimNode(nnkFormalParams) var
formalParams = newNimNode(nnkFormalParams).add(b)
p = p
prag = newEmptyNode()
formalParams.add b checkPragma(p, prag)
case p.kind case p.kind
of nnkPar, nnkTupleConstr: of nnkPar, nnkTupleConstr:
@ -44,9 +53,7 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
formalParams.add identDefs formalParams.add identDefs
result.add formalParams result.add formalParams
result.add newEmptyNode() result.add prag
#echo(treeRepr(result))
#echo(result.toStrLit())
macro `=>`*(p, b: untyped): untyped = macro `=>`*(p, b: untyped): untyped =
## Syntax sugar for anonymous procedures. ## Syntax sugar for anonymous procedures.
@ -58,56 +65,75 @@ macro `=>`*(p, b: untyped): untyped =
## ##
## passTwoAndTwo((x, y) => x + y) # 4 ## passTwoAndTwo((x, y) => x + y) # 4
#echo treeRepr(p) var
#echo(treeRepr(b)) params = @[ident"auto"]
var params: seq[NimNode] = @[newIdentNode("auto")] name = newEmptyNode()
kind = nnkLambda
pragma = newEmptyNode()
p = p
checkPragma(p, pragma)
if p.kind == nnkInfix and p[0].kind == nnkIdent and p[0].eqIdent"->":
params[0] = p[2]
p = p[1]
checkPragma(p, pragma) # check again after -> transform
since (1, 3):
if p.kind == nnkCall:
# foo(x, y) => x + y
kind = nnkProcDef
name = p[0]
let newP = newNimNode(nnkPar)
for i in 1..<p.len:
newP.add(p[i])
p = newP
case p.kind case p.kind
of nnkPar, nnkTupleConstr: of nnkPar, nnkTupleConstr:
for c in children(p): var untypedBeforeColon = 0
for i, c in p:
var identDefs = newNimNode(nnkIdentDefs) var identDefs = newNimNode(nnkIdentDefs)
case c.kind case c.kind
of nnkExprColonExpr: of nnkExprColonExpr:
let t = c[1]
since (1, 3):
# + 1 here because of return type in params
for j in (i - untypedBeforeColon + 1) .. i:
params[j][1] = t
untypedBeforeColon = 0
identDefs.add(c[0]) identDefs.add(c[0])
identDefs.add(c[1]) identDefs.add(t)
identDefs.add(newEmptyNode()) identDefs.add(newEmptyNode())
of nnkIdent: of nnkIdent:
identDefs.add(c) identDefs.add(c)
identDefs.add(newIdentNode("auto")) identDefs.add(newIdentNode("auto"))
identDefs.add(newEmptyNode()) identDefs.add(newEmptyNode())
inc untypedBeforeColon
of nnkInfix: of nnkInfix:
if c[0].kind == nnkIdent and c[0].ident == !"->": if c[0].kind == nnkIdent and c[0].eqIdent"->":
var procTy = createProcType(c[1], c[2]) var procTy = createProcType(c[1], c[2])
params[0] = procTy[0][0] params[0] = procTy[0][0]
for i in 1 ..< procTy[0].len: for i in 1 ..< procTy[0].len:
params.add(procTy[0][i]) params.add(procTy[0][i])
else: else:
error("Expected proc type (->) got (" & $c[0].ident & ").") error("Expected proc type (->) got (" & c[0].strVal & ").", c)
break break
else: else:
echo treeRepr c error("Incorrect procedure parameter list.", c)
error("Incorrect procedure parameter list.")
params.add(identDefs) params.add(identDefs)
of nnkIdent: of nnkIdent:
var identDefs = newNimNode(nnkIdentDefs) var identDefs = newNimNode(nnkIdentDefs)
identDefs.add(p) identDefs.add(p)
identDefs.add(newIdentNode("auto")) identDefs.add(ident"auto")
identDefs.add(newEmptyNode()) identDefs.add(newEmptyNode())
params.add(identDefs) params.add(identDefs)
of nnkInfix:
if p[0].kind == nnkIdent and p[0].ident == !"->":
var procTy = createProcType(p[1], p[2])
params[0] = procTy[0][0]
for i in 1 ..< procTy[0].len:
params.add(procTy[0][i])
else:
error("Expected proc type (->) got (" & $p[0].ident & ").")
else: else:
error("Incorrect procedure parameter list.") error("Incorrect procedure parameter list.", p)
result = newProc(params = params, body = b, procType = nnkLambda) result = newProc(body = b, params = params,
#echo(result.treeRepr) pragmas = pragma, name = name,
#echo(result.toStrLit()) procType = kind)
#return result # TODO: Bug?
macro `->`*(p, b: untyped): untyped = macro `->`*(p, b: untyped): untyped =
## Syntax sugar for procedure types. ## Syntax sugar for procedure types.
@ -190,7 +216,7 @@ macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).}
result.add(newProc(newEmptyNode(), params, body, nnkProcDef)) result.add(newProc(newEmptyNode(), params, body, nnkProcDef))
for arg in locals: result.add(arg) for arg in locals: result.add(arg)
when (NimMajor, NimMinor) >= (1, 1): since (1, 1):
import std / private / underscored_calls import std / private / underscored_calls
macro dup*[T](arg: T, calls: varargs[untyped]): T = macro dup*[T](arg: T, calls: varargs[untyped]): T =

View file

@ -124,7 +124,7 @@ macro genericParamsImpl(T: typedesc): untyped =
result.add ret result.add ret
break break
else: else:
error "wrong kind: " & $impl.kind error "wrong kind: " & $impl.kind, impl
since (1, 1): since (1, 1):
template genericParams*(T: typedesc): untyped = template genericParams*(T: typedesc): untyped =

View file

@ -1,19 +1,16 @@
discard """ discard """
output: '''10 output: '''
10
10
3
3
noReturn noReturn
6
calling mystuff calling mystuff
yes yes
calling mystuff calling mystuff
yes yes
calling sugarWithPragma
sugarWithPragma called
''' '''
""" """
import future, macros import sugar, macros
proc twoParams(x: (int, int) -> int): int = proc twoParams(x: (int, int) -> int): int =
result = x(5, 5) result = x(5, 5)
@ -30,23 +27,23 @@ proc noReturn(x: () -> void) =
proc doWithOneAndTwo(f: (int, int) -> int): int = proc doWithOneAndTwo(f: (int, int) -> int): int =
f(1,2) f(1,2)
echo twoParams(proc (a, b: auto): auto = a + b) doAssert twoParams(proc (a, b: auto): auto = a + b) == 10
echo twoParams((x, y) => x + y) doAssert twoParams((x, y) => x + y) == 10
doAssert oneParam(x => x+5) == 10
echo oneParam(x => x+5) doAssert noParams(() => 3) == 3
doAssert doWithOneAndTwo((x, y) => x + y) == 3
echo noParams(() => 3)
echo doWithOneAndTwo((x, y) => x + y)
noReturn((() -> void) => echo("noReturn")) noReturn((() -> void) => echo("noReturn"))
proc pass2(f: (int, int) -> int): (int) -> int = proc pass2(f: (int, int) -> int): (int) -> int =
((x: int) -> int) => f(2, x) ((x: int) -> int) => f(2, x)
echo pass2((x, y) => x + y)(4) doAssert pass2((x, y) => x + y)(4) == 6
fun(x, y: int) {.noSideEffect.} => x + y
doAssert typeof(fun) is (proc (x, y: int): int {.nimcall.})
doAssert fun(3, 4) == 7
proc register(name: string; x: proc()) = proc register(name: string; x: proc()) =
echo "calling ", name echo "calling ", name
@ -72,3 +69,5 @@ macro m(x: untyped): untyped =
m: m:
proc mystuff() = proc mystuff() =
echo "yes" echo "yes"
sugarWithPragma() {.m.} => echo "sugarWithPragma called"