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