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; +}