diff options
Diffstat (limited to 'compiler/idents.nim')
-rw-r--r-- | compiler/idents.nim | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/compiler/idents.nim b/compiler/idents.nim new file mode 100644 index 000000000..34177e76d --- /dev/null +++ b/compiler/idents.nim @@ -0,0 +1,123 @@ +# +# +# The Nim Compiler +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# Identifier handling +# An identifier is a shared immutable string that can be compared by its +# id. This module is essential for the compiler's performance. + +import wordrecg +import std/hashes + +when defined(nimPreviewSlimSystem): + import std/assertions + +type + PIdent* = ref TIdent + TIdent*{.acyclic.} = object + id*: int # unique id; use this for comparisons and not the pointers + s*: string + next*: PIdent # for hash-table chaining + h*: Hash # hash value of s + + IdentCache* = ref object + buckets: array[0..4096 * 2 - 1, PIdent] + wordCounter: int + idAnon*, idDelegator*, emptyIdent*: PIdent + +proc resetIdentCache*() = discard + +proc cmpIgnoreStyle*(a, b: cstring, blen: int): int = + if a[0] != b[0]: return 1 + var i = 0 + var j = 0 + result = 1 + while j < blen: + while a[i] == '_': inc(i) + while b[j] == '_': inc(j) + # tolower inlined: + var aa = a[i] + var bb = b[j] + if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A'))) + if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A'))) + result = ord(aa) - ord(bb) + if (result != 0) or (aa == '\0'): break + inc(i) + inc(j) + if result == 0: + if a[i] != '\0': result = 1 + +proc cmpExact(a, b: cstring, blen: int): int = + var i = 0 + var j = 0 + result = 1 + while j < blen: + var aa = a[i] + var bb = b[j] + result = ord(aa) - ord(bb) + if (result != 0) or (aa == '\0'): break + inc(i) + inc(j) + if result == 0: + if a[i] != '\0': result = 1 + +proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIdent = + var idx = h and high(ic.buckets) + result = ic.buckets[idx] + var last: PIdent = nil + var id = 0 + while result != nil: + if cmpExact(cstring(result.s), identifier, length) == 0: + if last != nil: + # make access to last looked up identifier faster: + last.next = result.next + result.next = ic.buckets[idx] + ic.buckets[idx] = result + return + elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0: + assert((id == 0) or (id == result.id)) + id = result.id + last = result + result = result.next + new(result) + result.h = h + result.s = newString(length) + for i in 0..<length: result.s[i] = identifier[i] + result.next = ic.buckets[idx] + ic.buckets[idx] = result + if id == 0: + inc(ic.wordCounter) + result.id = -ic.wordCounter + else: + result.id = id + +proc getIdent*(ic: IdentCache; identifier: string): PIdent = + result = getIdent(ic, cstring(identifier), identifier.len, + hashIgnoreStyle(identifier)) + +proc getIdent*(ic: IdentCache; identifier: string, h: Hash): PIdent = + result = getIdent(ic, cstring(identifier), identifier.len, h) + +proc newIdentCache*(): IdentCache = + result = IdentCache() + result.idAnon = result.getIdent":anonymous" + result.wordCounter = 1 + result.idDelegator = result.getIdent":delegator" + result.emptyIdent = result.getIdent("") + # initialize the keywords: + for s in succ(low(TSpecialWord))..high(TSpecialWord): + result.getIdent($s, hashIgnoreStyle($s)).id = ord(s) + +proc whichKeyword*(id: PIdent): TSpecialWord = + if id.id < 0: result = wInvalid + else: result = TSpecialWord(id.id) + +proc hash*(x: PIdent): Hash {.inline.} = x.h +proc `==`*(a, b: PIdent): bool {.inline.} = + if a.isNil or b.isNil: result = system.`==`(a, b) + else: result = a.id == b.id |