EnumUtils, speed up findStr in compiler (#15777)

* add parseEnumRange
* fix runnable example
* update changelog
* use parseEnumRange in compiler
* reorganise code
* add changelog, make single normalizer argument
This commit is contained in:
cooldome 2020-11-03 15:26:16 +00:00 • committed by GitHub
commit d62f3627aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 89 additions and 68 deletions

View file

@ -76,7 +76,7 @@
import parseutils
from math import pow, floor, log10
from algorithm import reverse
import macros # for `parseEnum`
import std/enumutils
when defined(nimVmExportFixed):
from unicode import toLower, toUpper
@ -1264,61 +1264,6 @@ proc parseBool*(s: string): bool =
of "n", "no", "false", "0", "off": result = false
else: raise newException(ValueError, "cannot interpret as a bool: " & s)
proc addOfBranch(s: string, field, enumType: NimNode): NimNode =
result = nnkOfBranch.newTree(
newLit s,
nnkCall.newTree(enumType, field) # `T(<fieldValue>)`
)
macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped =
# generates a case stmt, which assigns the correct enum field given
# a normalized string comparison to the `argSym` input.
# NOTE: for an enum with fields Foo, Bar, ... we cannot generate
# `of "Foo".nimIdentNormalize: Foo`.
# This will fail, if the enum is not defined at top level (e.g. in a block).
# Thus we check for the field value of the (possible holed enum) and convert
# the integer value to the generic argument `typ`.
let typ = typ.getTypeInst[1]
let impl = typ.getImpl[2]
expectKind impl, nnkEnumTy
result = nnkCaseStmt.newTree(newCall(bindSym"nimIdentNormalize", argSym))
# stores all processed field strings to give error msg for ambiguous enums
var foundFields: seq[string] = @[]
var fStr = "" # string of current field
var fNum = BiggestInt(0) # int value of current field
for f in impl:
case f.kind
of nnkEmpty: continue # skip first node of `enumTy`
of nnkSym, nnkIdent: fStr = f.strVal
of nnkEnumFieldDef:
case f[1].kind
of nnkStrLit: fStr = f[1].strVal
of nnkTupleConstr:
fStr = f[1][1].strVal
fNum = f[1][0].intVal
of nnkIntLit:
fStr = f[0].strVal
fNum = f[1].intVal
else: error("Invalid tuple syntax!", f[1])
else: error("Invalid node for enum type!", f)
# add field if string not already added
fStr = nimIdentNormalize(fStr)
if fStr notin foundFields:
result.add addOfBranch(fStr, newLit fNum, typ)
foundFields.add fStr
else:
error("Ambiguous enums cannot be parsed, field " & $fStr &
" appears multiple times!", f)
inc fNum
# finally add else branch to raise or use default
if default == nil:
let raiseStmt = quote do:
raise newException(ValueError, "Invalid enum value: " & $`argSym`)
result.add nnkElse.newTree(raiseStmt)
else:
expectKind(default, nnkSym)
result.add nnkElse.newTree(default)
proc parseEnum*[T: enum](s: string): T =
## Parses an enum ``T``. This errors at compile time, if the given enum
## type contains multiple fields with the same string value.
@ -1337,7 +1282,7 @@ proc parseEnum*[T: enum](s: string): T =
doAssertRaises(ValueError):
echo parseEnum[MyEnum]("third")
genEnumStmt(T, s, default = nil)
genEnumCaseStmt(T, s, default = nil, ord(low(T)), ord(high(T)), nimIdentNormalize)
proc parseEnum*[T: enum](s: string, default: T): T =
## Parses an enum ``T``. This errors at compile time, if the given enum
@ -1356,7 +1301,7 @@ proc parseEnum*[T: enum](s: string, default: T): T =
doAssert parseEnum[MyEnum]("second") == second
doAssert parseEnum[MyEnum]("last", third) == third
genEnumStmt(T, s, default)
genEnumCaseStmt(T, s, default, ord(low(T)), ord(high(T)), nimIdentNormalize)
proc repeat*(c: char, count: Natural): string {.noSideEffect,
rtl, extern: "nsuRepeatChar".} =