From 3e060cfb0ab6b1affe26a7b09e6b4192e0d68a7f Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Sun, 3 May 2020 11:22:49 +0300 Subject: [PATCH] => 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 --- changelog.md | 16 ++++++ lib/pure/htmlgen.nim | 8 +-- lib/pure/strutils.nim | 2 +- lib/pure/sugar.nim | 92 ++++++++++++++++++++++------------ lib/pure/typetraits.nim | 2 +- tests/macros/tclosuremacro.nim | 31 ++++++------ 6 files changed, 96 insertions(+), 55 deletions(-) diff --git a/changelog.md b/changelog.md index 4aa22b9d7a4f5..165518bd7cca4 100644 --- a/changelog.md +++ b/changelog.md @@ -61,6 +61,22 @@ - `paramCount` & `paramStr` are now defined in os.nim instead of nimscript.nim for nimscript/nimble. - `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 - 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 diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index e0518f5ee10a9..99af26c993a6a 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -64,7 +64,7 @@ proc getIdent(e: NimNode): string {.compileTime.} = result = getIdent(e[0]) for i in 1 .. e.len-1: 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 = var idx = find(s, attr) @@ -96,14 +96,14 @@ proc xmlCheckedTag*(argsList: NimNode, tag: string, optAttr = "", reqAttr = "", result.add(argsList[i][1]) result.add(newStrLitNode("\"")) else: - error("invalid attribute for '" & tag & "' element: " & name) + error("invalid attribute for '" & tag & "' element: " & name, argsList[i]) # check each required attribute exists: if req.len > 0: - error(req[0] & " attribute for '" & tag & "' element expected") + error(req[0] & " attribute for '" & tag & "' element expected", argsList) if isLeaf: for i in 0 ..< argsList.len: if argsList[i].kind != nnkExprEqExpr: - error("element " & tag & " cannot be nested") + error("element " & tag & " cannot be nested", argsList[i]) result.add(newStrLitNode(" />")) else: result.add(newStrLitNode(">")) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index a1f80ad7675b5..bb68085aad6bf 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1295,7 +1295,7 @@ macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped = foundFields.add fStr else: error("Ambiguous enums cannot be parsed, field " & $fStr & - " appears multiple times!") + " appears multiple times!", f) inc fNum # finally add else branch to raise or use default if default == nil: diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 062341ccb6627..dbc60b8a9cdda 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -11,16 +11,25 @@ ## macro system. import std/private/since -import macros -import typetraits +import macros, 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.} = - #echo treeRepr(p) - #echo treeRepr(b) 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 of nnkPar, nnkTupleConstr: @@ -44,9 +53,7 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} = formalParams.add identDefs result.add formalParams - result.add newEmptyNode() - #echo(treeRepr(result)) - #echo(result.toStrLit()) + result.add prag macro `=>`*(p, b: untyped): untyped = ## Syntax sugar for anonymous procedures. @@ -58,56 +65,75 @@ macro `=>`*(p, b: untyped): untyped = ## ## passTwoAndTwo((x, y) => x + y) # 4 - #echo treeRepr(p) - #echo(treeRepr(b)) - var params: seq[NimNode] = @[newIdentNode("auto")] + var + params = @[ident"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..": + if c[0].kind == nnkIdent and c[0].eqIdent"->": var procTy = createProcType(c[1], c[2]) params[0] = procTy[0][0] for i in 1 ..< procTy[0].len: params.add(procTy[0][i]) else: - error("Expected proc type (->) got (" & $c[0].ident & ").") + error("Expected proc type (->) got (" & c[0].strVal & ").", c) break else: - echo treeRepr c - error("Incorrect procedure parameter list.") + error("Incorrect procedure parameter list.", c) params.add(identDefs) of nnkIdent: var identDefs = newNimNode(nnkIdentDefs) identDefs.add(p) - identDefs.add(newIdentNode("auto")) + identDefs.add(ident"auto") identDefs.add(newEmptyNode()) 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: - error("Incorrect procedure parameter list.") - result = newProc(params = params, body = b, procType = nnkLambda) - #echo(result.treeRepr) - #echo(result.toStrLit()) - #return result # TODO: Bug? + error("Incorrect procedure parameter list.", p) + result = newProc(body = b, params = params, + pragmas = pragma, name = name, + procType = kind) macro `->`*(p, b: untyped): untyped = ## 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)) for arg in locals: result.add(arg) -when (NimMajor, NimMinor) >= (1, 1): +since (1, 1): import std / private / underscored_calls macro dup*[T](arg: T, calls: varargs[untyped]): T = diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 06bd1487ffcfc..0ac6d0d1ca3e2 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -124,7 +124,7 @@ macro genericParamsImpl(T: typedesc): untyped = result.add ret break else: - error "wrong kind: " & $impl.kind + error "wrong kind: " & $impl.kind, impl since (1, 1): template genericParams*(T: typedesc): untyped = diff --git a/tests/macros/tclosuremacro.nim b/tests/macros/tclosuremacro.nim index 9f2137dec09dd..5c41c317a9619 100644 --- a/tests/macros/tclosuremacro.nim +++ b/tests/macros/tclosuremacro.nim @@ -1,19 +1,16 @@ discard """ - output: '''10 -10 -10 -3 -3 + output: ''' noReturn -6 calling mystuff yes calling mystuff yes +calling sugarWithPragma +sugarWithPragma called ''' """ -import future, macros +import sugar, macros proc twoParams(x: (int, int) -> int): int = result = x(5, 5) @@ -30,23 +27,23 @@ proc noReturn(x: () -> void) = proc doWithOneAndTwo(f: (int, int) -> int): int = f(1,2) -echo twoParams(proc (a, b: auto): auto = a + b) -echo twoParams((x, y) => x + y) - -echo oneParam(x => x+5) - -echo noParams(() => 3) - -echo doWithOneAndTwo((x, y) => x + y) +doAssert twoParams(proc (a, b: auto): auto = a + b) == 10 +doAssert twoParams((x, y) => x + y) == 10 +doAssert oneParam(x => x+5) == 10 +doAssert noParams(() => 3) == 3 +doAssert doWithOneAndTwo((x, y) => x + y) == 3 noReturn((() -> void) => echo("noReturn")) proc pass2(f: (int, int) -> int): (int) -> int = ((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()) = echo "calling ", name @@ -72,3 +69,5 @@ macro m(x: untyped): untyped = m: proc mystuff() = echo "yes" + +sugarWithPragma() {.m.} => echo "sugarWithPragma called"