It compiles again
This commit is contained in:
parent
072932b6ff
commit
6b432053f0
30
actypes.d
30
actypes.d
|
@ -157,14 +157,9 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin template scopeImplementation(ScopeType)
|
struct Scope
|
||||||
{
|
{
|
||||||
ScopeType* parent;
|
Scope* getScopeByCursor(size_t cursorPosition) const
|
||||||
ScopeType*[] children;
|
|
||||||
size_t startLocation;
|
|
||||||
size_t endLocation;
|
|
||||||
|
|
||||||
ScopeType* getScopeByCursor(size_t cursorPosition) const
|
|
||||||
{
|
{
|
||||||
if (cursorPosition < startLocation) return null;
|
if (cursorPosition < startLocation) return null;
|
||||||
if (cursorPosition > endLocation) return null;
|
if (cursorPosition > endLocation) return null;
|
||||||
|
@ -176,11 +171,6 @@ mixin template scopeImplementation(ScopeType)
|
||||||
}
|
}
|
||||||
return cast(typeof(return)) &this;
|
return cast(typeof(return)) &this;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct Scope
|
|
||||||
{
|
|
||||||
mixin scopeImplementation!(typeof(this));
|
|
||||||
|
|
||||||
ACSymbol*[] getSymbolsInCursorScope(size_t cursorPosition) const
|
ACSymbol*[] getSymbolsInCursorScope(size_t cursorPosition) const
|
||||||
{
|
{
|
||||||
|
@ -209,7 +199,21 @@ struct Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
ACSymbol*[] symbols;
|
ACSymbol*[] symbols;
|
||||||
string[][] imports;
|
ImportInformation[] importInformation;
|
||||||
|
Scope* parent;
|
||||||
|
Scope*[] children;
|
||||||
|
size_t startLocation;
|
||||||
|
size_t endLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImportInformation
|
||||||
|
{
|
||||||
|
/// module relative path
|
||||||
|
string modulePath;
|
||||||
|
/// symbols to import from this module
|
||||||
|
string[string] importedSymbols;
|
||||||
|
/// true if the import is public
|
||||||
|
bool isPublic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
603
astconverter.d
603
astconverter.d
|
@ -18,86 +18,102 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
/**
|
|
||||||
* AST conversion takes place in several steps
|
|
||||||
* 1. AST is converted to a tree of SemanicSymbols, a tree of ACSymbols, and a
|
|
||||||
* tree of scopes. The following fields are set on the symbols:
|
|
||||||
* * name
|
|
||||||
* * location
|
|
||||||
* * alias this
|
|
||||||
* * base class names
|
|
||||||
* * protection level
|
|
||||||
* * symbol kind
|
|
||||||
* * function call tip
|
|
||||||
* * symbol file path
|
|
||||||
* Import statements are recorded in the scope tree.
|
|
||||||
* 2. Scope tree is traversed and all imports are resolved by adding appropriate
|
|
||||||
* ACSymbol instances.
|
|
||||||
* 3. Semantic symbol tree is traversed
|
|
||||||
* * types are resolved
|
|
||||||
* * base classes are resolved
|
|
||||||
* * mixin templates are resolved
|
|
||||||
* * alias this is resolved
|
|
||||||
*/
|
|
||||||
|
|
||||||
module astconverter;
|
module astconverter;
|
||||||
|
|
||||||
|
import std.algorithm;
|
||||||
import std.array;
|
import std.array;
|
||||||
import std.conv;
|
import std.conv;
|
||||||
|
import std.path;
|
||||||
import std.range;
|
import std.range;
|
||||||
import std.algorithm;
|
|
||||||
|
|
||||||
import stdx.d.ast;
|
import stdx.d.ast;
|
||||||
import stdx.d.lexer;
|
import stdx.d.lexer;
|
||||||
import stdx.d.parser;
|
import stdx.d.parser;
|
||||||
|
|
||||||
import actypes;
|
import actypes;
|
||||||
|
import constants;
|
||||||
import messages;
|
import messages;
|
||||||
import semantic;
|
import semantic;
|
||||||
import stupidlog;
|
import stupidlog;
|
||||||
|
|
||||||
class FirstPass : ASTVisitor
|
/**
|
||||||
|
* First Pass handles the following:
|
||||||
|
* $(UL
|
||||||
|
* $(LI symbol name)
|
||||||
|
* $(LI symbol location)
|
||||||
|
* $(LI alias this locations)
|
||||||
|
* $(LI base class names)
|
||||||
|
* $(LI protection level)
|
||||||
|
* $(LI symbol kind)
|
||||||
|
* $(LI function call tip)
|
||||||
|
* $(LI symbol file path)
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
final class FirstPass : ASTVisitor
|
||||||
{
|
{
|
||||||
|
this(Module mod, string symbolFile)
|
||||||
|
{
|
||||||
|
this.symbolFile = symbolFile;
|
||||||
|
this.mod = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
visit(mod);
|
||||||
|
}
|
||||||
|
|
||||||
override void visit(Constructor con)
|
override void visit(Constructor con)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
||||||
visitFunctionDeclaration(con);
|
visitConstructor(con.location, con.parameters, con.functionBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(SharedStaticConstructor con)
|
override void visit(SharedStaticConstructor con)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
||||||
visitFunctionDeclaration(con);
|
visitConstructor(con.location, null, con.functionBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(StaticConstructor con)
|
override void visit(StaticConstructor con)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
||||||
visitFunctionDeclaration(con);
|
visitConstructor(con.location, null, con.functionBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(Destructor des)
|
override void visit(Destructor des)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
||||||
visitFunctionDeclaration(des);
|
visitDestructor(des.location, des.functionBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(SharedStaticDestructor des)
|
override void visit(SharedStaticDestructor des)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
||||||
visitFunctionDeclaration(des);
|
visitDestructor(des.location, des.functionBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(StaticDestructor des)
|
override void visit(StaticDestructor des)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
||||||
visitFunctionDeclaration(des);
|
visitDestructor(des.location, des.functionBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(FunctionDeclaration dec)
|
override void visit(FunctionDeclaration dec)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||||
visitFunctionDeclaration(dec);
|
SemanticSymbol* symbol = new SemanticSymbol(dec.name.value.dup,
|
||||||
|
CompletionKind.functionName, symbolFile, dec.name.startIndex);
|
||||||
|
processParameters(symbol, dec.returnType, symbol.acSymbol.name,
|
||||||
|
dec.parameters);
|
||||||
|
symbol.protection = protection;
|
||||||
|
symbol.parent = currentSymbol;
|
||||||
|
currentSymbol.addChild(symbol);
|
||||||
|
if (dec.functionBody !is null)
|
||||||
|
{
|
||||||
|
currentSymbol = symbol;
|
||||||
|
dec.functionBody.accept(this);
|
||||||
|
currentSymbol = symbol.parent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(ClassDeclaration dec)
|
override void visit(ClassDeclaration dec)
|
||||||
|
@ -137,11 +153,12 @@ class FirstPass : ASTVisitor
|
||||||
Type t = dec.type;
|
Type t = dec.type;
|
||||||
foreach (declarator; dec.declarators)
|
foreach (declarator; dec.declarators)
|
||||||
{
|
{
|
||||||
SemanticSymbol* symbol = new SemanticSymbol;
|
SemanticSymbol* symbol = new SemanticSymbol(
|
||||||
symbol.acSymbol.type = t;
|
declarator.name.value.dup,
|
||||||
symbol.kind = CompletionKind.variableName;
|
CompletionKind.variableName,
|
||||||
symbol.name = declarator.name.value.dup;
|
symbolFile,
|
||||||
symbol.location = declarator.name.startIndex;
|
declarator.name.startIndex);
|
||||||
|
symbol.type = t;
|
||||||
symbol.protection = protection;
|
symbol.protection = protection;
|
||||||
symbol.parent = currentSymbol;
|
symbol.parent = currentSymbol;
|
||||||
currentSymbol.addChild(symbol);
|
currentSymbol.addChild(symbol);
|
||||||
|
@ -176,14 +193,16 @@ class FirstPass : ASTVisitor
|
||||||
override void visit(Module mod)
|
override void visit(Module mod)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(mod).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(mod).stringof);
|
||||||
rootSymbol = new SemanticSymbol;
|
//
|
||||||
rootSymbol.kind = CompletionKind.moduleName;
|
currentSymbol = new SemanticSymbol(null, CompletionKind.moduleName,
|
||||||
rootSymbol.startLocation = 0;
|
symbolFile);
|
||||||
rootSymbol.endLocation = size_t.max;
|
rootSymbol = currentSymbol;
|
||||||
currentSymbol = rootSymbol;
|
|
||||||
currentScope = new Scope();
|
currentScope = new Scope();
|
||||||
currentScope.startLocation = 0;
|
currentScope.startLocation = 0;
|
||||||
currentScope.endLocation = size_t.max;
|
currentScope.endLocation = size_t.max;
|
||||||
|
moduleScope = currentScope;
|
||||||
|
|
||||||
mod.accept(this);
|
mod.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,29 +210,25 @@ class FirstPass : ASTVisitor
|
||||||
{
|
{
|
||||||
assert (currentSymbol);
|
assert (currentSymbol);
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||||
SemanticSymbol* symbol = new SemanticSymbol;
|
SemanticSymbol* symbol = new SemanticSymbol(dec.name.value.dup,
|
||||||
symbol.name = dec.name.value.dup;
|
CompletionKind.enumName, symbolFile, dec.name.startIndex);
|
||||||
symbol.location = dec.name.startIndex;
|
|
||||||
symbol.kind = CompletionKind.enumName;
|
|
||||||
symbol.type = dec.type;
|
symbol.type = dec.type;
|
||||||
symbol.parent = currentSymbol;
|
symbol.parent = currentSymbol;
|
||||||
currentSymbol = symbol;
|
currentSymbol = symbol;
|
||||||
if (dec.enumBody !is null)
|
if (dec.enumBody !is null)
|
||||||
dec.enumBody.accept(this);
|
dec.enumBody.accept(this);
|
||||||
currentSymbol = symbol.parent;
|
currentSymbol = symbol.parent;
|
||||||
currentSymbol.children ~= symbol;
|
currentSymbol.addChild(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(EnumMember member)
|
override void visit(EnumMember member)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(member).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(member).stringof);
|
||||||
SemanticSymbol* symbol = new SemanticSymbol;
|
SemanticSymbol* symbol = new SemanticSymbol(member.name.value.dup,
|
||||||
symbol.kind = CompletionKind.enumMember;
|
CompletionKind.enumMember, symbolFile, member.name.startIndex);
|
||||||
symbol.name = member.name.value.dup;
|
|
||||||
symbol.location = member.name.startIndex;
|
|
||||||
symbol.type = member.type;
|
symbol.type = member.type;
|
||||||
symbol.parent = currentSymbol;
|
symbol.parent = currentSymbol;
|
||||||
currentSymbol.children ~= symbol;
|
currentSymbol.addChild(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(ModuleDeclaration dec)
|
override void visit(ModuleDeclaration dec)
|
||||||
|
@ -239,31 +254,63 @@ class FirstPass : ASTVisitor
|
||||||
currentScope.children ~= s;
|
currentScope.children ~= s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override void visit(ImportDeclaration importDeclaration)
|
||||||
|
{
|
||||||
|
Log.trace(__FUNCTION__, " ImportDeclaration");
|
||||||
|
|
||||||
|
|
||||||
|
foreach (single; importDeclaration.singleImports.filter!(
|
||||||
|
a => a !is null && a.identifierChain !is null))
|
||||||
|
{
|
||||||
|
ImportInformation info;
|
||||||
|
info.modulePath = convertChainToImportPath(single.identifierChain);
|
||||||
|
currentScope.importInformation ~= info;
|
||||||
|
}
|
||||||
|
if (importDeclaration.importBindings is null) return;
|
||||||
|
if (importDeclaration.importBindings.singleImport.identifierChain is null) return;
|
||||||
|
ImportInformation info;
|
||||||
|
info.modulePath = convertChainToImportPath(
|
||||||
|
importDeclaration.importBindings.singleImport.identifierChain);
|
||||||
|
foreach (bind; importDeclaration.importBindings.importBinds)
|
||||||
|
{
|
||||||
|
if (bind.right == TokenType.invalid)
|
||||||
|
info.importedSymbols[bind.left.value] = null;
|
||||||
|
else
|
||||||
|
info.importedSymbols[bind.left.value] = bind.right.value;
|
||||||
|
}
|
||||||
|
currentScope.importInformation ~= info;
|
||||||
|
}
|
||||||
|
|
||||||
// Create scope for block statements
|
// Create scope for block statements
|
||||||
override void visit(BlockStatement blockStatement)
|
override void visit(BlockStatement blockStatement)
|
||||||
{
|
{
|
||||||
// Log.trace(__FUNCTION__, " ", typeof(blockStatement).stringof);
|
// Log.trace(__FUNCTION__, " ", typeof(blockStatement).stringof);
|
||||||
Scope* s = new Scope;
|
Scope* s = new Scope;
|
||||||
|
s.parent = currentScope;
|
||||||
|
currentScope.children ~= s;
|
||||||
s.startLocation = blockStatement.startLocation;
|
s.startLocation = blockStatement.startLocation;
|
||||||
s.endLocation = blockStatement.endLocation;
|
s.endLocation = blockStatement.endLocation;
|
||||||
|
|
||||||
if (currentSymbol.kind == CompletionKind.functionName)
|
if (currentSymbol.acSymbol.kind == CompletionKind.functionName)
|
||||||
{
|
{
|
||||||
foreach (child; currentSymbol.children)
|
foreach (child; currentSymbol.children)
|
||||||
{
|
{
|
||||||
// Log.trace("Setting ", child.name, " location");
|
child.acSymbol.location = s.startLocation + 1;
|
||||||
child.location = s.startLocation + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Log.trace("Added scope ", s.startLocation, " ", s.endLocation);
|
|
||||||
s.parent = currentScope;
|
|
||||||
if (blockStatement.declarationsAndStatements !is null)
|
if (blockStatement.declarationsAndStatements !is null)
|
||||||
{
|
{
|
||||||
currentScope = s;
|
currentScope = s;
|
||||||
visit (blockStatement.declarationsAndStatements);
|
visit (blockStatement.declarationsAndStatements);
|
||||||
currentScope = s.parent;
|
currentScope = s.parent;
|
||||||
}
|
}
|
||||||
currentScope.children ~= s;
|
}
|
||||||
|
|
||||||
|
override void visit(VersionCondition versionCondition)
|
||||||
|
{
|
||||||
|
// TODO: This is a bit of a hack
|
||||||
|
if (predefinedVersions.canFind(versionCondition.token.value))
|
||||||
|
versionCondition.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
alias ASTVisitor.visit visit;
|
alias ASTVisitor.visit visit;
|
||||||
|
@ -272,109 +319,88 @@ private:
|
||||||
|
|
||||||
void visitAggregateDeclaration(AggType)(AggType dec, CompletionKind kind)
|
void visitAggregateDeclaration(AggType)(AggType dec, CompletionKind kind)
|
||||||
{
|
{
|
||||||
SemanticSymbol* symbol = new SemanticSymbol;
|
|
||||||
// Log.trace("visiting aggregate declaration ", dec.name.value);
|
// Log.trace("visiting aggregate declaration ", dec.name.value);
|
||||||
symbol.name = dec.name.value.dup;
|
CompletionKind k;
|
||||||
symbol.location = dec.name.startIndex;
|
static if (is (AggType == ClassDeclaration))
|
||||||
symbol.kind = kind;
|
k = CompletionKind.className;
|
||||||
|
else static if (is (AggType == InterfaceDeclaration))
|
||||||
|
k = CompletionKind.interfaceName;
|
||||||
|
else static if (is (AggType == StructDeclaration))
|
||||||
|
k = CompletionKind.structName;
|
||||||
|
else static if (is (AggType == UnionDeclaration))
|
||||||
|
k = CompletionKind.unionName;
|
||||||
|
else static assert (false, "Unhandled aggregate type " ~ AggType.stringof);
|
||||||
|
|
||||||
|
SemanticSymbol* symbol = new SemanticSymbol(dec.name.value.dup,
|
||||||
|
kind, symbolFile, dec.name.startIndex);
|
||||||
symbol.parent = currentSymbol;
|
symbol.parent = currentSymbol;
|
||||||
symbol.protection = protection;
|
symbol.protection = protection;
|
||||||
currentSymbol = symbol;
|
currentSymbol = symbol;
|
||||||
dec.accept(this);
|
dec.accept(this);
|
||||||
currentSymbol = symbol.parent;
|
currentSymbol = symbol.parent;
|
||||||
currentSymbol.children ~= symbol;
|
currentSymbol.addChild(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitFunctionDeclaration(DeclarationType)(DeclarationType dec)
|
void visitConstructor(size_t location, Parameters parameters,
|
||||||
{
|
FunctionBody functionBody)
|
||||||
SemanticSymbol* symbol = new SemanticSymbol;
|
|
||||||
|
|
||||||
static if (is (DeclarationType == FunctionDeclaration))
|
|
||||||
{
|
|
||||||
symbol.name = dec.name.value.dup;
|
|
||||||
symbol.location = dec.name.startIndex;
|
|
||||||
}
|
|
||||||
else static if (is (DeclarationType == Destructor)
|
|
||||||
|| is (DeclarationType == StaticDestructor)
|
|
||||||
|| is (DeclarationType == SharedStaticDestructor))
|
|
||||||
{
|
|
||||||
symbol.name = "*destructor*";
|
|
||||||
symbol.location = dec.location;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
symbol.name = "*constructor*";
|
|
||||||
symbol.location = dec.location;
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (is (DeclarationType == Destructor)
|
|
||||||
|| is (DeclarationType == StaticDestructor)
|
|
||||||
|| is (DeclarationType == SharedStaticDestructor))
|
|
||||||
{
|
|
||||||
symbol.callTip = "~this()";
|
|
||||||
}
|
|
||||||
else static if (is (DeclarationType == StaticConstructor)
|
|
||||||
|| is (DeclarationType == SharedStaticConstructor))
|
|
||||||
{
|
|
||||||
symbol.callTip = "this()";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string parameterString;
|
|
||||||
if (dec.parameters !is null)
|
|
||||||
{
|
|
||||||
parameterString = formatNode(dec.parameters);
|
|
||||||
foreach (Parameter p; dec.parameters.parameters)
|
|
||||||
{
|
|
||||||
SemanticSymbol* parameter = new SemanticSymbol;
|
|
||||||
parameter.name = p.name.value.dup;
|
|
||||||
parameter.type = p.type;
|
|
||||||
parameter.kind = CompletionKind.variableName;
|
|
||||||
parameter.startLocation = p.name.startIndex;
|
|
||||||
symbol.children ~= parameter;
|
|
||||||
// Log.trace("Parameter ", parameter.name, " added to ", symbol.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
parameterString = "()";
|
|
||||||
|
|
||||||
static if (is (DeclarationType == FunctionDeclaration))
|
|
||||||
symbol.callTip = "%s %s%s".format(formatNode(dec.returnType),
|
|
||||||
dec.name.value, parameterString);
|
|
||||||
else
|
|
||||||
symbol.callTip = "this%s".format(parameterString);
|
|
||||||
}
|
|
||||||
|
|
||||||
symbol.protection = protection;
|
|
||||||
symbol.kind = CompletionKind.functionName;
|
|
||||||
symbol.parent = currentSymbol;
|
|
||||||
currentSymbol = symbol;
|
|
||||||
|
|
||||||
if (dec.functionBody !is null)
|
|
||||||
{
|
|
||||||
dec.functionBody.accept(this);
|
|
||||||
}
|
|
||||||
currentSymbol = symbol.parent;
|
|
||||||
currentSymbol.children ~= symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string[] iotcToStringArray(const IdentifierOrTemplateChain iotc)
|
|
||||||
{
|
{
|
||||||
string[] parts;
|
SemanticSymbol* symbol = new SemanticSymbol("this",
|
||||||
foreach (ioti; iotc.identifiersOrTemplateInstances)
|
CompletionKind.functionName, symbolFile, location);
|
||||||
|
processParameters(symbol, null, "this", parameters);
|
||||||
|
symbol.protection = protection;
|
||||||
|
symbol.parent = currentSymbol;
|
||||||
|
currentSymbol.addChild(symbol);
|
||||||
|
if (functionBody !is null)
|
||||||
{
|
{
|
||||||
if (ioti.identifier != TokenType.invalid)
|
currentSymbol = symbol;
|
||||||
parts ~= ioti.identifier.value.dup;
|
functionBody.accept(this);
|
||||||
else
|
currentSymbol = symbol.parent;
|
||||||
parts ~= ioti.templateInstance.identifier.value.dup;
|
|
||||||
}
|
}
|
||||||
return parts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static string formatCalltip(Type returnType, string name, Parameters parameters,
|
void visitDestructor(size_t location, FunctionBody functionBody)
|
||||||
|
{
|
||||||
|
SemanticSymbol* symbol = new SemanticSymbol("~this",
|
||||||
|
CompletionKind.functionName, symbolFile, location);
|
||||||
|
symbol.acSymbol.callTip = "~this()";
|
||||||
|
symbol.protection = protection;
|
||||||
|
symbol.parent = currentSymbol;
|
||||||
|
currentSymbol.addChild(symbol);
|
||||||
|
if (functionBody !is null)
|
||||||
|
{
|
||||||
|
currentSymbol = symbol;
|
||||||
|
functionBody.accept(this);
|
||||||
|
currentSymbol = symbol.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processParameters(SemanticSymbol* symbol, Type returnType,
|
||||||
|
string functionName, Parameters parameters) const
|
||||||
|
{
|
||||||
|
if (parameters !is null)
|
||||||
|
{
|
||||||
|
foreach (Parameter p; parameters.parameters)
|
||||||
|
{
|
||||||
|
SemanticSymbol* parameter = new SemanticSymbol(p.name.value.dup,
|
||||||
|
CompletionKind.variableName, symbolFile, p.name.startIndex);
|
||||||
|
parameter.type = p.type;
|
||||||
|
symbol.addChild(parameter);
|
||||||
|
parameter.parent = symbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
symbol.acSymbol.callTip = formatCallTip(returnType, functionName,
|
||||||
|
parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
static string formatCallTip(Type returnType, string name, Parameters parameters,
|
||||||
string doc = null)
|
string doc = null)
|
||||||
{
|
{
|
||||||
return "%s %s%s".format(formatNode(returnType), name, formatNode(parameters));
|
string parameterString = parameters is null ? "()"
|
||||||
|
: formatNode(parameters);
|
||||||
|
if (returnType is null)
|
||||||
|
return "%s%s".format(name, parameterString);
|
||||||
|
return "%s %s%s".format(formatNode(returnType), name, parameterString);
|
||||||
}
|
}
|
||||||
|
|
||||||
static string formatNode(T)(T node)
|
static string formatNode(T)(T node)
|
||||||
|
@ -401,144 +427,116 @@ private:
|
||||||
|
|
||||||
/// Current scope
|
/// Current scope
|
||||||
Scope* currentScope;
|
Scope* currentScope;
|
||||||
|
|
||||||
|
/// Module scope
|
||||||
|
Scope* moduleScope;
|
||||||
|
|
||||||
|
/// Path to the file being converted
|
||||||
|
string symbolFile;
|
||||||
|
|
||||||
|
Module mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
struct SemanticConverter
|
* Second pass handles the following:
|
||||||
|
* $(UL
|
||||||
|
* $(LI Import statements)
|
||||||
|
* $(LI assigning symbols to scopes)
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
struct SecondPass
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
this(const(SemanticSymbol)* symbol, Scope* scopes)
|
this(SemanticSymbol* rootSymbol, Scope* moduleScope)
|
||||||
{
|
{
|
||||||
this.sc = scopes;
|
this.rootSymbol = rootSymbol;
|
||||||
this.symbol = symbol;
|
this.moduleScope = moduleScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
void convertModule()
|
void run()
|
||||||
{
|
{
|
||||||
convertSemanticSymbol(symbol);
|
assignToScopes(rootSymbol.acSymbol);
|
||||||
assert (current !is null);
|
resolveImports(moduleScope);
|
||||||
resolveTypes(current);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void resolveTypes(const(ACSymbol*) symbol)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ACSymbol* convertSemanticSymbol(const(SemanticSymbol)* symbol)
|
|
||||||
{
|
|
||||||
ACSymbol* s = null;
|
|
||||||
with (CompletionKind) final switch (symbol.kind)
|
|
||||||
{
|
|
||||||
case moduleName:
|
|
||||||
s = convertAggregateDeclaration(symbol);
|
|
||||||
current = s;
|
|
||||||
break;
|
|
||||||
case className:
|
|
||||||
case interfaceName:
|
|
||||||
case structName:
|
|
||||||
case unionName:
|
|
||||||
case enumName:
|
|
||||||
s = convertAggregateDeclaration(symbol);
|
|
||||||
break;
|
|
||||||
case enumMember:
|
|
||||||
s = convertEnumMember(symbol);
|
|
||||||
break;
|
|
||||||
case variableName:
|
|
||||||
case memberVariableName:
|
|
||||||
s = convertVariableDeclaration(symbol);
|
|
||||||
break;
|
|
||||||
case functionName:
|
|
||||||
s = convertFunctionDeclaration(symbol);
|
|
||||||
break;
|
|
||||||
case aliasName:
|
|
||||||
s = convertAliasDeclaration(symbol);
|
|
||||||
break;
|
|
||||||
case packageName:
|
|
||||||
assert (false, "Not implemented");
|
|
||||||
case keyword:
|
|
||||||
case array:
|
|
||||||
case assocArray:
|
|
||||||
case dummy:
|
|
||||||
assert (false, "This should never be reached");
|
|
||||||
}
|
|
||||||
if (sc !is null && symbol.kind != CompletionKind.moduleName)
|
|
||||||
{
|
|
||||||
sc.getScopeByCursor(s.location).symbols ~= s;
|
|
||||||
// Log.trace("Set scope location");
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACSymbol* convertAliasDeclaration(const(SemanticSymbol)* symbol)
|
|
||||||
{
|
|
||||||
ACSymbol* ac = new ACSymbol;
|
|
||||||
ac.name = symbol.name;
|
|
||||||
ac.kind = symbol.kind;
|
|
||||||
ac.location = symbol.location;
|
|
||||||
// TODO: Set type
|
|
||||||
return ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACSymbol* convertVariableDeclaration(const(SemanticSymbol)* symbol)
|
|
||||||
{
|
|
||||||
ACSymbol* ac = new ACSymbol;
|
|
||||||
ac.name = symbol.name;
|
|
||||||
ac.kind = CompletionKind.variableName;
|
|
||||||
ac.location = symbol.location;
|
|
||||||
if (symbol.type !is null)
|
|
||||||
ac.type = resolveType(symbol.type);
|
|
||||||
// TODO: Handle auto
|
|
||||||
return ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACSymbol* convertEnumMember(const(SemanticSymbol)* symbol)
|
|
||||||
{
|
|
||||||
ACSymbol* ac = new ACSymbol;
|
|
||||||
ac.name = symbol.name;
|
|
||||||
ac.kind = symbol.kind;
|
|
||||||
ac.location = symbol.location;
|
|
||||||
// TODO: type
|
|
||||||
return ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACSymbol* convertFunctionDeclaration(const(SemanticSymbol)* symbol)
|
|
||||||
{
|
|
||||||
// Log.trace("Converted ", symbol.name, " ", symbol.kind, " ", symbol.location);
|
|
||||||
ACSymbol* ac = new ACSymbol;
|
|
||||||
ac.name = symbol.name;
|
|
||||||
ac.kind = symbol.kind;
|
|
||||||
ac.location = symbol.location;
|
|
||||||
ac.callTip = symbol.callTip;
|
|
||||||
if (symbol.type !is null)
|
|
||||||
ac.type = resolveType(symbol.type);
|
|
||||||
return ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACSymbol* convertAggregateDeclaration(const(SemanticSymbol)* symbol)
|
|
||||||
{
|
|
||||||
// Log.trace("Converted ", symbol.name, " ", symbol.kind, " ", symbol.location);
|
|
||||||
ACSymbol* ac = new ACSymbol;
|
|
||||||
ac.name = symbol.name;
|
|
||||||
ac.kind = symbol.kind;
|
|
||||||
ac.location = symbol.location;
|
|
||||||
if (symbol.kind == CompletionKind.className
|
|
||||||
|| symbol.kind == CompletionKind.structName)
|
|
||||||
{
|
|
||||||
ACSymbol* thisSymbol = new ACSymbol("this",
|
|
||||||
CompletionKind.variableName, ac);
|
|
||||||
ac.parts ~= thisSymbol;
|
|
||||||
}
|
|
||||||
auto temp = current;
|
|
||||||
current = ac;
|
|
||||||
foreach (child; symbol.children)
|
|
||||||
current.parts ~= convertSemanticSymbol(child);
|
|
||||||
current = temp;
|
|
||||||
return ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void assignToScopes(ACSymbol* currentSymbol)
|
||||||
|
{
|
||||||
|
moduleScope.getScopeByCursor(currentSymbol.location).symbols
|
||||||
|
~= currentSymbol;
|
||||||
|
foreach (part; currentSymbol.parts)
|
||||||
|
assignToScopes(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resolveImports(Scope* currentScope)
|
||||||
|
{
|
||||||
|
foreach (importInfo; currentScope.importInformation)
|
||||||
|
{
|
||||||
|
// TODO: actually process imports here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SemanticSymbol* rootSymbol;
|
||||||
|
Scope* moduleScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Third pass handles the following:
|
||||||
|
* $(UL
|
||||||
|
* $(LI types)
|
||||||
|
* $(LI base classes)
|
||||||
|
* $(LI mixin templates)
|
||||||
|
* $(LI alias this)
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
struct ThirdPass
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
this(SemanticSymbol* rootSymbol, Scope* moduleScope)
|
||||||
|
{
|
||||||
|
this.rootSymbol = rootSymbol;
|
||||||
|
this.moduleScope = moduleScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
thirdPass(rootSymbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void thirdPass(SemanticSymbol* currentSymbol)
|
||||||
|
{
|
||||||
|
with (CompletionKind) final switch (currentSymbol.acSymbol.kind)
|
||||||
|
{
|
||||||
|
case className:
|
||||||
|
case interfaceName:
|
||||||
|
// resolve inheritance
|
||||||
|
goto case structName;
|
||||||
|
case structName:
|
||||||
|
case unionName:
|
||||||
|
break;
|
||||||
|
case variableName:
|
||||||
|
case memberVariableName:
|
||||||
|
break;
|
||||||
|
case functionName:
|
||||||
|
break;
|
||||||
|
case enumName:
|
||||||
|
break;
|
||||||
|
case keyword:
|
||||||
|
case enumMember:
|
||||||
|
case packageName:
|
||||||
|
case moduleName:
|
||||||
|
case dummy:
|
||||||
|
case array:
|
||||||
|
case assocArray:
|
||||||
|
case aliasName:
|
||||||
|
case templateName:
|
||||||
|
case mixinTemplateName:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ACSymbol* resolveType(const Type t)
|
ACSymbol* resolveType(const Type t)
|
||||||
in
|
in
|
||||||
|
@ -610,29 +608,30 @@ private:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ACSymbol* current;
|
SemanticSymbol* rootSymbol;
|
||||||
Scope* sc;
|
Scope* moduleScope;
|
||||||
const(SemanticSymbol)* symbol;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const(ACSymbol)*[] convertAstToSymbols(Module m)
|
const(ACSymbol)*[] convertAstToSymbols(Module m, string symbolFile)
|
||||||
{
|
{
|
||||||
SemanticVisitor visitor = new SemanticVisitor(SemanticType.partial);
|
FirstPass first = new FirstPass(m, symbolFile);
|
||||||
visitor.visit(m);
|
first.run();
|
||||||
SemanticConverter converter = SemanticConverter(visitor.rootSymbol, null);
|
SecondPass second = SecondPass(first.rootSymbol, first.moduleScope);
|
||||||
converter.convertModule();
|
second.run();
|
||||||
return cast(typeof(return)) converter.current.parts;
|
ThirdPass third = ThirdPass(second.rootSymbol, second.moduleScope);
|
||||||
|
return cast(typeof(return)) third.rootSymbol.acSymbol.parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
const(Scope)* generateAutocompleteTrees(const(Token)[] tokens)
|
const(Scope)* generateAutocompleteTrees(const(Token)[] tokens, string symbolFile)
|
||||||
{
|
{
|
||||||
Module m = parseModule(tokens, null);
|
Module m = parseModule(tokens, null);
|
||||||
SemanticVisitor visitor = new SemanticVisitor(SemanticType.full);
|
FirstPass first = new FirstPass(m, symbolFile);
|
||||||
visitor.visit(m);
|
first.run();
|
||||||
SemanticConverter converter = SemanticConverter(visitor.rootSymbol,
|
SecondPass second = SecondPass(first.rootSymbol, first.currentScope);
|
||||||
visitor.currentScope);
|
second.run();
|
||||||
converter.convertModule();
|
ThirdPass third = ThirdPass(second.rootSymbol, second.moduleScope);
|
||||||
return converter.sc;
|
third.run();
|
||||||
|
return cast(typeof(return)) third.moduleScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
version(unittest) Module parseTestCode(string code)
|
version(unittest) Module parseTestCode(string code)
|
||||||
|
@ -648,20 +647,22 @@ version(unittest) Module parseTestCode(string code)
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
unittest
|
private:
|
||||||
{
|
|
||||||
auto source = q{
|
|
||||||
module foo;
|
|
||||||
|
|
||||||
struct Bar { int x; int y;}
|
string[] iotcToStringArray(const IdentifierOrTemplateChain iotc)
|
||||||
}c;
|
{
|
||||||
Module m = parseTestCode(source);
|
string[] parts;
|
||||||
BasicSemanticVisitor visitor = new BasicSemanticVisitor;
|
foreach (ioti; iotc.identifiersOrTemplateInstances)
|
||||||
visitor.visit(m);
|
{
|
||||||
assert (visitor.rootSymbol !is null);
|
if (ioti.identifier != TokenType.invalid)
|
||||||
assert (visitor.rootSymbol.name == "foo");
|
parts ~= ioti.identifier.value.dup;
|
||||||
SemanticSymbol* bar = visitor.root.getPartByName("Bar");
|
else
|
||||||
assert (bar !is null);
|
parts ~= ioti.templateInstance.identifier.value.dup;
|
||||||
assert (bar.getPartByName("x") !is null);
|
}
|
||||||
assert (bar.getPartByName("y") !is null);
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string convertChainToImportPath(IdentifierChain chain)
|
||||||
|
{
|
||||||
|
return to!string(chain.identifiers.map!(a => a.value).join(dirSeparator).array) ~ ".d";
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,8 @@ AutocompleteResponse complete(AutocompleteRequest request, string[] importPaths)
|
||||||
case TokenType.identifier:
|
case TokenType.identifier:
|
||||||
case TokenType.rParen:
|
case TokenType.rParen:
|
||||||
case TokenType.rBracket:
|
case TokenType.rBracket:
|
||||||
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray);
|
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray,
|
||||||
|
"stdin");
|
||||||
auto expression = getExpression(beforeTokens[0 .. $ - 1]);
|
auto expression = getExpression(beforeTokens[0 .. $ - 1]);
|
||||||
response.setCompletions(completionScope, expression,
|
response.setCompletions(completionScope, expression,
|
||||||
request.cursorPosition, CompletionType.calltips);
|
request.cursorPosition, CompletionType.calltips);
|
||||||
|
@ -152,7 +153,8 @@ dotCompletion:
|
||||||
case TokenType.rParen:
|
case TokenType.rParen:
|
||||||
case TokenType.rBracket:
|
case TokenType.rBracket:
|
||||||
case TokenType.this_:
|
case TokenType.this_:
|
||||||
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray);
|
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray,
|
||||||
|
"stdin");
|
||||||
auto expression = getExpression(beforeTokens);
|
auto expression = getExpression(beforeTokens);
|
||||||
response.setCompletions(completionScope, expression,
|
response.setCompletions(completionScope, expression,
|
||||||
request.cursorPosition, CompletionType.identifiers, partial);
|
request.cursorPosition, CompletionType.identifiers, partial);
|
||||||
|
|
1
build.sh
1
build.sh
|
@ -23,4 +23,5 @@ dmd \
|
||||||
-Imsgpack-d/src\
|
-Imsgpack-d/src\
|
||||||
-Idscanner\
|
-Idscanner\
|
||||||
-wi\
|
-wi\
|
||||||
|
-g\
|
||||||
-ofdcd-server
|
-ofdcd-server
|
||||||
|
|
|
@ -71,6 +71,12 @@ enum CompletionKind : char
|
||||||
|
|
||||||
/// alias name
|
/// alias name
|
||||||
aliasName = 'l',
|
aliasName = 'l',
|
||||||
|
|
||||||
|
/// template name
|
||||||
|
templateName = 't',
|
||||||
|
|
||||||
|
/// mixin template name
|
||||||
|
mixinTemplateName = 'T'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -105,7 +105,7 @@ struct ModuleCache
|
||||||
auto tokens = source.byToken(config).array();
|
auto tokens = source.byToken(config).array();
|
||||||
Module mod = parseModule(tokens, location, &doesNothing);
|
Module mod = parseModule(tokens, location, &doesNothing);
|
||||||
|
|
||||||
symbols = convertAstToSymbols(mod);
|
symbols = convertAstToSymbols(mod, location);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
14
semantic.d
14
semantic.d
|
@ -31,7 +31,15 @@ struct SemanticSymbol
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void name(string n) @property { acSymbol.name = n; }
|
@disable this();
|
||||||
|
|
||||||
|
this(string name, CompletionKind kind, string symbolFile,
|
||||||
|
size_t location = size_t.max)
|
||||||
|
{
|
||||||
|
acSymbol = new ACSymbol(name, kind);
|
||||||
|
acSymbol.location = location;
|
||||||
|
acSymbol.symbolFile = symbolFile;
|
||||||
|
}
|
||||||
|
|
||||||
void addChild(SemanticSymbol* child)
|
void addChild(SemanticSymbol* child)
|
||||||
{
|
{
|
||||||
|
@ -57,5 +65,7 @@ public:
|
||||||
/// Protection level for this symobol
|
/// Protection level for this symobol
|
||||||
TokenType protection;
|
TokenType protection;
|
||||||
|
|
||||||
mixin scopeImplementation!(SemanticSymbol);
|
SemanticSymbol* parent;
|
||||||
|
|
||||||
|
SemanticSymbol*[] children;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue