0.2.0 development work

This commit is contained in:
Hackerpilot 2013-10-09 02:16:11 -07:00
parent 917fb5a913
commit e2be6948e9
12 changed files with 1247 additions and 1015 deletions

443
actypes.d
View File

@ -24,13 +24,12 @@ import std.algorithm;
import std.stdio;
import std.array;
import messages;
import autocomplete;
import std.array;
/**
* Any special information about a variable declaration symbol.
*/
enum SymbolQualifier
enum SymbolQualifier : ubyte
{
/// _none
none,
@ -45,12 +44,10 @@ enum SymbolQualifier
/**
* Autocompletion symbol
*/
class ACSymbol
struct ACSymbol
{
public:
this() {}
/**
* Params:
* name = the symbol's name
@ -77,23 +74,34 @@ public:
* kind = the symbol's completion kind
* resolvedType = the resolved type of the symbol
*/
this(string name, CompletionKind kind, ACSymbol resolvedType)
this(string name, CompletionKind kind, ACSymbol* type)
{
this.name = name;
this.kind = kind;
this.resolvedType = resolvedType;
this.type = type;
}
/**
* Comparison operator sorts based on the name field
*/
int opCmp(string str) const
{
if (str < this.name) return -1;
if (str > this.name) return 1;
return 0;
}
/// ditto
int opCmp(const(ACSymbol)* other) const
{
return this.opCmp(other.name);
}
/**
* Symbols that compose this symbol, such as enum members, class variables,
* methods, etc.
*/
ACSymbol[] parts;
/**
* Listing of superclasses
*/
string[] superClasses;
ACSymbol*[] parts;
/**
* Symbol's name
@ -101,14 +109,14 @@ public:
string name;
/**
* Symbol's location in bytes
* The symbol that represents the type.
*/
size_t location;
ACSymbol* type;
/**
* Any special information about this symbol
* Calltip to display if this is a function
*/
SymbolQualifier qualifier;
string callTip;
/**
* The kind of symbol
@ -116,250 +124,207 @@ public:
CompletionKind kind;
/**
* The return type if this is a function, or the element type if this is an
* array or associative array, or the variable type if this is a variable.
* This field is null if this symbol is a class
* Symbol qualifier
*/
Type type;
SymbolQualifier qualifier;
/**
* The symbol that represents the type. _resolvedType is an autocomplete
* class, type is an AST class, so after a module is parsed the symbols
* need to be post-processed to tie variable declarations to the symbols
* that actually contain the correct autocomplete information.
* Symbol location
*/
ACSymbol resolvedType;
size_t location;
/**
* Calltip to display if this is a function
* Module containing the symbol.
*/
string calltip;
string symbolFile;
/**
* Finds symbol parts by name
*/
ACSymbol[] getPartsByName(string name)
const(ACSymbol)*[] getPartsByName(string name) const
{
return parts.filter!(a => a.name == name).array;
return cast(typeof(return)) parts.filter!(a => a.name == name).array;
}
/**
* Sorts the parts array, and the parts array of each part, and so on.
*/
void sortParts()
{
parts.sort();
foreach (p; parts)
p.sortParts();
}
}
mixin template scopeImplementation(ScopeType)
{
ScopeType* parent;
ScopeType*[] children;
size_t startLocation;
size_t endLocation;
ScopeType* getScopeByCursor(size_t cursorPosition) const
{
if (cursorPosition < startLocation) return null;
if (cursorPosition > endLocation) return null;
foreach (child; children)
{
auto childScope = child.getScopeByCursor(cursorPosition);
if (childScope !is null)
return childScope;
}
return cast(typeof(return)) &this;
}
}
struct Scope
{
mixin scopeImplementation!(typeof(this));
ACSymbol*[] getSymbolsInCursorScope(size_t cursorPosition) const
{
auto s = getScopeByCursor(cursorPosition);
if (s is null)
return [];
return cast(typeof(return)) s.symbols;
}
const(ACSymbol)*[] getSymbolsByName(string name) const
{
const(ACSymbol)*[] retVal = cast(typeof(return)) symbols.filter!(a => a.name == name).array();
if (retVal.length > 0)
return retVal;
if (parent is null)
return [];
return parent.getSymbolsByName(name);
}
const(ACSymbol)*[] getSymbolsByNameAndCursor(string name, size_t cursorPosition) const
{
auto s = getScopeByCursor(cursorPosition);
if (s is null)
return [];
return s.getSymbolsByName(name);
}
ACSymbol*[] symbols;
}
/**
* Scope such as a block statement, struct body, etc.
* Initializes builtin types and the various properties of builtin types
*/
class Scope
static this()
{
public:
// TODO: make sure all parts are sorted.
auto bool_ = new ACSymbol("bool", CompletionKind.keyword);
auto int_ = new ACSymbol("int", CompletionKind.keyword);
auto long_ = new ACSymbol("long", CompletionKind.keyword);
auto byte_ = new ACSymbol("byte", CompletionKind.keyword);
auto char_ = new ACSymbol("char", CompletionKind.keyword);
auto dchar_ = new ACSymbol("dchar", CompletionKind.keyword);
auto short_ = new ACSymbol("short", CompletionKind.keyword);
auto ubyte_ = new ACSymbol("ubyte", CompletionKind.keyword);
auto uint_ = new ACSymbol("uint", CompletionKind.keyword);
auto ulong_ = new ACSymbol("ulong", CompletionKind.keyword);
auto ushort_ = new ACSymbol("ushort", CompletionKind.keyword);
auto wchar_ = new ACSymbol("wchar", CompletionKind.keyword);
/**
* Params:
* start = the index of the opening brace
* end = the index of the closing brace
*/
this(size_t start, size_t end)
auto alignof_ = new ACSymbol("alignof", CompletionKind.keyword, ulong_);
auto mangleof_ = new ACSymbol("mangleof", CompletionKind.keyword);
auto sizeof_ = new ACSymbol("sizeof", CompletionKind.keyword, ulong_);
auto stringof_ = new ACSymbol("stringof", CompletionKind.keyword);
arraySymbols ~= alignof_;
arraySymbols ~= new ACSymbol("dup", CompletionKind.keyword);
arraySymbols ~= new ACSymbol("idup", CompletionKind.keyword);
arraySymbols ~= new ACSymbol("init", CompletionKind.keyword);
arraySymbols ~= new ACSymbol("length", CompletionKind.keyword, ulong_);
arraySymbols ~= mangleof_;
arraySymbols ~= new ACSymbol("ptr", CompletionKind.keyword);
arraySymbols ~= new ACSymbol("reverse", CompletionKind.keyword);
arraySymbols ~= sizeof_;
arraySymbols ~= new ACSymbol("sort", CompletionKind.keyword);
arraySymbols ~= stringof_;
arraySymbols.sort();
assocArraySymbols ~= alignof_;
assocArraySymbols ~= new ACSymbol("byKey", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("byValue", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("dup", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("get", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("init", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("keys", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("length", CompletionKind.keyword, ulong_);
assocArraySymbols ~= mangleof_;
assocArraySymbols ~= new ACSymbol("rehash", CompletionKind.keyword);
assocArraySymbols ~= sizeof_;
assocArraySymbols ~= stringof_;
assocArraySymbols ~= new ACSymbol("values", CompletionKind.keyword);
assocArraySymbols.sort();
foreach (s; [bool_, int_, long_, byte_, char_, dchar_, short_, ubyte_, uint_,
ulong_, ushort_, wchar_])
{
this.start = start;
this.end = end;
s.parts ~= new ACSymbol("init", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("min", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("max", CompletionKind.keyword, s);
s.parts ~= alignof_;
s.parts ~= sizeof_;
s.parts ~= stringof_;
s.parts ~= mangleof_;
s.sortParts();
}
/**
* Gets all symbols in the scope that contains the cursor as well as its
* parent scopes.
*/
ACSymbol[] getSymbolsInCurrentScope(size_t cursorPosition)
auto cdouble_ = new ACSymbol("cdouble", CompletionKind.keyword);
auto cent_ = new ACSymbol("cent", CompletionKind.keyword);
auto cfloat_ = new ACSymbol("cfloat", CompletionKind.keyword);
auto creal_ = new ACSymbol("creal", CompletionKind.keyword);
auto double_ = new ACSymbol("double", CompletionKind.keyword);
auto float_ = new ACSymbol("float", CompletionKind.keyword);
auto idouble_ = new ACSymbol("idouble", CompletionKind.keyword);
auto ifloat_ = new ACSymbol("ifloat", CompletionKind.keyword);
auto ireal_ = new ACSymbol("ireal", CompletionKind.keyword);
auto real_ = new ACSymbol("real", CompletionKind.keyword);
auto ucent_ = new ACSymbol("ucent", CompletionKind.keyword);
foreach (s; [cdouble_, cent_, cfloat_, creal_, double_, float_,
idouble_, ifloat_, ireal_, real_, ucent_])
{
Scope s = findCurrentScope(cursorPosition);
if (s is null)
return [];
else
return s.getSymbols();
s.parts ~= alignof_;
s.parts ~= new ACSymbol("dig", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("epsilon", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("infinity", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("init", CompletionKind.keyword, s);
s.parts ~= mangleof_;
s.parts ~= new ACSymbol("mant_dig", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("max", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("max_10_exp", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("max_exp", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("min", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("min_exp", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("min_10_exp", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("min_normal", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("nan", CompletionKind.keyword, s);
s.parts ~= sizeof_;
s.parts ~= stringof_;
s.sortParts();
}
/**
* Gets all symbols in this scope and its parent scopes.
*/
ACSymbol[] getSymbols()
{
ACSymbol[] rVal;
rVal ~= symbols;
if (parent !is null)
rVal ~= parent.getSymbols();
return rVal;
}
ireal_.parts ~= new ACSymbol("im", CompletionKind.keyword, real_);
ifloat_.parts ~= new ACSymbol("im", CompletionKind.keyword, float_);
idouble_.parts ~= new ACSymbol("im", CompletionKind.keyword, double_);
ireal_.parts ~= new ACSymbol("re", CompletionKind.keyword, real_);
ifloat_.parts ~= new ACSymbol("re", CompletionKind.keyword, float_);
idouble_.parts ~= new ACSymbol("re", CompletionKind.keyword, double_);
/**
* Finds the scope containing the cursor position, then searches for a
* symbol with the given name.
*/
ACSymbol[] findSymbolsInCurrentScope(size_t cursorPosition, string name)
{
auto s = findCurrentScope(cursorPosition);
if (s is null)
{
writeln("Could not find scope");
return [];
}
else
return s.findSymbolsInScope(name);
}
auto void_ = new ACSymbol("void", CompletionKind.keyword);
/**
* Returns: the innermost Scope that contains the given cursor position.
*/
Scope findCurrentScope(size_t cursorPosition)
{
if (start != size_t.max && (cursorPosition < start || cursorPosition > end))
return null;
foreach (sc; children)
{
auto s = sc.findCurrentScope(cursorPosition);
if (s is null)
continue;
else
return s;
}
return this;
}
/**
* Finds a symbol with the given name in this scope or one of its parent
* scopes.
*/
ACSymbol[] findSymbolsInScope(string name)
{
ACSymbol[] currentMatches = symbols.filter!(a => a.name == name)().array();
if (currentMatches.length == 0 && parent !is null)
return parent.findSymbolsInScope(name);
return currentMatches;
}
/**
* Fills in the $(D resolvedType) fields of the symbols in this scope and
* all child scopes.
*/
void resolveSymbolTypes()
{
// We only care about resolving types of variables, all other symbols
// don't have any indirection
foreach (ref s; symbols.filter!(a => (a.kind == CompletionKind.variableName
|| a.kind == CompletionKind.functionName || a.kind == CompletionKind.memberVariableName
|| a.kind == CompletionKind.enumMember || a.kind == CompletionKind.aliasName)
&& a.resolvedType is null)())
{
//writeln("Resolving type of symbol ", s.name);
Type type = s.type;
if (type is null)
{
//writeln("Could not find it due to null type");
continue;
}
if (type.type2.builtinType != TokenType.invalid)
{
// This part is easy. Autocomplete properties of built-in types
auto foundSymbols = findSymbolsInScope(getTokenValue(type.type2.builtinType));
s.resolvedType = foundSymbols[0];
}
else if (type.type2.symbol !is null)
{
// Look up a type by its name for cases like class, enum,
// interface, struct, or union members.
// TODO: Does not work with qualified names or template instances
Symbol sym = type.type2.symbol;
if (sym.identifierOrTemplateChain.identifiersOrTemplateInstances.length != 1)
{
writeln("Could not resolve type");
continue;
}
ACSymbol[] resolvedType = findSymbolsInCurrentScope(s.location,
sym.identifierOrTemplateChain.identifiersOrTemplateInstances[0].identifier.value);
if (resolvedType.length > 0 && (resolvedType[0].kind == CompletionKind.interfaceName
|| resolvedType[0].kind == CompletionKind.className
|| resolvedType[0].kind == CompletionKind.aliasName
|| resolvedType[0].kind == CompletionKind.unionName
|| resolvedType[0].kind == CompletionKind.structName))
{
// writeln("Type resolved to ", resolvedType[0].name, " which has kind ",
// resolvedType[0].kind, " and call tip ", resolvedType[0].calltip);
s.resolvedType = resolvedType[0];
}
}
foreach (suffix; type.typeSuffixes)
{
//writeln("Handling type suffix");
// Handle type suffixes for declarations, e.g.:
// int[] a;
// SomeClass[string] b;
// double function(double, double) c;
auto sym = s.resolvedType;
s.resolvedType = new ACSymbol;
s.resolvedType.resolvedType = sym;
if (suffix.array)
{
if (suffix.type !is null)
{
// assocative array
s.resolvedType.qualifier = SymbolQualifier.assocArray;
s.resolvedType.parts ~= assocArraySymbols;
}
else
{
// normal array
s.resolvedType.qualifier = SymbolQualifier.array;
s.resolvedType.parts ~= arraySymbols;
}
}
else if (suffix.delegateOrFunction.type != TokenType.invalid)
{
s.resolvedType.qualifier = SymbolQualifier.func;
}
}
}
foreach (c; children)
{
c.resolveSymbolTypes();
}
foreach (ref ACSymbol c; symbols.filter!(a => a.kind == CompletionKind.className
|| a.kind == CompletionKind.interfaceName))
{
foreach (string sc; c.superClasses)
{
//writeln("Adding inherited fields from ", sc);
ACSymbol[] s = findSymbolsInScope(sc);
if (s.length > 0)
{
foreach (part; s[0].parts)
c.parts ~= part;
}
}
}
}
/**
* Index of the opening brace
*/
size_t start = size_t.max;
/**
* Index of the closing brace
*/
size_t end = size_t.max;
/**
* Symbols contained in this scope
*/
ACSymbol[] symbols;
/**
* The parent scope
*/
Scope parent;
/**
* Child scopes
*/
Scope[] children;
builtinSymbols = [bool_, int_, long_, byte_, char_, dchar_, short_, ubyte_, uint_,
ulong_, ushort_, wchar_, cdouble_, cent_, cfloat_, creal_, double_,
float_, idouble_, ifloat_, ireal_, real_, ucent_, void_];
}
ACSymbol*[] builtinSymbols;
ACSymbol*[] arraySymbols;
ACSymbol*[] assocArraySymbols;
ACSymbol*[] classSymbols;
ACSymbol*[] structSymbols;

View File

@ -1,570 +0,0 @@
/**
* This file is part of DCD, a development tool for the D programming language.
* Copyright (C) 2013 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 acvisitor;
import std.file;
import stdx.d.parser;
import stdx.d.ast;
import stdx.d.lexer;
import std.stdio;
import std.algorithm;
import std.path;
import std.range;
import std.conv;
import actypes;
import messages;
import modulecache;
import autocomplete;
/**
* Converts an AST into a simple symbol and scope heirarchy so that the
* autocompletion coed can do its job more easily.
*/
class AutocompleteVisitor : ASTVisitor
{
alias ASTVisitor.visit visit;
override void visit(Unittest dec)
{
// writeln("Unitttest visit");
auto symbol = new ACSymbol("*unittest*");
auto p = parentSymbol;
parentSymbol = symbol;
dec.accept(this);
parentSymbol = p;
}
override void visit(StructDeclaration dec)
{
// writeln("StructDeclaration visit");
auto symbol = new ACSymbol;
symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.structName;
mixin (visitAndAdd);
}
override void visit(ClassDeclaration dec)
{
// writeln("ClassDeclaration visit");
auto symbol = new ACSymbol;
symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.className;
mixin (visitAndAdd);
}
override void visit(ForStatement forStatement)
{
if (forStatement.declarationOrStatement is null) goto visitBody;
if (forStatement.declarationOrStatement.declaration is null) goto visitBody;
if (forStatement.declarationOrStatement.declaration.variableDeclaration is null) goto visitBody;
if (forStatement.statementNoCaseNoDefault is null) goto visitBody;
if (forStatement.statementNoCaseNoDefault.blockStatement is null) goto visitBody;
// writeln("Visiting for statement");
ACSymbol[] symbols;
VariableDeclaration varDec = forStatement.declarationOrStatement.declaration.variableDeclaration;
Type t = varDec.type;
foreach (Declarator declarator; varDec.declarators)
{
ACSymbol symbol = new ACSymbol();
symbol.name = declarator.name.value;
symbol.type = t;
symbol.kind = CompletionKind.variableName;
symbols ~= symbol;
//writeln("For statement variable ", symbol.name, " of type ", symbol.type, " added.");
}
BlockStatement block = forStatement.statementNoCaseNoDefault.blockStatement;
auto s = new Scope(forStatement.startIndex,
block.endLocation);
s.parent = scope_;
scope_.children ~= s;
auto p = scope_;
scope_ = s;
foreach (symbol; symbols)
{
//writeln("added ", symbol.name, " to scope");
symbol.location = scope_.start;
scope_.symbols ~= symbol;
}
if (block.declarationsAndStatements !is null)
{
//writeln("visiting body");
visit(block.declarationsAndStatements);
}
scope_ = p;
return;
visitBody:
// writeln("visiting body");
if (forStatement.statementNoCaseNoDefault !is null)
visit(forStatement.statementNoCaseNoDefault);
}
override void visit(ForeachStatement statement)
{
ACSymbol[] symbols;
if (statement.foreachTypeList is null)
{
statement.statementNoCaseNoDefault.accept(this);
}
else if (statement.foreachType !is null)
{
ACSymbol loopVariable = new ACSymbol(statement.foreachType.identifier.value);
loopVariable.type = statement.foreachType.type;
loopVariable.kind = CompletionKind.variableName;
symbols ~= loopVariable;
}
else foreach (ForeachType feType; statement.foreachTypeList.items.filter!(a => a.type !is null))
{
ACSymbol loopVariable = new ACSymbol(feType.identifier.value);
loopVariable.type = feType.type;
loopVariable.kind = CompletionKind.variableName;
symbols ~= loopVariable;
}
if (statement.statementNoCaseNoDefault !is null
&& statement.statementNoCaseNoDefault.blockStatement !is null)
{
BlockStatement block = statement.statementNoCaseNoDefault.blockStatement;
auto s = scope_;
scope_ = new Scope(statement.startIndex,
block.endLocation);
scope_.parent = s;
foreach (symbol; symbols)
{
symbol.location = block.startLocation;
scope_.symbols ~= symbol;
}
if (block.declarationsAndStatements !is null)
block.declarationsAndStatements.accept(this);
s.children ~= scope_;
scope_ = s;
}
}
override void visit(InterfaceDeclaration dec)
{
// writeln("InterfaceDeclaration visit");
auto symbol = new ACSymbol;
symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.interfaceName;
mixin (visitAndAdd);
}
override void visit(StructBody structBody)
{
// writeln("StructBody visit");
auto s = scope_;
scope_ = new Scope(structBody.startLocation, structBody.endLocation);
scope_.symbols ~= new ACSymbol("this", CompletionKind.variableName,
parentSymbol);
scope_.parent = s;
s.children ~= scope_;
structBody.accept(this);
scope_ = s;
}
override void visit(EnumDeclaration dec)
{
// TODO: Set enum type based on initializer of first member
// writeln("EnumDeclaration visit");
auto symbol = new ACSymbol;
symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.enumName;
auto type = dec.type;
auto p = parentSymbol;
parentSymbol = symbol;
if (dec.enumBody !is null)
{
Scope enumBodyScope = new Scope(dec.enumBody.startLocation,
dec.enumBody.endLocation);
foreach (member; dec.enumBody.enumMembers)
{
auto s = new ACSymbol;
s.kind = CompletionKind.enumMember;
s.name = member.name.value;
s.location = member.name.startIndex;
if (type is null)
s.resolvedType = scope_.findSymbolsInScope("int")[0];
else
s.type = type;
if (parentSymbol !is null)
parentSymbol.parts ~= s;
enumBodyScope.symbols ~= s;
}
enumBodyScope.parent = scope_;
scope_.children ~= enumBodyScope;
}
parentSymbol = p;
if (parentSymbol is null)
symbols ~= symbol;
else
parentSymbol.parts ~= symbol;
scope_.symbols ~= symbol;
}
override void visit(Constructor dec)
{
ACSymbol symbol = new ACSymbol("*constructor*");
symbol.location = dec.location;
symbol.kind = CompletionKind.functionName;
//symbol.type = dec.returnType;
ACSymbol[] parameterSymbols;
if (dec.parameters !is null)
{
foreach (parameter; dec.parameters.parameters)
{
// writeln("Adding parameter ", parameter.name.value);
ACSymbol paramSymbol = new ACSymbol;
paramSymbol.name = parameter.name.value;
paramSymbol.type = parameter.type;
paramSymbol.kind = CompletionKind.variableName;
paramSymbol.location = parameter.name.startIndex;
parameterSymbols ~= paramSymbol;
}
}
if (dec.parameters !is null && parentSymbol !is null)
{
symbol.calltip = format("%s this%s", parentSymbol.name,
formatNode(dec.parameters));
}
auto p = parentSymbol;
parentSymbol = symbol;
BlockStatement functionBody = dec.functionBody is null ? null
: (dec.functionBody.bodyStatement !is null
? dec.functionBody.bodyStatement.blockStatement : dec.functionBody.blockStatement);
if (functionBody !is null)
{
auto s = scope_;
scope_ = new Scope(functionBody.startLocation,
functionBody.endLocation);
scope_.parent = s;
foreach (parameterSymbol; parameterSymbols)
{
parameterSymbol.location = functionBody.startLocation;
scope_.symbols ~= parameterSymbol;
}
if (functionBody.declarationsAndStatements !is null)
functionBody.declarationsAndStatements.accept(this);
s.children ~= scope_;
scope_ = s;
}
parentSymbol = p;
if (parentSymbol is null)
symbols ~= symbol;
else
parentSymbol.parts ~= symbol;
scope_.symbols ~= symbol;
}
override void visit(FunctionDeclaration dec)
{
ACSymbol symbol = new ACSymbol;
symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.functionName;
symbol.type = dec.returnType;
ACSymbol[] parameterSymbols;
if (dec.parameters !is null)
{
foreach (parameter; dec.parameters.parameters)
{
// writeln("Adding parameter ", parameter.name.value);
ACSymbol paramSymbol = new ACSymbol;
paramSymbol.name = parameter.name.value;
paramSymbol.type = parameter.type;
paramSymbol.kind = CompletionKind.variableName;
paramSymbol.location = parameter.name.startIndex;
parameterSymbols ~= paramSymbol;
}
}
if (dec.parameters !is null)
{
string returnType;
if (dec.returnType !is null)
returnType = formatNode(dec.returnType);
else
{
if (dec.hasAuto)
{
returnType = "auto";
if (dec.hasRef)
returnType = "auto ref";
}
else if (dec.hasRef)
returnType = "ref";
}
symbol.calltip = formatCalltip(dec.returnType, dec.name.value, dec.parameters);
}
auto p = parentSymbol;
parentSymbol = symbol;
BlockStatement functionBody = dec.functionBody is null ? null
: (dec.functionBody.bodyStatement !is null
? dec.functionBody.bodyStatement.blockStatement : dec.functionBody.blockStatement);
if (functionBody !is null)
{
auto s = scope_;
scope_ = new Scope(functionBody.startLocation,
functionBody.endLocation);
scope_.parent = s;
foreach (parameterSymbol; parameterSymbols)
{
parameterSymbol.location = functionBody.startLocation;
scope_.symbols ~= parameterSymbol;
}
if (functionBody.declarationsAndStatements !is null)
functionBody.declarationsAndStatements.accept(this);
s.children ~= scope_;
scope_ = s;
}
parentSymbol = p;
if (parentSymbol is null)
symbols ~= symbol;
else
parentSymbol.parts ~= symbol;
scope_.symbols ~= symbol;
}
override void visit(VariableDeclaration dec)
{
// writeln("VariableDeclaration visit");
foreach (d; dec.declarators)
{
ACSymbol symbol = new ACSymbol;
if (dec.type.typeSuffixes.length > 0
&& dec.type.typeSuffixes[$-1].delegateOrFunction != TokenType.invalid)
{
TypeSuffix suffix = dec.type.typeSuffixes[$ - 1];
dec.type.typeSuffixes = dec.type.typeSuffixes[0 .. $ - 1];
symbol.calltip = formatCalltip(dec.type,
suffix.delegateOrFunction.value, suffix.parameters);
}
symbol.kind = CompletionKind.variableName;
symbol.type = dec.type;
symbol.name = d.name.value;
symbol.location = d.name.startIndex;
if (parentSymbol is null)
symbols ~= symbol;
else
parentSymbol.parts ~= symbol;
scope_.symbols ~= symbol;
}
}
override void visit(AliasDeclaration dec)
{
if (dec.type is null) foreach (aliasPart; dec.initializers)
{
ACSymbol aliasSymbol = new ACSymbol;
aliasSymbol.kind = CompletionKind.aliasName;
aliasSymbol.location = aliasPart.name.startIndex;
aliasSymbol.type = aliasPart.type;
if (aliasPart.type.typeSuffixes.length > 0
&& aliasPart.type.typeSuffixes[$-1].delegateOrFunction != TokenType.invalid)
{
TypeSuffix suffix = aliasPart.type.typeSuffixes[$ - 1];
aliasPart.type.typeSuffixes = aliasPart.type.typeSuffixes[0 .. $ - 1];
aliasSymbol.calltip = formatCalltip(dec.type, suffix.delegateOrFunction.value, suffix.parameters);
}
if (parentSymbol is null)
symbols ~= aliasSymbol;
else
parentSymbol.parts ~= aliasSymbol;
scope_.symbols ~= aliasSymbol;
}
else
{
// writeln("Visiting alias declaration ", dec.name.value);
ACSymbol aliasSymbol = new ACSymbol;
aliasSymbol.kind = CompletionKind.aliasName;
aliasSymbol.name = dec.name.value;
aliasSymbol.type = dec.type;
if (dec.type.typeSuffixes.length > 0
&& dec.type.typeSuffixes[$-1].delegateOrFunction != TokenType.invalid)
{
TypeSuffix suffix = dec.type.typeSuffixes[$ - 1];
dec.type.typeSuffixes = dec.type.typeSuffixes[0 .. $ - 1];
aliasSymbol.calltip = "%s %s%s".format(formatNode(dec.type),
suffix.delegateOrFunction.value,
formatNode(suffix.parameters));
}
aliasSymbol.location = dec.name.startIndex;
if (parentSymbol is null)
symbols ~= aliasSymbol;
else
parentSymbol.parts ~= aliasSymbol;
scope_.symbols ~= aliasSymbol;
}
}
override void visit(ImportDeclaration dec)
{
// TODO: handle public imports
if (!currentFile) return;
foreach (singleImport; dec.singleImports.filter!(a => a !is null
&& a.identifierChain !is null))
{
scope_.symbols ~= ModuleCache.getSymbolsInModule(
convertChainToImportPath(singleImport.identifierChain));
}
if (dec.importBindings !is null
&& dec.importBindings.singleImport.identifierChain !is null)
{
ACSymbol[] importedSymbols = ModuleCache.getSymbolsInModule(
convertChainToImportPath(dec.importBindings.singleImport.identifierChain));
foreach (ImportBind b; dec.importBindings.importBinds)
{
if (b.right == TokenType.invalid)
{
// Selecive import
foreach (ACSymbol symbol; importedSymbols.filter!(a => a.name == b.left))
scope_.symbols ~= symbol;
}
else
{
// renamed selective import
foreach (ACSymbol symbol; importedSymbols.filter!(a => a.name == b.right))
{
ACSymbol s = new ACSymbol;
s.kind = symbol.kind;
s.location = symbol.location;
s.name = b.left.value;
s.parts = symbol.parts;
s.qualifier = symbol.qualifier;
s.resolvedType = symbol.resolvedType;
s.superClasses = symbol.superClasses;
s.type = symbol.type;
scope_.symbols ~= s;
}
}
}
}
}
override void visit(BaseClassList classList)
{
if (parentSymbol is null)
return;
foreach (BaseClass bc; classList.items)
{
if (bc.identifierOrTemplateChain is null)
continue;
if (bc.identifierOrTemplateChain.identifiersOrTemplateInstances.length != 1)
continue;
IdentifierOrTemplateInstance i = bc.identifierOrTemplateChain.identifiersOrTemplateInstances[0];
if (i is null || i.identifier == TokenType.invalid)
continue;
parentSymbol.superClasses ~= i.identifier.value;
}
}
override void visit(BlockStatement blockStatement)
{
auto s = scope_;
scope_ = new Scope(blockStatement.startLocation,
blockStatement.endLocation);
scope_.parent = s;
blockStatement.accept(this);
s.children ~= scope_;
scope_ = s;
}
override void visit(Module mod)
{
scope_ = new Scope(0, size_t.max);
scope_.symbols ~= builtinSymbols;
mod.accept(this);
}
private static string convertChainToImportPath(IdentifierChain chain)
{
return to!string(chain.identifiers.map!(a => a.value).join(dirSeparator).array) ~ ".d";
}
ACSymbol[] symbols;
ACSymbol parentSymbol;
Scope scope_;
string[] imports = ["object"];
bool currentFile = false;
private:
static string formatCalltip(Type returnType, string name, Parameters parameters,
string doc = null)
{
return "%s %s%s".format(formatNode(returnType), name, formatNode(parameters));
}
static 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);
}
static enum string visitAndAdd = q{
auto p = parentSymbol;
parentSymbol = symbol;
dec.accept(this);
parentSymbol = p;
if (parentSymbol is null)
symbols ~= symbol;
else
parentSymbol.parts ~= symbol;
scope_.symbols ~= symbol;
};
}
void doesNothing(string, int, int, string) {}
AutocompleteVisitor processModule(const(Token)[] tokens)
{
Module mod = parseModule(tokens, "", &doesNothing);
auto visitor = new AutocompleteVisitor;
visitor.currentFile = true;
visitor.visit(mod);
return visitor;
}

656
astconverter.d Normal file
View File

@ -0,0 +1,656 @@
/*******************************************************************************
* Authors: Brian Schott
* Copyright: Brian Schott
* Date: Sep 21 2013
*
* License:
* 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.array;
import std.conv;
import std.range;
import std.algorithm;
import stdx.d.ast;
import stdx.d.lexer;
import stdx.d.parser;
import actypes;
import messages;
import semantic;
import stupidlog;
enum SemanticType
{
partial,
full
}
/**
* Basic visitor class used for caching completions of imported modules.
*/
class SemanticVisitor : ASTVisitor
{
this (SemanticType sType)
{
this.semanticType = sType;
}
override void visit(Constructor con)
{
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
visitFunctionDeclaration(con);
}
override void visit(SharedStaticConstructor con)
{
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
visitFunctionDeclaration(con);
}
override void visit(StaticConstructor con)
{
// Log.trace(__FUNCTION__, " ", typeof(con).stringof);
visitFunctionDeclaration(con);
}
override void visit(Destructor des)
{
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
visitFunctionDeclaration(des);
}
override void visit(SharedStaticDestructor des)
{
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
visitFunctionDeclaration(des);
}
override void visit(StaticDestructor des)
{
// Log.trace(__FUNCTION__, " ", typeof(des).stringof);
visitFunctionDeclaration(des);
}
override void visit(FunctionDeclaration dec)
{
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
visitFunctionDeclaration(dec);
}
override void visit(ClassDeclaration dec)
{
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
visitAggregateDeclaration(dec, CompletionKind.className);
}
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;
symbol.type = t;
symbol.protection = protection;
symbol.kind = CompletionKind.variableName;
symbol.name = declarator.name.value.dup;
symbol.location = declarator.name.startIndex;
symbol.parent = currentSymbol;
currentSymbol.children ~= symbol;
}
}
override void visit(AliasThisDeclaration dec)
{
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
currentSymbol.aliasThis ~= dec.identifier.value.dup;
}
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;
}
TokenType p = protection;
foreach (Attribute attr; dec.attributes)
{
if (isProtection(attr.attribute))
p = attr.attribute;
}
dec.accept(this);
protection = p;
}
override void visit(Module mod)
{
// Log.trace(__FUNCTION__, " ", typeof(mod).stringof);
rootSymbol = new SemanticSymbol;
rootSymbol.kind = CompletionKind.moduleName;
rootSymbol.startLocation = 0;
rootSymbol.endLocation = size_t.max;
currentSymbol = rootSymbol;
currentScope = new Scope();
currentScope.startLocation = 0;
currentScope.endLocation = size_t.max;
mod.accept(this);
}
override void visit(EnumDeclaration dec)
{
assert (currentSymbol);
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
SemanticSymbol* symbol = new SemanticSymbol;
symbol.name = dec.name.value.dup;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.enumName;
symbol.type = dec.type;
symbol.parent = currentSymbol;
currentSymbol = symbol;
if (dec.enumBody !is null)
dec.enumBody.accept(this);
currentSymbol = symbol.parent;
currentSymbol.children ~= symbol;
}
override void visit(EnumMember member)
{
// Log.trace(__FUNCTION__, " ", typeof(member).stringof);
SemanticSymbol* symbol = new SemanticSymbol;
symbol.kind = CompletionKind.enumMember;
symbol.name = member.name.value.dup;
symbol.location = member.name.startIndex;
symbol.type = member.type;
symbol.parent = currentSymbol;
currentSymbol.children ~= symbol;
}
override void visit(ModuleDeclaration dec)
{
// Log.trace(__FUNCTION__, " ", typeof(dec).stringof);
foreach (Token t; dec.moduleName.identifiers)
moduleName ~= t.value.dup;
}
// creates scopes for
override void visit(StructBody structBody)
{
// Log.trace(__FUNCTION__, " ", typeof(structBody).stringof);
Scope* s = new Scope;
s.startLocation = structBody.startLocation;
s.endLocation = structBody.endLocation;
// Log.trace("Added scope ", s.startLocation, " ", s.endLocation);
s.parent = currentScope;
currentScope = s;
foreach (dec; structBody.declarations)
visit(dec);
currentScope = s.parent;
currentScope.children ~= s;
}
// Create scope for block statements
override void visit(BlockStatement blockStatement)
{
Log.trace(__FUNCTION__, " ", typeof(blockStatement).stringof);
Scope* s = new Scope;
s.startLocation = blockStatement.startLocation;
s.endLocation = blockStatement.endLocation;
if (currentSymbol.kind == CompletionKind.functionName)
{
foreach (child; currentSymbol.children)
{
Log.trace("Setting ", child.name, " location");
child.location = s.startLocation + 1;
}
}
// Log.trace("Added scope ", s.startLocation, " ", s.endLocation);
s.parent = currentScope;
if (blockStatement.declarationsAndStatements !is null)
{
currentScope = s;
visit (blockStatement.declarationsAndStatements);
currentScope = s.parent;
}
currentScope.children ~= s;
}
alias ASTVisitor.visit visit;
private:
void visitAggregateDeclaration(AggType)(AggType dec, CompletionKind kind)
{
SemanticSymbol* symbol = new SemanticSymbol;
// Log.trace("visiting aggregate declaration ", dec.name.value);
symbol.name = dec.name.value.dup;
symbol.location = dec.name.startIndex;
symbol.kind = kind;
symbol.parent = currentSymbol;
symbol.protection = protection;
currentSymbol = symbol;
dec.accept(this);
currentSymbol = symbol.parent;
currentSymbol.children ~= symbol;
}
void visitFunctionDeclaration(DeclarationType)(DeclarationType dec)
{
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;
foreach (ioti; iotc.identifiersOrTemplateInstances)
{
if (ioti.identifier != TokenType.invalid)
parts ~= ioti.identifier.value.dup;
else
parts ~= ioti.templateInstance.identifier.value.dup;
}
return parts;
}
static string formatCalltip(Type returnType, string name, Parameters parameters,
string doc = null)
{
return "%s %s%s".format(formatNode(returnType), name, formatNode(parameters));
}
static 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);
}
/// Current protection type
TokenType protection;
/// Current symbol
SemanticSymbol* currentSymbol;
/// The module
SemanticSymbol* rootSymbol;
/// Package and module name
string[] moduleName;
/// Current scope
Scope* currentScope;
SemanticType semanticType;
}
struct SemanticConverter
{
public:
this(const(SemanticSymbol)* symbol, Scope* scopes)
{
this.sc = scopes;
this.symbol = symbol;
}
void convertModule()
{
convertSemanticSymbol(symbol);
assert (current !is null);
}
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:
ACSymbol* resolveType(const Type t)
in
{
assert (t !is null);
assert (t.type2 !is null);
}
body
{
ACSymbol* s;
if (t.type2.builtinType != TokenType.invalid)
s = convertBuiltinType(t.type2);
else if (t.type2.typeConstructor != TokenType.invalid)
s = resolveType(t.type2.type);
else if (t.type2.symbol !is null)
{
if (t.type2.symbol.dot)
Log.trace("TODO: global scoped symbol handling");
string[] symbolParts = expandSymbol(
t.type2.symbol.identifierOrTemplateChain);
}
foreach (suffix; t.typeSuffixes)
s = processSuffix(s, suffix);
return null;
}
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];
strings[i] = identOrTemplate.templateInstance is null ?
identOrTemplate.identifier.value.dup
: identOrTemplate.identifier.value.dup;
}
return strings;
}
static ACSymbol* processSuffix(ACSymbol* symbol, const TypeSuffix suffix)
{
if (suffix.star)
return symbol;
if (suffix.array || suffix.type)
{
ACSymbol* s = new ACSymbol;
s.parts = arraySymbols;
s.type = symbol;
s.qualifier = suffix.array ? SymbolQualifier.array : SymbolQualifier.assocArray;
return s;
}
if (suffix.parameters)
{
Log.error("TODO: Function type suffix");
return null;
}
return null;
}
static ACSymbol* convertBuiltinType(const Type2 type2)
{
string stringRepresentation = getTokenValue(type2.builtinType);
if (stringRepresentation is null) return null;
// TODO: Make this use binary search instead
foreach (s; builtinSymbols)
if (s.name == stringRepresentation)
return s;
return null;
}
ACSymbol* current;
Scope* sc;
const(SemanticSymbol)* symbol;
}
const(ACSymbol)*[] convertAstToSymbols(Module m)
{
SemanticVisitor visitor = new SemanticVisitor(SemanticType.partial);
visitor.visit(m);
SemanticConverter converter = SemanticConverter(visitor.rootSymbol, null);
converter.convertModule();
return cast(typeof(return)) converter.current.parts;
}
const(Scope)* generateAutocompleteTrees(const(Token)[] tokens)
{
Module m = parseModule(tokens, null);
SemanticVisitor visitor = new SemanticVisitor(SemanticType.full);
visitor.visit(m);
SemanticConverter converter = SemanticConverter(visitor.rootSymbol,
visitor.currentScope);
converter.convertModule();
return converter.sc;
}
version(unittest) Module parseTestCode(string code)
{
LexerConfig config;
const(Token)[] tokens = byToken(cast(ubyte[]) code, config);
Parser p = new Parser;
p.fileName = "unittest";
p.tokens = tokens;
Module m = p.parseModule();
assert (p.errorCount == 0);
assert (p.warningCount == 0);
return m;
}
unittest
{
auto source = q{
module foo;
struct Bar { int x; int y;}
}c;
Module m = parseTestCode(source);
BasicSemanticVisitor visitor = new BasicSemanticVisitor;
visitor.visit(m);
assert (visitor.rootSymbol !is null);
assert (visitor.rootSymbol.name == "foo");
SemanticSymbol* bar = visitor.root.getPartByName("Bar");
assert (bar !is null);
assert (bar.getPartByName("x") !is null);
assert (bar.getPartByName("y") !is null);
}

View File

@ -34,19 +34,27 @@ import stdx.d.parser;
import std.string;
import messages;
import acvisitor;
import actypes;
import constants;
import modulecache;
import astconverter;
import stupidlog;
AutocompleteResponse complete(AutocompleteRequest request, string[] importPaths)
{
writeln("Got a completion request");
Log.info("Got a completion request");
AutocompleteResponse response;
LexerConfig config;
auto tokens = request.sourceCode.byToken(config);
auto tokenArray = tokens.array();
config.fileName = "stdin";
auto tokens = byToken(cast(ubyte[]) request.sourceCode, config);
const(Token)[] tokenArray = void;
try {
tokenArray = tokens.array();
} catch (Exception e) {
Log.error("Could not provide autocomplete due to lexing exception: ", e.msg);
return response;
}
auto sortedTokens = assumeSorted(tokenArray);
string partial;
@ -56,7 +64,7 @@ AutocompleteResponse complete(AutocompleteRequest request, string[] importPaths)
if (beforeTokens.length >= 1 && beforeTokens[$ - 1] == TokenType.identifier)
{
//writeln("partial completion");
Log.trace("partial completion");
partial = beforeTokens[$ - 1].value;
tokenType = beforeTokens[$ - 1].type;
beforeTokens = beforeTokens[0 .. $ - 1];
@ -92,10 +100,10 @@ AutocompleteResponse complete(AutocompleteRequest request, string[] importPaths)
case TokenType.identifier:
case TokenType.rParen:
case TokenType.rBracket:
auto visitor = processModule(tokenArray);
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray);
auto expression = getExpression(beforeTokens[0 .. $ - 1]);
response.setCompletions(visitor, expression, request.cursorPosition,
CompletionType.calltips);
response.setCompletions(completionScope, expression,
request.cursorPosition, CompletionType.calltips);
break;
default:
break;
@ -144,10 +152,10 @@ dotCompletion:
case TokenType.rParen:
case TokenType.rBracket:
case TokenType.this_:
auto visitor = processModule(tokenArray);
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray);
auto expression = getExpression(beforeTokens);
response.setCompletions(visitor, expression, request.cursorPosition,
CompletionType.identifiers, partial);
response.setCompletions(completionScope, expression,
request.cursorPosition, CompletionType.identifiers, partial);
break;
case TokenType.lParen:
case TokenType.lBrace:
@ -164,7 +172,7 @@ dotCompletion:
}
void setCompletions(T)(ref AutocompleteResponse response,
AutocompleteVisitor visitor, T tokens, size_t cursorPosition,
const(Scope)* completionScope, T tokens, size_t cursorPosition,
CompletionType completionType, string partial = null)
{
// Autocomplete module imports instead of symbols
@ -175,14 +183,11 @@ void setCompletions(T)(ref AutocompleteResponse response,
return;
}
visitor.scope_.resolveSymbolTypes();
// Handle the simple case where we get all symbols in scope and filter it
// based on the currently entered text.
if (partial !is null && tokens.length == 0)
{
// writeln("Showing all symbols in current scope that start with ", partial);
foreach (s; visitor.scope_.getSymbolsInCurrentScope(cursorPosition)
foreach (s; completionScope.getSymbolsInCursorScope(cursorPosition)
.filter!(a => a.name.toUpper().startsWith(partial.toUpper())))
{
response.completionKinds ~= s.kind;
@ -196,10 +201,11 @@ void setCompletions(T)(ref AutocompleteResponse response,
return;
// Find the symbol corresponding to the beginning of the chain
ACSymbol[] symbols = visitor.scope_.findSymbolsInCurrentScope(cursorPosition, tokens[0].value);
const(ACSymbol)*[] symbols = completionScope.getSymbolsByNameAndCursor(
tokens[0].value, cursorPosition);
if (symbols.length == 0)
{
writeln("Could not find declaration of ", tokens[0].value);
Log.trace("Could not find declaration of ", tokens[0].value);
return;
}
@ -209,11 +215,9 @@ void setCompletions(T)(ref AutocompleteResponse response,
|| symbols[0].kind == CompletionKind.aliasName
|| symbols[0].kind == CompletionKind.enumMember)
{
symbols = symbols[0].resolvedType is null ? [] : [symbols[0].resolvedType];
symbols = symbols[0].type is null ? [] : [symbols[0].type];
if (symbols.length == 0)
{
return;
}
}
loop: for (size_t i = 1; i < tokens.length; i++)
@ -236,40 +240,40 @@ void setCompletions(T)(ref AutocompleteResponse response,
}
with (TokenType) switch (tokens[i].type)
{
case TokenType.int_:
case TokenType.uint_:
case TokenType.long_:
case TokenType.ulong_:
case TokenType.char_:
case TokenType.wchar_:
case TokenType.dchar_:
case TokenType.bool_:
case TokenType.byte_:
case TokenType.ubyte_:
case TokenType.short_:
case TokenType.ushort_:
case TokenType.cent_:
case TokenType.ucent_:
case TokenType.float_:
case TokenType.ifloat_:
case TokenType.cfloat_:
case TokenType.idouble_:
case TokenType.cdouble_:
case TokenType.double_:
case TokenType.real_:
case TokenType.ireal_:
case TokenType.creal_:
case int_:
case uint_:
case long_:
case ulong_:
case char_:
case wchar_:
case dchar_:
case bool_:
case byte_:
case ubyte_:
case short_:
case ushort_:
case cent_:
case ucent_:
case float_:
case ifloat_:
case cfloat_:
case idouble_:
case cdouble_:
case double_:
case real_:
case ireal_:
case creal_:
case this_:
symbols = symbols[0].getPartsByName(getTokenValue(tokens[i].type));
if (symbols.length == 0)
break loop;
break;
case identifier:
// writeln("looking for ", tokens[i].value, " in ", symbols[0].name);
Log.trace("looking for ", tokens[i].value, " in ", symbols[0].name);
symbols = symbols[0].getPartsByName(tokens[i].value);
if (symbols.length == 0)
{
// writeln("Couldn't find it.");
Log.trace("Couldn't find it.");
break loop;
}
if (symbols[0].kind == CompletionKind.variableName
@ -279,7 +283,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
&& (completionType == CompletionType.identifiers
|| i + 1 < tokens.length)))
{
symbols = symbols[0].resolvedType is null ? [] : [symbols[0].resolvedType];
symbols = symbols[0].type is null ? [] : [symbols[0].type];
}
if (symbols.length == 0)
break loop;
@ -287,7 +291,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
&& (completionType == CompletionType.identifiers
|| i + 1 < tokens.length))
{
symbols = symbols[0].resolvedType is null ? [] : [symbols[0].resolvedType];
symbols = symbols[0].type is null ? [] : [symbols[0].type];
}
if (symbols.length == 0)
break loop;
@ -308,12 +312,12 @@ void setCompletions(T)(ref AutocompleteResponse response,
p.setTokens(tokens[h .. i].array());
if (!p.isSliceExpression())
{
symbols = symbols[0].resolvedType is null ? [] : [symbols[0].resolvedType];
symbols = symbols[0].type is null ? [] : [symbols[0].type];
}
}
else if (symbols[0].qualifier == SymbolQualifier.assocArray)
{
symbols = symbols[0].resolvedType is null ? [] :[symbols[0].resolvedType];
symbols = symbols[0].type is null ? [] :[symbols[0].type];
skip();
}
else
@ -322,14 +326,14 @@ void setCompletions(T)(ref AutocompleteResponse response,
skip();
Parser p;
p.setTokens(tokens[h .. i].array());
ACSymbol[] overloads;
const(ACSymbol)*[] overloads;
if (p.isSliceExpression())
overloads = symbols[0].getPartsByName("opSlice");
else
overloads = symbols[0].getPartsByName("opIndex");
if (overloads.length > 0)
{
symbols = overloads[0].resolvedType is null ? [] : [overloads[0].resolvedType];
symbols = overloads[0].type is null ? [] : [overloads[0].type];
}
else
return;
@ -344,7 +348,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
if (symbols.length == 0)
{
writeln("Could not get completions");
Log.error("Could not get completions");
return;
}
if (completionType == CompletionType.identifiers)
@ -354,7 +358,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
&& (partial is null ? true : a.name.toUpper().startsWith(partial.toUpper()))
&& !response.completions.canFind(a.name)))
{
//writeln("Adding ", s.name, " to the completion list");
Log.trace("Adding ", s.name, " to the completion list");
response.completionKinds ~= s.kind;
response.completions ~= s.name;
}
@ -362,9 +366,9 @@ void setCompletions(T)(ref AutocompleteResponse response,
}
else if (completionType == CompletionType.calltips)
{
//writeln("Showing call tips for ", symbols[0].name, " of type ", symbols[0].kind);
Log.trace("Showing call tips for ", symbols[0].name, " of type ", symbols[0].kind);
if (symbols[0].kind != CompletionKind.functionName
&& symbols[0].calltip is null)
&& symbols[0].callTip is null)
{
auto call = symbols[0].getPartsByName("opCall");
if (call.length == 0)
@ -385,8 +389,8 @@ void setCompletions(T)(ref AutocompleteResponse response,
response.completionType = CompletionType.calltips;
foreach (symbol; symbols)
{
// writeln("Adding calltip ", symbol.calltip);
response.completions ~= symbol.calltip;
Log.trace("Adding calltip ", symbol.callTip);
response.completions ~= symbol.callTip;
}
}
@ -522,118 +526,3 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
}
}
}
/**
* Initializes builtin types and the various properties of builtin types
*/
static this()
{
auto bool_ = new ACSymbol("bool", CompletionKind.keyword);
auto int_ = new ACSymbol("int", CompletionKind.keyword);
auto long_ = new ACSymbol("long", CompletionKind.keyword);
auto byte_ = new ACSymbol("byte", CompletionKind.keyword);
auto char_ = new ACSymbol("char", CompletionKind.keyword);
auto dchar_ = new ACSymbol("dchar", CompletionKind.keyword);
auto short_ = new ACSymbol("short", CompletionKind.keyword);
auto ubyte_ = new ACSymbol("ubyte", CompletionKind.keyword);
auto uint_ = new ACSymbol("uint", CompletionKind.keyword);
auto ulong_ = new ACSymbol("ulong", CompletionKind.keyword);
auto ushort_ = new ACSymbol("ushort", CompletionKind.keyword);
auto wchar_ = new ACSymbol("wchar", CompletionKind.keyword);
auto alignof_ = new ACSymbol("alignof", CompletionKind.keyword, ulong_);
auto mangleof_ = new ACSymbol("mangleof", CompletionKind.keyword);
auto sizeof_ = new ACSymbol("sizeof", CompletionKind.keyword, ulong_);
auto stringof_ = new ACSymbol("stringof", CompletionKind.keyword);
arraySymbols ~= alignof_;
arraySymbols ~= new ACSymbol("dup", CompletionKind.keyword);
arraySymbols ~= new ACSymbol("idup", CompletionKind.keyword);
arraySymbols ~= new ACSymbol("init", CompletionKind.keyword);
arraySymbols ~= new ACSymbol("length", CompletionKind.keyword, ulong_);
arraySymbols ~= mangleof_;
arraySymbols ~= new ACSymbol("ptr", CompletionKind.keyword);
arraySymbols ~= new ACSymbol("reverse", CompletionKind.keyword);
arraySymbols ~= sizeof_;
arraySymbols ~= new ACSymbol("sort", CompletionKind.keyword);
arraySymbols ~= stringof_;
assocArraySymbols ~= alignof_;
assocArraySymbols ~= new ACSymbol("byKey", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("byValue", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("dup", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("get", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("init", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("keys", CompletionKind.keyword);
assocArraySymbols ~= new ACSymbol("length", CompletionKind.keyword, ulong_);
assocArraySymbols ~= mangleof_;
assocArraySymbols ~= new ACSymbol("rehash", CompletionKind.keyword);
assocArraySymbols ~= sizeof_;
assocArraySymbols ~= stringof_;
assocArraySymbols ~= new ACSymbol("values", CompletionKind.keyword);
foreach (s; [bool_, int_, long_, byte_, char_, dchar_, short_, ubyte_, uint_,
ulong_, ushort_, wchar_])
{
s.parts ~= new ACSymbol("init", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("min", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("max", CompletionKind.keyword, s);
s.parts ~= alignof_;
s.parts ~= sizeof_;
s.parts ~= stringof_;
s.parts ~= mangleof_;
}
auto cdouble_ = new ACSymbol("cdouble", CompletionKind.keyword);
auto cent_ = new ACSymbol("cent", CompletionKind.keyword);
auto cfloat_ = new ACSymbol("cfloat", CompletionKind.keyword);
auto creal_ = new ACSymbol("creal", CompletionKind.keyword);
auto double_ = new ACSymbol("double", CompletionKind.keyword);
auto float_ = new ACSymbol("float", CompletionKind.keyword);
auto idouble_ = new ACSymbol("idouble", CompletionKind.keyword);
auto ifloat_ = new ACSymbol("ifloat", CompletionKind.keyword);
auto ireal_ = new ACSymbol("ireal", CompletionKind.keyword);
auto real_ = new ACSymbol("real", CompletionKind.keyword);
auto ucent_ = new ACSymbol("ucent", CompletionKind.keyword);
foreach (s; [cdouble_, cent_, cfloat_, creal_, double_, float_,
idouble_, ifloat_, ireal_, real_, ucent_])
{
s.parts ~= alignof_;
s.parts ~= new ACSymbol("dig", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("epsilon", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("infinity", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("init", CompletionKind.keyword, s);
s.parts ~= mangleof_;
s.parts ~= new ACSymbol("mant_dig", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("max", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("max_10_exp", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("max_exp", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("min", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("min_exp", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("min_10_exp", CompletionKind.keyword, int_);
s.parts ~= new ACSymbol("min_normal", CompletionKind.keyword, s);
s.parts ~= new ACSymbol("nan", CompletionKind.keyword, s);
s.parts ~= sizeof_;
s.parts ~= stringof_;
}
ireal_.parts ~= new ACSymbol("im", CompletionKind.keyword, real_);
ifloat_.parts ~= new ACSymbol("im", CompletionKind.keyword, float_);
idouble_.parts ~= new ACSymbol("im", CompletionKind.keyword, double_);
ireal_.parts ~= new ACSymbol("re", CompletionKind.keyword, real_);
ifloat_.parts ~= new ACSymbol("re", CompletionKind.keyword, float_);
idouble_.parts ~= new ACSymbol("re", CompletionKind.keyword, double_);
auto void_ = new ACSymbol("void", CompletionKind.keyword);
builtinSymbols = [bool_, int_, long_, byte_, char_, dchar_, short_, ubyte_, uint_,
ulong_, ushort_, wchar_, cdouble_, cent_, cfloat_, creal_, double_,
float_, idouble_, ifloat_, ireal_, real_, ucent_, void_];
}
ACSymbol[] builtinSymbols;
ACSymbol[] arraySymbols;
ACSymbol[] assocArraySymbols;
ACSymbol[] classSymbols;
ACSymbol[] structSymbols;

View File

@ -1,2 +1,26 @@
dmd -wi client.d messages.d msgpack-d/src/msgpack.d -Imsgpack-d/src -ofdcd-client
dmd -wi -g server.d modulecache.d actypes.d messages.d constants.d acvisitor.d autocomplete.d dscanner/stdx/d/ast.d dscanner/stdx/d/parser.d dscanner/stdx/d/lexer.d dscanner/stdx/d/entities.d dscanner/formatter.d msgpack-d/src/msgpack.d -Imsgpack-d/src -Idscanner/ -ofdcd-server
dmd -wi client.d\
messages.d\
msgpack-d/src/msgpack.d\
-Imsgpack-d/src\
-ofdcd-client
dmd \
actypes.d\
astconverter.d\
autocomplete.d\
constants.d\
messages.d\
modulecache.d\
semantic.d\
server.d\
stupidlog.d\
dscanner/stdx/d/ast.d\
dscanner/stdx/d/parser.d\
dscanner/stdx/d/lexer.d\
dscanner/stdx/d/entities.d\
dscanner/formatter.d\
msgpack-d/src/msgpack.d\
-Imsgpack-d/src\
-Idscanner\
-wi\
-ofdcd-server

View File

@ -53,7 +53,9 @@ immutable string[] traits = [
"getMember",
"getOverloads",
"getProtection",
"getUnitTests",
"getVirtualFunctions",
"getVirtualIndex",
"getVirtualMethods",
"hasMember",
"identifier",
@ -68,6 +70,7 @@ immutable string[] traits = [
"isLazy",
"isNested",
"isOut",
"isOverrideFunction",
"isPOD",
"isRef",
"isSame",
@ -199,3 +202,87 @@ immutable string[] structProperties = [
"sizeof",
"stringof"
];
immutable string[] predefinedVersions;
static this()
{
version(AArch64) predefinedVersions ~= "AArch64";
version(AIX) predefinedVersions ~= "AIX";
version(all) predefinedVersions ~= "all";
version(Alpha) predefinedVersions ~= "Alpha";
version(Alpha_HardFloat) predefinedVersions ~= "Alpha_HardFloat";
version(Alpha_SoftFloat) predefinedVersions ~= "Alpha_SoftFloat";
version(Android) predefinedVersions ~= "Android";
version(ARM) predefinedVersions ~= "ARM";
version(ARM_HardFloat) predefinedVersions ~= "ARM_HardFloat";
version(ARM_SoftFloat) predefinedVersions ~= "ARM_SoftFloat";
version(ARM_SoftFP) predefinedVersions ~= "ARM_SoftFP";
version(ARM_Thumb) predefinedVersions ~= "ARM_Thumb";
version(assert) predefinedVersions ~= "assert";
version(BigEndian) predefinedVersions ~= "BigEndian";
version(BSD) predefinedVersions ~= "BSD";
version(Cygwin) predefinedVersions ~= "Cygwin";
version(D_Coverage) predefinedVersions ~= "D_Coverage";
version(D_Ddoc) predefinedVersions ~= "D_Ddoc";
version(D_HardFloat) predefinedVersions ~= "D_HardFloat";
version(DigitalMars) predefinedVersions ~= "DigitalMars";
version(D_InlineAsm_X86) predefinedVersions ~= "D_InlineAsm_X86";
version(D_InlineAsm_X86_64) predefinedVersions ~= "D_InlineAsm_X86_64";
version(D_LP64) predefinedVersions ~= "D_LP64";
version(D_NoBoundsChecks) predefinedVersions ~= "D_NoBoundsChecks";
version(D_PIC) predefinedVersions ~= "D_PIC";
version(DragonFlyBSD) predefinedVersions ~= "DragonFlyBSD";
version(D_SIMD) predefinedVersions ~= "D_SIMD";
version(D_SoftFloat) predefinedVersions ~= "D_SoftFloat";
version(D_Version2) predefinedVersions ~= "D_Version2";
version(D_X32) predefinedVersions ~= "D_X32";
version(FreeBSD) predefinedVersions ~= "FreeBSD";
version(GNU) predefinedVersions ~= "GNU";
version(Haiku) predefinedVersions ~= "Haiku";
version(HPPA) predefinedVersions ~= "HPPA";
version(HPPA64) predefinedVersions ~= "HPPA64";
version(Hurd) predefinedVersions ~= "Hurd";
version(IA64) predefinedVersions ~= "IA64";
version(LDC) predefinedVersions ~= "LDC";
version(linux) predefinedVersions ~= "linux";
version(LittleEndian) predefinedVersions ~= "LittleEndian";
version(MIPS32) predefinedVersions ~= "MIPS32";
version(MIPS64) predefinedVersions ~= "MIPS64";
version(MIPS_EABI) predefinedVersions ~= "MIPS_EABI";
version(MIPS_HardFloat) predefinedVersions ~= "MIPS_HardFloat";
version(MIPS_N32) predefinedVersions ~= "MIPS_N32";
version(MIPS_N64) predefinedVersions ~= "MIPS_N64";
version(MIPS_O32) predefinedVersions ~= "MIPS_O32";
version(MIPS_O64) predefinedVersions ~= "MIPS_O64";
version(MIPS_SoftFloat) predefinedVersions ~= "MIPS_SoftFloat";
version(NetBSD) predefinedVersions ~= "NetBSD";
version(none) predefinedVersions ~= "none";
version(OpenBSD) predefinedVersions ~= "OpenBSD";
version(OSX) predefinedVersions ~= "OSX";
version(Posix) predefinedVersions ~= "Posix";
version(PPC) predefinedVersions ~= "PPC";
version(PPC64) predefinedVersions ~= "PPC64";
version(PPC_HardFloat) predefinedVersions ~= "PPC_HardFloat";
version(PPC_SoftFloat) predefinedVersions ~= "PPC_SoftFloat";
version(S390) predefinedVersions ~= "S390";
version(S390X) predefinedVersions ~= "S390X";
version(SDC) predefinedVersions ~= "SDC";
version(SH) predefinedVersions ~= "SH";
version(SH64) predefinedVersions ~= "SH64";
version(SkyOS) predefinedVersions ~= "SkyOS";
version(Solaris) predefinedVersions ~= "Solaris";
version(SPARC) predefinedVersions ~= "SPARC";
version(SPARC64) predefinedVersions ~= "SPARC64";
version(SPARC_HardFloat) predefinedVersions ~= "SPARC_HardFloat";
version(SPARC_SoftFloat) predefinedVersions ~= "SPARC_SoftFloat";
version(SPARC_V8Plus) predefinedVersions ~= "SPARC_V8Plus";
version(SysV3) predefinedVersions ~= "SysV3";
version(SysV4) predefinedVersions ~= "SysV4";
version(unittest) predefinedVersions ~= "unittest";
version(Win32) predefinedVersions ~= "Win32";
version(Win64) predefinedVersions ~= "Win64";
version(Windows) predefinedVersions ~= "Windows";
version(X86) predefinedVersions ~= "X86";
version(X86_64) predefinedVersions ~= "X86_64";
}

@ -1 +1 @@
Subproject commit cd3bb40730a34cb31a0f35149369973cda359719
Subproject commit 982510c50c9b59c70619a80c771b202c5db12be1

View File

@ -23,6 +23,10 @@ module messages;
*/
enum CompletionKind : char
{
/// Invalid completion kind. This is used internally and will never
/// be returned in a completion response.
dummy = '?',
/// class names
className = 'c',
@ -86,11 +90,18 @@ enum CompletionType : string
calltips = "calltips"
}
enum RequestKind
/**
* Request kind
*/
enum RequestKind : ubyte
{
/// Autocompletion
autocomplete,
/// Clear the completion cache
clearCache,
/// Add import directory to server
addImport,
/// Shut down the server
shutdown
}

View File

@ -29,14 +29,20 @@ import std.path;
import std.algorithm;
import std.conv;
import acvisitor;
import actypes;
import autocomplete;
import semantic;
import astconverter;
import stupidlog;
struct CacheEntry
{
ACSymbol[] symbols;
const(ACSymbol)*[] symbols;
SysTime modificationTime;
void opAssign(ref const CacheEntry other)
{
this.symbols = cast(typeof(symbols)) other.symbols;
this.modificationTime = other.modificationTime;
}
}
/**
@ -64,8 +70,11 @@ struct ModuleCache
importPaths ~= path;
foreach (fileName; dirEntries(path, "*.{d,di}", SpanMode.depth))
{
writeln("Loading and caching completions for ", fileName);
Log.info("Loading and caching completions for ", fileName);
getSymbolsInModule(fileName);
import core.memory;
GC.collect();
GC.minimize();
}
}
@ -75,16 +84,16 @@ struct ModuleCache
* Returns:
* The symbols defined in the given module
*/
static ACSymbol[] getSymbolsInModule(string moduleName)
static const(ACSymbol)*[] getSymbolsInModule(string moduleName)
{
writeln("Getting symbols for module ", moduleName);
// Log.info("Getting symbols for module ", moduleName);
string location = resolveImportLoctation(moduleName);
if (location is null)
return [];
if (!needsReparsing(location))
return cache[location].symbols;
auto visitor = new AutocompleteVisitor;
const(ACSymbol)*[] symbols;
try
{
File f = File(location);
@ -92,25 +101,23 @@ struct ModuleCache
f.rawRead(source);
LexerConfig config;
config.fileName = location;
auto tokens = source.byToken(config).array();
Module mod = parseModule(tokens, location, &doesNothing);
visitor.visit(mod);
visitor.scope_.resolveSymbolTypes();
symbols = convertAstToSymbols(mod);
}
catch (Exception ex)
{
writeln("Couln't parse ", location, " due to exception: ", ex.msg);
Log.error("Couln't parse ", location, " due to exception: ", ex.msg);
return [];
}
SysTime access;
SysTime modification;
getTimes(location, access, modification);
if (location !in cache)
cache[location] = CacheEntry.init;
cache[location].modificationTime = modification;
cache[location].symbols = visitor.symbols;
return cache[location].symbols;
CacheEntry c = CacheEntry(symbols, modification);
cache[location] = c;
return symbols;
}
/**
@ -122,7 +129,7 @@ struct ModuleCache
*/
static string resolveImportLoctation(string moduleName)
{
// writeln("Resolving location of ", moduleName);
// Log.trace("Resolving location of ", moduleName);
if (isRooted(moduleName))
return moduleName;
@ -168,3 +175,5 @@ private:
// Listing of paths to check for imports
static string[] importPaths;
}
private void doesNothing(string a, int b, int c, string d) {}

61
semantic.d Normal file
View File

@ -0,0 +1,61 @@
/**
* This file is part of DCD, a development tool for the D programming language.
* Copyright (C) 2013 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 semantic;
import messages;
import actypes;
import stdx.d.ast;
import stdx.d.lexer;
/**
* Intermediate form between ACSymbol and the AST classes. Stores enough
* information to resolve things like base classes and alias this.
*/
struct SemanticSymbol
{
public:
/// Symbol name
string name;
/// Base classes
string[][] baseClasses;
/// Completion kind
CompletionKind kind;
/// Variable type or function return type
Type type;
/// Function call tip. Null if this is not a function
string callTip;
/// Alias this symbols
string[] aliasThis;
/// MixinTemplates
string[] mixinTemplates;
/// Protection level for this symobol
TokenType protection;
/// Symbol location
size_t location;
mixin scopeImplementation!(SemanticSymbol);
}

View File

@ -26,12 +26,16 @@ import std.path;
import std.file;
import std.array;
import std.process;
import std.datetime;
import msgpack;
import messages;
import autocomplete;
import modulecache;
import stupidlog;
import actypes;
import core.memory;
enum CONFIG_FILE_NAME = "dcd.conf";
@ -42,9 +46,15 @@ version(OSX) version = useXDG;
int main(string[] args)
{
Log.info("Starting up...");
StopWatch sw = StopWatch(AutoStart.yes);
// No relative paths
version (Posix) chdir("/");
Log.output = stdout;
Log.level = LogLevel.trace;
ushort port = 9166;
bool help;
string[] importPaths;
@ -75,30 +85,47 @@ int main(string[] args)
socket.listen(0);
scope (exit)
{
writeln("Shutting down sockets...");
Log.info("Shutting down sockets...");
socket.shutdown(SocketShutdown.BOTH);
socket.close();
writeln("Sockets shut down.");
Log.info("Sockets shut down.");
}
foreach (path; importPaths)
ModuleCache.addImportPath(path);
writeln("Import directories: ", ModuleCache.getImportPaths());
Log.info("Import directories: ", ModuleCache.getImportPaths());
ubyte[] buffer = new ubyte[1024 * 1024 * 4]; // 4 megabytes should be enough for anybody...
writeln("Startup complete");
sw.stop();
Log.info("Startup completed in ", sw.peek().to!("msecs", float), " milliseconds");
while (true)
{
auto s = socket.accept();
s.blocking = true;
if (s.remoteAddress.toHostNameString() != "localhost")
{
Log.error("Warning: Connection attempt from ",
s.remoteAddress.toHostNameString(), "ignored. DCD only accepts "
~ " requests from localhost");
s.shutdown(SocketShutdown.BOTH);
s.close();
continue;
}
// TODO: Only process connections from localhost
scope (exit)
{
s.shutdown(SocketShutdown.BOTH);
s.close();
}
ptrdiff_t bytesReceived = s.receive(buffer);
auto requestWatch = StopWatch(AutoStart.yes);
size_t messageLength;
// bit magic!
(cast(ubyte*) &messageLength)[0..size_t.sizeof] = buffer[0..size_t.sizeof];
@ -115,7 +142,7 @@ int main(string[] args)
if (bytesReceived == Socket.ERROR)
{
writeln("Socket recieve failed");
Log.error("Socket recieve failed");
break;
}
@ -131,12 +158,12 @@ int main(string[] args)
}
else if (request.kind == RequestKind.clearCache)
{
writeln("Clearing cache.");
Log.info("Clearing cache.");
ModuleCache.clear();
}
else if (request.kind == RequestKind.shutdown)
{
writeln("Shutting down.");
Log.info("Shutting down.");
break;
}
else
@ -145,6 +172,7 @@ int main(string[] args)
ubyte[] responseBytes = msgpack.pack(response);
assert(s.send(responseBytes) == responseBytes.length);
}
Log.info("Request processed in ", requestWatch.peek().to!("msecs", float), " milliseconds");
}
return 0;
}
@ -178,11 +206,11 @@ void warnAboutOldConfigLocation()
version (linux) if ("~/.config/dcd".expandTilde().exists()
&& "~/.config/dcd".expandTilde().isFile())
{
writeln("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
writeln("!! Upgrade warning:");
writeln("!! '~/.config/dcd' should be moved to '$XDG_CONFIG_HOME/dcd/dcd.conf'");
writeln("!! or '$HOME/.config/dcd/dcd.conf'");
writeln("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Log.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Log.error("!! Upgrade warning:");
Log.error("!! '~/.config/dcd' should be moved to '$XDG_CONFIG_HOME/dcd/dcd.conf'");
Log.error("!! or '$HOME/.config/dcd/dcd.conf'");
Log.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
}
@ -192,7 +220,7 @@ string[] loadConfiguredImportDirs()
immutable string configLocation = getConfigurationLocation();
if (!configLocation.exists())
return [];
writeln("Loading configuration from ", configLocation);
Log.info("Loading configuration from ", configLocation);
File f = File(configLocation, "rt");
return f.byLine(KeepTerminator.no).map!(a => a.idup).filter!(a => a.exists()).array();
}

72
stupidlog.d Normal file
View File

@ -0,0 +1,72 @@
/*******************************************************************************
* Authors: Brian Schott
* Copyright: Brian Schott
* Date: Oct 5 2013
*
* License:
* 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 stupidlog;
import std.stdio;
import core.vararg;
enum LogLevel : uint
{
fatal = 0,
error,
info,
trace
}
struct Log
{
static void trace(T...)(lazy string message, T args)
{
if (level < LogLevel.trace) return;
if (output is stdout)
output.writeln("[\033[01;36mtrace\033[0m] " ~ message, args);
else
output.writeln("[trace] " ~ message, args);
}
static void info(T...)(lazy string message, T args)
{
if (level < LogLevel.info) return;
if (output is stdout)
output.writeln("[\033[01;32minfo\033[0m ] " ~ message, args);
else
output.writeln("[info ] " ~ message, args);
}
static void error(T...)(lazy string message, T args)
{
if (level < LogLevel.error) return;
if (output is stdout)
output.writeln("[\033[01;31merror\033[0m] " ~ message, args);
else
output.writeln("[error] " ~ message, args);
}
static void fatal(T...)(lazy string message, T args)
{
if (output is stdout)
output.writeln("[\033[01;35mfatal\033[0m] " ~ message, args);
else
output.writeln("[fatal] " ~ message, args);
}
static LogLevel level;
static File output;
}