www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - reflective enums part 2

This is the reflective enums stuff rewritten for DMD 1.006.  It's not 
much shorter than the previous version, but a lot of it is replacements 
for basic string stuff that is not 'compile timeable'.  The last version 
used std.metastrings, but that code is not usable here.

I didn't make these functions very general because I assume that either 
libraries of compile-timeable stuff will appear, or (more likely) a few 
of the restrictions on what can be done at compile time will disappear, 
making std.strings 'just work' at compile time.

Right now it looks like the only allocations CFTE code can do is string 
concatenation (I think Walter mentioned this in the announcement).

Kevin

// -*- c++ -*-

import std.stdio;
import std.string;
import std.file;

// For DMD 1.006

// Reusable functions

char[] split_delim(char[] A, char delim, bool second)
{
     for(int i = 0; i < A.length; i++) {
         if (A[i] == delim) {
             return second ? A[i+1..$] : A[0..i];
         }
     }
     return second ? "" : A;
}

char[] trim(char[] A, char delim)
{
     while(A.length && A[0] == delim)   A = A[1..$];
     while(A.length && A[$-1] == delim) A = A[0..$-1];
     return A;
}

char[] numberToString(T)(T x)
{
     if (x == 0)
         return "0";

     char[] rv, neg = (x < 0) ? "-" : "";

     if (neg.length)
         x = -x;

     while(x > 0) {
         int d = x % 10;
         rv = ("0123456789"[d..d+1]) ~ rv;
         x = x / 10;
     }

     return neg ~ rv;
}

int stringToNumber(char[] x)
{
     int sign = (x[0] == '-') ? 1 : 0;
     long mult = 1, denom = 0;

     for(int i = x.length-1; i >= sign; i--) {
         assert(x[i] >= '0' && x[i] <= '9');
         denom += (x[i] - '0') * mult;
         mult *= 10;
     }

     return sign ? -denom : denom;
}

char[] format2(char[] fmt, char[] f0, char[] f1)
{
     char[] rv;
     bool slash = false;

     foreach(i, ch; fmt) {
         if (slash) {
             slash = false;
             switch(ch) {
             case '0': rv ~= f0; break;
             case '1': rv ~= f1; break;
             default: rv ~= ch;
             }
         } else {
             slash = (ch == '/');
             if (! slash)
                 rv ~= fmt[i..i+1];
         }
     }

     return rv;
}

// Task-specific functions:

char[] format_enum_list(T = int)(char[] fmt, char[] A, T start = 0)
{
     char[] code;
     T V = start;

     while(A.length) {
         char[] front = trim(split_delim(A,     ',', false), ' ');
         char[] ename = trim(split_delim(front, '=', false), ' ');
         char[] value = trim(split_delim(front, '=', true),  ' ');

         if (value.length)
             V = stringToNumber(value);

         code ~= format2(fmt, ename, numberToString(V));

         A = split_delim(A, ',', true);
         V = V + 1;
     }

     return code;
}

struct Enum(char[] A, T = int) {
     const char[] def2_ = format_enum_list!(T)("const enum_t /0 = 
(/1);\n", A);
     const char[] def_ = "typedef "~T.stringof~" enum_t;\n"~def2_;
     mixin(def_);

     static char[] getString(T x)
     {
         const char[] cases = format_enum_list!(T)("case /1: return 
\"/0\";\n", A);
         mixin("switch(x) {\n"~ cases~ "default: break;}");
         throw new Exception("enumeration out of range");
     }

     int opApply(int delegate(inout T) dg)
     {
         int i_, rv_;
         const char[] applies =
             format_enum_list!(T)("i_=/1; rv_ = dg(i_); if (rv_) return 
rv_;\n", A);
         mixin(applies);
         return rv_;
     }
}

int main(char[][] args)
{
     alias Enum!("start, middle, end=10, ps, pps") PState;

     int p = PState.middle;

     writefln("p is %s, with name %s\n", p, PState.getString(p));

     PState P;
     foreach(v; P) {
         writefln("Enum %s has name=%s", v, PState.getString(v));
     }

     alias Enum!("up, down, charmed, strange, top, bottom") Enum2;

     Enum2.enum_t eggbert = Enum2.charmed;

     // Note: this would be a syntax error because the two
     // enums are distinct types and are not interchangeable
     // without a cast (intentionally).
     //PState.enum_t middle = eggbert;

     writefln("%s\n", P.enum_t.stringof);

     return 0;
}
Feb 17 2007