summary refs log tree commit diff stats
path: root/tests/stdlib/tstrset.nim
blob: 9cdb5ed3522f8fd6c6a5ca0f406479f2d7373161 (plain) (blame)
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
# test a simple yet highly efficient set of strings

type
  TRadixNodeKind = enum rnLinear, rnFull, rnLeaf
  PRadixNode = ref TRadixNode
  TRadixNode = object {.inheritable.}
    kind: TRadixNodeKind
  TRadixNodeLinear = object of TRadixNode
    len: int8
    keys: array [0..31, char]
    vals: array [0..31, PRadixNode]  
  TRadixNodeFull = object of TRadixNode
    b: array [char, PRadixNode]
  TRadixNodeLeaf = object of TRadixNode
    s: string
  PRadixNodeLinear = ref TRadixNodeLinear
  PRadixNodeFull = ref TRadixNodeFull
  PRadixNodeLeaf = ref TRadixNodeLeaf
    
proc search(r: PRadixNode, s: string): PRadixNode =
  var r = r
  var i = 0
  while r != nil:
    case r.kind
    of rnLinear:
      var x = PRadixNodeLinear(r)
      for j in 0..ze(x.len)-1:
        if x.keys[j] == s[i]:
          if s[i] == '\0': return r
          r = x.vals[j]
          inc(i)
          break
      break # character not found
    of rnFull:
      var x = PRadixNodeFull(r)
      var y = x.b[s[i]]
      if s[i] == '\0':
        return if y != nil: r else: nil
      r = y
      inc(i)
    of rnLeaf:
      var x = PRadixNodeLeaf(r)
      var j = 0
      while true:
        if x.s[j] != s[i]: return nil
        if s[i] == '\0': return r
        inc(j)
        inc(i)

proc contains*(r: PRadixNode, s: string): bool =
  return search(r, s) != nil

proc testOrincl*(r: var PRadixNode, s: string): bool =
  nil
    
proc incl*(r: var PRadixNode, s: string) = discard testOrIncl(r, s)

proc excl*(r: var PRadixNode, s: string) =
  var x = search(r, s)
  if x == nil: return
  case x.kind
  of rnLeaf: PRadixNodeLeaf(x).s = ""
  of rnFull: PRadixNodeFull(x).b['\0'] = nil
  of rnLinear:
    var x = PRadixNodeLinear(x)
    for i in 0..ze(x.len)-1:
      if x.keys[i] == '\0':
        swap(x.keys[i], x.keys[ze(x.len)-1])
        dec(x.len)
        break

var
  root: PRadixNode
