mirror of https://gitlab.com/basile.b/dexed.git
166 lines
4.4 KiB
D
166 lines
4.4 KiB
D
module imports;
|
|
|
|
import
|
|
core.stdc.string;
|
|
import
|
|
std.algorithm, std.array, std.file, std.functional;
|
|
import
|
|
iz;
|
|
import
|
|
dparse.lexer, dparse.ast, parser, dparse.rollback_allocator;
|
|
import
|
|
common;
|
|
|
|
private alias moduleDeclarationToText = (ModuleDeclaration md) => md.moduleName
|
|
.identifiers
|
|
.map!(a => a.text)
|
|
.join(".");
|
|
|
|
struct Result
|
|
{
|
|
string[] data;
|
|
}
|
|
|
|
/**
|
|
* Lists the modules imported by a module
|
|
*
|
|
* On the first line writes the module name or # between double quotes then
|
|
* each import is written on a new line. Import detection is not accurate,
|
|
* the imports injected by a mixin template or by a string variable are not detected,
|
|
* the imports deactivated by a static condition neither.
|
|
*
|
|
* The results are used by to automatically detect the static libraries used by a
|
|
* dexed runnable module.
|
|
*/
|
|
export extern(C) string[]* listImports(const(char)* src)
|
|
{
|
|
string[]* result = &(new Result).data;
|
|
LexerConfig config;
|
|
RollbackAllocator rba;
|
|
StringCache sCache = StringCache(StringCache.defaultBucketCount);
|
|
|
|
scope mod = src[0 .. src.strlen]
|
|
.getTokensForParser(config, &sCache)
|
|
.parseModule("", &rba, &ignoreErrors);
|
|
if (auto md = mod.moduleDeclaration)
|
|
*result ~= '"' ~ moduleDeclarationToText(md) ~ '"';
|
|
else
|
|
*result ~= "\"#\"";
|
|
|
|
ImportLister il = construct!(ImportLister)(result);
|
|
scope (exit) destruct(il);
|
|
|
|
il.visit(mod);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Lists the modules imported by several modules
|
|
*
|
|
* The output consists of several consecutive lists, as formated for
|
|
* listImports. When no moduleDeclaration is available, the first line of
|
|
* a list matches the filename.
|
|
*
|
|
* The results are used by to build a key value store linking libraries to other
|
|
* libraries, which is part of dexed "libman".
|
|
*/
|
|
export extern(C) string[]* listFilesImports(const(char)* joinedFiles)
|
|
{
|
|
string[]* result = &(new Result).data;
|
|
RollbackAllocator rba;
|
|
StringCache sCache = StringCache(StringCache.defaultBucketCount);
|
|
LexerConfig config = LexerConfig("", StringBehavior.source);
|
|
ImportLister il = construct!(ImportLister)(result);
|
|
|
|
scope(exit)
|
|
{
|
|
destruct(il);
|
|
destroy(sCache);
|
|
destroy(rba);
|
|
}
|
|
|
|
foreach(fname; joinedFilesToFiles(joinedFiles))
|
|
{
|
|
scope mod = readText(fname)
|
|
.getTokensForParser(config, &sCache)
|
|
.parseModule("", &rba, &ignoreErrors);
|
|
if (auto md = mod.moduleDeclaration)
|
|
*result ~= '"' ~ moduleDeclarationToText(md) ~ '"';
|
|
else
|
|
*result ~= '"' ~ cast(string)fname ~ '"';
|
|
|
|
il.visit(mod);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static assert(!MustAddGcRange!ImportLister);
|
|
|
|
@TellRangeAdded
|
|
private final class ImportLister: ASTVisitor
|
|
{
|
|
alias visit = ASTVisitor.visit;
|
|
size_t mixinDepth;
|
|
@NoGc string[]* results;
|
|
|
|
this(string[]* results)
|
|
{
|
|
assert(results);
|
|
this.results = results;
|
|
}
|
|
|
|
override void visit(const(Module) mod)
|
|
{
|
|
mixinDepth = 0;
|
|
mod.accept(this);
|
|
}
|
|
|
|
override void visit(const ConditionalDeclaration decl)
|
|
{
|
|
bool acc = true;
|
|
if (decl.compileCondition)
|
|
{
|
|
const ver = decl.compileCondition.versionCondition;
|
|
if (ver && ver.token.text in badVersions)
|
|
acc = false;
|
|
}
|
|
if (acc)
|
|
decl.accept(this);
|
|
}
|
|
|
|
override void visit(const(ImportDeclaration) decl)
|
|
{
|
|
foreach (const(SingleImport) si; decl.singleImports)
|
|
{
|
|
if (!si.identifierChain.identifiers.length)
|
|
continue;
|
|
*results ~= si.identifierChain.identifiers.map!(a => a.text).join(".");
|
|
}
|
|
if (decl.importBindings) with (decl.importBindings.singleImport)
|
|
*results ~= identifierChain.identifiers.map!(a => a.text).join(".");
|
|
}
|
|
|
|
override void visit(const(MixinExpression) mix)
|
|
{
|
|
++mixinDepth;
|
|
mix.accept(this);
|
|
--mixinDepth;
|
|
}
|
|
|
|
override void visit(const PrimaryExpression primary)
|
|
{
|
|
if (mixinDepth && primary.primary.type.isStringLiteral)
|
|
{
|
|
assert(primary.primary.text.length > 1);
|
|
|
|
size_t startIndex = 1;
|
|
startIndex += primary.primary.text[0] == 'q';
|
|
auto il = parseAndVisit!(ImportLister)(primary.primary.text[startIndex..$-1], results);
|
|
destruct(il);
|
|
}
|
|
primary.accept(this);
|
|
}
|
|
}
|
|
|