Rather large refactoring
This commit is contained in:
parent
6b80fda116
commit
280775baa2
44
actypes.d
44
actypes.d
|
@ -18,15 +18,15 @@
|
|||
|
||||
module actypes;
|
||||
|
||||
import stdx.d.lexer;
|
||||
import stdx.d.ast;
|
||||
import std.algorithm;
|
||||
import std.stdio;
|
||||
import std.array;
|
||||
import messages;
|
||||
import std.array;
|
||||
import std.typecons;
|
||||
import std.container;
|
||||
import std.stdio;
|
||||
import std.typecons;
|
||||
|
||||
import stdx.d.lexer;
|
||||
|
||||
import messages;
|
||||
|
||||
/**
|
||||
* Compares symbols by their name
|
||||
|
@ -307,16 +307,6 @@ RedBlackTree!(ACSymbol*, comparitor, true) aggregateSymbols;
|
|||
*/
|
||||
RedBlackTree!(ACSymbol*, comparitor, true) classSymbols;
|
||||
|
||||
/**
|
||||
* Type of the _argptr variable
|
||||
*/
|
||||
Type argptrType;
|
||||
|
||||
/**
|
||||
* Type of _arguments
|
||||
*/
|
||||
Type argumentsType;
|
||||
|
||||
/**
|
||||
* Initializes builtin types and the various properties of builtin types
|
||||
*/
|
||||
|
@ -451,28 +441,6 @@ static this()
|
|||
ulong_, ushort_, wchar_, cdouble_, cent_, cfloat_, creal_, double_,
|
||||
float_, idouble_, ifloat_, ireal_, real_, ucent_, void_]);
|
||||
|
||||
// _argptr has type void*
|
||||
argptrType = new Type;
|
||||
argptrType.type2 = new Type2;
|
||||
argptrType.type2.builtinType = tok!"void";
|
||||
TypeSuffix argptrTypeSuffix = new TypeSuffix;
|
||||
argptrTypeSuffix.star = true;
|
||||
argptrType.typeSuffixes ~= argptrTypeSuffix;
|
||||
|
||||
// _arguments has type TypeInfo[]
|
||||
argumentsType = new Type;
|
||||
argumentsType = new Type;
|
||||
argumentsType.type2 = new Type2;
|
||||
argumentsType.type2.symbol = new Symbol;
|
||||
argumentsType.type2.symbol.identifierOrTemplateChain = new IdentifierOrTemplateChain;
|
||||
IdentifierOrTemplateInstance i = new IdentifierOrTemplateInstance;
|
||||
i.identifier.text = "TypeInfo";
|
||||
i.identifier.type = tok!"identifier";
|
||||
argumentsType.type2.symbol.identifierOrTemplateChain.identifiersOrTemplateInstances ~= i;
|
||||
TypeSuffix argumentsTypeSuffix = new TypeSuffix;
|
||||
argumentsTypeSuffix.array = true;
|
||||
argumentsType.typeSuffixes ~= argptrTypeSuffix;
|
||||
|
||||
builtinSymbols = bSym;
|
||||
arraySymbols = arrSym;
|
||||
assocArraySymbols = asarrSym;
|
||||
|
|
956
astconverter.d
956
astconverter.d
|
@ -1,956 +0,0 @@
|
|||
/**
|
||||
* This file is part of DCD, a development tool for the D programming language.
|
||||
* Copyright (C) 2014 Brian Schott
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module astconverter;
|
||||
|
||||
import std.algorithm;
|
||||
import std.array;
|
||||
import std.conv;
|
||||
import std.path;
|
||||
import std.range;
|
||||
import std.typecons;
|
||||
|
||||
import stdx.d.ast;
|
||||
import stdx.d.lexer;
|
||||
import stdx.d.parser;
|
||||
|
||||
import actypes;
|
||||
import constants;
|
||||
import messages;
|
||||
import semantic;
|
||||
import stupidlog;
|
||||
import modulecache;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
mod = null;
|
||||
}
|
||||
|
||||
override void visit(Unittest u)
|
||||
{
|
||||
// Create a dummy symbol because we don't want unit test symbols leaking
|
||||
// into the symbol they're declared in.
|
||||
SemanticSymbol* s = new SemanticSymbol("*unittest*",
|
||||
CompletionKind.dummy, null, 0);
|
||||
s.parent = currentSymbol;
|
||||
currentSymbol = s;
|
||||
u.accept(this);
|
||||
currentSymbol = s.parent;
|
||||
}
|
||||
|
||||
override void visit(Constructor con)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
||||
visitConstructor(con.location, con.parameters, con.functionBody, con.comment);
|
||||
}
|
||||
|
||||
override void visit(SharedStaticConstructor con)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
||||
visitConstructor(con.location, null, con.functionBody, con.comment);
|
||||
}
|
||||
|
||||
override void visit(StaticConstructor con)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
||||
visitConstructor(con.location, null, con.functionBody, con.comment);
|
||||
}
|
||||
|
||||
override void visit(Destructor des)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
||||
visitDestructor(des.location, des.functionBody, des.comment);
|
||||
}
|
||||
|
||||
override void visit(SharedStaticDestructor des)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
||||
visitDestructor(des.location, des.functionBody, des.comment);
|
||||
}
|
||||
|
||||
override void visit(StaticDestructor des)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
||||
visitDestructor(des.location, des.functionBody, des.comment);
|
||||
}
|
||||
|
||||
override void visit(FunctionDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
SemanticSymbol* symbol = new SemanticSymbol(getCached(dec.name.text),
|
||||
CompletionKind.functionName, symbolFile, dec.name.index);
|
||||
processParameters(symbol, dec.returnType, symbol.acSymbol.name,
|
||||
dec.parameters, dec.comment);
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = dec.comment;
|
||||
currentSymbol.addChild(symbol);
|
||||
if (dec.functionBody !is null)
|
||||
{
|
||||
currentSymbol = symbol;
|
||||
dec.functionBody.accept(this);
|
||||
currentSymbol = symbol.parent;
|
||||
}
|
||||
}
|
||||
|
||||
override void visit(ClassDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.className);
|
||||
}
|
||||
|
||||
override void visit(TemplateDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.templateName);
|
||||
}
|
||||
|
||||
override void visit(InterfaceDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.interfaceName);
|
||||
}
|
||||
|
||||
override void visit(UnionDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.unionName);
|
||||
}
|
||||
|
||||
override void visit(StructDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.structName);
|
||||
}
|
||||
|
||||
override void visit(BaseClass bc)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(bc).stringof);
|
||||
currentSymbol.baseClasses ~= iotcToStringArray(bc.identifierOrTemplateChain);
|
||||
}
|
||||
|
||||
override void visit(VariableDeclaration dec)
|
||||
{
|
||||
assert (currentSymbol);
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
Type t = dec.type;
|
||||
foreach (declarator; dec.declarators)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol(
|
||||
getCached(declarator.name.text),
|
||||
CompletionKind.variableName,
|
||||
symbolFile,
|
||||
declarator.name.index);
|
||||
symbol.type = t;
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = dec.comment;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
override void visit(AliasDeclaration aliasDeclaration)
|
||||
{
|
||||
if (aliasDeclaration.initializers.length == 0)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol(
|
||||
getCached(aliasDeclaration.name.text),
|
||||
CompletionKind.aliasName,
|
||||
symbolFile,
|
||||
aliasDeclaration.name.index);
|
||||
symbol.type = aliasDeclaration.type;
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (initializer; aliasDeclaration.initializers)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol(
|
||||
getCached(initializer.name.text),
|
||||
CompletionKind.aliasName,
|
||||
symbolFile,
|
||||
initializer.name.index);
|
||||
symbol.type = initializer.type;
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override void visit(AliasThisDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
currentSymbol.aliasThis ~= getCached(dec.identifier.text);
|
||||
}
|
||||
|
||||
override void visit(Declaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
if (dec.attributeDeclaration !is null
|
||||
&& isProtection(dec.attributeDeclaration.attribute.attribute))
|
||||
{
|
||||
protection = dec.attributeDeclaration.attribute.attribute;
|
||||
return;
|
||||
}
|
||||
IdType p = protection;
|
||||
foreach (Attribute attr; dec.attributes)
|
||||
{
|
||||
if (isProtection(attr.attribute))
|
||||
protection = attr.attribute;
|
||||
}
|
||||
dec.accept(this);
|
||||
protection = p;
|
||||
}
|
||||
|
||||
override void visit(Module mod)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(mod).stringof);
|
||||
//
|
||||
currentSymbol = new SemanticSymbol(null, CompletionKind.moduleName,
|
||||
symbolFile);
|
||||
rootSymbol = currentSymbol;
|
||||
currentScope = new Scope(0, size_t.max);
|
||||
ImportInformation i;
|
||||
i.modulePath = "object";
|
||||
i.importParts ~= "object";
|
||||
currentScope.importInformation ~= i;
|
||||
moduleScope = currentScope;
|
||||
mod.accept(this);
|
||||
}
|
||||
|
||||
override void visit(EnumDeclaration dec)
|
||||
{
|
||||
assert (currentSymbol);
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
SemanticSymbol* symbol = new SemanticSymbol(getCached(dec.name.text),
|
||||
CompletionKind.enumName, symbolFile, dec.name.index);
|
||||
symbol.type = dec.type;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = dec.comment;
|
||||
currentSymbol = symbol;
|
||||
if (dec.enumBody !is null)
|
||||
dec.enumBody.accept(this);
|
||||
currentSymbol = symbol.parent;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
|
||||
override void visit(EnumMember member)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(member).stringof);
|
||||
SemanticSymbol* symbol = new SemanticSymbol(getCached(member.name.text),
|
||||
CompletionKind.enumMember, symbolFile, member.name.index);
|
||||
symbol.type = member.type;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = member.comment;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
|
||||
override void visit(ModuleDeclaration moduleDeclaration)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
foreach (identifier; moduleDeclaration.moduleName.identifiers)
|
||||
{
|
||||
moduleName ~= getCached(identifier.text);
|
||||
}
|
||||
}
|
||||
|
||||
// creates scopes for
|
||||
override void visit(StructBody structBody)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(structBody).stringof);
|
||||
Scope* s = new Scope(structBody.startLocation, structBody.endLocation);
|
||||
// Log.trace("Added scope ", s.startLocation, " ", s.endLocation);
|
||||
|
||||
ACSymbol* thisSymbol = new ACSymbol("this", CompletionKind.variableName,
|
||||
currentSymbol.acSymbol);
|
||||
thisSymbol.location = s.startLocation;
|
||||
thisSymbol.symbolFile = symbolFile;
|
||||
currentSymbol.acSymbol.parts.insert(thisSymbol);
|
||||
|
||||
s.parent = currentScope;
|
||||
currentScope = s;
|
||||
foreach (dec; structBody.declarations)
|
||||
visit(dec);
|
||||
currentScope = s.parent;
|
||||
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.importParts = single.identifierChain.identifiers.map!(a => a.text).array;
|
||||
info.modulePath = convertChainToImportPath(single.identifierChain);
|
||||
info.isPublic = protection == tok!"public";
|
||||
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);
|
||||
info.importParts = importDeclaration.importBindings.singleImport
|
||||
.identifierChain.identifiers.map!(a => a.text).array;
|
||||
foreach (bind; importDeclaration.importBindings.importBinds)
|
||||
{
|
||||
Tuple!(string, string) bindTuple;
|
||||
bindTuple[0] = getCached(bind.left.text);
|
||||
bindTuple[1] = bind.right == tok!"" ? null : getCached(bind.right.text);
|
||||
info.importedSymbols ~= bindTuple;
|
||||
}
|
||||
info.isPublic = protection == tok!"public";
|
||||
currentScope.importInformation ~= info;
|
||||
}
|
||||
|
||||
// Create scope for block statements
|
||||
override void visit(BlockStatement blockStatement)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(blockStatement).stringof);
|
||||
Scope* s = new Scope(blockStatement.startLocation,
|
||||
blockStatement.endLocation);
|
||||
s.parent = currentScope;
|
||||
currentScope.children ~= s;
|
||||
|
||||
if (currentSymbol.acSymbol.kind == CompletionKind.functionName)
|
||||
{
|
||||
foreach (child; currentSymbol.children)
|
||||
{
|
||||
if (child.acSymbol.location == size_t.max)
|
||||
{
|
||||
// Log.trace("Reassigning location of ", child.acSymbol.name);
|
||||
child.acSymbol.location = s.startLocation + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blockStatement.declarationsAndStatements !is null)
|
||||
{
|
||||
currentScope = s;
|
||||
visit (blockStatement.declarationsAndStatements);
|
||||
currentScope = s.parent;
|
||||
}
|
||||
}
|
||||
|
||||
override void visit(VersionCondition versionCondition)
|
||||
{
|
||||
// TODO: This is a bit of a hack
|
||||
if (predefinedVersions.canFind(versionCondition.token.text))
|
||||
versionCondition.accept(this);
|
||||
}
|
||||
|
||||
alias visit = ASTVisitor.visit;
|
||||
|
||||
private:
|
||||
|
||||
void visitAggregateDeclaration(AggType)(AggType dec, CompletionKind kind)
|
||||
{
|
||||
// Log.trace("visiting aggregate declaration ", dec.name.text);
|
||||
SemanticSymbol* symbol = new SemanticSymbol(getCached(dec.name.text),
|
||||
kind, symbolFile, dec.name.index);
|
||||
if (kind == CompletionKind.className)
|
||||
symbol.acSymbol.parts.insert(classSymbols[]);
|
||||
else
|
||||
symbol.acSymbol.parts.insert(aggregateSymbols[]);
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.protection = protection;
|
||||
symbol.acSymbol.doc = dec.comment;
|
||||
currentSymbol = symbol;
|
||||
dec.accept(this);
|
||||
currentSymbol = symbol.parent;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
|
||||
void visitConstructor(size_t location, Parameters parameters,
|
||||
FunctionBody functionBody, string doc)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol("*constructor*",
|
||||
CompletionKind.functionName, symbolFile, location);
|
||||
processParameters(symbol, null, "this", parameters, doc);
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = doc;
|
||||
currentSymbol.addChild(symbol);
|
||||
if (functionBody !is null)
|
||||
{
|
||||
currentSymbol = symbol;
|
||||
functionBody.accept(this);
|
||||
currentSymbol = symbol.parent;
|
||||
}
|
||||
}
|
||||
|
||||
void visitDestructor(size_t location, FunctionBody functionBody, string doc)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol("~this",
|
||||
CompletionKind.functionName, symbolFile, location);
|
||||
symbol.acSymbol.callTip = "~this()";
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = doc;
|
||||
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, string doc) const
|
||||
{
|
||||
if (parameters !is null)
|
||||
{
|
||||
foreach (Parameter p; parameters.parameters)
|
||||
{
|
||||
SemanticSymbol* parameter = new SemanticSymbol(getCached(p.name.text),
|
||||
CompletionKind.variableName, symbolFile, size_t.max);
|
||||
parameter.type = p.type;
|
||||
symbol.addChild(parameter);
|
||||
parameter.parent = symbol;
|
||||
}
|
||||
if (parameters.hasVarargs)
|
||||
{
|
||||
SemanticSymbol* argptr = new SemanticSymbol("_argptr",
|
||||
CompletionKind.variableName, null, size_t.max);
|
||||
argptr.type = argptrType;
|
||||
argptr.parent = symbol;
|
||||
symbol.addChild(argptr);
|
||||
|
||||
SemanticSymbol* arguments = new SemanticSymbol("_arguments",
|
||||
CompletionKind.variableName, null, size_t.max);
|
||||
arguments.type = argumentsType;
|
||||
arguments.parent = symbol;
|
||||
symbol.addChild(arguments);
|
||||
}
|
||||
}
|
||||
symbol.acSymbol.callTip = formatCallTip(returnType, functionName,
|
||||
parameters, doc);
|
||||
symbol.type = returnType;
|
||||
}
|
||||
|
||||
static string formatCallTip(Type returnType, string name, Parameters parameters,
|
||||
string doc = null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// Current protection type
|
||||
IdType protection;
|
||||
|
||||
/// Current symbol
|
||||
SemanticSymbol* currentSymbol;
|
||||
|
||||
/// The module
|
||||
SemanticSymbol* rootSymbol;
|
||||
|
||||
/// Package and module name
|
||||
string[] moduleName;
|
||||
|
||||
/// Current scope
|
||||
Scope* currentScope;
|
||||
|
||||
/// Module scope
|
||||
Scope* moduleScope;
|
||||
|
||||
/// Path to the file being converted
|
||||
string symbolFile;
|
||||
|
||||
Module mod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Second pass handles the following:
|
||||
* $(UL
|
||||
* $(LI Import statements)
|
||||
* $(LI assigning symbols to scopes)
|
||||
* )
|
||||
*/
|
||||
struct SecondPass
|
||||
{
|
||||
public:
|
||||
|
||||
this(SemanticSymbol* rootSymbol, Scope* moduleScope)
|
||||
{
|
||||
this.rootSymbol = rootSymbol;
|
||||
this.moduleScope = moduleScope;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
assignToScopes(rootSymbol.acSymbol);
|
||||
resolveImports(moduleScope);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void assignToScopes(ACSymbol* currentSymbol)
|
||||
{
|
||||
Scope* s = moduleScope.getScopeByCursor(currentSymbol.location);
|
||||
s.symbols.insert(currentSymbol);
|
||||
foreach (part; currentSymbol.parts[])
|
||||
assignToScopes(part);
|
||||
}
|
||||
|
||||
// This method is really ugly due to the casts...
|
||||
static ACSymbol* createImportSymbols(ImportInformation info,
|
||||
Scope* currentScope)
|
||||
{
|
||||
immutable string firstPart = info.importParts[0];
|
||||
ACSymbol*[] symbols = currentScope.getSymbolsByName(firstPart);
|
||||
immutable bool found = symbols.length > 0;
|
||||
ACSymbol* firstSymbol = found
|
||||
? symbols[0] : new ACSymbol(firstPart, CompletionKind.packageName);
|
||||
if (!found)
|
||||
{
|
||||
currentScope.symbols.insert(firstSymbol);
|
||||
}
|
||||
ACSymbol* currentSymbol = cast(ACSymbol*) firstSymbol;
|
||||
foreach (size_t i, string importPart; info.importParts[1 .. $])
|
||||
{
|
||||
symbols = currentSymbol.getPartsByName(importPart);
|
||||
ACSymbol* s = symbols.length > 0
|
||||
? cast(ACSymbol*) symbols[0] : new ACSymbol(importPart, CompletionKind.packageName);
|
||||
currentSymbol.parts.insert(s);
|
||||
currentSymbol = s;
|
||||
}
|
||||
currentSymbol.kind = CompletionKind.moduleName;
|
||||
return currentSymbol;
|
||||
}
|
||||
|
||||
void resolveImports(Scope* currentScope)
|
||||
{
|
||||
foreach (importInfo; currentScope.importInformation)
|
||||
{
|
||||
ACSymbol*[] symbols = ModuleCache.getSymbolsInModule(
|
||||
ModuleCache.resolveImportLoctation(importInfo.modulePath));
|
||||
ACSymbol* moduleSymbol = createImportSymbols(importInfo, currentScope);
|
||||
currentScope.symbols.insert(moduleSymbol);
|
||||
currentScope.symbols.insert(symbols);
|
||||
if (importInfo.importedSymbols.length == 0)
|
||||
{
|
||||
if (importInfo.isPublic && currentScope.parent is null)
|
||||
{
|
||||
rootSymbol.acSymbol.parts.insert(symbols);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
symbolLoop: foreach (symbol; symbols)
|
||||
{
|
||||
foreach (tup; importInfo.importedSymbols)
|
||||
{
|
||||
if (tup[0] != symbol.name)
|
||||
continue symbolLoop;
|
||||
if (tup[1] !is null)
|
||||
{
|
||||
ACSymbol* s = new ACSymbol(tup[1],
|
||||
symbol.kind, symbol.type);
|
||||
// TODO: Compiler gets confused here, so cast the types.
|
||||
s.parts = cast(typeof(s.parts)) symbol.parts;
|
||||
// TODO: Re-format callTip with new name?
|
||||
s.callTip = symbol.callTip;
|
||||
s.doc = symbol.doc;
|
||||
s.qualifier = symbol.qualifier;
|
||||
s.location = symbol.location;
|
||||
s.symbolFile = symbol.symbolFile;
|
||||
currentScope.symbols.insert(s);
|
||||
moduleSymbol.parts.insert(s);
|
||||
if (importInfo.isPublic && currentScope.parent is null)
|
||||
rootSymbol.acSymbol.parts.insert(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
moduleSymbol.parts.insert(symbol);
|
||||
currentScope.symbols.insert(symbol);
|
||||
if (importInfo.isPublic && currentScope.parent is null)
|
||||
rootSymbol.acSymbol.parts.insert(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (childScope; currentScope.children)
|
||||
resolveImports(childScope);
|
||||
}
|
||||
|
||||
SemanticSymbol* rootSymbol;
|
||||
Scope* moduleScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Third pass handles the following:
|
||||
* $(UL
|
||||
* $(LI types)
|
||||
* $(LI base classes)
|
||||
* $(LI mixin templates)
|
||||
* $(LI alias this)
|
||||
* $(LI alias declarations)
|
||||
* )
|
||||
*/
|
||||
struct ThirdPass
|
||||
{
|
||||
public:
|
||||
this(SemanticSymbol* rootSymbol, Scope* moduleScope) pure
|
||||
{
|
||||
this.rootSymbol = rootSymbol;
|
||||
this.moduleScope = moduleScope;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
thirdPass(rootSymbol);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void thirdPass(SemanticSymbol* currentSymbol)
|
||||
{
|
||||
// Log.trace("third pass on ", currentSymbol.acSymbol.name);
|
||||
with (CompletionKind) final switch (currentSymbol.acSymbol.kind)
|
||||
{
|
||||
case className:
|
||||
case interfaceName:
|
||||
resolveInheritance(currentSymbol);
|
||||
goto case structName;
|
||||
case structName:
|
||||
case unionName:
|
||||
resolveAliasThis(currentSymbol);
|
||||
resolveMixinTemplates(currentSymbol);
|
||||
break;
|
||||
case variableName:
|
||||
case memberVariableName:
|
||||
case functionName:
|
||||
case aliasName:
|
||||
ACSymbol* t = resolveType(currentSymbol.type,
|
||||
currentSymbol.acSymbol.location);
|
||||
while (t !is null && t.kind == CompletionKind.aliasName)
|
||||
t = t.type;
|
||||
currentSymbol.acSymbol.type = t;
|
||||
break;
|
||||
case enumName:
|
||||
case keyword:
|
||||
case enumMember:
|
||||
case packageName:
|
||||
case moduleName:
|
||||
case dummy:
|
||||
case array:
|
||||
case assocArray:
|
||||
case templateName:
|
||||
case mixinTemplateName:
|
||||
break;
|
||||
}
|
||||
foreach (child; currentSymbol.children)
|
||||
{
|
||||
thirdPass(child);
|
||||
}
|
||||
}
|
||||
|
||||
void resolveInheritance(SemanticSymbol* currentSymbol)
|
||||
{
|
||||
// Log.trace("Resolving inheritance for ", currentSymbol.acSymbol.name);
|
||||
outer: foreach (string[] base; currentSymbol.baseClasses)
|
||||
{
|
||||
ACSymbol* baseClass;
|
||||
if (base.length == 0)
|
||||
continue;
|
||||
auto symbols = moduleScope.getSymbolsByNameAndCursor(
|
||||
base[0], currentSymbol.acSymbol.location);
|
||||
if (symbols.length == 0)
|
||||
continue;
|
||||
baseClass = symbols[0];
|
||||
foreach (part; base[1..$])
|
||||
{
|
||||
symbols = baseClass.getPartsByName(part);
|
||||
if (symbols.length == 0)
|
||||
continue outer;
|
||||
baseClass = symbols[0];
|
||||
}
|
||||
currentSymbol.acSymbol.parts.insert(baseClass.parts[]);
|
||||
}
|
||||
}
|
||||
|
||||
void resolveAliasThis(SemanticSymbol* currentSymbol)
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
void resolveMixinTemplates(SemanticSymbol* currentSymbol)
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
ACSymbol* resolveType(Type t, size_t location)
|
||||
{
|
||||
if (t is null) return null;
|
||||
if (t.type2 is null) return null;
|
||||
ACSymbol* s;
|
||||
if (t.type2.builtinType != tok!"")
|
||||
s = convertBuiltinType(t.type2);
|
||||
else if (t.type2.typeConstructor != tok!"")
|
||||
s = resolveType(t.type2.type, location);
|
||||
else if (t.type2.symbol !is null)
|
||||
{
|
||||
// TODO: global scoped symbol handling
|
||||
string[] symbolParts = expandSymbol(
|
||||
t.type2.symbol.identifierOrTemplateChain);
|
||||
auto symbols = moduleScope.getSymbolsByNameAndCursor(
|
||||
symbolParts[0], location);
|
||||
if (symbols.length == 0)
|
||||
goto resolveSuffixes;
|
||||
s = symbols[0];
|
||||
foreach (symbolPart; symbolParts[1..$])
|
||||
{
|
||||
auto parts = s.getPartsByName(symbolPart);
|
||||
if (parts.length == 0)
|
||||
goto resolveSuffixes;
|
||||
s = parts[0];
|
||||
}
|
||||
}
|
||||
resolveSuffixes:
|
||||
foreach (suffix; t.typeSuffixes)
|
||||
s = processSuffix(s, suffix);
|
||||
return s;
|
||||
}
|
||||
|
||||
static string[] expandSymbol(const IdentifierOrTemplateChain chain)
|
||||
{
|
||||
string[] strings = new string[chain.identifiersOrTemplateInstances.length];
|
||||
for (size_t i = 0; i != chain.identifiersOrTemplateInstances.length; ++i)
|
||||
{
|
||||
auto identOrTemplate = chain.identifiersOrTemplateInstances[i];
|
||||
if (identOrTemplate is null)
|
||||
continue;
|
||||
strings[i] = getCached(identOrTemplate.templateInstance is null ?
|
||||
identOrTemplate.identifier.text
|
||||
: identOrTemplate.templateInstance.identifier.text);
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
static ACSymbol* processSuffix(ACSymbol* symbol, const TypeSuffix suffix)
|
||||
{
|
||||
import std.container;
|
||||
if (suffix.star)
|
||||
return symbol;
|
||||
if (suffix.array || suffix.type)
|
||||
{
|
||||
ACSymbol* s = new ACSymbol(null);
|
||||
s.parts = new RedBlackTree!(ACSymbol*, comparitor, true);
|
||||
s.parts.insert(suffix.array ? (cast() arraySymbols)[]
|
||||
: (cast() assocArraySymbols)[]);
|
||||
s.type = symbol;
|
||||
s.qualifier = suffix.array ? SymbolQualifier.array : SymbolQualifier.assocArray;
|
||||
return s;
|
||||
}
|
||||
if (suffix.parameters)
|
||||
{
|
||||
ACSymbol* s = new ACSymbol(null);
|
||||
s.type = symbol;
|
||||
s.qualifier = SymbolQualifier.func;
|
||||
s.callTip = suffix.delegateOrFunction.text ~ formatNode(suffix.parameters);
|
||||
return s;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static ACSymbol* convertBuiltinType(const Type2 type2)
|
||||
{
|
||||
string stringRepresentation = str(type2.builtinType);
|
||||
if (stringRepresentation is null) return null;
|
||||
// TODO: Make this use binary search instead
|
||||
auto t = cast() builtinSymbols;
|
||||
ACSymbol s = ACSymbol(stringRepresentation);
|
||||
return t.equalRange(&s).front();
|
||||
}
|
||||
|
||||
SemanticSymbol* rootSymbol;
|
||||
Scope* moduleScope;
|
||||
}
|
||||
|
||||
ACSymbol*[] convertAstToSymbols(const(Token)[] tokens, string symbolFile)
|
||||
{
|
||||
Module m = parseModuleSimple(tokens, symbolFile);
|
||||
|
||||
FirstPass first = new FirstPass(m, symbolFile);
|
||||
first.run();
|
||||
|
||||
SecondPass second = SecondPass(first.rootSymbol, first.moduleScope);
|
||||
second.run();
|
||||
|
||||
ThirdPass third = ThirdPass(second.rootSymbol, second.moduleScope);
|
||||
third.run();
|
||||
|
||||
return third.rootSymbol.acSymbol.parts.array();
|
||||
}
|
||||
|
||||
const(Scope)* generateAutocompleteTrees(const(Token)[] tokens, string symbolFile)
|
||||
{
|
||||
Module m = parseModule(tokens, "editor buffer", &doesNothing);
|
||||
|
||||
FirstPass first = new FirstPass(m, symbolFile);
|
||||
first.run();
|
||||
|
||||
SecondPass second = SecondPass(first.rootSymbol, first.currentScope);
|
||||
second.run();
|
||||
|
||||
ThirdPass third = ThirdPass(second.rootSymbol, second.moduleScope);
|
||||
third.run();
|
||||
|
||||
return cast(typeof(return)) third.moduleScope;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Module parseModuleSimple(const(Token)[] tokens, string fileName)
|
||||
{
|
||||
auto parser = new SimpleParser();
|
||||
parser.fileName = fileName;
|
||||
parser.tokens = tokens;
|
||||
parser.messageFunction = &doesNothing;
|
||||
auto mod = parser.parseModule();
|
||||
return mod;
|
||||
}
|
||||
|
||||
class SimpleParser : Parser
|
||||
{
|
||||
override Unittest parseUnittest()
|
||||
{
|
||||
expect(tok!"unittest");
|
||||
skipBraces();
|
||||
return null;
|
||||
}
|
||||
|
||||
override FunctionBody parseFunctionBody()
|
||||
{
|
||||
if (currentIs(tok!";"))
|
||||
advance();
|
||||
else if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
else
|
||||
{
|
||||
if (currentIs(tok!"in"))
|
||||
{
|
||||
advance();
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
if (currentIs(tok!"out"))
|
||||
{
|
||||
advance();
|
||||
if (currentIs(tok!"("))
|
||||
skipParens();
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
}
|
||||
}
|
||||
else if (currentIs(tok!"out"))
|
||||
{
|
||||
advance();
|
||||
if (currentIs(tok!"("))
|
||||
skipParens();
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
if (currentIs(tok!"in"))
|
||||
{
|
||||
advance();
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
}
|
||||
}
|
||||
expect(tok!"body");
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
string[] iotcToStringArray(const IdentifierOrTemplateChain iotc)
|
||||
{
|
||||
string[] parts;
|
||||
foreach (ioti; iotc.identifiersOrTemplateInstances)
|
||||
{
|
||||
if (ioti.identifier != tok!"")
|
||||
parts ~= getCached(ioti.identifier.text);
|
||||
else
|
||||
parts ~= getCached(ioti.templateInstance.identifier.text);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
private static string convertChainToImportPath(IdentifierChain chain)
|
||||
{
|
||||
return to!string(chain.identifiers.map!(a => a.text).join(dirSeparator).array);
|
||||
}
|
||||
|
||||
version(unittest) Module parseTestCode(string code)
|
||||
{
|
||||
LexerConfig config;
|
||||
const(Token)[] tokens = byToken(cast(ubyte[]) code, config).array();
|
||||
Module m = parseModule(tokens, "unittest");
|
||||
return m;
|
||||
}
|
||||
|
||||
string formatNode(T)(T node)
|
||||
{
|
||||
if (node is null) return "";
|
||||
import formatter;
|
||||
auto app = appender!(char[])();
|
||||
auto f = new Formatter!(typeof(app))(app);
|
||||
f.format(node);
|
||||
return to!string(app.data);
|
||||
}
|
||||
|
||||
private void doesNothing(string a, size_t b, size_t c, string d, bool e) {}
|
||||
|
||||
/**
|
||||
* Dummy doc comment for getCached
|
||||
*/
|
||||
string getCached(string s)
|
||||
{
|
||||
return s.length == 0 ? ""
|
||||
: ModuleCache.stringCache.cacheGet(cast(const(ubyte)[]) s);
|
||||
}
|
|
@ -35,7 +35,7 @@ import messages;
|
|||
import actypes;
|
||||
import constants;
|
||||
import modulecache;
|
||||
import astconverter;
|
||||
import conversion.astconverter;
|
||||
import stupidlog;
|
||||
|
||||
/**
|
||||
|
@ -191,7 +191,7 @@ auto getTokensBeforeCursor(const(ubyte[]) sourceCode, size_t cursorPosition,
|
|||
{
|
||||
LexerConfig config;
|
||||
config.fileName = "stdin";
|
||||
StringCache* cache = new StringCache(StringCache.defaultBucketCount);
|
||||
shared(StringCache)* cache = new shared StringCache(StringCache.defaultBucketCount);
|
||||
auto tokens = byToken(cast(ubyte[]) sourceCode, config, cache);
|
||||
tokenArray = tokens.array();
|
||||
auto sortedTokens = assumeSorted(tokenArray);
|
||||
|
|
13
build.sh
13
build.sh
|
@ -1,5 +1,6 @@
|
|||
dmd -wi client.d\
|
||||
messages.d\
|
||||
stupidlog.d\
|
||||
msgpack-d/src/msgpack.d\
|
||||
-Imsgpack-d/src\
|
||||
-release -inline -noboundscheck -O\
|
||||
|
@ -7,7 +8,10 @@ dmd -wi client.d\
|
|||
|
||||
dmd \
|
||||
actypes.d\
|
||||
astconverter.d\
|
||||
conversion/astconverter.d\
|
||||
conversion/first.d\
|
||||
conversion/second.d\
|
||||
conversion/third.d\
|
||||
autocomplete.d\
|
||||
constants.d\
|
||||
messages.d\
|
||||
|
@ -16,15 +20,16 @@ dmd \
|
|||
server.d\
|
||||
stupidlog.d\
|
||||
dscanner/stdx/d/ast.d\
|
||||
dscanner/stdx/d/entities.d\
|
||||
dscanner/stdx/d/lexer.d\
|
||||
dscanner/stdx/d/parser.d\
|
||||
dscanner/stdx/lexer.d\
|
||||
dscanner/stdx/d/lexer.d\
|
||||
dscanner/stdx/d/entities.d\
|
||||
dscanner/stdx/allocator.d\
|
||||
dscanner/formatter.d\
|
||||
msgpack-d/src/msgpack.d\
|
||||
-Imsgpack-d/src\
|
||||
-Idscanner\
|
||||
-wi\
|
||||
-wi -g\
|
||||
-O -release -noboundscheck -inline\
|
||||
-ofdcd-server
|
||||
|
||||
|
|
3
client.d
3
client.d
|
@ -31,6 +31,7 @@ import std.string;
|
|||
|
||||
import msgpack;
|
||||
import messages;
|
||||
import stupidlog;
|
||||
|
||||
int main(string[] args)
|
||||
{
|
||||
|
@ -52,7 +53,7 @@ int main(string[] args)
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
stderr.writeln(e.msg);
|
||||
Log.fatal(e.msg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* This file is part of DCD, a development tool for the D programming language.
|
||||
* Copyright (C) 2014 Brian Schott
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module conversion.astconverter;
|
||||
|
||||
import stdx.d.lexer;
|
||||
import stdx.d.ast;
|
||||
import stdx.d.parser;
|
||||
|
||||
import conversion.first;
|
||||
import conversion.second;
|
||||
import conversion.third;
|
||||
import actypes;
|
||||
|
||||
|
||||
const(Scope)* generateAutocompleteTrees(const(Token)[] tokens, string symbolFile)
|
||||
{
|
||||
ParseAllocator p = new ParseAllocator;
|
||||
Module m = parseModule(tokens, "editor buffer", p, &doesNothing);
|
||||
shared(StringCache)* cache = new shared StringCache(StringCache.defaultBucketCount);
|
||||
FirstPass first = new FirstPass(m, symbolFile, cache);
|
||||
first.run();
|
||||
|
||||
SecondPass second = SecondPass(first);
|
||||
second.run();
|
||||
|
||||
ThirdPass third = ThirdPass(second);
|
||||
third.run();
|
||||
|
||||
p.deallocateAll();
|
||||
return cast(typeof(return)) third.moduleScope;
|
||||
}
|
||||
|
||||
Module parseModuleSimple(const(Token)[] tokens, string fileName, ParseAllocator p)
|
||||
{
|
||||
auto parser = new SimpleParser();
|
||||
parser.fileName = fileName;
|
||||
parser.tokens = tokens;
|
||||
parser.messageFunction = &doesNothing;
|
||||
parser.allocator = p;
|
||||
return parser.parseModule();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
class SimpleParser : Parser
|
||||
{
|
||||
override Unittest parseUnittest()
|
||||
{
|
||||
expect(tok!"unittest");
|
||||
skipBraces();
|
||||
return null;
|
||||
}
|
||||
|
||||
override FunctionBody parseFunctionBody()
|
||||
{
|
||||
if (currentIs(tok!";"))
|
||||
advance();
|
||||
else if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
else
|
||||
{
|
||||
if (currentIs(tok!"in"))
|
||||
{
|
||||
advance();
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
if (currentIs(tok!"out"))
|
||||
{
|
||||
advance();
|
||||
if (currentIs(tok!"("))
|
||||
skipParens();
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
}
|
||||
}
|
||||
else if (currentIs(tok!"out"))
|
||||
{
|
||||
advance();
|
||||
if (currentIs(tok!"("))
|
||||
skipParens();
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
if (currentIs(tok!"in"))
|
||||
{
|
||||
advance();
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
}
|
||||
}
|
||||
expect(tok!"body");
|
||||
if (currentIs(tok!"{"))
|
||||
skipBraces();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void doesNothing(string a, size_t b, size_t c, string d, bool e) {}
|
|
@ -0,0 +1,539 @@
|
|||
/**
|
||||
* This file is part of DCD, a development tool for the D programming language.
|
||||
* Copyright (C) 2014 Brian Schott
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module conversion.first;
|
||||
|
||||
import stdx.d.ast;
|
||||
import stdx.d.lexer;
|
||||
import actypes;
|
||||
import semantic;
|
||||
import messages;
|
||||
import stupidlog;
|
||||
|
||||
/**
|
||||
* 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, shared(StringCache)* stringCache)
|
||||
{
|
||||
this.symbolFile = symbolFile;
|
||||
this.mod = mod;
|
||||
this.stringCache = stringCache;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
visit(mod);
|
||||
mod = null;
|
||||
}
|
||||
|
||||
override void visit(const Unittest u)
|
||||
{
|
||||
// Create a dummy symbol because we don't want unit test symbols leaking
|
||||
// into the symbol they're declared in.
|
||||
SemanticSymbol* s = new SemanticSymbol("*unittest*",
|
||||
CompletionKind.dummy, null, 0);
|
||||
s.parent = currentSymbol;
|
||||
currentSymbol = s;
|
||||
u.accept(this);
|
||||
currentSymbol = s.parent;
|
||||
}
|
||||
|
||||
override void visit(const Constructor con)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
||||
visitConstructor(con.location, con.parameters, con.functionBody, con.comment);
|
||||
}
|
||||
|
||||
override void visit(const SharedStaticConstructor con)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
||||
visitConstructor(con.location, null, con.functionBody, con.comment);
|
||||
}
|
||||
|
||||
override void visit(const StaticConstructor con)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
|
||||
visitConstructor(con.location, null, con.functionBody, con.comment);
|
||||
}
|
||||
|
||||
override void visit(const Destructor des)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
||||
visitDestructor(des.location, des.functionBody, des.comment);
|
||||
}
|
||||
|
||||
override void visit(const SharedStaticDestructor des)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
||||
visitDestructor(des.location, des.functionBody, des.comment);
|
||||
}
|
||||
|
||||
override void visit(const StaticDestructor des)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
|
||||
visitDestructor(des.location, des.functionBody, des.comment);
|
||||
}
|
||||
|
||||
override void visit(const FunctionDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
SemanticSymbol* symbol = new SemanticSymbol(stringCache.intern(dec.name.text),
|
||||
CompletionKind.functionName, symbolFile, dec.name.index, dec.returnType);
|
||||
processParameters(symbol, dec.returnType, symbol.acSymbol.name,
|
||||
dec.parameters, dec.comment);
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = dec.comment;
|
||||
currentSymbol.addChild(symbol);
|
||||
if (dec.functionBody !is null)
|
||||
{
|
||||
currentSymbol = symbol;
|
||||
dec.functionBody.accept(this);
|
||||
currentSymbol = symbol.parent;
|
||||
}
|
||||
}
|
||||
|
||||
override void visit(const ClassDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.className);
|
||||
}
|
||||
|
||||
override void visit(const TemplateDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.templateName);
|
||||
}
|
||||
|
||||
override void visit(const InterfaceDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.interfaceName);
|
||||
}
|
||||
|
||||
override void visit(const UnionDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.unionName);
|
||||
}
|
||||
|
||||
override void visit(const StructDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
visitAggregateDeclaration(dec, CompletionKind.structName);
|
||||
}
|
||||
|
||||
override void visit(const BaseClass bc)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(bc).stringof);
|
||||
currentSymbol.baseClasses ~= iotcToStringArray(
|
||||
bc.identifierOrTemplateChain, stringCache);
|
||||
}
|
||||
|
||||
override void visit(const VariableDeclaration dec)
|
||||
{
|
||||
assert (currentSymbol);
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
const Type t = dec.type;
|
||||
foreach (declarator; dec.declarators)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol(
|
||||
stringCache.intern(declarator.name.text),
|
||||
CompletionKind.variableName,
|
||||
symbolFile,
|
||||
declarator.name.index,
|
||||
t);
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = dec.comment;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
override void visit(const AliasDeclaration aliasDeclaration)
|
||||
{
|
||||
if (aliasDeclaration.initializers.length == 0)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol(
|
||||
stringCache.intern(aliasDeclaration.name.text),
|
||||
CompletionKind.aliasName,
|
||||
symbolFile,
|
||||
aliasDeclaration.name.index,
|
||||
aliasDeclaration.type);
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = aliasDeclaration.comment;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (initializer; aliasDeclaration.initializers)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol(
|
||||
stringCache.intern(initializer.name.text),
|
||||
CompletionKind.aliasName,
|
||||
symbolFile,
|
||||
initializer.name.index,
|
||||
initializer.type);
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = aliasDeclaration.comment;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override void visit(const AliasThisDeclaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
currentSymbol.aliasThis ~= stringCache.intern(dec.identifier.text);
|
||||
}
|
||||
|
||||
override void visit(const Declaration dec)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
if (dec.attributeDeclaration !is null
|
||||
&& isProtection(dec.attributeDeclaration.attribute.attribute))
|
||||
{
|
||||
protection = dec.attributeDeclaration.attribute.attribute;
|
||||
return;
|
||||
}
|
||||
IdType p = protection;
|
||||
foreach (const Attribute attr; dec.attributes)
|
||||
{
|
||||
if (isProtection(attr.attribute))
|
||||
protection = attr.attribute;
|
||||
}
|
||||
dec.accept(this);
|
||||
protection = p;
|
||||
}
|
||||
|
||||
override void visit(const Module mod)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(mod).stringof);
|
||||
//
|
||||
currentSymbol = new SemanticSymbol(null, CompletionKind.moduleName,
|
||||
symbolFile);
|
||||
rootSymbol = currentSymbol;
|
||||
currentScope = new Scope(0, size_t.max);
|
||||
ImportInformation i;
|
||||
i.modulePath = "object";
|
||||
i.importParts ~= "object";
|
||||
currentScope.importInformation ~= i;
|
||||
moduleScope = currentScope;
|
||||
mod.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const EnumDeclaration dec)
|
||||
{
|
||||
assert (currentSymbol);
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
SemanticSymbol* symbol = new SemanticSymbol(stringCache.intern(dec.name.text),
|
||||
CompletionKind.enumName, symbolFile, dec.name.index, dec.type);
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = dec.comment;
|
||||
currentSymbol = symbol;
|
||||
if (dec.enumBody !is null)
|
||||
dec.enumBody.accept(this);
|
||||
currentSymbol = symbol.parent;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
|
||||
override void visit(const EnumMember member)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(member).stringof);
|
||||
SemanticSymbol* symbol = new SemanticSymbol(stringCache.intern(member.name.text),
|
||||
CompletionKind.enumMember, symbolFile, member.name.index, member.type);
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = member.comment;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
|
||||
override void visit(const ModuleDeclaration moduleDeclaration)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
|
||||
foreach (identifier; moduleDeclaration.moduleName.identifiers)
|
||||
{
|
||||
moduleName ~= stringCache.intern(identifier.text);
|
||||
}
|
||||
}
|
||||
|
||||
// creates scopes for
|
||||
override void visit(const StructBody structBody)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(structBody).stringof);
|
||||
Scope* s = new Scope(structBody.startLocation, structBody.endLocation);
|
||||
// Log.trace("Added scope ", s.startLocation, " ", s.endLocation);
|
||||
|
||||
ACSymbol* thisSymbol = new ACSymbol("this", CompletionKind.variableName,
|
||||
currentSymbol.acSymbol);
|
||||
thisSymbol.location = s.startLocation;
|
||||
thisSymbol.symbolFile = symbolFile;
|
||||
currentSymbol.acSymbol.parts.insert(thisSymbol);
|
||||
|
||||
s.parent = currentScope;
|
||||
currentScope = s;
|
||||
foreach (dec; structBody.declarations)
|
||||
visit(dec);
|
||||
currentScope = s.parent;
|
||||
currentScope.children ~= s;
|
||||
}
|
||||
|
||||
override void visit(const ImportDeclaration importDeclaration)
|
||||
{
|
||||
import std.typecons;
|
||||
import std.algorithm;
|
||||
import std.array;
|
||||
// Log.trace(__FUNCTION__, " ImportDeclaration");
|
||||
foreach (single; importDeclaration.singleImports.filter!(
|
||||
a => a !is null && a.identifierChain !is null))
|
||||
{
|
||||
ImportInformation info;
|
||||
info.importParts = single.identifierChain.identifiers.map!(a => stringCache.intern(a.text)).array;
|
||||
info.modulePath = convertChainToImportPath(single.identifierChain);
|
||||
info.isPublic = protection == tok!"public";
|
||||
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);
|
||||
info.importParts = importDeclaration.importBindings.singleImport
|
||||
.identifierChain.identifiers.map!(a => stringCache.intern(a.text)).array;
|
||||
foreach (bind; importDeclaration.importBindings.importBinds)
|
||||
{
|
||||
Tuple!(string, string) bindTuple;
|
||||
bindTuple[0] = stringCache.intern(bind.left.text);
|
||||
bindTuple[1] = bind.right == tok!"" ? null : stringCache.intern(bind.right.text);
|
||||
info.importedSymbols ~= bindTuple;
|
||||
}
|
||||
info.isPublic = protection == tok!"public";
|
||||
currentScope.importInformation ~= info;
|
||||
}
|
||||
|
||||
// Create scope for block statements
|
||||
override void visit(const BlockStatement blockStatement)
|
||||
{
|
||||
// Log.trace(__FUNCTION__, " ", typeof(blockStatement).stringof);
|
||||
Scope* s = new Scope(blockStatement.startLocation,
|
||||
blockStatement.endLocation);
|
||||
s.parent = currentScope;
|
||||
currentScope.children ~= s;
|
||||
|
||||
if (currentSymbol.acSymbol.kind == CompletionKind.functionName)
|
||||
{
|
||||
foreach (child; currentSymbol.children)
|
||||
{
|
||||
if (child.acSymbol.location == size_t.max)
|
||||
{
|
||||
// Log.trace("Reassigning location of ", child.acSymbol.name);
|
||||
child.acSymbol.location = s.startLocation + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blockStatement.declarationsAndStatements !is null)
|
||||
{
|
||||
currentScope = s;
|
||||
visit (blockStatement.declarationsAndStatements);
|
||||
currentScope = s.parent;
|
||||
}
|
||||
}
|
||||
|
||||
override void visit(const VersionCondition versionCondition)
|
||||
{
|
||||
import std.algorithm;
|
||||
import constants;
|
||||
// TODO: This is a bit of a hack
|
||||
if (predefinedVersions.canFind(versionCondition.token.text))
|
||||
versionCondition.accept(this);
|
||||
}
|
||||
|
||||
alias visit = ASTVisitor.visit;
|
||||
|
||||
/// Module scope
|
||||
Scope* moduleScope;
|
||||
|
||||
/// The module
|
||||
SemanticSymbol* rootSymbol;
|
||||
|
||||
shared(StringCache)* stringCache;
|
||||
|
||||
private:
|
||||
|
||||
void visitAggregateDeclaration(AggType)(AggType dec, CompletionKind kind)
|
||||
{
|
||||
// Log.trace("visiting aggregate declaration ", dec.name.text);
|
||||
SemanticSymbol* symbol = new SemanticSymbol(stringCache.intern(dec.name.text),
|
||||
kind, symbolFile, dec.name.index);
|
||||
if (kind == CompletionKind.className)
|
||||
symbol.acSymbol.parts.insert(classSymbols[]);
|
||||
else
|
||||
symbol.acSymbol.parts.insert(aggregateSymbols[]);
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.protection = protection;
|
||||
symbol.acSymbol.doc = dec.comment;
|
||||
currentSymbol = symbol;
|
||||
dec.accept(this);
|
||||
currentSymbol = symbol.parent;
|
||||
currentSymbol.addChild(symbol);
|
||||
}
|
||||
|
||||
void visitConstructor(size_t location, const Parameters parameters,
|
||||
const FunctionBody functionBody, string doc)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol("*constructor*",
|
||||
CompletionKind.functionName, symbolFile, location);
|
||||
processParameters(symbol, null, "this", parameters, doc);
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = doc;
|
||||
currentSymbol.addChild(symbol);
|
||||
if (functionBody !is null)
|
||||
{
|
||||
currentSymbol = symbol;
|
||||
functionBody.accept(this);
|
||||
currentSymbol = symbol.parent;
|
||||
}
|
||||
}
|
||||
|
||||
void visitDestructor(size_t location, const FunctionBody functionBody, string doc)
|
||||
{
|
||||
SemanticSymbol* symbol = new SemanticSymbol("~this",
|
||||
CompletionKind.functionName, symbolFile, location);
|
||||
symbol.acSymbol.callTip = "~this()";
|
||||
symbol.protection = protection;
|
||||
symbol.parent = currentSymbol;
|
||||
symbol.acSymbol.doc = doc;
|
||||
currentSymbol.addChild(symbol);
|
||||
if (functionBody !is null)
|
||||
{
|
||||
currentSymbol = symbol;
|
||||
functionBody.accept(this);
|
||||
currentSymbol = symbol.parent;
|
||||
}
|
||||
}
|
||||
|
||||
void processParameters(SemanticSymbol* symbol, const Type returnType,
|
||||
string functionName, const Parameters parameters, string doc)
|
||||
{
|
||||
if (parameters !is null)
|
||||
{
|
||||
foreach (const Parameter p; parameters.parameters)
|
||||
{
|
||||
SemanticSymbol* parameter = new SemanticSymbol(
|
||||
stringCache.intern(p.name.text),
|
||||
CompletionKind.variableName, symbolFile, size_t.max,
|
||||
p.type);
|
||||
symbol.addChild(parameter);
|
||||
parameter.parent = symbol;
|
||||
}
|
||||
if (parameters.hasVarargs)
|
||||
{
|
||||
SemanticSymbol* argptr = new SemanticSymbol("_argptr",
|
||||
CompletionKind.variableName, null, size_t.max, argptrType);
|
||||
argptr.parent = symbol;
|
||||
symbol.addChild(argptr);
|
||||
|
||||
SemanticSymbol* arguments = new SemanticSymbol("_arguments",
|
||||
CompletionKind.variableName, null, size_t.max, argumentsType);
|
||||
arguments.parent = symbol;
|
||||
symbol.addChild(arguments);
|
||||
}
|
||||
}
|
||||
symbol.acSymbol.callTip = formatCallTip(returnType, functionName,
|
||||
parameters, doc);
|
||||
}
|
||||
|
||||
static string formatCallTip(const Type returnType, string name,
|
||||
const Parameters parameters, string doc = null)
|
||||
{
|
||||
import std.string;
|
||||
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);
|
||||
}
|
||||
|
||||
/// Current protection type
|
||||
IdType protection;
|
||||
|
||||
/// Package and module name
|
||||
string[] moduleName;
|
||||
|
||||
/// Current scope
|
||||
Scope* currentScope;
|
||||
|
||||
/// Current symbol
|
||||
SemanticSymbol* currentSymbol;
|
||||
|
||||
/// Path to the file being converted
|
||||
string symbolFile;
|
||||
|
||||
Module mod;
|
||||
}
|
||||
|
||||
string formatNode(T)(const T node)
|
||||
{
|
||||
import formatter;
|
||||
import std.array;
|
||||
import std.conv;
|
||||
if (node is null) return "";
|
||||
auto app = appender!(char[])();
|
||||
auto f = new Formatter!(typeof(app))(app);
|
||||
f.format(node);
|
||||
return to!string(app.data);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
string[] iotcToStringArray(const IdentifierOrTemplateChain iotc,
|
||||
shared(StringCache)* stringCache)
|
||||
{
|
||||
string[] parts;
|
||||
foreach (ioti; iotc.identifiersOrTemplateInstances)
|
||||
{
|
||||
if (ioti.identifier != tok!"")
|
||||
parts ~= stringCache.intern(ioti.identifier.text);
|
||||
else
|
||||
parts ~= stringCache.intern(ioti.templateInstance.identifier.text);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
private static string convertChainToImportPath(const IdentifierChain ic)
|
||||
{
|
||||
import std.conv;
|
||||
import std.algorithm;
|
||||
import std.range;
|
||||
import std.path;
|
||||
return to!string(ic.identifiers.map!(a => cast() a.text).join(dirSeparator).array);
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* This file is part of DCD, a development tool for the D programming language.
|
||||
* Copyright (C) 2014 Brian Schott
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module conversion.second;
|
||||
|
||||
import conversion.first;
|
||||
import actypes;
|
||||
import semantic;
|
||||
import messages;
|
||||
import stdx.lexer : StringCache;
|
||||
|
||||
/**
|
||||
* Second pass handles the following:
|
||||
* $(UL
|
||||
* $(LI Import statements)
|
||||
* $(LI assigning symbols to scopes)
|
||||
* )
|
||||
*/
|
||||
struct SecondPass
|
||||
{
|
||||
public:
|
||||
|
||||
this(ref FirstPass first)
|
||||
{
|
||||
this.rootSymbol = first.rootSymbol;
|
||||
this.moduleScope = first.moduleScope;
|
||||
this.stringCache = first.stringCache;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
assignToScopes(rootSymbol.acSymbol);
|
||||
resolveImports(moduleScope);
|
||||
}
|
||||
|
||||
SemanticSymbol* rootSymbol;
|
||||
Scope* moduleScope;
|
||||
shared(StringCache)* stringCache;
|
||||
|
||||
private:
|
||||
|
||||
void assignToScopes(ACSymbol* currentSymbol)
|
||||
{
|
||||
Scope* s = moduleScope.getScopeByCursor(currentSymbol.location);
|
||||
s.symbols.insert(currentSymbol);
|
||||
foreach (part; currentSymbol.parts[])
|
||||
assignToScopes(part);
|
||||
}
|
||||
|
||||
// This method is really ugly due to the casts...
|
||||
static ACSymbol* createImportSymbols(ImportInformation info,
|
||||
Scope* currentScope)
|
||||
{
|
||||
immutable string firstPart = info.importParts[0];
|
||||
ACSymbol*[] symbols = currentScope.getSymbolsByName(firstPart);
|
||||
immutable bool found = symbols.length > 0;
|
||||
ACSymbol* firstSymbol = found
|
||||
? symbols[0] : new ACSymbol(firstPart, CompletionKind.packageName);
|
||||
if (!found)
|
||||
{
|
||||
currentScope.symbols.insert(firstSymbol);
|
||||
}
|
||||
ACSymbol* currentSymbol = cast(ACSymbol*) firstSymbol;
|
||||
foreach (size_t i, string importPart; info.importParts[1 .. $])
|
||||
{
|
||||
symbols = currentSymbol.getPartsByName(importPart);
|
||||
ACSymbol* s = symbols.length > 0
|
||||
? cast(ACSymbol*) symbols[0] : new ACSymbol(importPart, CompletionKind.packageName);
|
||||
currentSymbol.parts.insert(s);
|
||||
currentSymbol = s;
|
||||
}
|
||||
currentSymbol.kind = CompletionKind.moduleName;
|
||||
return currentSymbol;
|
||||
}
|
||||
|
||||
void resolveImports(Scope* currentScope)
|
||||
{
|
||||
import modulecache;
|
||||
foreach (importInfo; currentScope.importInformation)
|
||||
{
|
||||
string location = ModuleCache.resolveImportLoctation(importInfo.modulePath);
|
||||
ACSymbol*[] symbols = location is null ? [] : ModuleCache.getSymbolsInModule(location);
|
||||
ACSymbol* moduleSymbol = createImportSymbols(importInfo, currentScope);
|
||||
currentScope.symbols.insert(moduleSymbol);
|
||||
currentScope.symbols.insert(symbols);
|
||||
if (importInfo.importedSymbols.length == 0)
|
||||
{
|
||||
if (importInfo.isPublic && currentScope.parent is null)
|
||||
{
|
||||
rootSymbol.acSymbol.parts.insert(symbols);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
symbolLoop: foreach (symbol; symbols)
|
||||
{
|
||||
foreach (tup; importInfo.importedSymbols)
|
||||
{
|
||||
if (tup[0] != symbol.name)
|
||||
continue symbolLoop;
|
||||
if (tup[1] !is null)
|
||||
{
|
||||
ACSymbol* s = new ACSymbol(tup[1],
|
||||
symbol.kind, symbol.type);
|
||||
// TODO: Compiler gets confused here, so cast the types.
|
||||
s.parts = cast(typeof(s.parts)) symbol.parts;
|
||||
// TODO: Re-format callTip with new name?
|
||||
s.callTip = symbol.callTip;
|
||||
s.doc = symbol.doc;
|
||||
s.qualifier = symbol.qualifier;
|
||||
s.location = symbol.location;
|
||||
s.symbolFile = symbol.symbolFile;
|
||||
currentScope.symbols.insert(s);
|
||||
moduleSymbol.parts.insert(s);
|
||||
if (importInfo.isPublic && currentScope.parent is null)
|
||||
rootSymbol.acSymbol.parts.insert(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
moduleSymbol.parts.insert(symbol);
|
||||
currentScope.symbols.insert(symbol);
|
||||
if (importInfo.isPublic && currentScope.parent is null)
|
||||
rootSymbol.acSymbol.parts.insert(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (childScope; currentScope.children)
|
||||
resolveImports(childScope);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
/**
|
||||
* This file is part of DCD, a development tool for the D programming language.
|
||||
* Copyright (C) 2014 Brian Schott
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module conversion.third;
|
||||
|
||||
import stdx.d.ast;
|
||||
import stdx.d.lexer;
|
||||
import conversion.second;
|
||||
import semantic;
|
||||
import actypes;
|
||||
import messages;
|
||||
|
||||
|
||||
/**
|
||||
* Third pass handles the following:
|
||||
* $(UL
|
||||
* $(LI types)
|
||||
* $(LI base classes)
|
||||
* $(LI mixin templates)
|
||||
* $(LI alias this)
|
||||
* $(LI alias declarations)
|
||||
* )
|
||||
*/
|
||||
struct ThirdPass
|
||||
{
|
||||
public:
|
||||
this(ref SecondPass second, string name = "none") pure
|
||||
{
|
||||
this.rootSymbol = second.rootSymbol;
|
||||
this.moduleScope = second.moduleScope;
|
||||
this.stringCache = second.stringCache;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
string name;
|
||||
|
||||
void run()
|
||||
{
|
||||
thirdPass(rootSymbol);
|
||||
}
|
||||
|
||||
SemanticSymbol* rootSymbol;
|
||||
Scope* moduleScope;
|
||||
|
||||
private:
|
||||
|
||||
void thirdPass(SemanticSymbol* currentSymbol)
|
||||
{
|
||||
// Log.trace("third pass on ", currentSymbol.acSymbol.name);
|
||||
with (CompletionKind) final switch (currentSymbol.acSymbol.kind)
|
||||
{
|
||||
case className:
|
||||
case interfaceName:
|
||||
resolveInheritance(currentSymbol);
|
||||
goto case structName;
|
||||
case structName:
|
||||
case unionName:
|
||||
resolveAliasThis(currentSymbol);
|
||||
resolveMixinTemplates(currentSymbol);
|
||||
break;
|
||||
case variableName:
|
||||
case memberVariableName:
|
||||
case functionName:
|
||||
case aliasName:
|
||||
ACSymbol* t = resolveType(currentSymbol.type,
|
||||
currentSymbol.acSymbol.location);
|
||||
while (t !is null && t.kind == CompletionKind.aliasName)
|
||||
t = t.type;
|
||||
currentSymbol.acSymbol.type = t;
|
||||
break;
|
||||
case enumName:
|
||||
case keyword:
|
||||
case enumMember:
|
||||
case packageName:
|
||||
case moduleName:
|
||||
case dummy:
|
||||
case array:
|
||||
case assocArray:
|
||||
case templateName:
|
||||
case mixinTemplateName:
|
||||
break;
|
||||
}
|
||||
foreach (child; currentSymbol.children)
|
||||
{
|
||||
thirdPass(child);
|
||||
}
|
||||
}
|
||||
|
||||
void resolveInheritance(SemanticSymbol* currentSymbol)
|
||||
{
|
||||
// Log.trace("Resolving inheritance for ", currentSymbol.acSymbol.name);
|
||||
outer: foreach (string[] base; currentSymbol.baseClasses)
|
||||
{
|
||||
ACSymbol* baseClass;
|
||||
if (base.length == 0)
|
||||
continue;
|
||||
auto symbols = moduleScope.getSymbolsByNameAndCursor(
|
||||
base[0], currentSymbol.acSymbol.location);
|
||||
if (symbols.length == 0)
|
||||
continue;
|
||||
baseClass = symbols[0];
|
||||
foreach (part; base[1..$])
|
||||
{
|
||||
symbols = baseClass.getPartsByName(part);
|
||||
if (symbols.length == 0)
|
||||
continue outer;
|
||||
baseClass = symbols[0];
|
||||
}
|
||||
currentSymbol.acSymbol.parts.insert(baseClass.parts[]);
|
||||
}
|
||||
}
|
||||
|
||||
void resolveAliasThis(SemanticSymbol* currentSymbol)
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
void resolveMixinTemplates(SemanticSymbol* currentSymbol)
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
ACSymbol* resolveType(const Type t, size_t location)
|
||||
{
|
||||
if (t is null) return null;
|
||||
if (t.type2 is null) return null;
|
||||
ACSymbol* s;
|
||||
if (t.type2.builtinType != tok!"")
|
||||
s = convertBuiltinType(t.type2);
|
||||
else if (t.type2.typeConstructor != tok!"")
|
||||
s = resolveType(t.type2.type, location);
|
||||
else if (t.type2.symbol !is null)
|
||||
{
|
||||
// TODO: global scoped symbol handling
|
||||
string[] symbolParts = expandSymbol(
|
||||
t.type2.symbol.identifierOrTemplateChain, stringCache, name);
|
||||
auto symbols = moduleScope.getSymbolsByNameAndCursor(
|
||||
symbolParts[0], location);
|
||||
if (symbols.length == 0)
|
||||
goto resolveSuffixes;
|
||||
s = symbols[0];
|
||||
foreach (symbolPart; symbolParts[1..$])
|
||||
{
|
||||
auto parts = s.getPartsByName(symbolPart);
|
||||
if (parts.length == 0)
|
||||
goto resolveSuffixes;
|
||||
s = parts[0];
|
||||
}
|
||||
}
|
||||
resolveSuffixes:
|
||||
foreach (suffix; t.typeSuffixes)
|
||||
s = processSuffix(s, suffix);
|
||||
return s;
|
||||
}
|
||||
|
||||
static string[] expandSymbol(const IdentifierOrTemplateChain chain,
|
||||
shared(StringCache)* stringCache, string n)
|
||||
{
|
||||
if (chain.identifiersOrTemplateInstances.length == 0)
|
||||
return [];
|
||||
string[] strings = new string[chain.identifiersOrTemplateInstances.length];
|
||||
for (size_t i = 0; i < chain.identifiersOrTemplateInstances.length; ++i)
|
||||
{
|
||||
auto identOrTemplate = chain.identifiersOrTemplateInstances[i];
|
||||
if (identOrTemplate is null)
|
||||
continue;
|
||||
strings[i] = stringCache.intern(identOrTemplate.templateInstance is null ?
|
||||
identOrTemplate.identifier.text
|
||||
: identOrTemplate.templateInstance.identifier.text);
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
static ACSymbol* processSuffix(ACSymbol* symbol, const TypeSuffix suffix)
|
||||
{
|
||||
import std.container;
|
||||
import formatter;
|
||||
if (suffix.star)
|
||||
return symbol;
|
||||
if (suffix.array || suffix.type)
|
||||
{
|
||||
ACSymbol* s = new ACSymbol(null);
|
||||
s.parts = new RedBlackTree!(ACSymbol*, comparitor, true);
|
||||
s.parts.insert(suffix.array ? (cast() arraySymbols)[]
|
||||
: (cast() assocArraySymbols)[]);
|
||||
s.type = symbol;
|
||||
s.qualifier = suffix.array ? SymbolQualifier.array : SymbolQualifier.assocArray;
|
||||
return s;
|
||||
}
|
||||
if (suffix.parameters)
|
||||
{
|
||||
import conversion.first;
|
||||
ACSymbol* s = new ACSymbol(null);
|
||||
s.type = symbol;
|
||||
s.qualifier = SymbolQualifier.func;
|
||||
s.callTip = suffix.delegateOrFunction.text ~ formatNode(suffix.parameters);
|
||||
return s;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static ACSymbol* convertBuiltinType(const Type2 type2)
|
||||
{
|
||||
string stringRepresentation = str(type2.builtinType);
|
||||
if (stringRepresentation is null) return null;
|
||||
// TODO: Make this use binary search instead
|
||||
auto t = cast() builtinSymbols;
|
||||
ACSymbol s = ACSymbol(stringRepresentation);
|
||||
return t.equalRange(&s).front();
|
||||
}
|
||||
|
||||
shared(StringCache)* stringCache;
|
||||
}
|
2
dscanner
2
dscanner
|
@ -1 +1 @@
|
|||
Subproject commit 42762f8dcd2ca542d07e3fd5bc3ae0586fc7ea68
|
||||
Subproject commit fdce684849a0d2fc67203220e33367b4d3692f15
|
|
@ -24,7 +24,6 @@ import stdx.lexer;
|
|||
import stdx.d.lexer;
|
||||
import stdx.d.parser;
|
||||
import stdx.d.ast;
|
||||
import std.stdio;
|
||||
import std.array;
|
||||
import std.path;
|
||||
import std.algorithm;
|
||||
|
@ -33,7 +32,10 @@ import std.container;
|
|||
|
||||
import actypes;
|
||||
import semantic;
|
||||
import astconverter;
|
||||
import conversion.astconverter;
|
||||
import conversion.first;
|
||||
import conversion.second;
|
||||
import conversion.third;
|
||||
import stupidlog;
|
||||
|
||||
bool cacheComparitor(CacheEntry* a, CacheEntry* b) pure nothrow
|
||||
|
@ -65,6 +67,7 @@ bool existanceCheck(A)(A path)
|
|||
static this()
|
||||
{
|
||||
ModuleCache.cache = new RedBlackTree!(CacheEntry*, cacheComparitor);
|
||||
ModuleCache.stringCache = new shared StringCache(StringCache.defaultBucketCount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,12 +90,16 @@ struct ModuleCache
|
|||
*/
|
||||
static void addImportPaths(string[] paths)
|
||||
{
|
||||
import core.memory;
|
||||
string[] addedPaths = paths.filter!(a => existanceCheck(a)).array();
|
||||
importPaths ~= addedPaths;
|
||||
|
||||
foreach (path; addedPaths)
|
||||
{
|
||||
foreach (fileName; dirEntries(path, "*.{d,di}", SpanMode.depth))
|
||||
{
|
||||
GC.disable();
|
||||
scope(exit) GC.enable();
|
||||
getSymbolsInModule(fileName);
|
||||
}
|
||||
}
|
||||
|
@ -106,9 +113,7 @@ struct ModuleCache
|
|||
*/
|
||||
static ACSymbol*[] getSymbolsInModule(string location)
|
||||
{
|
||||
if (location is null)
|
||||
return [];
|
||||
|
||||
assert (location !is null);
|
||||
if (!needsReparsing(location))
|
||||
{
|
||||
CacheEntry e;
|
||||
|
@ -127,17 +132,33 @@ struct ModuleCache
|
|||
try
|
||||
{
|
||||
import core.memory;
|
||||
import std.stdio;
|
||||
File f = File(location);
|
||||
ubyte[] source = uninitializedArray!(ubyte[])(cast(size_t)f.size);
|
||||
ubyte[] source = (cast(ubyte*) GC.malloc(cast(size_t) f.size,
|
||||
GC.BlkAttr.NO_SCAN | GC.BlkAttr.NO_MOVE))[0 .. f.size];
|
||||
f.rawRead(source);
|
||||
|
||||
GC.disable();
|
||||
LexerConfig config;
|
||||
config.fileName = location;
|
||||
StringCache* cache = new StringCache(StringCache.defaultBucketCount);
|
||||
shared(StringCache)* cache = new shared StringCache(StringCache.defaultBucketCount);
|
||||
auto tokens = source.byToken(config, cache).array();
|
||||
symbols = convertAstToSymbols(tokens, location);
|
||||
GC.enable();
|
||||
source[] = 0;
|
||||
GC.free(source.ptr);
|
||||
ParseAllocator p = new ParseAllocator(location);
|
||||
Module m = parseModuleSimple(tokens, location, p);
|
||||
|
||||
FirstPass first = new FirstPass(m, location, stringCache);
|
||||
first.run();
|
||||
cache = null;
|
||||
|
||||
SecondPass second = SecondPass(first);
|
||||
second.run();
|
||||
|
||||
ThirdPass third = ThirdPass(second, location);
|
||||
third.run();
|
||||
|
||||
p.deallocateAll();
|
||||
p = null;
|
||||
symbols = third.rootSymbol.acSymbol.parts.array();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -193,12 +214,12 @@ struct ModuleCache
|
|||
return cast(const(string[])) importPaths;
|
||||
}
|
||||
|
||||
static this()
|
||||
static string intern(string s)
|
||||
{
|
||||
stringCache = new StringCache(StringCache.defaultBucketCount);
|
||||
return s is null || s.length == 0 ? "" : stringCache.intern(s);
|
||||
}
|
||||
|
||||
static StringCache* stringCache;
|
||||
static shared(StringCache)* stringCache;
|
||||
|
||||
private:
|
||||
|
||||
|
|
40
semantic.d
40
semantic.d
|
@ -42,11 +42,12 @@ public:
|
|||
* location = the location of this symbol
|
||||
*/
|
||||
this(string name, CompletionKind kind, string symbolFile,
|
||||
size_t location = size_t.max)
|
||||
size_t location = size_t.max, const Type type = null)
|
||||
{
|
||||
acSymbol = new ACSymbol(name, kind);
|
||||
acSymbol.location = location;
|
||||
acSymbol.symbolFile = symbolFile;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,7 +66,7 @@ public:
|
|||
string[][] baseClasses;
|
||||
|
||||
/// Variable type or function return type
|
||||
Type type;
|
||||
const Type type;
|
||||
|
||||
/// Alias this symbols
|
||||
string[] aliasThis;
|
||||
|
@ -82,3 +83,38 @@ public:
|
|||
/// Child symbols
|
||||
SemanticSymbol*[] children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of the _argptr variable
|
||||
*/
|
||||
Type argptrType;
|
||||
|
||||
/**
|
||||
* Type of _arguments
|
||||
*/
|
||||
Type argumentsType;
|
||||
|
||||
static this()
|
||||
{
|
||||
// _argptr has type void*
|
||||
argptrType = new Type;
|
||||
argptrType.type2 = new Type2;
|
||||
argptrType.type2.builtinType = tok!"void";
|
||||
TypeSuffix argptrTypeSuffix = new TypeSuffix;
|
||||
argptrTypeSuffix.star = true;
|
||||
argptrType.typeSuffixes ~= argptrTypeSuffix;
|
||||
|
||||
// _arguments has type TypeInfo[]
|
||||
argumentsType = new Type;
|
||||
argumentsType = new Type;
|
||||
argumentsType.type2 = new Type2;
|
||||
argumentsType.type2.symbol = new Symbol;
|
||||
argumentsType.type2.symbol.identifierOrTemplateChain = new IdentifierOrTemplateChain;
|
||||
IdentifierOrTemplateInstance i = new IdentifierOrTemplateInstance;
|
||||
i.identifier.text = "TypeInfo";
|
||||
i.identifier.type = tok!"identifier";
|
||||
argumentsType.type2.symbol.identifierOrTemplateChain.identifiersOrTemplateInstances ~= i;
|
||||
TypeSuffix argumentsTypeSuffix = new TypeSuffix;
|
||||
argumentsTypeSuffix.array = true;
|
||||
argumentsType.typeSuffixes ~= argptrTypeSuffix;
|
||||
}
|
||||
|
|
4
server.d
4
server.d
|
@ -63,7 +63,7 @@ int main(string[] args)
|
|||
}
|
||||
catch (ConvException e)
|
||||
{
|
||||
stderr.writeln(e.msg);
|
||||
Log.fatal(e.msg);
|
||||
printHelp(args[0]);
|
||||
return 1;
|
||||
}
|
||||
|
@ -96,6 +96,8 @@ int main(string[] args)
|
|||
|
||||
sw.stop();
|
||||
Log.info("Startup completed in ", sw.peek().to!("msecs", float), " milliseconds");
|
||||
float internBytes = cast(float) ModuleCache.stringCache.allocated / (1024 * 1024);
|
||||
Log.info("String interning took up ", internBytes, " megabytes");
|
||||
|
||||
// No relative paths
|
||||
version (Posix) chdir("/");
|
||||
|
|
Loading…
Reference in New Issue