mirror of https://gitlab.com/basile.b/dexed.git
456 lines
14 KiB
D
456 lines
14 KiB
D
module symlist;
|
|
|
|
import
|
|
core.stdc.string;
|
|
import
|
|
std.array, std.conv, std.json, std.format,
|
|
std.algorithm, std.string;
|
|
import
|
|
iz;
|
|
import
|
|
dparse.lexer, dparse.ast, parser, dparse.formatter : Formatter;
|
|
import
|
|
dparse.rollback_allocator;
|
|
import
|
|
common;
|
|
|
|
/**
|
|
* Visit and enumerate all the declaration of a module.
|
|
*
|
|
* Params:
|
|
* src = The module to visit, as source code.
|
|
* deep = Defines if the nested declarations are visited.
|
|
* Returns:
|
|
* The serialized symbols, as a C string.
|
|
*/
|
|
export extern(C) FpcArray!char* listSymbols(const(char)* src, bool deep)
|
|
{
|
|
Appender!(AstErrors) errors;
|
|
|
|
void handleErrors(string fname, size_t line, size_t col, string message, bool err)
|
|
{
|
|
errors ~= construct!(AstError)(cast(ErrorType) err, message, line, col);
|
|
}
|
|
|
|
LexerConfig config;
|
|
RollbackAllocator rba;
|
|
StringCache sCache = StringCache(StringCache.defaultBucketCount);
|
|
|
|
auto mod = src[0 .. src.strlen]
|
|
.getTokensForParser(config, &sCache)
|
|
.parseModule("", &rba, &handleErrors);
|
|
|
|
alias SL = SymbolListBuilder!(ListFmt.Pas);
|
|
SL sl = construct!(SL)(errors, deep);
|
|
scope(exit)
|
|
{
|
|
destruct(sl);
|
|
destroy(errors);
|
|
destroy(sCache);
|
|
destroy(rba);
|
|
}
|
|
|
|
sl.visit(mod);
|
|
return sl.serialize();
|
|
}
|
|
|
|
private:
|
|
|
|
enum ListFmt
|
|
{
|
|
Pas,
|
|
Json
|
|
}
|
|
|
|
enum SymbolType
|
|
{
|
|
_alias,
|
|
_class,
|
|
_enum,
|
|
_error,
|
|
_function,
|
|
_interface,
|
|
_import,
|
|
_mixin, // (template decl)
|
|
_struct,
|
|
_template,
|
|
_union,
|
|
_unittest,
|
|
_variable,
|
|
_warning
|
|
}
|
|
|
|
string makeSymbolTypeArray()
|
|
{
|
|
import std.traits : EnumMembers;
|
|
string result = "string[SymbolType.max + 1] symbolTypeStrings = [";
|
|
foreach(st; EnumMembers!SymbolType)
|
|
result ~= `"` ~ to!string(st) ~ `",`;
|
|
result ~= "];";
|
|
return result;
|
|
}
|
|
|
|
mixin(makeSymbolTypeArray);
|
|
|
|
static assert (!MustAddGcRange!(SymbolListBuilder!(ListFmt.Pas)));
|
|
|
|
@TellRangeAdded final class SymbolListBuilder(ListFmt Fmt): ASTVisitor
|
|
{
|
|
private immutable bool _deep;
|
|
|
|
static if (Fmt == ListFmt.Pas)
|
|
{
|
|
Array!char pasStream;
|
|
}
|
|
else
|
|
{
|
|
JSONValue json;
|
|
JSONValue* jarray;
|
|
}
|
|
|
|
uint utc;
|
|
|
|
this(Appender!(AstErrors) errors, bool deep)
|
|
{
|
|
_deep = deep;
|
|
static if (Fmt == ListFmt.Pas)
|
|
{
|
|
pasStream.put("object TSymbolList\rsymbols=<");
|
|
}
|
|
else
|
|
{
|
|
json = parseJSON("[]");
|
|
jarray = &json;
|
|
}
|
|
addAstErrors(errors.data);
|
|
}
|
|
|
|
~this()
|
|
{
|
|
static if (Fmt == ListFmt.Pas)
|
|
{
|
|
destruct(pasStream);
|
|
}
|
|
}
|
|
|
|
alias visit = ASTVisitor.visit;
|
|
|
|
void addAstErrors(AstErrors errors)
|
|
{
|
|
foreach(error; errors)
|
|
{
|
|
string type = (error.type == ErrorType.error) ?
|
|
symbolTypeStrings[SymbolType._error] :
|
|
symbolTypeStrings[SymbolType._warning];
|
|
static if (Fmt == ListFmt.Pas)
|
|
{
|
|
pasStream.put("\ritem\r");
|
|
pasStream.put(format("line=%d\r", error.line));
|
|
pasStream.put(format("col=%d\r", error.column));
|
|
pasStream.put(format("name='%s'\r", patchPascalString!100(error.message)));
|
|
pasStream.put(format("symType=%s\r", type));
|
|
pasStream.put("end");
|
|
}
|
|
else
|
|
{
|
|
JSONValue item = parseJSON("{}");
|
|
item["line"] = JSONValue(error.line);
|
|
item["col"] = JSONValue(error.column);
|
|
item["name"] = JSONValue(error.message);
|
|
item["type"] = JSONValue(type);
|
|
jarray.array ~= item;
|
|
}
|
|
}
|
|
}
|
|
|
|
FpcArray!char* serialize()
|
|
{
|
|
static if (Fmt == ListFmt.Pas)
|
|
{
|
|
pasStream.put(">\rend");
|
|
return (FpcArray!char).fromArray(pasStream);
|
|
}
|
|
else
|
|
{
|
|
JSONValue result = parseJSON("{}");
|
|
result["items"] = json;
|
|
version (assert)
|
|
return result.toPrettyString.toStringz;
|
|
else
|
|
return result.toString.toStringz;
|
|
}
|
|
}
|
|
|
|
/// visitor implementation if the declaration has a "name".
|
|
void namedVisitorImpl(DT, SymbolType st, bool dig = true)(const(DT) dt)
|
|
if (__traits(hasMember, DT, "name"))
|
|
{
|
|
static if (Fmt == ListFmt.Pas)
|
|
{
|
|
pasStream.put("\ritem\r");
|
|
pasStream.put(format("line=%d\r", dt.name.line));
|
|
pasStream.put(format("col=%d\r", dt.name.column));
|
|
static if (is(DT == FunctionDeclaration))
|
|
{
|
|
pasStream.put(format("name='%s'\r", funcDeclText(dt).patchPascalString()));
|
|
}
|
|
else
|
|
{
|
|
pasStream.put(format("name='%s'\r", dt.name.text));
|
|
}
|
|
pasStream.put("symType=" ~ symbolTypeStrings[st] ~ "\r");
|
|
static if (dig) if (_deep)
|
|
{
|
|
pasStream.put("subs = <");
|
|
dt.accept(this);
|
|
pasStream.put(">\r");
|
|
}
|
|
pasStream.put("end");
|
|
}
|
|
else
|
|
{
|
|
JSONValue item = parseJSON("{}");
|
|
item["line"] = JSONValue(dt.name.line);
|
|
item["col"] = JSONValue(dt.name.column);
|
|
static if (is(DT == FunctionDeclaration))
|
|
{
|
|
if (dt.parameters && dt.parameters.parameters &&
|
|
dt.parameters.parameters.length)
|
|
{
|
|
import dparse.formatter : fmtNode = format;
|
|
funcNameApp.length = 0;
|
|
fmtVisitor.format(dt.parameters);
|
|
item["name"] = JSONValue(dt.name.text ~ app.funcNameApp[]);
|
|
}
|
|
else item["name"] = JSONValue(dt.name.text);
|
|
}
|
|
else
|
|
{
|
|
item["name"] = JSONValue(dt.name.text);
|
|
}
|
|
item["type"] = JSONValue(symbolTypeStrings[st]);
|
|
static if (dig) if (_deep)
|
|
{
|
|
JSONValue subs = parseJSON("[]");
|
|
const JSONValue* old = jarray;
|
|
jarray = &subs;
|
|
dt.accept(this);
|
|
item["items"] = subs;
|
|
jarray = old;
|
|
}
|
|
json.array ~= item;
|
|
}
|
|
}
|
|
|
|
/// visitor implementation for special cases.
|
|
void otherVisitorImpl(DT, bool dig = true)
|
|
(const(DT) dt, SymbolType st, string name, size_t line, size_t col)
|
|
{
|
|
static if (Fmt == ListFmt.Pas)
|
|
{
|
|
pasStream.put("\ritem\r");
|
|
pasStream.put(format("line=%d\r", line));
|
|
pasStream.put(format("col=%d\r", col));
|
|
pasStream.put(format("name='%s'\r", name));
|
|
pasStream.put("symType=" ~ symbolTypeStrings[st] ~ "\r");
|
|
static if (dig)
|
|
{
|
|
pasStream.put("subs = <");
|
|
dt.accept(this);
|
|
pasStream.put(">\r");
|
|
}
|
|
pasStream.put("end");
|
|
}
|
|
else
|
|
{
|
|
JSONValue item = parseJSON("{}");
|
|
item["line"] = JSONValue(line);
|
|
item["col"] = JSONValue(col);
|
|
item["name"] = JSONValue(name);
|
|
item["type"] = JSONValue(symbolTypeStrings[st]);
|
|
static if (dig)
|
|
{
|
|
JSONValue subs = parseJSON("[]");
|
|
const JSONValue* old = jarray;
|
|
jarray = &subs;
|
|
dt.accept(this);
|
|
item["items"] = subs;
|
|
jarray = old;
|
|
}
|
|
json.array ~= item;
|
|
}
|
|
}
|
|
|
|
override void visit(const AliasDeclaration decl)
|
|
{
|
|
if (decl.initializers.length)
|
|
namedVisitorImpl!(AliasInitializer, SymbolType._alias)(decl.initializers[0]);
|
|
}
|
|
|
|
override void visit(const AnonymousEnumMember decl)
|
|
{
|
|
namedVisitorImpl!(AnonymousEnumMember, SymbolType._enum)(decl);
|
|
}
|
|
|
|
override void visit(const AutoDeclarationPart decl)
|
|
{
|
|
otherVisitorImpl(decl, SymbolType._variable, decl.identifier.text,
|
|
decl.identifier.line, decl.identifier.column);
|
|
}
|
|
|
|
override void visit(const ClassDeclaration decl)
|
|
{
|
|
namedVisitorImpl!(ClassDeclaration, SymbolType._class)(decl);
|
|
}
|
|
|
|
override void visit(const Constructor decl)
|
|
{
|
|
otherVisitorImpl(decl, SymbolType._function, "ctor", decl.line, decl.column);
|
|
}
|
|
|
|
override void visit(const Destructor decl)
|
|
{
|
|
otherVisitorImpl(decl, SymbolType._function, "dtor", decl.line, decl.column);
|
|
}
|
|
|
|
override void visit(const EnumDeclaration decl)
|
|
{
|
|
namedVisitorImpl!(EnumDeclaration, SymbolType._enum)(decl);
|
|
}
|
|
|
|
override void visit(const EponymousTemplateDeclaration decl)
|
|
{
|
|
namedVisitorImpl!(EponymousTemplateDeclaration, SymbolType._template)(decl);
|
|
}
|
|
|
|
override void visit(const FunctionDeclaration decl)
|
|
{
|
|
namedVisitorImpl!(FunctionDeclaration, SymbolType._function)(decl);
|
|
}
|
|
|
|
override void visit(const InterfaceDeclaration decl)
|
|
{
|
|
namedVisitorImpl!(InterfaceDeclaration, SymbolType._interface)(decl);
|
|
}
|
|
|
|
override void visit(const ImportDeclaration decl)
|
|
{
|
|
foreach (const(SingleImport) si; decl.singleImports)
|
|
{
|
|
if (!si.identifierChain.identifiers.length)
|
|
continue;
|
|
|
|
otherVisitorImpl(decl, SymbolType._import,
|
|
si.identifierChain.identifiers.map!(a => a.text).join("."),
|
|
si.identifierChain.identifiers[0].line,
|
|
si.identifierChain.identifiers[0].column);
|
|
}
|
|
if (decl.importBindings) with (decl.importBindings.singleImport)
|
|
otherVisitorImpl(decl, SymbolType._import,
|
|
identifierChain.identifiers.map!(a => a.text).join("."),
|
|
identifierChain.identifiers[0].line,
|
|
identifierChain.identifiers[0].column);
|
|
}
|
|
|
|
override void visit(const Invariant decl)
|
|
{
|
|
otherVisitorImpl(decl, SymbolType._function, "invariant", decl.line, 0);
|
|
}
|
|
|
|
override void visit(const MixinTemplateDeclaration decl)
|
|
{
|
|
namedVisitorImpl!(TemplateDeclaration, SymbolType._mixin)(decl.templateDeclaration);
|
|
}
|
|
|
|
override void visit(const Postblit pb)
|
|
{
|
|
otherVisitorImpl(pb, SymbolType._function, "postblit", pb.line, pb.column);
|
|
pb.accept(this);
|
|
}
|
|
|
|
override void visit(const StructDeclaration decl)
|
|
{
|
|
namedVisitorImpl!(StructDeclaration, SymbolType._struct)(decl);
|
|
}
|
|
|
|
override void visit(const TemplateDeclaration decl)
|
|
{
|
|
namedVisitorImpl!(TemplateDeclaration, SymbolType._template)(decl);
|
|
}
|
|
|
|
override void visit(const UnionDeclaration decl)
|
|
{
|
|
namedVisitorImpl!(UnionDeclaration, SymbolType._union)(decl);
|
|
}
|
|
|
|
override void visit(const Unittest decl)
|
|
{
|
|
otherVisitorImpl(decl, SymbolType._unittest, format("test%.4d",utc++),
|
|
decl.line, decl.column);
|
|
}
|
|
|
|
override void visit(const VariableDeclaration decl)
|
|
{
|
|
if (decl.declarators)
|
|
foreach (elem; decl.declarators)
|
|
namedVisitorImpl!(Declarator, SymbolType._variable, false)(elem);
|
|
else if (decl.autoDeclaration)
|
|
visit(decl.autoDeclaration);
|
|
}
|
|
|
|
override void visit(const StaticConstructor decl)
|
|
{
|
|
otherVisitorImpl(decl, SymbolType._function, "static ctor", decl.line, decl.column);
|
|
}
|
|
|
|
override void visit(const StaticDestructor decl)
|
|
{
|
|
otherVisitorImpl(decl, SymbolType._function, "static dtor", decl.line, decl.column);
|
|
}
|
|
|
|
override void visit(const SharedStaticConstructor decl)
|
|
{
|
|
otherVisitorImpl(decl, SymbolType._function, "shared static ctor", decl.line, decl.column);
|
|
}
|
|
|
|
override void visit(const SharedStaticDestructor decl)
|
|
{
|
|
otherVisitorImpl(decl, SymbolType._function, "shared static dtor", decl.line, decl.column);
|
|
}
|
|
|
|
override void visit(const AliasInitializer) {}
|
|
override void visit(const AlignAttribute) {}
|
|
override void visit(const ArrayInitializer) {}
|
|
override void visit(const AsmStatement) {}
|
|
override void visit(const AtAttribute) {}
|
|
override void visit(const Attribute) {}
|
|
override void visit(const AttributeDeclaration) {}
|
|
override void visit(const BaseClassList) {}
|
|
override void visit(const BreakStatement) {}
|
|
override void visit(const Catches) {}
|
|
override void visit(const Constraint) {}
|
|
override void visit(const ContinueStatement) {}
|
|
override void visit(const Deprecated) {}
|
|
override void visit(const Expression) {}
|
|
override void dynamicDispatch(const ExpressionNode) {}
|
|
override void visit(const ExpressionStatement) {}
|
|
override void visit(const FunctionAttribute) {}
|
|
override void visit(const FunctionContract) {}
|
|
override void visit(const GotoStatement) {}
|
|
override void visit(const Initializer) {}
|
|
override void visit(const LabeledStatement) {}
|
|
override void visit(const MemberFunctionAttribute){}
|
|
override void visit(const MixinDeclaration) {}
|
|
override void visit(const NamespaceList) {}
|
|
override void visit(const PragmaStatement) {}
|
|
override void visit(const ReturnStatement) {}
|
|
override void visit(const StaticAssertDeclaration){}
|
|
override void visit(const StaticAssertStatement){}
|
|
override void visit(const StructInitializer) {}
|
|
override void visit(const SynchronizedStatement){}
|
|
override void visit(const ThrowExpression) {}
|
|
override void visit(const Type) {}
|
|
override void visit(const Type2) {}
|
|
}
|
|
|