diff options
Diffstat (limited to 'tests')
-rwxr-xr-x | tests/accept/compile/tdumpast.nim | 39 | ||||
-rw-r--r-- | tests/accept/run/tstringinterp.nim | 71 | ||||
-rw-r--r-- | tests/accept/run/tusingstatement.nim | 102 |
3 files changed, 191 insertions, 21 deletions
diff --git a/tests/accept/compile/tdumpast.nim b/tests/accept/compile/tdumpast.nim index fb31af0ec..8561c6e42 100755 --- a/tests/accept/compile/tdumpast.nim +++ b/tests/accept/compile/tdumpast.nim @@ -2,28 +2,26 @@ import macros -proc dumpit(n: PNimrodNode): string {.compileTime.} = - if n == nil: return "nil" - result = $n.kind - add(result, "(") - case n.kind - of nnkEmpty: nil # same as nil node in this representation - of nnkNilLit: add(result, "nil") - of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) - of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) - of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal) - of nnkIdent: add(result, $n.ident) - of nnkSym, nnkNone: assert false - else: - add(result, dumpit(n[0])) - for j in 1..n.len-1: - add(result, ", ") - add(result, dumpit(n[j])) - add(result, ")") +template plus(a, b: expr): expr = + a + b + +macro call(e: expr): expr = + return newCall("foo", newStrLitNode("bar")) -macro dumpAST(n: stmt): stmt = +macro dumpAST(n: stmt): stmt = # dump AST as a side-effect and return the inner node - echo dumpit(n) + echo n.prettyPrint + echo n.toYaml + + var plusAst = getAst(plus(1, 2)) + echo plusAst.prettyPrint + + var callAst = getAst(call()) + echo callAst.prettyPrint + + var e = parseExpr("foo(bar + baz)") + echo e.prettyPrint + result = n[1] dumpAST: @@ -32,4 +30,3 @@ dumpAST: proc sub(x, y: int): int = return x - y - diff --git a/tests/accept/run/tstringinterp.nim b/tests/accept/run/tstringinterp.nim new file mode 100644 index 000000000..55baae7ec --- /dev/null +++ b/tests/accept/run/tstringinterp.nim @@ -0,0 +1,71 @@ +discard """ + file: "tstringinterp.nim" + output: "Hello Alice, 64 | Hello Bob, 10" +""" + +import macros, parseutils, strutils + +proc concat(strings: openarray[string]) : string = + result = newString(0) + for s in items(strings): result.add(s) + +# This will run though the intee +template ProcessInterpolations(e: expr) = + var + s = e[1].strVal + + for f in interpolatedFragments(s): + if f.kind == ikString: + addString(f.value) + else: + addExpr(f.value) + +macro formatStyleInterpolation(e: expr): expr = + var + formatString = "" + arrayNode = newNimNode(nnkBracket) + idx = 1 + + proc addString(s: string) = + formatString.add(s) + + proc addExpr(e: expr) = + arrayNode.add(e) + formatString.add("$" & $(idx)) + inc idx + + ProcessInterpolations(e) + + result = parseExpr("\"x\" % [y]") + result[1].strVal = formatString + result[2] = arrayNode + +macro concatStyleInterpolation(e: expr): expr = + var args : seq[PNimrodNode] + newSeq(args, 0) + + proc addString(s: string) = args.add(newStrLitNode(s)) + proc addExpr(e: expr) = args.add(e) + + ProcessInterpolations(e) + + result = newCall("concat", args) + +### + +proc sum(a, b, c: int): int = + return (a + b + c) + +var + alice = "Alice" + bob = "Bob" + a = 10 + b = 20 + c = 34 + +var + s1 = concatStyleInterpolation"Hello ${alice}, ${sum (a, b, c)}}" + s2 = formatStyleInterpolation"Hello ${bob}, ${sum (alice.len, bob.len, 2)}" + +write(stdout, s1 & " | " & s2) + diff --git a/tests/accept/run/tusingstatement.nim b/tests/accept/run/tusingstatement.nim new file mode 100644 index 000000000..0017af556 --- /dev/null +++ b/tests/accept/run/tusingstatement.nim @@ -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 + + |