├── .gitignore ├── .travis.yml ├── LICENSE ├── metatools.nim ├── metatools.nimble └── test ├── config.nims ├── test ├── tinline000.nim ├── tinline001.nim ├── tinline002.nim ├── tinline003.nim ├── tinline004.nim ├── tinline005.nim ├── tinline006.nim ├── tinline007.nim ├── tinline008.nim ├── tinline009.nim ├── tinline010.nim ├── tinline011.nim ├── tinline012.nim ├── tinline013.nim └── tinline014.nim /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | /metatools 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | before_install: 3 | - git clone -b devel git://github.com/nim-lang/Nim.git --depth 1 4 | - cd Nim 5 | - git clone --depth 1 git://github.com/nim-lang/csources 6 | - cd csources 7 | - sh build.sh 8 | - cd .. 9 | - bin/nim c koch 10 | - ./koch boot -d:release 11 | - export PATH=$PWD/bin:$PATH 12 | - ./koch nimble -d:release 13 | - cd .. 14 | script: 15 | - nimble install -y 16 | - cd test && ./test 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Xiao-Yong Jin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /metatools.nim: -------------------------------------------------------------------------------- 1 | import macros 2 | 3 | proc isMagic(x: NimNode): bool = 4 | #echo x.treerepr 5 | let pragmas = x[4] 6 | if pragmas.kind==nnkPragma and pragmas[0].kind==nnkExprColonExpr and 7 | $pragmas[0][0]=="magic": result = true 8 | 9 | proc getParam(fp: NimNode, n: int): auto = 10 | # n counts from 1 11 | var n = n-1 12 | for i in 1..= c.len-2: n -= c.len-2 15 | else: return (c[n].copyNimTree, c[^2].copyNimTree) 16 | 17 | proc has(n:NimNode, k:NimNodeKind):bool = 18 | for c in n: 19 | if c.kind == k: return true 20 | return false 21 | 22 | #[ 23 | const exprNodes = { 24 | nnkExprEqExpr, 25 | nnkExprColonExpr, 26 | nnkCurlyExpr, 27 | nnkBracketExpr, 28 | nnkPragmaExpr, 29 | nnkDotExpr, 30 | nnkCheckedFieldExpr, 31 | nnkDerefExpr, 32 | nnkIfExpr, 33 | nnkElifExpr, 34 | nnkElseExpr, 35 | nnkStaticExpr, 36 | nnkStmtListExpr, 37 | nnkBlockExpr, 38 | nnkTypeOfExpr } 39 | ]# 40 | 41 | proc rebuild*(n:NimNode):NimNode = 42 | # Typed AST has extra information in its nodes. 43 | # Replacing nodes in a typed AST can break its consistencies, 44 | # for which the compiler is not well prepared. 45 | # Here we simply rebuild some offensive nodes from scratch, 46 | # and force the compiler to rebuild its type information. 47 | 48 | # Note that the compiler currently (v0.17) only retype the AST 49 | # after a macro returns, so to preserve type information while 50 | # traversing the AST, call this proc on the result 51 | # result = rebuild result 52 | # just before macro returns. 53 | 54 | # Special node kinds have to be taken care of. 55 | if n.kind == nnkAddr: 56 | # We process typed NimNode, so addr is already checked. 57 | result = newCall(bindsym"unsafeAddr", rebuild n[0]) 58 | elif n.kind == nnkConv: 59 | result = newNimNode(nnkCall, n).add(rebuild n[0], rebuild n[1]) 60 | elif n.kind in nnkCallKinds and n[^1].kind == nnkBracket and 61 | n[^1].len>0 and n[^1].has(nnkHiddenCallConv): 62 | # special case of varargs 63 | result = newCall(rebuild n[0]) 64 | for i in 1..0 and n[1].kind == nnkBracket: 69 | # # One dirty hack for the builtin echo, with no nnkHiddenCallConv (this is been caught above) 70 | # result = newCall(rebuild n[0]) 71 | # for c in n[1]: result.add rebuild c 72 | # echo "In rebuild: echo: ",result.treerepr 73 | # elif n.kind in nnkCallKinds and n[^1].kind == nnkHiddenStdConv and n[^1][1].kind == nnkBracket and 74 | # n[^1][1].len>0 and n[^1][1].has(nnkHiddenCallConv): 75 | elif n.kind in nnkCallKinds and n[^1].kind == nnkHiddenStdConv and n[^1][1].kind == nnkBracket and 76 | n[^1][1].len>0: 77 | # Deals with varargs 78 | result = newCall(rebuild n[0]) 79 | for i in 1.. 0: 253 | result.add tg[i-1] 254 | return 255 | if ty.isG: return typeof n 256 | elif ty.kind == nnkBracketExpr: 257 | let ts = matchT(n.gettypeinst,ty,g) 258 | result = n 259 | if ts.len > 0: 260 | for i in countdown(ts.len-1,0): result = result.newDotExpr ts[i] 261 | return 262 | echo "Internal WARNING: matchGeneric: Unsupported" 263 | echo "MG:I: ",n.lisprepr 264 | echo "MG:T: ",ty.lisprepr 265 | echo "MG:G: ",g.lisprepr 266 | 267 | proc cleanIterator(n:NimNode):NimNode = 268 | var fa = newPar() 269 | proc replaceFastAsgn(n:NimNode):NimNode = 270 | if n.kind == nnkFastAsgn: 271 | let n0 = genSym(nskLet, n[0].strVal) 272 | fa.add newPar(n[0],n[1],n0) 273 | template asgn(x,y:untyped):untyped = 274 | let x = y 275 | let n1 = replaceFastAsgn n[1] 276 | result = getAst(asgn(n0,n1)) 277 | else: 278 | result = n.copyNimNode 279 | for c in n: result.add replaceFastAsgn c 280 | proc removeDeclare(n:NimNode):NimNode = 281 | if n.kind == nnkVarSection: 282 | var keep = newseq[int](0) 283 | for c in 0.. 3: 292 | echo "Internal ERROR: cleanIterator: removeDeclare: unhandled situation" 293 | echo n.treerepr 294 | quit 1 295 | break 296 | inc i 297 | if i < fa.len and (n[c][^2].kind != nnkEmpty or n[c][^1].kind != nnkEmpty): 298 | echo "Internal ERROR: cleanIterator: removeDeclare: unhandled situation" 299 | echo n.treerepr 300 | quit 1 301 | elif i >= fa.len: keep.add c 302 | # echo keep," ",n.repr 303 | if keep.len == 0: 304 | # echo "Removing declaration: ",n.lisprepr 305 | result = newNimNode(nnkDiscardStmt,n).add newStrLitNode(n.lisprepr) 306 | else: 307 | result = n.copyNimNode 308 | for i in keep: result.add removeDeclare n[i] 309 | else: 310 | result = n.copyNimNode 311 | for c in n: result.add removeDeclare c 312 | result = replaceFastAsgn n 313 | if fa.len > 0: 314 | result = result.removeDeclare 315 | for x in fa: 316 | result = result.replace(x[0],x[2]) 317 | # echo x[0].lisprepr,"\n :: ",x[0].gettypeinst.lisprepr 318 | # echo x[1].lisprepr,"\n :: ",x[1].gettypeinst.lisprepr 319 | proc fixDeclare(n:NimNode):NimNode = 320 | # Inlined iterators have var sections that are not clearly typed. 321 | # We try to find inconsistencies from the type of the actual symbol being declared. 322 | result = n.copyNimNode 323 | if n.kind == nnkVarSection: 324 | for i in 0.. 0: 377 | dd.add(d[^2].copy, d[^1].rep(x,y)) 378 | ll.add dd 379 | if ll.len > 0: result = ll 380 | else: result = newNimNode(nnkDiscardStmt,n).add(newStrLitNode(n.repr)) 381 | else: 382 | result = n.copyNimNode 383 | for c in n: 384 | result.add c.rep(x,y) 385 | result = n.copy 386 | for x in get n: result = result.rep(x[0],x[1]) 387 | 388 | proc regenSym(n:NimNode):NimNode = 389 | # Only regen nskVar and nskLet symbols. 390 | 391 | # We need to regenerate symbols for multiple inlined procs, 392 | # because cpp backend put variables on top level, although 393 | # the c backend works without this. 394 | proc get(n:NimNode,k:NimNodeKind):NimNode = 395 | result = newPar() 396 | if n.kind == k: 397 | for d in n: 398 | if d.kind != nnkIdentDefs or d.len<3: 399 | echo "Internal ERROR: regenSym: get: can't handle:" 400 | echo n.treerepr 401 | quit 1 402 | for i in 0..>>>>> inlineProcsY" 422 | # echo "call:\n", call.lisprepr 423 | # echo "procImpl:\n", procImpl.treerepr 424 | let fp = procImpl[3] # formal params 425 | proc removeRoutines(n:NimNode):NimNode = 426 | # We are inlining, so we don't need RoutineNodes anymore. 427 | if n.kind in RoutineNodes: 428 | result = newNimNode(nnkDiscardStmt,n).add(newStrLitNode(n.repr)) 429 | else: 430 | result = n.copyNimNode 431 | for c in n: result.add removeRoutines c 432 | proc removeTypeSections(n:NimNode):NimNode = 433 | # Type section is special. Once the type is instantiated, it exists, and we don't want duplicates. 434 | if n.kind == nnkTypeSection: 435 | result = newNimNode(nnkDiscardStmt,n).add(newStrLitNode(n.repr)) 436 | else: 437 | result = n.copyNimNode 438 | for c in n: result.add removeTypeSections c 439 | var 440 | pre = newStmtList() 441 | body = procImpl.body.copyNimTree.removeRoutines.removeTypeSections 442 | # echo "### body w/o routines:" 443 | # echo body.repr 444 | body = cleanIterator body 445 | # echo "### body after clean up iterator:" 446 | # echo body.repr 447 | for i in 1..=2 and procImpl[5][1].kind == nnkGenericParams: 500 | let gp = procImpl[5][1] 501 | for c in gp: 502 | c.expectKind nnkIdentDefs 503 | for i in 0..>>>>> inlineProcsX" 602 | # echo body.repr 603 | proc recurse(it: NimNode): NimNode = 604 | if it.kind == nnkTypeOfExpr: return it.copyNimTree 605 | if it.kind in CallNodes and it.callName.kind==nnkSym: 606 | let procImpl = it.callName.getImpl 607 | # echo "inspecting call" 608 | # echo it.lisprepr 609 | # echo procImpl.repr 610 | if procImpl.body.kind!=nnkEmpty and 611 | not isMagic(procImpl) and 612 | procImpl.kind != nnkIteratorDef: 613 | return recurse inlineProcsY(it, procImpl) 614 | result = copyNimNode(it) 615 | for c in it: result.add recurse c 616 | result = recurse(body) 617 | # echo "<<<<<< inlineProcsX" 618 | # echo result.repr 619 | 620 | macro inlineProcs*(body: typed): auto = 621 | # echo ">>>>>> inlineProcs:" 622 | # echo body.repr 623 | # echo body.treerepr 624 | #result = body 625 | result = rebuild inlineProcsX body 626 | # echo "<<<<<< inlineProcs:" 627 | # echo result.repr 628 | # echo result.treerepr 629 | 630 | 631 | when isMainModule: 632 | proc f1(r: var any; x: any) = r = 2*x 633 | proc f2(x: any): auto = 2*x 634 | 635 | proc a1(x: float) = 636 | inlineProcs: 637 | var r: float 638 | var s: type(r) 639 | f1(r, x) 640 | proc a2(x: float) = 641 | inlineProcs: 642 | var r = f2(x) 643 | 644 | a1(1.0) 645 | a2(1.0) 646 | -------------------------------------------------------------------------------- /metatools.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.2.0" 4 | author = "Xiao-Yong Jin" 5 | description = "Metaprogramming tools for Nim" 6 | license = "MIT" 7 | skipDirs = @["test"] 8 | 9 | # Dependencies 10 | 11 | requires "nim >= 0.19.9" 12 | 13 | -------------------------------------------------------------------------------- /test/config.nims: -------------------------------------------------------------------------------- 1 | --path:".." 2 | --nimcache:nimcache 3 | -------------------------------------------------------------------------------- /test/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -rf nimcache 4 | 5 | if (($#>0));then 6 | declare -ar Ts=( "$@" ) 7 | else 8 | declare -ar Ts=( $(echo t*.nim) ) 9 | fi 10 | logfile(){ printf "out/%s.log" "${Ts[$1]}"; } 11 | [[ -d out ]] || mkdir out 12 | declare -i j ret 13 | declare -ai F 14 | for ((j=0;j<${#Ts[@]};++j));do 15 | printf "Testing: % 6d / %d" $j "${#Ts[@]}" 16 | nim c -r "${Ts[j]}" > "$(logfile $j)" 2>&1 17 | ret=$? 18 | if ((ret!=0));then 19 | printf '\rFail: %s\n' "${Ts[j]}" 20 | F+=( $j ) 21 | else 22 | rm "$(logfile $j)" 23 | printf '\r%60s\r' ' ' 24 | fi 25 | rm -f "${Ts[j]%.nim}" 26 | done 27 | echo 28 | echo "Total: ${#Ts[@]}" 29 | echo "Passed: $((${#Ts[@]}-${#F[@]}))" 30 | if ((${#F[@]}>0));then 31 | echo "Failed: ${#F[@]}" 32 | echo 33 | echo "Check log file(s):" 34 | for j in ${F[@]};do 35 | echo "$(logfile $j)" 36 | done 37 | else 38 | rm -rf out 39 | fi 40 | 41 | rm -rf nimcache 42 | -------------------------------------------------------------------------------- /test/tinline000.nim: -------------------------------------------------------------------------------- 1 | import metatools 2 | 3 | proc f1(r: var any; x: any) = r = 2*x 4 | proc f2(x: any): auto = 2*x 5 | 6 | proc a1(x: float) = 7 | inlineProcs: 8 | var r: float 9 | var s: type(r) 10 | f1(r, x) 11 | proc a2(x: float) = 12 | inlineProcs: 13 | var r = f2(x) 14 | 15 | echo "* Basics" 16 | a1(1.0) 17 | a2(1.0) 18 | -------------------------------------------------------------------------------- /test/tinline001.nim: -------------------------------------------------------------------------------- 1 | import metatools 2 | 3 | echo "* multiple iterators" 4 | type T = array[3,float] 5 | proc loop(x:var T, y:T) = 6 | echo "loop" 7 | let n = 3.0 8 | for k in 0..0: s &= " , " 9 | s &= $x[i] 10 | echo "x = [ ",s," ] has size ",N*sizeof(T) 11 | block: 12 | inlineProcs: 13 | var v = [0,1,2,3] 14 | g v 15 | -------------------------------------------------------------------------------- /test/tinline006.nim: -------------------------------------------------------------------------------- 1 | import metatools 2 | 3 | echo "* object construction" 4 | proc oc(x:int):auto = 5 | type A = object 6 | x:int 7 | return A(x:x) 8 | block: 9 | inlineProcs: 10 | var x = 3 11 | echo oc(x).x 12 | -------------------------------------------------------------------------------- /test/tinline007.nim: -------------------------------------------------------------------------------- 1 | import metatools 2 | 3 | proc g[T;N:static[int]](x:array[N,T]) = 4 | var s = "" 5 | for i in 0..0: s &= " , " 7 | s &= $x[i] 8 | echo "x = [ ",s," ] has size ",N*sizeof(T) 9 | echo "* Types with generic parameters" 10 | proc gt[T] = 11 | type 12 | M[N:static[int]] = object 13 | d:array[N,T] 14 | var A:M[3] 15 | proc g[N:static[int]](x:M[N]) = x.d.g 16 | proc `[]`[N:static[int]](x:M[N],i:int):T = x.d[i] 17 | proc `[]=`[N:static[int]](x:var M[N],i:int,y:T) = x.d[i] = y 18 | inlineProcs: 19 | for i in 0..