btw, since I don't want to write the return myself, I persuaded the compiler to
do it for me
import macros
from strutils import `%`
macro Enum*(typeName, body) : untyped =
let enumVar = newIdentNode("e")
let strVar = newIdentNode("s")
var caseConverterStatement = nnkCaseStmt.newTree(
strVar
)
let converterDecl = nnkConverterDef.newTree(
newIdentNode("to" & $typename.ident),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
typename,
nnkIdentDefs.newTree(
strVar,
newIdentNode("string"),
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkstmtList.newTree(
caseConverterStatement
)
)
let converterRaise = nnkElse.newTree(
nnkstmtList.newTree(
nnkRaiseStmt.newTree(
nnkCall.newTree(
newIdentNode("newException"),
newIdentNode("ValueError"),
nnkInfix.newTree(
bindSym("%"),
newStrLitNode("could not convert string '$#' to " &
$typename.ident),
nnkBracket.newTree(
strVar
)
)
)
)
)
)
let makeConverterBranch = proc(typeName, enumName : NimNode, enumStr =
newEmptyNode()) : NimNode =
nnkOfbranch.newtree(
if enumStr.kind() == nnkStrLit: enumStr else:
newStrLitNode($enumName.ident),
nnkstmtList.newTree(
nnkReturnStmt.newTree(
nnkDotExpr.newTree(
typeName,
enumName
)
)
)
)
let makeDollarBranch = proc(typeName, enumName : NimNode, enumStr =
newEmptyNode()) : NimNode =
nnkOfbranch.newtree(
nnkDotExpr.newTree(
typename,
enumName
),
nnkstmtList.newTree(
if enumStr.kind() == nnkStrLit: enumStr else:
newStrLitNode($enumName.ident)
)
)
var caseDollarStatement = nnkCaseStmt.newTree(
enumVar
)
let dollarDecl = nnkProcDef.newTree(
nnkAccQuoted.newTree(
newIdentNode("$"),
),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newIdentNode("string"),
nnkIdentDefs.newtree(
enumVar,
typeName,
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkstmtList.newTree(
caseDollarStatement
)
)
var enumNames = nnkEnumTy.newTree(newEmptyNode())
let enumDecl = nnkTypeSection.newtree(
nnkTypeDef.newtree(
typeName, newEmptyNode(), enumNames
)
)
for child in body:
case child.kind:
of nnkIdent:
enumNames.add(child)
caseDollarStatement.add(makeDollarBranch(typeName, child))
caseConverterStatement.add(makeConverterBranch(typeName,child))
of nnkAsgn:
enumNames.add(child[0])
caseDollarStatement.add(makeDollarBranch(typeName,
child[0], child[1]))
caseConverterStatement.add(makeConverterBranch(typeName,child[0], child[1]))
else:
discard
caseConverterStatement.add(converterRaise)
result = nnkstmtList.newTree(
enumDecl,
dollarDecl,
converterDecl
)
################# Usage #########################
Enum(Roles):
user; manager
catering = "whatever"
sysadmin
block:
let user = Roles.user
let manager = Roles.manager
let catering = Roles.catering
echo user
echo manager
echo catering
var u = Roles("user")
echo u == Roles.user
u = Roles("user2")
this language is damn great...