diff --git a/actypes.d b/actypes.d
index ef15612..02e3aa2 100644
--- a/actypes.d
+++ b/actypes.d
@@ -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;
diff --git a/acvisitor.d b/acvisitor.d
deleted file mode 100644
index 89baff2..0000000
--- a/acvisitor.d
+++ /dev/null
@@ -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 .
- */
-
-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;
-}
diff --git a/astconverter.d b/astconverter.d
new file mode 100644
index 0000000..e00c4bd
--- /dev/null
+++ b/astconverter.d
@@ -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 .
+ ******************************************************************************/
+
+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);
+}
diff --git a/autocomplete.d b/autocomplete.d
index e5be929..80632e2 100644
--- a/autocomplete.d
+++ b/autocomplete.d
@@ -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;
diff --git a/build.sh b/build.sh
index 67ca672..f7d84a2 100755
--- a/build.sh
+++ b/build.sh
@@ -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
diff --git a/constants.d b/constants.d
index 9c78779..d7b4f37 100644
--- a/constants.d
+++ b/constants.d
@@ -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";
+}
diff --git a/dscanner b/dscanner
index cd3bb40..982510c 160000
--- a/dscanner
+++ b/dscanner
@@ -1 +1 @@
-Subproject commit cd3bb40730a34cb31a0f35149369973cda359719
+Subproject commit 982510c50c9b59c70619a80c771b202c5db12be1
diff --git a/messages.d b/messages.d
index 613eeec..39f8276 100644
--- a/messages.d
+++ b/messages.d
@@ -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
}
diff --git a/modulecache.d b/modulecache.d
index 95c53d0..956f63e 100644
--- a/modulecache.d
+++ b/modulecache.d
@@ -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) {}
diff --git a/semantic.d b/semantic.d
new file mode 100644
index 0000000..60dabb5
--- /dev/null
+++ b/semantic.d
@@ -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 .
+ */
+
+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);
+}
diff --git a/server.d b/server.d
index 59d09d7..4bf0ca1 100644
--- a/server.d
+++ b/server.d
@@ -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();
}
diff --git a/stupidlog.d b/stupidlog.d
new file mode 100644
index 0000000..86ca842
--- /dev/null
+++ b/stupidlog.d
@@ -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 .
+ ******************************************************************************/
+
+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;
+}