Code Features

Download source code.


import std.stdio;
import std.string;

// Type from string

// Usage: 
// ToType!("int") x;
template ToType (string s)
{
    alias ToTypeHelper!(s).type ToType;
}

template ToTypeHelper (string s)
{
    mixin ("alias " ~ s ~ " type;");
}

//-----------------
// Array operations
//-----------------

// Given ["a", "b"] produces [["a"], ["b"]]
string [][] toArrays (string [] strings)
{
    string [][] result = [];
    foreach (s; strings)
        result ~= [s];
    return result;
}

// Concatenate strings
string ctConcat (string [] arr)
{
    string result = "";
    foreach (s; arr)
        result ~= s;
    return result;
}

// Concatenate strings with a separator
string ctConcat (string [] arr, char sep)
{
    assert (arr.length != 0);
    string result = "";
    foreach (s; arr)
        result ~= s ~ sep;
    return result [0..$-1]; // remove the trailing sep
}

// Compile-time insertion sort
string [] ctSort (string [] arr)
{
    if (arr.length == 0)
        return arr;
    else
        return ctInsert (ctSort (arr [0..$-1]), arr[$-1]);
}

// Insert a string into a sorted array
string [] ctInsert (string [] arr, string str)
{
    int i = 0;
    while (i < arr.length && arr[i] < str)
        ++i;
    return arr [0..i] ~ str ~ arr [i..$];
}

// Return set difference of sorted arrays
string [] ctDiff (string [] a1, string [] a2)
{
    if (a1.length == 0)
        return [];
    else if (a2.length == 0)
        return a1;
    else if (a1[$-1] == a2 [$-1])
        return ctDiff (a1 [0..$-1], a2 [0..$-1]);
    else if (a1[$-1] > a2 [$-1])
        return ctDiff (a1 [0..$-1], a2) ~ a1 [$-1];
    else
        return ctDiff (a1, a2[0..$-1]);
}

// Given ["a", "b", "c"] produces
// [["a"], ["a", "b"], ["a", "b", "c"]
string [][] prefixes (string [] a)
{
    string [][] result = [];
    for (int i = 1; i <= a.length; ++i)
        result ~= a [0..i];
    return result;
}

// Given ["a", "b", "c"] and len = 2
// produces [["a", "b"], ["a", "c"], ["b", "c"]]
string [][] combinations (int len, string [] arr)
{
    if (len == 0)
        return [[]];

    string [][] result = [];
    // generate all prefixes
    foreach (string [] t; prefixes (arr))
    {
        // split (non-empty) prefix at the penultimate position
        string [] beg = t [0..$-1];
        string fin = t [$-1];
        // recurse with the beginning of the prefix
        foreach (string [] shortCmb; combinations (len - 1, beg))
            result ~= [shortCmb ~ fin];
    }
    return result;
}

// Given ["a", "b", "c"] produces
// [["a"], ["b"], ["c"], 
//  ["a", "b"], ["a", "c"], ["b", "c"],
//  ["a", "b", "c"]]
string [][] allCombinations (string [] arr)
{
    string [][] result = [];
    for (int i = 1; i <= arr.length; ++i)
        result ~= combinations (i, arr);
    return result;
}

// Function features
// The goal is to create the following hierarchy from ["A", "B", "C"]
//
//  interface A_B_C {}
//
//  interface A_B: A_B_C {}
//  interface A_C: A_B_C {}
//  interface B_C: A_B_C {}
//
//  interface A: A_B, A_C {}
//  interface B: A_B, B_C {}
//  interface C: A_C, B_C {}
//
//  interface NoFeatures: A, B, C {}


// Prepares a list of base classes (in the array of arrays form)
// by inserting elements of "missing" into the array of "present"
// where "present" are the atoms in the derived interface
// Called with ["b"] ["a", "c"]
// returns [["a", "b"], ["a", "c"]]
string [][] listBases (string [] present, string [] omitted)
{
    string [][] result = [];
    foreach (o; omitted)
        result ~= ctInsert (present, o);
    return result;
}

// Given [["a", "b"], ["a", "c"], ["b", "c"]] produces
// ": a_b, a_c, b_c
string concatBases (string [][] bases)
{
    if (bases.length == 0)
        return "";

    string result = ": ";
    foreach (a; bases)
        result ~= ctConcat (a, '_') ~ ",";
    return result [0..$-1]; // remove the trailing comma
}

// Creates a declaration of the hierarchy of interfaces
string declareAllFeatures (string [] allFeatures)
{
    string [] all = ctSort (allFeatures);
    string result;
    foreach (c; allCombinations (all))
    {
        result ~= "interface " ~ ctConcat (c, '_');
        string [][] bases = listBases (c, ctDiff (all, c));
        result ~= concatBases (bases);
        result ~= " {}\n";
    }
    result ~= "interface NoFeatures" ~ concatBases (toArrays (all)) ~ " {}\n"; 
    return result;
}

// Creates an interface name corresponing to a list of atoms
// Given ["B", "A"] producese "A_B", or "NoFeatures" for an empty list
string MakeFeatures (string[] a)
{
    if (a.length == 0)
        return "NoFeatures";
    else
        return ctConcat (ctSort (a), '_');
}

//------
// Usage
//------

// Declare the whole hierarchy of function features
mixin (declareAllFeatures (["NoEx", "Tested", "Portable"]));

// foo guarantees Portable and NoEx
void foo (ToType!(MakeFeatures (["Portable", "NoEx"])) x)
{
    writeln ("Overload: Portable, NoEx");
}

// foo guarantees Tested, Portable, and NoEx
void foo (ToType!(MakeFeatures (["Tested", "Portable", "NoEx"])) x)
{
    writeln ("Overload: Tested, Portable, NoEx");
}

// bar guarantees Tested
void bar (ToType!(MakeFeatures (["Tested"])) x)
{
    writeln ("Tested called");
    foo (x);
}

/* This will soon work
class Base
{
public:
    void M (ToType!(MakeFeatures (["Tested"])))
    {
        writeln ("Base class method");
    }
}

class Derived
{
public:
    override void M (ToType!(MakeFeatures (["Tested", "Portable"])))
    {
        writeln ("Derived class method");
    }
}
*/

void main ()
{
    // Runtime tests:
    // writeln (prefixes (["a", "b", "c"]));
    // writeln (combinations (2, ["a", "b", "c"]));
    // writeln (allCombinations (["a", "b", "c", "d"]));
    writeln ("---This is the declaration produced by declareAllFeatures---");
    writeln (declareAllFeatures (["Tested", "Portable"]));

    writeln ("---Testing function features---");
    // Create various requirements
    ToType!(MakeFeatures ([])) n;  // Don't require anything
    ToType!(MakeFeatures (["Portable"])) p;
    ToType!(MakeFeatures (["Tested"])) t;
    ToType!(MakeFeatures (["Portable", "Tested"])) pt;

    foo (p); // calls foo that guarantees Portable and NoEx
    foo (n); // calls foo that guarantees Portable and NoEx
    foo (t); // calls foo that guarantees Tested, Portable, and NoEx
    
    // bar (pt);
    // function features.bar (Tested) does not match parameter types (Portable_Tested)    
    // bar (p);
    // function features.bar (Tested) does not match parameter types (Portable)
    bar (t);
    /*
    // Virtual function override
    Base b = new Base;
    Derived d = new Derived;
    b.M (t);
    d.M (pt);
    */
}