Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[superseded] ref syntax for lvalue expressions: byRef: myref=x[1].foo[2] #11824

Closed
wants to merge 14 commits into from
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@

- Added `os.isRelativeTo` to tell whether a path is relative to another

- Added `sugar.byAddr` allowing a reference syntax for lvalue expressions, analog to C++ `auto& a = expr`.

## Library changes

- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
Expand Down
36 changes: 36 additions & 0 deletions lib/pure/sugar.nim
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,39 @@ when (NimMajor, NimMinor) >= (1, 1):
of "bird": "word"
else: d
assert z == @["word", "word"]

proc splitDefinition*(def: NimNode): tuple[lhs: NimNode, rhs: NimNode, op: string] {.since: (1,1).} =
## allows library constructs such as:
## `myDef: a2=expr`
## `myDef: a2*=expr`
def.expectKind nnkStmtList
def.expectLen 1
let def2 = def[0]
case def2.kind
of nnkInfix:
result.lhs = def2[1]
result.rhs = def2[2]
result.op = def2[0].strVal
of nnkAsgn:
result.lhs = def2[0]
result.rhs = def2[1]
result.op = "="
else: doAssert false, $def2.kind
expectKind(result.lhs, nnkIdent)

macro byAddr*(def: untyped): untyped {.since: (1,1).} =
## Defines a reference syntax for lvalue expressions, analog to C++ `auto& a = expr`.
## The expression is evaluated only once, and any side effects will only be
## evaluated once, at declaration time.
runnableExamples:
var x = @[1,2,3]
byAddr: x1=x[1]
x1+=10
doAssert type(x1) is int and x == @[1,12,3]

let (name, exp, op) = splitDefinition(def)
if op != "=":
error("expected `=`, got `" & op & "`", def)
result = quote do:
let myAddr = addr `exp`
template `name`: untyped = myAddr[]
10 changes: 10 additions & 0 deletions tests/stdlib/msugar.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import std/sugar

type Bar = object
x: int
type Foo = object
bar: Bar

var foo*: Foo
byAddr: barx = foo.bar.x
export barx
17 changes: 0 additions & 17 deletions tests/stdlib/tstring.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
discard """
output: '''OK
@[@[], @[], @[], @[], @[]]
'''
"""
const characters = "abcdefghijklmnopqrstuvwxyz"
const numbers = "1234567890"

Expand Down Expand Up @@ -79,15 +74,3 @@ proc test_string_cmp() =
test_string_slice()
test_string_cmp()


#--------------------------
# bug #7816
import sugar
import sequtils

proc tester[T](x: T) =
let test = toSeq(0..4).map(i => newSeq[int]())
echo test

tester(1)

39 changes: 39 additions & 0 deletions tests/stdlib/tsugar.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import sugar
import macros

block byAddrBlock:
var count = 0
proc identity(a: int): auto =
block: count.inc; a
var x = @[1,2,3]
byAddr: x1=x[identity(1)] # the lvalue expression is evaluated only here
doAssert count == 1
x1 += 10
doAssert type(x1) is int # use x1 just like a normal variable
doAssert x == @[1,12,3]
doAssert count == 1 # count has not changed
doAssert compiles (block: byAddr: x2=x[0])
doAssert not compiles (block: byAddr: x2=y[0])
# correctly does not compile when using invalid lvalue expression

block byPtrfBlock:
type Foo = object
x: string
proc fun(a: Foo): auto =
doAssert not compiles (block: byAddr: x=a.x)
let foo = Foo(x: "asdf")
fun(foo)

import ./msugar
# test byAddr with export
barx += 10
doAssert $foo == "(bar: (x: 10))"

# bug #7816
import sequtils

proc tester[T](x: T) =
let test = toSeq(0..4).map(i => newSeq[int]())
doAssert $test == """@[@[], @[], @[], @[], @[]]"""

tester(1)