view src/docgen/config/reflection.d @ 806:bcb74c9b895c

Moved out files in the trunk folder to the root.
author Aziz K?ksal <aziz.koeksal@gmail.com>
date Sun, 09 Mar 2008 00:12:19 +0100
parents trunk/src/docgen/config/reflection.d@cb8edb09108a
children
line wrap: on
line source

/**
 * Author: Jari-Matti Mäkelä
 * License: GPL3
 */
module docgen.config.reflection;

import docgen.misc.meta;
import docgen.misc.options;

////
//
// Macros for reading input
//
////

char[] _wrong(char[] key) {
  return `if (val.length != 1) throw new Exception(
    "Wrong number of arguments for `~key~`");`;
}

char[] _switch(char[] stuff) {
  return "switch(key) {" ~ stuff ~ "}";
}

char[] _parseI(char[] key) {
  return `case "` ~ key ~ `":` ~ _wrong(key) ~ key ~
    `= Integer.parse(val[0]); continue;`;
}

char[] _parseS(char[] key) {
  return `case "` ~ key ~ `":` ~ _wrong(key) ~ key ~
    `= val[0]; continue;`;
}

char[] _parseB(char[] key) {
  return `case "` ~ key ~ `":` ~ _wrong(key) ~ key ~
    `= val[0] == "true" ? true : val[0] == "false" ? false : err(); continue;`;
}

char[] _parseList(char[] key) {
  return `case "` ~ key ~ `":foreach(v; val) ` ~
    key ~ `~= v; continue;`;
}

template _parseEnum_(bool list, char[] key, V...) {
  static if (V.length>1)
    const char[] _parseEnum_ =
      `case "` ~ V[0] ~ `":` ~ key ~ (list ? "~" : "") ~ `=` ~ V[1] ~ `; continue;` \n ~
      _parseEnum_!(list, key, V[2..$]);
  else
    const char[] _parseEnum_ = "";
}

template _parseEnum(char[] key, V...) {
  const char[] _parseEnum = `case "` ~ key ~
    `":` ~ _wrong(key) ~ `switch(val[0]) {` ~
    _parseEnum_!(false, key, V) ~
      `default: err(); } continue;`;
}

template _parseEnumList(char[] key, V...) {
  const char[] _parseEnumList = `case "` ~ key ~
    `":` ~ `foreach (item; val) switch(item) {` ~
    _parseEnum_!(true, key, V) ~
      `default: err(); } continue;`;
}

////
//
// Reflection for properties. This code will hopefully get better when the dmdfe bugs get fixed.
//
////

// dmdfe bug -- Seriously WTF?
char[] fixType(char[] type) {
  return type[$-1] == ' ' ? type[0..$-1] : type;
}

// take the last part of field name
char[] getLastName(char[] field) {
  if (field.length == 0) return "";
  int t = 0;
  // dmdfe bug: a while loop causes index out of bounds error
  for (int i=field.length-1; i >= 0; i--)
    if (field[i] == '.') { t = i+1; break; }
  return field[t..$];
}

// dmdfe bug: cannot return evalType alias
template _evalType(char[] type) {
  mixin("alias "~type~" value;");
}

// Note: stuple wrappers needed for tuples, otherwise:
// dmdfe bug: cc1d: ../.././gcc/d/dmd/expression.c:4865: virtual Expression* DotIdExp::semantic(Scope*): Assertion `0' failed.
template evalType(char[] type) {
  alias _evalType!(type).value evalType;
}

// wraps the reflected struct and enum data inside struct because otherwise the handling becomes impossibly hard
template getType(T) {
  static if(is(T == struct))
    alias Struct!(T) getType;
  else static if(is(T == enum))
    alias Enum!(T) getType;
  else static if(isEnumList!(T))
    alias Enum!(enumListType!(T), true) getType;
  else
    alias T getType;
}

template getTypes(alias S, int idx = S.tupleof.length) {
  static if(idx)
    alias Tuple!(getTypes!(S, idx-1), getType!(typeof(S.tupleof[idx-1]))) getTypes;
  else
    alias Tuple!() getTypes;
}

/**
 * Extracts the comma separated struct field names using .tupleof.stringof.
 * This is needed since the struct.tupleof[n].stringof is broken for enums and tuples in dmdfe.
 *
 * Bugs: handling of tuples
 */
char[] __getNames(char[] type) {
  char[] tmp;
  bool end = false;

  foreach(c; type[5..$]) {
    if (c != ' ' && c != '(' && c != ')' && end) tmp ~= c;
    if (c == ',') end = false;
    if (c == '.') end = true;
  }

  return tmp;
}

template _getNames(char[] str, T...) {
  static if (str.length) {
    static if (str[0] == ',')
      alias _getNames!(str[1..$], T, "") _getNames;
    else
      alias _getNames!(str[1..$], T[0..$-1], T[$-1] ~ str[0]) _getNames;
  } else
    alias T _getNames;
}

template getNames(char[] str) {
  alias _getNames!(__getNames(str), "") getNames;
}

