=> 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:
parent
b56432bd8a
commit
3e060cfb0a
6 changed files with 96 additions and 55 deletions
16
changelog.md
16
changelog.md
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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(">"))
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
else:
|
||||||
error("Expected proc type (->) got (" & $p[0].ident & ").")
|
error("Incorrect procedure parameter list.", p)
|
||||||
else:
|
result = newProc(body = b, params = params,
|
||||||
error("Incorrect procedure parameter list.")
|
pragmas = pragma, name = name,
|
||||||
result = newProc(params = params, body = b, procType = nnkLambda)
|
procType = kind)
|
||||||
#echo(result.treeRepr)
|
|
||||||
#echo(result.toStrLit())
|
|
||||||
#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 =
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue