idents.nim 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # Identifier handling
  10. # An identifier is a shared immutable string that can be compared by its
  11. # id. This module is essential for the compiler's performance.
  12. import
  13. hashes, strutils, wordrecg
  14. type
  15. TIdObj* = object of RootObj
  16. id*: int # unique id; use this for comparisons and not the pointers
  17. PIdObj* = ref TIdObj
  18. PIdent* = ref TIdent
  19. TIdent*{.acyclic.} = object of TIdObj
  20. s*: string
  21. next*: PIdent # for hash-table chaining
  22. h*: Hash # hash value of s
  23. IdentCache* = ref object
  24. buckets: array[0..4096 * 2 - 1, PIdent]
  25. wordCounter: int
  26. idAnon*, idDelegator*, emptyIdent*: PIdent
  27. var
  28. legacy: IdentCache
  29. proc resetIdentCache*() =
  30. for i in low(legacy.buckets)..high(legacy.buckets):
  31. legacy.buckets[i] = nil
  32. proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
  33. if a[0] != b[0]: return 1
  34. var i = 0
  35. var j = 0
  36. result = 1
  37. while j < blen:
  38. while a[i] == '_': inc(i)
  39. while b[j] == '_': inc(j)
  40. # tolower inlined:
  41. var aa = a[i]
  42. var bb = b[j]
  43. if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A')))
  44. if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
  45. result = ord(aa) - ord(bb)
  46. if (result != 0) or (aa == '\0'): break
  47. inc(i)
  48. inc(j)
  49. if result == 0:
  50. if a[i] != '\0': result = 1
  51. proc cmpExact(a, b: cstring, blen: int): int =
  52. var i = 0
  53. var j = 0
  54. result = 1
  55. while j < blen:
  56. var aa = a[i]
  57. var bb = b[j]
  58. result = ord(aa) - ord(bb)
  59. if (result != 0) or (aa == '\0'): break
  60. inc(i)
  61. inc(j)
  62. if result == 0:
  63. if a[i] != '\0': result = 1
  64. {.this: self.}
  65. proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PIdent =
  66. var idx = h and high(buckets)
  67. result = buckets[idx]
  68. var last: PIdent = nil
  69. var id = 0
  70. while result != nil:
  71. if cmpExact(cstring(result.s), identifier, length) == 0:
  72. if last != nil:
  73. # make access to last looked up identifier faster:
  74. last.next = result.next
  75. result.next = buckets[idx]
  76. buckets[idx] = result
  77. return
  78. elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
  79. assert((id == 0) or (id == result.id))
  80. id = result.id
  81. last = result
  82. result = result.next
  83. new(result)
  84. result.h = h
  85. result.s = newString(length)
  86. for i in countup(0, length - 1): result.s[i] = identifier[i]
  87. result.next = buckets[idx]
  88. buckets[idx] = result
  89. if id == 0:
  90. inc(wordCounter)
  91. result.id = -wordCounter
  92. else:
  93. result.id = id
  94. proc getIdent*(self: IdentCache; identifier: string): PIdent =
  95. result = getIdent(cstring(identifier), len(identifier),
  96. hashIgnoreStyle(identifier))
  97. proc getIdent*(self: IdentCache; identifier: string, h: Hash): PIdent =
  98. result = getIdent(cstring(identifier), len(identifier), h)
  99. proc newIdentCache*(): IdentCache =
  100. if legacy.isNil:
  101. result = IdentCache()
  102. result.idAnon = result.getIdent":anonymous"
  103. result.wordCounter = 1
  104. result.idDelegator = result.getIdent":delegator"
  105. result.emptyIdent = result.getIdent("")
  106. # initialize the keywords:
  107. for s in countup(succ(low(specialWords)), high(specialWords)):
  108. result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s)
  109. legacy = result
  110. else:
  111. result = legacy
  112. proc whichKeyword*(id: PIdent): TSpecialWord =
  113. if id.id < 0: result = wInvalid
  114. else: result = TSpecialWord(id.id)
  115. proc getIdent*(identifier: string): PIdent =
  116. ## for backwards compatibility.
  117. if legacy.isNil:
  118. discard newIdentCache()
  119. legacy.getIdent identifier