## 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()