struct Struct(alias T) {
  const type = "struct"; // used for pattern matching... apparently there's no other way
  const name = fixType(T.stringof); // dmdfe bug: trailing space
  alias STuple!(getNames!(T.tupleof.stringof)) names;
  alias STuple!(getTypes!(T)) types;
}

struct Enum(alias T, bool list = false) {
  const type = list ? "enumlist" : "enum"; // used for pattern matching... apparently there's no other way
  const name =  T.stringof[1..$]; // dmdfe bug: returns enum base type instead enum type
  alias evalType!("___"~name).tuple elements;
}

// determines the enumtype[] type
template isEnumList(T : T[]) {
  const isEnumList = T.stringof[0] == '_';
}

template isEnumList(T) {
  const isEnumList = false;
}

template enumListType(T : T[]) {
  alias T enumListType;
}

template enumListType(T) {
  static assert(false, "Not enum list type!");
}

char[] createIParser(char[] field) {
  return `_parseI("` ~ field ~ `") ~` \n;
}

char[] createBParser(char[] field) {
  return `_parseB("` ~ field ~ `") ~` \n;
}
 
char[] createSParser(char[] field) {
  return `_parseS("` ~ field ~ `") ~` \n;
}
 
char[] createLParser(char[] field) {
  return `_parseList("` ~ field ~ `") ~` \n;
}

char[] createEParser(char[] field, char[] contents) {
  return `_parseEnum!("` ~ field ~ `",` ~ contents ~ `) ~` \n;
}

char[] createELParser(char[] field, char[] contents) {
  return `_parseEnumList!("` ~ field ~ `",` ~ contents ~ `) ~` \n;
}

template _makeEnumString(char[] t, E...) {
  static if (E.length)
    const _makeEnumString = `"` ~ E[0] ~ `", "` ~ t ~ "." ~ E[0] ~ `",` ~
                            _makeEnumString!(t, E[1..$]);
  else
    const _makeEnumString = "";
}

// avoids the following dmdfe bugs:
//  - Error: elements is not a type (typeof(T).elements)
//  - Error: tuple is not a valid template value argument (T.elements where T is the complex type def)
template makeEnumString(char[] t, T) {
  const makeEnumString = _makeEnumString!(t, T.elements);
}

/**
 * Generates code for parsing data from the configuration data structure.
 */
template makeTypeString(int i, N, T, char[] prefix) {
  static assert(N.tuple.length == T.tuple.length);
  static if (i < N.tuple.length) {
    static if (is(T.tuple[i] == bool))
      const makeTypeString = createBParser(prefix ~ N.tuple[i]) ~ makeTypeString!(i+1, N, T, prefix);
    else static if (is(T.tuple[i] : int))
      const makeTypeString = createIParser(prefix ~ N.tuple[i]) ~ makeTypeString!(i+1, N, T, prefix);
    else static if (is(T.tuple[i] == char[]))
      const makeTypeString = createSParser(prefix ~ N.tuple[i]) ~ makeTypeString!(i+1, N, T, prefix);
    else static if (is(T.tuple[i] == char[][]))
      const makeTypeString = createLParser(prefix ~ N.tuple[i]) ~ makeTypeString!(i+1, N, T, prefix);
    else static if (is(T.tuple[i] == struct)) {
      static if (T.tuple[i].type == "struct")
        const makeTypeString = makeTypeString!(0, typeof(T.tuple[i].names),
                               typeof(T.tuple[i].types), prefix~N.tuple[i]~".") ~
                               makeTypeString!(i+1, N, T, prefix);
      else static if (T.tuple[i].type == "enum")
        const makeTypeString = createEParser(prefix ~ N.tuple[i],
                               makeEnumString!(T.tuple[i].name, T.tuple[i])[0..$-1]) ~
                               makeTypeString!(i+1, N, T, prefix);
      else static if (T.tuple[i].type == "enumlist")
        const makeTypeString = createELParser(prefix ~ N.tuple[i],
                               makeEnumString!(T.tuple[i].name, T.tuple[i])[0..$-1]) ~
                               makeTypeString!(i+1, N, T, prefix);
      else {
        const makeTypeString = "?" ~ makeTypeString!(i+1, N, T, prefix);
        static assert(false, "Unknown type");
      }
    } else {
      const makeTypeString = "?" ~ makeTypeString!(i+1, N, T, prefix);
      static assert(false, "Unknown type");
    }
  } else
    const makeTypeString = "";
}

template makeTypeStringForStruct(alias opt) {
  const makeTypeStringForStruct = makeTypeString!(0, opt.names, opt.types, opt.name~".")[0..$-2];
}

/* some leftovers 
template handleType(T, char[] prefix="") {
  static if(is(typeof(T) == struct)) {
    static if(T.type == "enum")
      // another dmdfe weirdness: T.stringof == "Enum!(Type)" here, but if do
      // alias T handleType;, the result.stringof is "struct Enum".
      alias T handleType;
  } else
    alias T handleType;
}
*/