#
#
#            Nimrod's Runtime Library
#        (c) Copyright 2008 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#


## This module contains the interface to the compiler's abstract syntax tree.
## Abstract syntax trees should be modified in macros.

#[[[cog
#def toEnum(name, elems):
#  body = ""
#  counter = 0
#  for e in elems:
#    if counter % 4 == 0: p = "\n    "
#    else: p = ""
#    body = body + p + 'n' + e + ', '
#    counter = counter + 1
#
#  return ("  TNimrod%s* = enum%s\n  TNim%ss* = set[TNimrod%s]\n" %
#            (name, body[:-2], name, name))
#
#enums = eval(open("data/ast.yml").read())
#cog.out("type\n")
#for key, val in enums.items():
#  if key[-4:] == "Flag": continue
#  cog.out(toEnum(key, val))
#]]]
type
  TNimrodNodeKind* = enum
    nnkNone, nnkEmpty, nnkIdent, nnkSym, 
    nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit, 
    nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit, 
    nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit, 
    nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall, 
    nnkCommand, nnkCall, nnkGenericCall, nnkExplicitTypeListCall, 
    nnkExprEqExpr, nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, 
    nnkInfix, nnkPrefix, nnkPostfix, nnkPar, 
    nnkCurly, nnkBracket, nnkBracketExpr, nnkPragmaExpr, 
    nnkRange, nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, 
    nnkIfExpr, nnkElifExpr, nnkElseExpr, nnkLambda, 
    nnkAccQuoted, nnkHeaderQuoted, nnkTableConstr, nnkQualified, 
    nnkHiddenStdConv, nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, 
    nnkCast, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, 
    nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, 
    nnkChckRange, nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, 
    nnkAsgn, nnkFastAsgn, nnkDefaultTypeParam, nnkGenericParams, 
    nnkFormalParams, nnkOfInherit, nnkModule, nnkProcDef, 
    nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, 
    nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse, 
    nnkMacroStmt, nnkAsmStmt, nnkPragma, nnkIfStmt, 
    nnkWhenStmt, nnkForStmt, nnkWhileStmt, nnkCaseStmt, 
    nnkVarSection, nnkConstSection, nnkConstDef, nnkTypeSection, 
    nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally, 
    nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, 
    nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, 
    nnkFromStmt, nnkImportAs, nnkIncludeStmt, nnkAccessStmt, 
    nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType, 
    nnkBlockType, nnkVm, nnkTypeOfExpr, nnkObjectTy, 
    nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, 
    nnkRefTy, nnkPtrTy, nnkVarTy, nnkProcTy, 
    nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
  TNimNodeKinds* = set[TNimrodNodeKind]
  TNimrodTypeKind* = enum
    ntyNone, ntyBool, ntyChar, ntyEmpty, 
    ntyArrayConstr, ntyNil, ntyGeneric, ntyGenericInst, 
    ntyGenericParam, ntyEnum, ntyAnyEnum, ntyArray, 
    ntyObject, ntyTuple, ntySet, ntyRange, 
    ntyPtr, ntyRef, ntyVar, ntySequence, 
    ntyProc, ntyPointer, ntyOpenArray, ntyString, 
    ntyCString, ntyForward, ntyInt, ntyInt8, 
    ntyInt16, ntyInt32, ntyInt64, ntyFloat, 
    ntyFloat32, ntyFloat64, ntyFloat128
  TNimTypeKinds* = set[TNimrodTypeKind]
  TNimrodSymKind* = enum
    nskUnknownSym, nskConditional, nskDynLib, nskParam, 
    nskTypeParam, nskTemp, nskType, nskConst, 
    nskVar, nskProc, nskIterator, nskConverter, 
    nskMacro, nskTemplate, nskField, nskEnumField, 
    nskForVar, nskModule, nskLabel, nskStub
  TNimSymKinds* = set[TNimrodSymKind]
#[[[end]]]

type
  TNimrodNode {.final.} = object   # hidden
  TNimrodSymbol {.final.} = object # hidden
  TNimrodType {.final.} = object   # hidden
  PNimrodType* {.compilerproc.} = ref TNimrodType
  PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
  PNimrodNode* {.compilerproc.} = ref TNimrodNode
  expr* = PNimrodNode
  stmt* = PNimrodNode

# Nodes should be reference counted to make the `copy` operation very fast!
# However, this is difficult to achieve: modify(n[0][1]) should propagate to
# its father. How to do this without back references?

proc `[]`* (n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
proc `[]=`* (n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
  ## provide access to `n`'s children

type
  TNimrodIdent = object of TObject

converter StrToIdent*(s: string): TNimrodIdent {.magic: "StrToIdent".}
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
proc `==`* (a, b: TNimrodIdent): bool {.magic: "EqIdent".}

proc len*(n: PNimrodNode): int {.magic: "NLen".}

## returns the number of children that a node has
proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
proc add*(father: PNimrodNode, child: openArray[PNimrodNode]) {.magic: "NAddMultiple".}
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}

proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}
proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".}
proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}
proc typ*(n: PNimrodNode): PNimrodType {.magic: "NGetType".}
proc strVal*(n: PNimrodNode): string  {.magic: "NStrVal".}

proc `intVal=`*(n: PNimrodNode, val: biggestInt) {.magic: "NSetIntVal".}
proc `floatVal=`*(n: PNimrodNode, val: biggestFloat) {.magic: "NSetFloatVal".}
proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}
proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
proc `typ=`*(n: PNimrodNode, typ: PNimrodType) {.magic: "NSetType".}
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}

proc newNimNode*(kind: TNimrodNodeKind,
                 n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}

proc error*(msg: string) {.magic: "NError".}
proc warning*(msg: string) {.magic: "NWarning".}
proc hint*(msg: string) {.magic: "NHint".}

proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
  result = newNimNode(nnkStrLit)
  result.strVal = s

proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} =
  result = newNimNode(nnkIntLit)
  result.intVal = i

proc newIntLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =
  result = newNimNode(nnkFloatLit)
  result.floatVal = f

proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} =
  result = newNimNode(nnkIdent)
  result.ident = i

proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
  return newStrLitNode(repr(n))

proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
  if n.kind != k: error("macro expects a node of kind: " & repr(k))

proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
  if n.len < min: error("macro expects a node with " & $min & " children")

proc newCall*(theProc: TNimrodIdent,
              args: openArray[PNimrodNode]): PNimrodNode {.compileTime.} =
  ## produces a new call node. `theProc` is the proc that is called with
  ## the arguments ``args[0..]``.
  result = newNimNode(nnkCall)
  result.add(newIdentNode(theProc))
  result.add(args)