Files
Nim/tools/enumgen.nim
Andreas Rumpf cbb2fe0a63 IC: progress (#25344)
Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
2025-12-11 18:22:38 +01:00

248 lines
7.3 KiB
Nim

## Generate effective NIF representation for `Enum`
import ".." / compiler / [astdef, options]
import std / [syncio, assertions, strutils, tables]
# We need to duplicate this type here as ast.nim's version of it does not work
# as it sets the string values explicitly breaking our logic...
type
TCallingConventionMirror = enum
ccNimCall
ccStdCall
ccCDecl
ccSafeCall
ccSysCall
ccInline
ccNoInline
ccFastCall
ccThisCall
ccClosure
ccNoConvention
ccMember
const
SpecialCases = [
("nkCommand", "cmd"),
("nkIfStmt", "if"),
("nkError", "err"),
("nkType", "onlytype"),
("nkTypeSection", "type"),
("tySequence", "seq"),
("tyVar", "mut"),
("tyProc", "proctype"),
("tyUncheckedArray", "uarray"),
("nkExprEqExpr", "vv"),
("nkExprColonExpr", "kv"),
("nkDerefExpr", "deref"),
("nkReturnStmt", "ret"),
("nkBreakStmt", "brk"),
("nkStmtListExpr", "expr"),
("nkEnumFieldDef", "efld"),
("nkNilLit", "nil"),
("ccNoConvention", "noconv"),
("mExpr", "exprm"),
("mStmt", "stmtm"),
("mEqNimrodNode", "eqnimnode"),
("mPNimrodNode", "nimnode"),
("mNone", "nonem"),
("mAsgn", "asgnm"),
("mOf", "ofm"),
("mAddr", "addrm"),
("mType", "typem"),
("mStatic", "staticm"),
("mRange", "rangem"),
("mVar", "varm"),
("mInSet", "contains"),
("mNil", "nilm"),
("tyBuiltInTypeClass", "bconcept"),
("tyUserTypeClass", "uconcept"),
("tyUserTypeClassInst", "uconceptinst"),
("tyCompositeTypeClass", "cconcept"),
("tyGenericInvocation", "ginvoke"),
("tyGenericBody", "gbody"),
("tyGenericInst", "ginst"),
("tyGenericParam", "gparam"),
("nkStmtList", "stmts"),
("nkDotExpr", "dot"),
("nkBracketExpr", "at")
]
SuffixesToReplace = [
("Section", ""), ("Branch", ""), ("Stmt", ""), ("I", ""),
("Expr", "x"), ("Def", "")
]
PrefixesToReplace = [
("Length", "len"),
("SetLength", "setlen"),
("Append", "add")
]
AdditionalNodes = [
"nf", # "node flag"
"tf", # "type flag"
"sf", # "sym flag"
"htype", # annotated with a hidden type
"missing"
]
proc genEnum[E](f: var File; enumName: string; known: var OrderedTable[string, bool]; prefixLen = 2) =
var mappingA = initOrderedTable[string, E]()
var cases = ""
for e in low(E)..high(E):
var es = $e
if es.startsWith("nkHidden"):
es = es.replace("nkHidden", "nkh") # prefix will be removed
else:
for (suffix, repl) in items SuffixesToReplace:
if es.len - prefixLen > suffix.len and es.endsWith(suffix):
es.setLen es.len - len(suffix)
es.add repl
break
for (suffix, repl) in items PrefixesToReplace:
if es.len - prefixLen > suffix.len and es.substr(prefixLen).startsWith(suffix):
es = es.substr(0, prefixLen-1) & repl & es.substr(prefixLen+suffix.len)
break
let s = es.substr(prefixLen)
var done = false
for enu, key in items SpecialCases:
if $e == enu:
assert(not mappingA.hasKey(key))
if known.hasKey(key): echo "conflict: ", key
known[key] = true
assert key.len > 0
mappingA[key] = e
cases.add " of " & $e & ": " & escape(key) & "\n"
done = true
break
if not done:
let key = s.toLowerAscii
if not mappingA.hasKey(key):
assert key.len > 0, $e
if known.hasKey(key): echo "conflict: ", key
known[key] = true
mappingA[key] = e
cases.add " of " & $e & ": " & escape(key) & "\n"
done = true
if not done:
var d = 0
while d < 10:
let key = s.toLowerAscii & $d
if not mappingA.hasKey(key):
assert key.len > 0
mappingA[key] = e
cases.add " of " & $e & ": " & escape(key) & "\n"
done = true
break
inc d
if not done:
echo "Could not map: " & s
#echo mapping
var code = ""
code.add "proc toNifTag*(s: " & enumName & "): string =\n"
code.add " case s\n"
code.add cases
code.add "\n\n"
let procname = "parse" # & enumName.substr(1)
code.add "proc " & procname & "*(t: typedesc[" & enumName & "]; s: string): " & enumName & " =\n"
code.add " case s\n"
for (k, v) in pairs mappingA:
code.add " of " & escape(k) & ": " & $v & "\n"
code.add " else: " & $low(E) & "\n\n\n"
f.write code
proc genEnum[E](f: var File; enumName: string; prefixLen = 2) =
var known = initOrderedTable[string, bool]()
genEnum[E](f, enumName, known, prefixLen)
proc genFlags[E](f: var File; enumName: string; prefixLen = 2) =
var mappingA = initOrderedTable[string, E]()
var mappingB = initOrderedTable[string, E]()
var cases = ""
for e in low(E)..high(E):
let s = ($e).substr(prefixLen)
var done = false
for c in s:
if c in {'A'..'Z'}:
let key = $c.toLowerAscii
if not mappingA.hasKey(key):
mappingA[key] = e
cases.add " of " & $e & ": dest.add " & escape(key) & "\n"
done = true
break
if not done:
var d = 0
while d < 10:
let key = $s[0].toLowerAscii & $d
if not mappingB.hasKey(key):
mappingB[key] = e
cases.add " of " & $e & ": dest.add " & escape(key) & "\n"
done = true
break
inc d
if not done:
quit "Could not map: " & s
#echo mapping
var code = ""
code.add "proc genFlags*(s: set[" & enumName & "]; dest: var string) =\n"
code.add " for e in s:\n"
code.add " case e\n"
code.add cases
code.add "\n\n"
code.add "proc parse*(t: typedesc[" & enumName & "]; s: string): set[" & enumName & "] =\n"
code.add " result = {}\n"
code.add " var i = 0\n"
code.add " while i < s.len:\n"
code.add " case s[i]\n"
for c in 'a'..'z':
var letterFound = false
var digitsFound = 0
for d in '0'..'9':
if mappingB.hasKey($c & $d):
if not letterFound:
letterFound = true
code.add " of '" & c & "':\n"
if digitsFound == 0:
code.add " if"
else:
code.add " elif"
inc digitsFound
code.add " i+1 < s.len and s[i+1] == '" & d & "':\n"
code.add " result.incl " & $mappingB[$c & $d] & "\n"
code.add " inc i\n"
if mappingA.hasKey($c):
if digitsFound == 0:
code.add " of '" & c & "': "
else:
code.add " else: "
code.add "result.incl " & $mappingA[$c] & "\n"
code.add " else: discard\n"
code.add " inc i\n\n"
f.write code
var f = open("compiler/ic/enum2nif.nim", fmWrite)
f.write "# Generated by tools/enumgen.nim. DO NOT EDIT!\n\n"
f.write "import \"..\" / [ast, options]\n\n"
# use the same mapping for TNodeKind and TMagic so that we can detect conflicts!
var nodeTags = initOrderedTable[string, bool]()
for a in AdditionalNodes:
nodeTags[a] = true
genEnum[TNodeKind](f, "TNodeKind", nodeTags)
genEnum[TSymKind](f, "TSymKind")
genEnum[TTypeKind](f, "TTypeKind")
genEnum[TLocKind](f, "TLocKind", 3)
genEnum[TCallingConventionMirror](f, "TCallingConvention", 2)
genEnum[TMagic](f, "TMagic", nodeTags, 1)
genEnum[TStorageLoc](f, "TStorageLoc")
genEnum[TLibKind](f, "TLibKind")
genFlags[TSymFlag](f, "TSymFlag")
genFlags[TNodeFlag](f, "TNodeFlag")
genFlags[TTypeFlag](f, "TTypeFlag")
genFlags[TLocFlag](f, "TLocFlag")
genFlags[TOption](f, "TOption", 3)
f.close()