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...

Reply via email to