using statement (ala C#) implemented as macro (added as test).
simplified the usage of the interpolatedFragments iterator.
This commit is contained in:
parent
dbcca9b3b9
commit
9acfc43119
5 changed files with 152 additions and 49 deletions
|
|
@ -1,6 +1,6 @@
|
|||
discard """
|
||||
file: "tstringinterp.nim"
|
||||
output: "Hello Alice \$ 64 | Hello Bob, 10"
|
||||
output: "Hello Alice, 64 | Hello Bob, 10"
|
||||
"""
|
||||
|
||||
import macros, parseutils, strutils
|
||||
|
|
@ -13,23 +13,12 @@ proc concat(strings: openarray[string]) : string =
|
|||
template ProcessInterpolations(e: expr) =
|
||||
var
|
||||
s = e[1].strVal
|
||||
stringStart = 0
|
||||
|
||||
for i in interpolatedFragments(s):
|
||||
var leadingString = s[stringStart..i.interpStart-1]
|
||||
var interpolatedExpr = s[i.exprStart..i.exprEnd]
|
||||
|
||||
addString(leadingString)
|
||||
|
||||
var interpTargetAst = parseExpr("$(x)")
|
||||
interpTargetAst[1][0] = parseExpr(interpolatedExpr)
|
||||
addExpr(interpTargetAst)
|
||||
|
||||
stringStart = i.interpEnd + 1
|
||||
|
||||
if stringStart != s.len:
|
||||
var endingString = s[stringStart..s.len]
|
||||
addString(endingString)
|
||||
for f in interpolatedFragments(s):
|
||||
if f.kind == ikString:
|
||||
addString(f.value)
|
||||
else:
|
||||
addExpr(f.value)
|
||||
|
||||
macro formatStyleInterpolation(e: expr): expr =
|
||||
var
|
||||
|
|
@ -75,7 +64,7 @@ var
|
|||
c = 34
|
||||
|
||||
var
|
||||
s1 = concatStyleInterpolation"Hello ${alice} \$ ${sum (a, b, c)}"
|
||||
s1 = concatStyleInterpolation"Hello ${alice}, ${sum (a, b, c)}}"
|
||||
s2 = formatStyleInterpolation"Hello ${bob}, ${sum (alice.len, bob.len, 2)}"
|
||||
|
||||
write(stdout, s1 & " | " & s2)
|
||||
|
|
|
|||
102
tests/accept/run/tusingstatement.nim
Normal file
102
tests/accept/run/tusingstatement.nim
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
discard """
|
||||
file: "tusingstatement.nim"
|
||||
output: "Using test.Closing test."
|
||||
"""
|
||||
|
||||
import
|
||||
macros
|
||||
|
||||
# This macro mimics the using statement from C#
|
||||
#
|
||||
# XXX:
|
||||
# It doen't match the C# version exactly yet.
|
||||
# In particular, it's not recursive, which prevents it from dealing
|
||||
# with exceptions thrown from the variable initializers when multiple.
|
||||
# variables are used.
|
||||
#
|
||||
# Also, since nimrod relies less on exceptions in general, a more
|
||||
# idiomatic definition could be:
|
||||
# var x = init()
|
||||
# if opened(x):
|
||||
# try:
|
||||
# body
|
||||
# finally:
|
||||
# close(x)
|
||||
#
|
||||
# `opened` here could be an overloaded proc which any type can define.
|
||||
# A common practice can be returing an Optional[Resource] obj for which
|
||||
# `opened` is defined to `optional.hasValue`
|
||||
macro using(e: expr) : stmt =
|
||||
if e.len != 2:
|
||||
error "Using statement: unexpected number of arguments. Got " &
|
||||
$e.len & ", expected: 1 or more variable assignments and a block"
|
||||
|
||||
var args = e[0]
|
||||
var body = e[1]
|
||||
|
||||
var
|
||||
variables : seq[PNimrodNode]
|
||||
closingCalls : seq[PNimrodNode]
|
||||
|
||||
newSeq(variables, 0)
|
||||
newSeq(closingCalls, 0)
|
||||
|
||||
for i in countup(1, args.len-1):
|
||||
if args[i].kind == nnkExprEqExpr:
|
||||
var varName = args[i][0]
|
||||
var varValue = args[i][1]
|
||||
|
||||
var varAssignment = newNimNode(nnkIdentDefs)
|
||||
varAssignment.add(varName)
|
||||
varAssignment.add(newNimNode(nnkEmpty)) # empty means no type
|
||||
varAssignment.add(varValue)
|
||||
variables.add(varAssignment)
|
||||
|
||||
closingCalls.add(newCall(!"close", varName))
|
||||
else:
|
||||
error "Using statement: Unexpected expression. Got " &
|
||||
$args[i].kind & " instead of assignment."
|
||||
|
||||
var varSection = newNimNode(nnkVarSection)
|
||||
varSection.add(variables)
|
||||
|
||||
var finallyBlock = newNimNode(nnkStmtList)
|
||||
finallyBlock.add(closingCalls)
|
||||
|
||||
# XXX: Use a template here once getAst is working properly
|
||||
var targetAst = parseStmt"""block:
|
||||
var
|
||||
x = foo()
|
||||
y = bar()
|
||||
|
||||
try:
|
||||
body()
|
||||
|
||||
finally:
|
||||
close x
|
||||
close y
|
||||
"""
|
||||
|
||||
targetAst[0][1][0] = varSection
|
||||
targetAst[0][1][1][0] = body
|
||||
targetAst[0][1][1][1][0] = finallyBlock
|
||||
|
||||
return targetAst
|
||||
|
||||
type
|
||||
TResource* = object
|
||||
field*: string
|
||||
|
||||
proc openResource(param: string): TResource =
|
||||
result.field = param
|
||||
|
||||
proc close(r: var TResource) =
|
||||
write(stdout, "Closing " & r.field & ".")
|
||||
|
||||
proc use(r: var TResource) =
|
||||
write(stdout, "Using " & r.field & ".")
|
||||
|
||||
using(r = openResource("test")):
|
||||
use r
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue