Files
Nim/tools/enumgen.nim
Copilot 47d3fb28bd Resolve merge conflicts with devel branch refactoring (#25423)
The PR branch had merge conflicts with `devel` due to a major compiler
refactoring that extracted type definitions from `compiler/ast.nim` into
a new `compiler/astdef.nim` file.

## Changes

- Resolved conflict in `compiler/ast.nim` by accepting `devel`'s
refactored structure
- Merged 763 commits from `devel` branch (commit range:
`ce6a345..b3273e7`)
- Preserved original PR changes removing deprecated symbols from
`lib/core/macros.nim`

The core PR functionality (removal of deprecated macros API since
v0.18.1) remains intact while incorporating the upstream AST
refactoring.

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
2026-01-09 20:06:36 +08:00

302 lines
8.5 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"),
("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"),
("nkStmtList", "stmts"),
("nkDotExpr", "dot"),
("nkBracketExpr", "at"),
("tyNone", "n0"), # we always use a digit for type kinds so there can be no overlap with node kinds
("tyBool", "b0"),
("tyChar", "c0"),
("tyEmpty", "e0"),
("tyAlias", "a0"),
("tyNil", "n1"),
("tyUntyped", "U0"),
("tyTyped", "t0"),
("tyTypeDesc", "t1"),
("tyGenericInvocation", "g0"),
("tyGenericBody", "g1"),
("tyGenericInst", "g2"),
("tyGenericParam", "g4"),
("tyDistinct", "d0"),
("tyEnum", "e1"),
("tyOrdinal", "o0"),
("tyArray", "a1"),
("tyObject", "o1"),
("tyTuple", "t2"),
("tySet", "s0"),
("tyRange", "r0"),
("tyPtr", "p0"),
("tyRef", "r1"),
("tyVar", "v0"),
("tySequence", "s1"),
("tyProc", "p1"),
("tyPointer", "p2"),
("tyOpenArray", "o3"),
("tyString", "s2"),
("tyCstring", "c1"),
("tyForward", "F0"),
("tyInt", "i0"),
("tyInt8", "i1"),
("tyInt16", "i2"),
("tyInt32", "i3"),
("tyInt64", "i4"),
("tyFloat", "f0"),
("tyFloat32", "f1"),
("tyFloat64", "f2"),
("tyFloat128", "f3"),
("tyUInt", "u0"),
("tyUInt8", "u1"),
("tyUInt16", "u2"),
("tyUInt32", "u3"),
("tyUInt64", "u4"),
("tyOwned", "o2"),
("tySink", "s3"),
("tyLent", "L0"),
("tyVarargs", "v1"),
("tyUncheckedArray", "U1"),
("tyError", "e2"),
("tyBuiltInTypeClass", "b1"),
("tyUserTypeClass", "U2"),
("tyUserTypeClassInst", "U3"),
("tyCompositeTypeClass", "c2"),
("tyInferred", "I0"),
("tyAnd", "a2"),
("tyOr", "o4"),
("tyNot", "n2"),
("tyAnything", "a3"),
("tyStatic", "s4"),
("tyFromExpr", "F1"),
("tyConcept", "c3"),
("tyVoid", "v2"),
("tyIterable", "I1")
]
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()