discard """
  nimout: '''
Infix
  Ident "=>"
  Call
    Ident "name"
    Ident "a"
    ExprColonExpr
      Ident "b"
      Ident "cint"
  NilLit
macrocache ok
'''

  output: '''
x = 10
x + y = 30
proc foo[T, N: static[int]]()
proc foo[T; N: static[int]]()
a[0]: 42
a[1]: 45
x: some string
([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")])
([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")])
0
0
0
'''
"""


import macros, sugar, macrocache


block tdump:
  let
    x = 10
    y = 20
  dump x
  dump(x + y)


block texprcolonexpr:
  macro def(x): untyped =
    echo treeRepr(x)

  def name(a, b:cint) => nil



block tgenericparams:
  macro test():string =
    let expr0 = "proc foo[T, N: static[int]]()"
    let expr1 = "proc foo[T; N: static[int]]()"

    newLit($toStrLit(parseExpr(expr0)) & "\n" & $toStrLit(parseExpr(expr1)))

  echo test()



block tidgen:
  # Test compile-time state in same module
  var gid {.compileTime.} = 3

  macro genId(): int =
    result = newIntLitNode(gid)
    inc gid

  proc Id1(): int {.compileTime.} = return genId()
  proc Id2(): int {.compileTime.} = return genId()

  doAssert Id1() == 3
  doAssert Id2() == 4



block tlexerex:
  macro match(s: cstring|string; pos: int; sections: varargs[untyped]): untyped =
    for sec in sections:
      expectKind sec, nnkOfBranch
      expectLen sec, 2
    result = newStmtList()

  var input = "the input"
  var pos = 0
  match input, pos:
  of r"[a-zA-Z_]\w+": echo "an identifier"
  of r"\d+": echo "an integer"
  of r".": echo "something else"



block tcopylineinfo:
  # issue #5617, feature request
  type Test = object

  macro mixer(n: typed): untyped =
    let x = newIdentNode("echo")
    x.copyLineInfo(n)
    result = newLit(x.lineInfo == n.lineInfo)

  var z = mixer(Test)
  doAssert z

block tsetgetlineinfo:
  # issue #21098, feature request
  type Test = object

  macro mixer1(n: typed): untyped =
    let x = newIdentNode("echo")
    var lineInfo = n.lineInfoObj
    x.setLineInfo lineInfo
    result = newLit(x.lineInfo == n.lineInfo)

  macro mixer2(n: typed): untyped =
    let x = newIdentNode("echo")
    var lineInfo = n.lineInfoObj
    lineInfo.line += 1
    x.setLineInfo lineInfo
    result = newLit(x.lineInfo != n.lineInfo)

  doAssert mixer1(Test)

  doAssert mixer2(Test)



block tdebugstmt:
  macro debug(n: varargs[untyped]): untyped =
    result = newNimNode(nnkStmtList, n)
    for i in 0..n.len-1:
      add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
      add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
      add(result, newCall("writeLine", newIdentNode("stdout"), n[i]))

  var
    a: array[0..10, int]
    x = "some string"
  a[0] = 42
  a[1] = 45

  debug(a[0], a[1], x)

const
  pairs = {"key": "val", "keyB": "2"}

macro bilookups(arg: static[openArray[(string, string)]]): untyped =
  var a = newTree(nnkBracket)
  var b = newTree(nnkBracket)
  for (k, v) in items(arg):
    a.add(newTree(nnkTupleConstr, newLit k, newLit v))
    b.add(newTree(nnkTupleConstr, newLit v, newLit k))
  result = newTree(nnkTupleConstr, a, b)

macro bilookups2(arg: untyped): untyped =
  var a = newTree(nnkBracket)
  var b = newTree(nnkBracket)
  arg.expectKind(nnkTableConstr)
  for x in items(arg):
    x.expectKind(nnkExprColonExpr)
    a.add(newTree(nnkTupleConstr, x[0], x[1]))
    b.add(newTree(nnkTupleConstr, x[1], x[0]))
  result = newTree(nnkTupleConstr, a, b)

const cnst1 = bilookups(pairs)
echo cnst1
const cnst2 = bilookups2({"key": "val", "keyB": "2"})
echo cnst2



# macrocache #11404
const
  mcTable = CacheTable"nimTest"
  mcSeq = CacheSeq"nimTest"
  mcCounter = CacheCounter"nimTest"

