#
#
#           The Nim Compiler
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# Built-in types and compilerprocs are registered here.

import
  ast, astalgo, msgs, platform, idents,
  modulegraphs, lineinfos

export createMagic

proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt]

proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
  result = newType(kind, g.idgen, g.systemModule)
  result.size = size
  result.align = size.int16

proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
  result = systemModuleSym(g, getIdent(g.cache, name))
  if result == nil:
    localError(g.config, info, "system module needs: " & name)
    result = newSym(skError, getIdent(g.cache, name), g.idgen, g.systemModule, g.systemModule.info, {})
    result.typ = newType(tyError, g.idgen, g.systemModule)

proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
  result = nil
  let id = getIdent(g.cache, name)
  for r in systemModuleSyms(g, id):
    if r.magic == m:
      # prefer the tyInt variant:
      if r.typ.returnType != nil and r.typ.returnType.kind == tyInt: return r
      result = r
  if result != nil: return result
  localError(g.config, info, "system module needs: " & name)
  result = newSym(skError, id, g.idgen, g.systemModule, g.systemModule.info, {})
  result.typ = newType(tyError, g.idgen, g.systemModule)

proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
  result = getSysSym(g, info, name).typ

proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
  template sysTypeFromName(s: string): untyped = sysTypeFromName(g, info, s)
  result = g.sysTypes[kind]
  if result == nil:
    case kind
    of tyVoid: result = sysTypeFromName("void")
    of tyInt: result = sysTypeFromName("int")
    of tyInt8: result = sysTypeFromName("int8")
    of tyInt16: result = sysTypeFromName("int16")
    of tyInt32: result = sysTypeFromName("int32")
    of tyInt64: result = sysTypeFromName("int64")
    of tyUInt: result = sysTypeFromName("uint")
    of tyUInt8: result = sysTypeFromName("uint8")
    of tyUInt16: result = sysTypeFromName("uint16")
    of tyUInt32: result = sysTypeFromName("uint32")
    of tyUInt64: result = sysTypeFromName("uint64")
    of tyFloat: result = sysTypeFromName("float")
    of tyFloat32: result = sysTypeFromName("float32")
    of tyFloat64: result = sysTypeFromName("float64")
    of tyFloat128: result = sysTypeFromName("float128")
    of tyBool: result = sysTypeFromName("bool")
    of tyChar: result = sysTypeFromName("char")
    of tyString: result = sysTypeFromName("string")
    of tyCstring: result = sysTypeFromName("cstring")
    of tyPointer: result = sysTypeFromName("pointer")
    of tyNil: result = newSysType(g, tyNil, g.config.target.ptrSize)
    else: internalError(g.config, "request for typekind: " & $kind)
    g.sysTypes[kind] = result
  if result.kind != kind:
    if kind == tyFloat64 and result.kind == tyFloat: discard # because of aliasing
    else:
      internalError(g.config, "wanted: " & $kind & " got: " & $result.kind)
  if result == nil: internalError(g.config, "type not found: " & $kind)

proc resetSysTypes*(g: ModuleGraph) =
  g.systemModule = nil
  g.compilerprocs = initStrTable()
  g.exposed = initStrTable()
  for i in low(g.sysTypes)..high(g.sysTypes):
    g.sysTypes[i] = nil

proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
  # for now we do not cache these:
  result = newSysType(g, tyFloat, size=8)
  result.n = literal

proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} =
  if t.n != nil and t.kind in {tyInt, tyFloat}:
    result = copyType(t, id, t.owner)
    result.n = nil
  else:
    result = t

proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) =
  let s = son.skipIntLit(id)
  father.add(s)
  propagateToOwner(father, s)

proc makeVarType*(owner: PSym; baseType: PType; idgen: IdGenerator; kind = tyVar): PType =
  if baseType.kind == kind:
    result = baseType
  else:
    result = newType(kind, idgen, owner)
    addSonSkipIntLit(result, baseType, idgen)

proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
  let ident = getIdent(g.cache, name)
  result = strTableGet(g.compilerprocs, ident)
  if result == nil:
    result = loadCompilerProc(g, name)

proc registerCompilerProc*(g: ModuleGraph; s: PSym) =
  strTableAdd(g.compilerprocs, s)

proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) =
  # Nimscript symbols must be al unique:
  let conflict = strTableGet(g.exposed, s.name)
  if conflict == nil:
    strTableAdd(g.exposed, s)
  else:
    localError(g.config, s.info,
      "symbol conflicts with other .exportNims symbol at: " & g.config$conflict.info)

proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
  strTableGet(g.exposed, getIdent(g.cache, name))

proc resetNimScriptSymbols*(g: ModuleGraph) = g.exposed = initStrTable()

proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
  case t.kind
  of tyInt,  tyInt8, tyInt16, tyInt32, tyInt64,
     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64:
    result = getSysMagic(g, info, "==", mEqI)
  of tyEnum:
    result = getSysMagic(g, info, "==", mEqEnum)
  of tyBool:
    result = getSysMagic(g, info, "==", mEqB)
  of tyRef, tyPtr, tyPointer:
    result = getSysMagic(g, info, "==", mEqRef)
  of tyString:
    result = getSysMagic(g, info, "==", mEqStr)
  of tyChar:
    result = getSysMagic(g, info, "==", mEqCh)
  of tySet:
    result = getSysMagic(g, info, "==", mEqSet)
  of tyProc:
    result = getSysMagic(g, info, "==", mEqProc)
  else:
    result = nil
    globalError(g.config, info,
      "can't find magic equals operator for type kind " & $t.kind)

proc makePtrType*(baseType: PType; idgen: IdGenerator): PType =
  result = newType(tyPtr, idgen, baseType.owner)
  addSonSkipIntLit(result, baseType, idgen)

proc makeAddr*(n: PNode; idgen: IdGenerator): PNode =
  if n.kind == nkHiddenAddr:
    result = n
  else:
    result = newTree(nkHiddenAddr, n)
    result.typ() = makePtrType(n.typ.skipTypes({tySink}), idgen)
