-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
Copy pathidents.nim
123 lines (109 loc) · 3.49 KB
/
idents.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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