static:
  doAssert(mcCounter.value == 0) # CacheCounter.value
  mcCounter.inc                  # CacheCounter.inc
  doAssert(mcCounter.value == 1) # CacheCounter.value

  let a = newLit(1)
  let b = newLit(2)
  let c = newLit(3)
  let d = newLit(4)

  mcSeq.add a # CacheSeq.add
  mcSeq.add b # CacheSeq.add
  mcSeq.add c # CacheSeq.add

  doAssert(mcSeq.len == 3)  # CacheSeq.len
  #doAssert(c in mcSeq)      # CacheSeq.contains
  #doAssert(d notin mcSeq)   # CacheSeq.contains

  mcSeq.incl d              # CacheSeq.incl
  doAssert(mcSeq.len == 4)  # CacheSeq.len

  mcSeq.incl c              # CacheSeq.incl
  doAssert(mcSeq.len == 4)  # CacheSeq.len

  doAssert(mcSeq[3] == d)   # CacheSeq.[]

  #doAssert(mcSeq.pop() == d)# CacheSeq.pop
  #doAssert(mcSeq.len == 3)  # CacheSeq.len

  doAssert(mcTable.len == 0)  # CacheTable.len
  mcTable["a"] = a            # CacheTable.[]=
  doAssert(mcTable.len == 1)  # CacheTable.len

  doAssert(mcTable["a"] == a) # CacheTable.[]
  #doAssert("a" in mcTable)    # CacheTable.contains
  #doAssert(mcTable.hasKey("a"))# CacheTable.hasKey

  for k, v in mcTable:  # CacheTable.items
    doAssert(k == "a")
    doAssert(v == a)

  echo "macrocache ok"

block tupleNewLitTests:
  macro t(): untyped =
    result = newLit (1, "foo", (), (1,), (a1: 'x', a2: @["ba"]))
  doAssert $t() == """(1, "foo", (), (1,), (a1: 'x', a2: @["ba"]))"""
    # this `$` test is needed because tuple equality doesn't distinguish
    # between named vs unnamed tuples
  doAssert t() == (1, "foo", (), (1, ), (a1: 'x', a2: @["ba"]))

from strutils import contains
block getImplTransformed:
  macro bar(a: typed): string =
    # newLit a.getImpl.repr # this would be before code transformation
    let b = a.getImplTransformed
    newLit b.repr
  template toExpand() =
    for ai in 0..2: echo ai
  proc baz(a=1): int =
    defer: discard
    toExpand()
    12
  const code = bar(baz)
  # sanity check:
  doAssert "finally" in code # `defer` is lowered to try/finally
  doAssert "while" in code # `for` is lowered to `while`
  doAssert "toExpand" notin code
    # template is expanded (but that would already be the case with
    # `a.getImpl.repr`, unlike the other transformations mentioned above


# test macro resemming
macro makeVar(): untyped =
  quote:
    var tensorY {.inject.}: int

macro noop(a: typed): untyped =
  a

noop:
  makeVar
echo tensorY

macro xbenchmark(body: typed): untyped =
  result = body

xbenchmark:
  proc fastSHA(inputtest: string) =
    discard inputtest
  fastSHA("hey")

block: # issue #4547
  macro lazy(stmtList : typed) : untyped =
    let decl = stmtList[0]
    decl.expectKind nnkLetSection
    let name = decl[0][0].strVal
    let call = decl[0][2].copy
    call.expectKind nnkCall
    let ident = newIdentNode("get" & name)
    result = quote do:
      var value : type(`call`)
      proc `ident`() : type(`call`) =
        if value.isNil:
          value = `call`
        value
  type MyObject = object
    a,b: int    
  # this part, the macro call and it's result (written in the comment below) is important
  lazy:
    let y = new(MyObject)
  #[
    var value: type(new(MyObject))
    proc gety(): type(new(MyObject)) =
      if value.isNil:
        value = new(MyObject)
      value    
  ]#
  doAssert gety().a == 0  # works and should work
  doAssert gety().b == 0  # works and should work
  doAssert not declared(y)
  doAssert not compiles(y.a)       # identifier y should not exist anymore
  doAssert not compiles(y.b)       # identifier y should not exist anymore

block: # bug #13511
  type
    Builder = ref object
      components: seq[Component]
    Component = object

  proc add(builder: var Builder, component: Component) {.compileTime.} =
    builder.components.add(component)

  macro debugAst(arg: typed): untyped =
    ## just for debugging purpose.
    discard arg.treeRepr
    return arg

  static:
    var component = Component()
    var builder = Builder()

    template foo(): untyped =
      ## WAS: this doc comment causes compilation failure.
      builder

    debugAst:
      add(foo(), component)

block: # bug #15118
  macro flop(name: static string) =
    let id = genSym(nskType, "env")
    let r =
      nnkStmtList.newTree(
        nnkTypeSection.newTree(
          nnkTypeDef.newTree(
            id,
            newEmptyNode(),
            nnkRefTy.newTree(
              nnkObjectTy.newTree(
                newEmptyNode(),
                newEmptyNode(),
                nnkRecList.newTree(
                  nnkIdentDefs.newTree(
                    newIdentNode(name),
                    newIdentNode("int"),
                    newEmptyNode()
                  )
                )
              ) 
            )
          )
        ),

        # var f: T
  
        nnkVarSection.newTree(
          nnkIdentDefs.newTree(
            newIdentNode("f"),
            id,
            newEmptyNode()
          )
        ),

        # echo f.a
        nnkCommand.newTree(
          newIdentNode("new"),
          newIdentNode("f")
        ),

        nnkCommand.newTree(
          newIdentNode("echo"),
          nnkDotExpr.newTree(
            newIdentNode("f"),
            newIdentNode(name)
          )
        )
      )
    r


  block:
    flop("a")

  block:
    flop("b")

static:
  block:
    const containsTable = CacheTable"containsTable"
    doAssert "foo" notin containsTable
    containsTable["foo"] = newLit 42
    doAssert "foo" in containsTable
