DCD/src/conversion/third.d

348 lines
8.6 KiB
D

/**
* 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 std.d.ast;
import std.d.lexer;
import conversion.second;
import semantic;
import actypes;
import messages;
import std.allocator;
import string_interning;
/**
* Third pass handles the following:
* $(UL
* $(LI types)
* $(LI base classes)
* $(LI mixin templates)
* $(LI alias this)
* $(LI alias declarations)
* )
*/
struct ThirdPass
{
public:
/**
* Params:
* second = The second conversion pass. Results are taken from this to
* run the third pass.
*/
this(ref SecondPass second) pure
{
this.rootSymbol = second.rootSymbol;
this.moduleScope = second.moduleScope;
this.symbolAllocator = second.symbolAllocator;
}
/**
* Runs the third pass.
*/
void run()
{
thirdPass(rootSymbol);
}
/**
* The module-level symbol
*/
SemanticSymbol* rootSymbol;
/**
* The module-level scope
*/
Scope* moduleScope;
/**
* The Symbol allocator
*/
CAllocator symbolAllocator;
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);
break;
case withSymbol:
case variableName:
case memberVariableName:
case functionName:
case aliasName:
ACSymbol* t = resolveType(currentSymbol.initializer,
currentSymbol.type, currentSymbol.acSymbol.location);
while (t !is null && (t.kind == CompletionKind.aliasName
|| (currentSymbol.acSymbol.kind == CompletionKind.withSymbol
&& t.kind == CompletionKind.variableName)))
{
t = t.type;
}
currentSymbol.acSymbol.type = t;
break;
case structName:
case unionName:
case enumName:
case keyword:
case enumMember:
case packageName:
case moduleName:
case dummy:
case array:
case assocArray:
case templateName:
case mixinTemplateName:
case importSymbol:
break;
}
foreach (child; currentSymbol.children)
{
thirdPass(child);
}
// Alias this and mixin templates are resolved after child nodes are
// resolved so that the correct symbol information will be available.
with (CompletionKind) switch (currentSymbol.acSymbol.kind)
{
case className:
case interfaceName:
case structName:
case unionName:
resolveAliasThis(currentSymbol);
resolveMixinTemplates(currentSymbol);
break;
default:
break;
}
}
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)
{
foreach (aliasThis; currentSymbol.aliasThis)
{
auto parts = currentSymbol.acSymbol.getPartsByName(aliasThis);
if (parts.length == 0 || parts[0].type is null)
continue;
ACSymbol* s = allocate!ACSymbol(symbolAllocator, IMPORT_SYMBOL_NAME,
CompletionKind.importSymbol);
s.type = parts[0].type;
currentSymbol.acSymbol.parts.insert(s);
}
}
void resolveMixinTemplates(SemanticSymbol* currentSymbol)
{
foreach (mix; currentSymbol.mixinTemplates[])
{
auto symbols = moduleScope.getSymbolsByNameAndCursor(mix[0],
currentSymbol.acSymbol.location);
if (symbols.length == 0)
continue;
auto symbol = symbols[0];
foreach (m; mix[1 .. $])
{
auto s = symbol.getPartsByName(m);
if (s.length == 0)
{
symbol = null;
break;
}
else
symbol = s[0];
}
currentSymbol.acSymbol.parts.insert(symbol.parts[]);
}
}
ACSymbol* resolveInitializerType(I)(ref const I initializer, size_t location)
{
if (initializer.empty)
return null;
auto slice = initializer[];
bool literal = slice.front[0] == '*';
if (literal && initializer.length > 1)
{
slice.popFront();
literal = false;
}
auto symbols = moduleScope.getSymbolsByNameAndCursor(internString(
literal ? slice.front[1 .. $] : slice.front), location);
if (symbols.length == 0)
return null;
if (literal)
return symbols[0];
slice.popFront();
auto s = symbols[0];
while (s !is null && s.type !is null && !slice.empty)
{
s = s.type;
if (slice.front == "foreach")
{
if (s.qualifier == SymbolQualifier.array)
s = s.type;
else
{
ACSymbol*[] f = s.getPartsByName(internString("front"));
if (f.length > 0)
s = f[0].type;
else
s = null;
}
}
else if (slice.front == "[]")
s = s.type;
else
{
auto parts = s.getPartsByName(internString(slice.front));
if (parts.length == 0)
return null;
s = parts[0];
}
slice.popFront();
}
return s;
}
ACSymbol* resolveType(I)(ref const I initializer, const Type t, size_t location)
{
if (t is null)
return resolveInitializerType(initializer, location);
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(initializer, t.type2.type, location);
else if (t.type2.symbol !is null)
{
// TODO: global scoped symbol handling
size_t l = t.type2.symbol.identifierOrTemplateChain.identifiersOrTemplateInstances.length;
string[] symbolParts = (cast(string*) Mallocator.it.allocate(l * string.sizeof))[0 .. l];
scope(exit) Mallocator.it.deallocate(symbolParts);
expandSymbol(symbolParts, 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, t);
return s;
}
static void expandSymbol(string[] strings, const IdentifierOrTemplateChain chain)
{
for (size_t i = 0; i < chain.identifiersOrTemplateInstances.length; ++i)
{
auto identOrTemplate = chain.identifiersOrTemplateInstances[i];
if (identOrTemplate is null)
{
strings[i] = null;
continue;
}
strings[i] = internString(identOrTemplate.templateInstance is null ?
identOrTemplate.identifier.text
: identOrTemplate.templateInstance.identifier.text);
}
}
ACSymbol* processSuffix(ACSymbol* symbol, const TypeSuffix suffix, const Type t)
{
if (suffix.star.type != tok!"")
return symbol;
if (suffix.array || suffix.type)
{
ACSymbol* s = allocate!ACSymbol(symbolAllocator, null);
s.parts.insert(suffix.array ? arraySymbols[]
: assocArraySymbols[]);
s.type = symbol;
s.qualifier = suffix.array ? SymbolQualifier.array : SymbolQualifier.assocArray;
return s;
}
if (suffix.parameters)
{
import conversion.first : formatNode;
import memory.allocators : QuickAllocator;
import memory.appender : Appender;
ACSymbol* s = allocate!ACSymbol(symbolAllocator, null);
s.type = symbol;
s.qualifier = SymbolQualifier.func;
QuickAllocator!1024 q;
auto app = Appender!(char, typeof(q), 2048)(q);
scope(exit) q.deallocate(app.mem);
app.formatNode(t);
s.callTip = internString(cast(string) app[]);
return s;
}
return null;
}
ACSymbol* convertBuiltinType(const Type2 type2)
{
string stringRepresentation = getBuiltinTypeName(type2.builtinType);
ACSymbol s = ACSymbol(stringRepresentation);
assert(s.name.ptr == stringRepresentation.ptr);
return builtinSymbols.equalRange(&s).front();
}
}