Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/condsyms.nim
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,4 @@ proc initDefines*() =
defineSymbol("nimNewDot")
defineSymbol("nimHasNilChecks")
defineSymbol("nimSymKind")
defineSymbol("nimVmEqIdent")
2 changes: 1 addition & 1 deletion compiler/idents.nim
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ proc resetIdentCache*() =
for i in low(legacy.buckets)..high(legacy.buckets):
legacy.buckets[i] = nil

proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
if a[0] != b[0]: return 1
var i = 0
var j = 0
Expand Down
32 changes: 29 additions & 3 deletions compiler/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1392,10 +1392,36 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
regs[ra].node.typ = n.typ
of opcEqIdent:
decodeBC(rkInt)
if regs[rb].node.kind == nkIdent and regs[rc].node.kind == nkIdent:
regs[ra].intVal = ord(regs[rb].node.ident.id == regs[rc].node.ident.id)
# aliases for shorter and easier to understand code below
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not bad, but since rawExecute is already really long (!), it should be extracted into a helper proc.

let aNode = regs[rb].node
let bNode = regs[rc].node
# these are cstring to prevent string copy, and cmpIgnoreStyle from
# takes cstring arguments
var aStrVal: cstring
var bStrVal: cstring
# extract strVal from argument ``a``
case aNode.kind
of {nkStrLit..nkTripleStrLit}:
aStrVal = aNode.strVal.cstring
of nkIdent:
aStrVal = aNode.ident.s.cstring
of nkSym:
aStrVal = aNode.sym.name.s.cstring
else:
regs[ra].intVal = 0
stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
# extract strVal from argument ``b``
case bNode.kind
of {nkStrLit..nkTripleStrLit}:
bStrVal = bNode.strVal.cstring
of nkIdent:
bStrVal = bNode.ident.s.cstring
of nkSym:
bStrVal = bNode.sym.name.s.cstring
else:
stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
# set result
regs[ra].intVal =
ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0)
of opcStrToIdent:
decodeB(rkNode)
if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}:
Expand Down
83 changes: 51 additions & 32 deletions lib/core/macros.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1141,38 +1141,57 @@ proc copy*(node: NimNode): NimNode {.compileTime.} =
## An alias for copyNimTree().
return node.copyNimTree()

proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
proc toLower(c: char): char {.inline.} =
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
else: result = c
var i = 0
var j = 0
# first char is case sensitive
if a[0] != b[0]: return 1
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)

proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical.

proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
## is the same as ``s``. Note that this is the preferred way to check! Most
## other ways like ``node.ident`` are much more error-prone, unfortunately.
case node.kind
of nnkSym, nnkIdent:
result = eqIdent(node.strVal, s)
of nnkOpenSymChoice, nnkClosedSymChoice:
result = eqIdent($node[0], s)
else:
result = false
when defined(nimVmEqIdent):
proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.

proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
## ``a`` can be an identifier or a symbol.

proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
## ``b`` can be an identifier or a symbol.

proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
## ``a`` and ``b`` can be an identifier or a symbol.

else:
# this procedure is optimized for native code, it should not be compiled to nimVM bytecode.
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
proc toLower(c: char): char {.inline.} =
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
else: result = c
var i = 0
var j = 0
# first char is case sensitive
if a[0] != b[0]: return 1
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)


proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical.

proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
## is the same as ``s``. Note that this is the preferred way to check! Most
## other ways like ``node.ident`` are much more error-prone, unfortunately.
case node.kind
of nnkSym, nnkIdent:
result = eqIdent(node.strVal, s)
of nnkOpenSymChoice, nnkClosedSymChoice:
result = eqIdent($node[0], s)
else:
result = false

proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}=
## Search nnkFormalParams for an argument.
Expand Down
6 changes: 4 additions & 2 deletions lib/pure/cstrutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ proc endsWith*(s, suffix: cstring): bool {.noSideEffect,

proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect,
rtl, extern: "csuCmpIgnoreStyle".} =
## Compares two strings normalized (i.e. case and
## underscores do not matter). Returns:
## Semantically the same as ``cmp(normalize($a), normalize($b))``. It
## is just optimized to not allocate temporary strings. This should
## NOT be used to compare Nim identifier names. use `macros.eqIdent`
## for that. Returns:
##
## | 0 iff a == b
## | < 0 iff a < b
Expand Down
11 changes: 6 additions & 5 deletions lib/pure/strutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,8 @@ proc normalize*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuNormalize".} =
## Normalizes the string `s`.
##
## That means to convert it to lower case and remove any '_'. This is needed
## for Nim identifiers for example.
## That means to convert it to lower case and remove any '_'. This
## should NOT be used to normalize Nim identifier names.
result = newString(s.len)
var j = 0
for i in 0..len(s) - 1:
Expand Down Expand Up @@ -418,8 +418,10 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,

proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
rtl, extern: "nsuCmpIgnoreStyle", procvar.} =
## Compares two strings normalized (i.e. case and
## underscores do not matter). Returns:
## Semantically the same as ``cmp(normalize(a), normalize(b))``. It
## is just optimized to not allocate temporary strings. This should
## NOT be used to compare Nim identifier names. use `macros.eqIdent`
## for that. Returns:
##
## | 0 iff a == b
## | < 0 iff a < b
Expand All @@ -436,7 +438,6 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
inc(i)
inc(j)


proc strip*(s: string, leading = true, trailing = true,
chars: set[char] = Whitespace): string
{.noSideEffect, rtl, extern: "nsuStrip".} =
Expand Down
42 changes: 42 additions & 0 deletions tests/macros/tmacro1.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,47 @@ macro test*(a: untyped): untyped =
t.b = true
t.z = 4.5


test:
"hi"

import strutils

template assertNot(arg: untyped): untyped =
assert(not(arg))

static:
## test eqIdent
let a = "abc_def"
let b = "abcDef"
let c = "AbcDef"

assert eqIdent( a , b )
assert eqIdent(newIdentNode(a), b )
assert eqIdent( a , newIdentNode(b))
assert eqIdent(newIdentNode(a), newIdentNode(b))

assert eqIdent( a , b )
assert eqIdent(genSym(nskLet, a), b )
assert eqIdent( a , genSym(nskLet, b))
assert eqIdent(genSym(nskLet, a), genSym(nskLet, b))

assert eqIdent(newIdentNode( a), newIdentNode( b))
assert eqIdent(genSym(nskLet, a), newIdentNode( b))
assert eqIdent(newIdentNode( a), genSym(nskLet, b))
assert eqIdent(genSym(nskLet, a), genSym(nskLet, b))

assertNot eqIdent( c , b )
assertNot eqIdent(newIdentNode(c), b )
assertNot eqIdent( c , newIdentNode(b))
assertNot eqIdent(newIdentNode(c), newIdentNode(b))

assertNot eqIdent( c , b )
assertNot eqIdent(genSym(nskLet, c), b )
assertNot eqIdent( c , genSym(nskLet, b))
assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b))

assertNot eqIdent(newIdentNode( c), newIdentNode( b))
assertNot eqIdent(genSym(nskLet, c), newIdentNode( b))
assertNot eqIdent(newIdentNode( c), genSym(nskLet, b))
assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b))