Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement inlineimport #14

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions runtestsbetterc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
dmd -unittest -debug -g -betterC -I=source -i -run testsbetterc/*
154 changes: 154 additions & 0 deletions source/taurus/inlineimport.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
module taurus.inlineimport;

/**
Imports a symbol from a module inline. Useful for module dependencies with little
usage.

Checks are done in every symbol, meaning the sequence will fail on the first
invalid symbol. $(BR)
`From` should be used When compiled in **betterC**. Because `from` checks valid
symbols from the start, the program fails if some module depends on something
that isn't supported in **betterC**.
- The following do not compile under `betterC`:
- **`from.std.traits.isIntegral!byte`**
- **`from.std.isIntegral!byte`**
- **`From!"std".isIntegral!byte`**

- The correct way to import is to specify the starting point with `From`:
- **`From!"std.traits".isIntegral!byte`**

Params: mod = module to search from.

Examples:
---
assert(from.std.traits.isIntegral!byte);
assert(From!"std.array".split("Hello.World", ".") == ["Hello", "World"]);

assert(!__traits(compiles, from.not.exists)); // fails at 'not'
---
*/
enum From(string mod) = FromImpl!mod.init;
enum from = From!null; ///

version (D_BetterC) {} else
/// disabled in betterC due to the usage of dynamic arrays
@safe pure nothrow unittest
{
assert(from.std.array.split("Hello.World", ".") == ["Hello", "World"]);
assert(From!"std.array".split("Hello.World", ".") == ["Hello", "World"]);

assert(from.std.split("Hello.World", ".") == ["Hello", "World"]);
assert(From!"std".split("Hello.World", ".") == ["Hello", "World"]);
}

///
@safe pure nothrow @nogc unittest
{
assert(From!"std.math".abs(-1) == 1);
assert(From!"std.algorithm".max(0, 2, 1) == 2);
}

///
@safe pure nothrow @nogc unittest
{
assert(!__traits(compiles, { cast(void) from._does.not.exist; }));
assert(!__traits(compiles, { cast(void) From!"std.algorithm"._; }));
assert(!__traits(compiles, { cast(void) From!"std.math.abs"(-1); }));
assert(!__traits(compiles, { assert(From!"std.math.abs(-1)" == 1); }));
}

private struct FromImpl(string mod)
{
/*
Returns `FromImpl` if `mod.sym` or `sym` are modules, `aliases to sym` if is a
valid symbol or `fails`
*/
template opDispatch(string sym)
{
/*
order is important:
- mod is empty -> enum FromImpl!sym
- mod.sym is valid -> enum FromImpl!mod.sym
- mod : sym is valid -> alias sym
*/

static if (!mod.length)
{
static if(isModule!sym)
enum opDispatch = FromImpl!sym.init;
else
alias opDispatch = fail!("Unable to locate module '" ~ sym ~ "'");
}

else static if (isModule!(mod, ".", sym))
enum opDispatch = FromImpl!(mod ~ "." ~ sym).init;

else
{
static if(inModule)
mixin("import ", mod, ":", sym, "; alias opDispatch = ", sym, ";");
else
alias opDispatch = fail!("Symbol '" ~ sym ~ "' not found in module '" ~ mod ~ "'");
}

private enum inModule = __traits(compiles, { mixin("import ", mod, ":", sym, ";"); });
}

/*
Helps with code like:
- from(...)
- from.std(...)
- from.std.algorithm(...)
- From!""(...)
- From!"std"(...)
- From!"std.algorithm"(...)
- From!"std.math.abs"(...)
*/
template opCall(Args ...)
{
static if (!ModuleCount!mod)
alias opCall = fail!("Invalid module '"~ mod ~ "' syntax call.");

else static if (ModuleCount!mod == 1 || isModule!mod)
alias opCall = fail!("Missing symbol from module '" ~ mod ~ "'!");

else
alias opCall = fail!(`Call must be bound to a symbol! Do you mean 'From!"`
~ mod[0 .. StaticIndexOfLastDot!mod] ~ `".`
~ mod[StaticIndexOfLastDot!mod + 1 .. $] ~ `'?`);

// counts possible modules based on '.'
private template ModuleCount(string s)
{
static if (!s.length) enum ModuleCount = 0;
else static if(StaticIndexOfLastDot!s != -1)
{
enum i = StaticIndexOfLastDot!s;

static if (i == 0 || i + 1 == s.length) enum ModuleCount = 0;
else enum ModuleCount = 1 + ModuleCount!(s[0 .. i - 1]);
}
else enum ModuleCount = 1;
}

// gets the index of the last '.' or -1 if none
private template StaticIndexOfLastDot(string s)
{
enum StaticIndexOfLastDot = {
static foreach_reverse(i, c; s)
// `if (__ctfe)` is redundant here but avoids the "Unreachable code" warning.
static if (c == '.') if(__ctfe) return i;
return -1;
}();
}
}

// allows to display the static assert error message
private template fail(string msg)
{
noreturn fail(T ...)(auto ref T t) { static assert (false, msg); }
}

// check if `T` is importable
private enum isModule(T ...) = __traits(compiles, { mixin("import ", T, ";"); });
}
17 changes: 17 additions & 0 deletions testsbetterc/inlineimport.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module testsbetterc.enumify;

version(D_BetterC) {} else static assert(false, "Must compile with -betterC");
version(unittest) {} else static assert(false, "Must compile with -unittest");

extern(C) int main() {
import core.stdc.stdio : puts, printf;
import taurus.inlineimport;

static foreach (test; __traits(getUnitTests, __traits(parent, from))) {
printf("Running %.*s\n", cast(int) test.stringof.length, test.stringof.ptr);
test();
}

puts("Tests ran successfully.");
return 0;
}