class="n">fmoduleId == c.module.id or fmoduleId == c.friendModule.id proc suggestField(c: PContext, s: PSym, outputs: var int) = if filterSym(s) and fieldVisible(c, s): SuggestWriteln(SymToStr(s, isLocal=true, sectionSuggest)) inc outputs when not defined(nimhygiene): {.pragma: inject.} template wholeSymTab(cond, section: expr) {.immediate.} = for i in countdown(c.tab.tos-1, 0): for item in items(c.tab.stack[i]): let it {.inject.} = item if cond: SuggestWriteln(SymToStr(it, isLocal = i > ModuleTablePos, section)) inc outputs proc suggestSymList(c: PContext, list: PNode, outputs: var int) = for i in countup(0, sonsLen(list) - 1): if list.sons[i].kind == nkSym: suggestField(c, list.sons[i].sym, outputs) #else: InternalError(list.info, "getSymFromList") proc suggestObject(c: PContext, n: PNode, outputs: var int) = case n.kind of nkRecList: for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs) of nkRecCase: var L = sonsLen(n) if L > 0: suggestObject(c, n.sons[0], outputs) for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), outputs) of nkSym: suggestField(c, n.sym, outputs) else: nil proc nameFits(c: PContext, s: PSym, n: PNode): bool = var op = n.sons[0] if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0] var opr: PIdent case op.kind of nkSym: opr = op.sym.name of nkIdent: opr = op.ident else: return false result = opr.id == s.name.id proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool = case candidate.kind of OverloadableSyms: var m: TCandidate initCandidate(m, candidate, nil) sigmatch.partialMatch(c, n, nOrig, m) result = m.state != csNoMatch else: result = false proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) = wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig), sectionContext) proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil: result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg) proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) = assert typ != nil wholeSymTab(filterSym(it) and typeFits(c, it, typ), sectionSuggest) proc suggestEverything(c: PContext, n: PNode, outputs: var int) = # do not produce too many symbols: for i in countdown(c.tab.tos-1, 1): for it in items(c.tab.stack[i]): if filterSym(it): SuggestWriteln(SymToStr(it, isLocal = i > ModuleTablePos, sectionSuggest)) inc outputs proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) = # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but # ``myObj``. var typ = n.Typ if typ == nil: # a module symbol has no type for example: if n.kind == nkSym and n.sym.kind == skModule: if n.sym == c.module: # all symbols accessible, because we are in the current module: for it in items(c.tab.stack[ModuleTablePos]): if filterSym(it): SuggestWriteln(SymToStr(it, isLocal=false, sectionSuggest)) inc outputs else: for it in items(n.sym.tab): if filterSym(it): SuggestWriteln(SymToStr(it, isLocal=false, sectionSuggest)) inc outputs else: # fallback: suggestEverything(c, n, outputs) elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: # look up if the identifier belongs to the enum: var t = typ while t != nil: suggestSymList(c, t.n, outputs) t = t.sons[0] suggestOperations(c, n, typ, outputs) else: typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef}) if typ.kind == tyObject: var t = typ while true: suggestObject(c, t.n, outputs) if t.sons[0] == nil: break t = skipTypes(t.sons[0], {tyGenericInst}) suggestOperations(c, n, typ, outputs) elif typ.kind == tyTuple and typ.n != nil: suggestSymList(c, typ.n, outputs) suggestOperations(c, n, typ, outputs) else: suggestOperations(c, n, typ, outputs) proc findClosestDot(n: PNode): PNode = if n.kind == nkDotExpr and msgs.inCheckpoint(n.info) == cpExact: result = n else: for i in 0.. <safeLen(n): result = findClosestDot(n.sons[i]) if result != nil: return const CallNodes = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit} proc findClosestCall(n: PNode): PNode = if n.kind in callNodes and msgs.inCheckpoint(n.info) == cpExact: result = n else: for i in 0.. <safeLen(n): result = findClosestCall(n.sons[i]) if result != nil: return proc isTracked(current: TLineInfo, tokenLen: int): bool = # the column of an identifier is at its *end*, so we subtract to get the # start of it. for i in countup(0, high(checkPoints)): if current.fileIndex == checkPoints[i].fileIndex: if current.line == checkPoints[i].line: let col = checkPoints[i].col if col >= current.col-tokenLen and col <= current.col: return true proc findClosestSym(n: PNode): PNode = if n.kind == nkSym and msgs.inCheckpoint(n.info) == cpExact: result = n elif n.kind notin {nkNone..nkNilLit}: for i in 0.. <sonsLen(n): result = findClosestSym(n.sons[i]) if result != nil: return proc safeSemExpr(c: PContext, n: PNode): PNode = try: result = c.semExpr(c, n) except ERecoverableError: result = ast.emptyNode proc fuzzySemCheck(c: PContext, n: PNode): PNode = result = safeSemExpr(c, n) if result == nil or result.kind == nkEmpty: result = newNodeI(n.kind, n.info) if n.kind notin {nkNone..nkNilLit}: for i in 0 .. < sonsLen(n): result.addSon(fuzzySemCheck(c, n.sons[i])) var usageSym*: PSym lastLineInfo: TLineInfo proc findUsages(node: PNode, s: PSym) = if usageSym == nil and isTracked(node.info, s.name.s.len): usageSym = s SuggestWriteln(SymToStr(s, isLocal=false, sectionUsage)) elif s == usageSym: if lastLineInfo != node.info: SuggestWriteln(SymToStr(s, isLocal=false, sectionUsage, node.info)) lastLineInfo = node.info proc findDefinition(node: PNode, s: PSym) = if isTracked(node.info, s.name.s.len): SuggestWriteln(SymToStr(s, isLocal=false, sectionDef)) SuggestQuit() proc suggestSym*(n: PNode, s: PSym) {.inline.} = ## misnamed: should be 'symDeclared' if optUsages in gGlobalOptions: findUsages(n, s) if optDef in gGlobalOptions: findDefinition(n, s) proc markUsed(n: PNode, s: PSym) = incl(s.flags, sfUsed) if {sfDeprecated, sfError} * s.flags != {}: if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s) if sfError in s.flags: LocalError(n.info, errWrongSymbolX, s.name.s) suggestSym(n, s) proc useSym*(sym: PSym): PNode = result = newSymNode(sym) markUsed(result, sym) proc suggestExpr*(c: PContext, node: PNode) = var cp = msgs.inCheckpoint(node.info) if cp == cpNone: return var outputs = 0 # This keeps semExpr() from coming here recursively: if c.InCompilesContext > 0: return inc(c.InCompilesContext) if optSuggest in gGlobalOptions: var n = findClosestDot(node) if n == nil: n = node else: cp = cpExact if n.kind == nkDotExpr and cp == cpExact: var obj = safeSemExpr(c, n.sons[0]) suggestFieldAccess(c, obj, outputs) else: #debug n suggestEverything(c, n, outputs) if optContext in gGlobalOptions: var n = findClosestCall(node) if n == nil: n = node else: cp = cpExact if n.kind in CallNodes: var a = copyNode(n) var x = safeSemExpr(c, n.sons[0]) if x.kind == nkEmpty or x.typ == nil: x = n.sons[0] addSon(a, x) for i in 1..sonsLen(n)-1: # use as many typed arguments as possible: var x = safeSemExpr(c, n.sons[i]) if x.kind == nkEmpty or x.typ == nil: break addSon(a, x) suggestCall(c, a, n, outputs) dec(c.InCompilesContext) if outputs > 0 and optUsages notin gGlobalOptions: SuggestQuit() proc suggestStmt*(c: PContext, n: PNode) = suggestExpr(c, n) proc findSuggest*(c: PContext, n: PNode) = if n == nil: return suggestExpr(c, n) for i in 0.. <safeLen(n): findSuggest(c, n.sons[i])