# # # The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # Algorithms for the abstract syntax tree: hash tables, lists # and sets of nodes are supported. Efficiency is important as # the data structures here are used in various places of the compiler. import ast, astyaml, options, lineinfos, idents, rodutils, msgs import std/[hashes, intsets] import std/strutils except addf export astyaml.treeToYaml, astyaml.typeToYaml, astyaml.symToYaml, astyaml.lineInfoToStr when defined(nimPreviewSlimSystem): import std/assertions proc hashNode*(p: RootRef): Hash # these are for debugging only: They are not really deprecated, but I want # the warning so that release versions do not contain debugging statements: proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.} proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.} proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.} template debug*(x: PSym|PType|PNode) {.deprecated.} = when compiles(c.config): debug(c.config, x) elif compiles(c.graph.config): debug(c.graph.config, x) else: error() template debug*(x: auto) {.deprecated.} = echo x template mdbg*: bool {.deprecated.} = when compiles(c.graph): c.module.fileIdx == c.graph.config.projectMainIdx elif compiles(c.module): c.module.fileIdx == c.config.projectMainIdx elif compiles(c.c.module): c.c.module.fileIdx == c.c.config.projectMainIdx elif compiles(m.c.module): m.c.module.fileIdx == m.c.config.projectMainIdx elif compiles(cl.c.module): cl.c.module.fileIdx == cl.c.config.projectMainIdx elif compiles(p): when compiles(p.lex): p.lex.fileIdx == p.lex.config.projectMainIdx else: p.module.module.fileIdx == p.config.projectMainIdx elif compiles(m.module.fileIdx): m.module.fileIdx == m.config.projectMainIdx elif compiles(L.fileIdx): L.fileIdx == L.config.projectMainIdx else: error() # --------------------------------------------------------------------------- proc lookupInRecord*(n: PNode, field: PIdent): PSym proc mustRehash*(length, counter: int): bool proc nextTry*(h, maxHash: Hash): Hash {.inline.} # ------------- table[int, int] --------------------------------------------- const InvalidKey* = low(int) type TIIPair*{.final.} = object key*, val*: int TIIPairSeq* = seq[TIIPair] TIITable*{.final.} = object # table[int, int] counter*: int data*: TIIPairSeq proc initIITable*(x: var TIITable) proc iiTableGet*(t: TIITable, key: int): int proc iiTablePut*(t: var TIITable, key, val: int) # implementation proc skipConvCastAndClosure*(n: PNode): PNode = result = n while true: case result.kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64, nkClosure: result = result[0] of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: result = result[1] else: break proc sameValue*(a, b: PNode): bool = result = false case a.kind of nkCharLit..nkUInt64Lit: if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) == getInt(b) of nkFloatLit..nkFloat64Lit: if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal else: # don't raise an internal error for 'nim check': #InternalError(a.info, "SameValue") discard proc leValue*(a, b: PNode): bool = # a <= b? result = false case a.kind of nkCharLit..nkUInt64Lit: if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) <= getInt(b) of nkFloatLit..nkFloat64Lit: if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal of nkStrLit..nkTripleStrLit: if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal else: # don't raise an internal error for 'nim check': #InternalError(a.info, "leValue") discard proc weakLeValue*(a, b: PNode): TImplication = if a.kind notin nkLiterals or b.kind notin nkLiterals: result = impUnknown else: result = if leValue(a, b): impYes else: impNo proc lookupInRecord(n: PNode, field: PIdent): PSym = result = nil case n.kind of nkRecList: for i in 0.. 0 and a[alen] != '`': dec(alen) if alen <= 0: alen = a.len var i = 1 var j = 1 while true: while i < alen and a[i] == '_': inc i while j < b.len and b[j] == '_': inc j var aa = if i < alen: toLowerAscii(a[i]) else: '\0' var bb = if j < b.len: toLowerAscii(b[j]) else: '\0' if aa != bb: return false # the characters are identical: if i >= alen: # both cursors at the end: if j >= b.len: return true # not yet at the end of 'b': return false elif j >= b.len: return false inc i inc j proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym = ## Named parameters are special because a named parameter can be ## gensym'ed and then they have '\`' suffix that we need to ## ignore, see compiler / evaltempl.nim, snippet: ## ```nim ## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id), ## if c.instLines: actual.info else: templ.info) ## ``` result = nil for i in 1.. counter) result = (length * 2 < counter * 3) or (length - counter < 4) import std/tables const backrefStyle = "\e[90m" const enumStyle = "\e[34m" const numberStyle = "\e[33m" const stringStyle = "\e[32m" const resetStyle = "\e[0m" type DebugPrinter = object conf: ConfigRef visited: Table[pointer, int] renderSymType: bool indent: int currentLine: int firstItem: bool useColor: bool res: string proc indentMore(this: var DebugPrinter) = this.indent += 2 proc indentLess(this: var DebugPrinter) = this.indent -= 2 proc newlineAndIndent(this: var DebugPrinter) = this.res.add "\n" this.currentLine += 1 for i in 0.." if this.useColor: this.res.add resetStyle return proc value(this: var DebugPrinter; value: PType) proc value(this: var DebugPrinter; value: PNode) proc value(this: var DebugPrinter; value: PSym) = earlyExit(this, value) this.openCurly this.key("kind") this.value(value.kind) this.key("name") this.value(value.name.s) this.key("id") this.value(value.id) if value.kind in {skField, skEnumField, skParam}: this.key("position") this.value(value.position) if card(value.flags) > 0: this.key("flags") this.value(value.flags) if this.renderSymType and value.typ != nil: this.key "typ" this.value(value.typ) this.closeCurly proc value(this: var DebugPrinter; value: PType) = earlyExit(this, value) this.openCurly this.key "kind" this.value value.kind this.key "id" this.value value.id if value.sym != nil: this.key "sym" this.value value.sym #this.value value.sym.name.s if card(value.flags) > 0: this.key "flags" this.value value.flags if value.kind in IntegralTypes and value.n != nil: this.key "n" this.value value.n this.key "sons" this.openBracket for i, a in value.ikids: if i > 0: this.comma this.value a this.closeBracket if value.n != nil: this.key "n" this.value value.n this.closeCurly proc value(this: var DebugPrinter; value: PNode) = earlyExit(this, value) this.openCurly this.key "kind" this.value value.kind if value.comment.len > 0: this.key "comment" this.value value.comment when defined(useNodeIds): this.key "id" this.value value.id if this.conf != nil: this.key "info" this.value $lineInfoToStr(this.conf, value.info) if value.flags != {}: this.key "flags" this.value value.flags if value.typ != nil: this.key "typ" this.value value.typ.kind else: this.key "typ" this.value "nil" case value.kind of nkCharLit..nkUInt64Lit: this.key "intVal" this.value value.intVal of nkFloatLit, nkFloat32Lit, nkFloat64Lit: this.key "floatVal" this.value value.floatVal.toStrMaxPrecision of nkStrLit..nkTripleStrLit: this.key "strVal" this.value value.strVal of nkSym: this.key "sym" this.value value.sym #this.value value.sym.name.s of nkIdent: if value.ident != nil: this.key "ident" this.value value.ident.s else: if this.renderSymType and value.typ != nil: this.key "typ" this.value value.typ if value.len > 0: this.key "sons" this.openBracket for i in 0..= 0: result = t.data[replaceSlot] # found it if not onConflictKeepOld: t.data[replaceSlot] = n # overwrite it with newer definition! return result # but return the old one elif mustRehash(t.data.len, t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) else: assert(t.data[h] == nil) t.data[h] = n inc(t.counter) result = nil proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld = false): bool {.discardable.} = result = strTableInclReportConflict(t, n, onConflictKeepOld) != nil proc strTableGet*(t: TStrTable, name: PIdent): PSym = var h: Hash = name.h and high(t.data) while true: result = t.data[h] if result == nil: break if result.name.id == name.id: break h = nextTry(h, high(t.data)) type TIdentIter* = object # iterator over all syms with same identifier h*: Hash # current hash name*: PIdent proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym = # hot spots var h = ti.h and high(tab.data) var start = h var p {.cursor.} = tab.data[h] while p != nil: if p.name.id == ti.name.id: break h = nextTry(h, high(tab.data)) if h == start: p = nil break p = tab.data[h] if p != nil: result = p # increase the count else: result = nil ti.h = nextTry(h, high(tab.data)) proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym = ti.h = s.h ti.name = s if tab.counter == 0: result = nil else: result = nextIdentIter(ti, tab) proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable, excluding: IntSet): PSym = var h: Hash = ti.h and high(tab.data) var start = h result = tab.data[h] while result != nil: if result.name.id == ti.name.id and not contains(excluding, result.id): break h = nextTry(h, high(tab.data)) if h == start: result = nil break result = tab.data[h] ti.h = nextTry(h, high(tab.data)) if result != nil and contains(excluding, result.id): result = nil proc firstIdentExcluding*(ti: var TIdentIter, tab: TStrTable, s: PIdent, excluding: IntSet): PSym = ti.h = s.h ti.name = s if tab.counter == 0: result = nil else: result = nextIdentExcluding(ti, tab, excluding) type TTabIter* = object h: Hash proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym = # usage: # var # i: TTabIter # s: PSym # s = InitTabIter(i, table) # while s != nil: # ... # s = NextIter(i, table) # result = nil while (ti.h <= high(tab.data)): result = tab.data[ti.h] inc(ti.h) # ... and increment by one always if result != nil: break proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym = ti.h = 0 if tab.counter == 0: result = nil else: result = nextIter(ti, tab) iterator items*(tab: TStrTable): PSym = var it: TTabIter = default(TTabIter) var s = initTabIter(it, tab) while s != nil: yield s s = nextIter(it, tab) proc initIITable(x: var TIITable) = x.counter = 0 newSeq(x.data, StartSize) for i in 0..= 0: result = t.data[index].val else: result = InvalidKey proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) = var h: Hash h = key and high(data) while data[h].key != InvalidKey: assert(data[h].key != key) h = nextTry(h, high(data)) assert(data[h].key == InvalidKey) data[h].key = key data[h].val = val proc iiTablePut(t: var TIITable, key, val: int) = var index = iiTableRawGet(t, key) if index >= 0: assert(t.data[index].key != InvalidKey) t.data[index].val = val else: if mustRehash(t.data.len, t.counter): var n: TIIPairSeq newSeq(n, t.data.len * GrowthFactor) for i in 0..high(n): n[i].key = InvalidKey for i in 0..high(t.data): if t.data[i].key != InvalidKey: iiTableRawInsert(n, t.data[i].key, t.data[i].val) swap(t.data, n) iiTableRawInsert(t.data, key, val) inc(t.counter) proc listSymbolNames*(symbols: openArray[PSym]): string = result = "" for sym in symbols: if result.len > 0: result.add ", " result.add sym.name.s proc isDiscriminantField*(n: PNode): bool = if n.kind == nkCheckedFieldExpr: sfDiscriminant in n[0][1].sym.flags elif n.kind == nkDotExpr: sfDiscriminant in n[1].sym.flags else: false