1 /** 2 * This file is part of DCD, a development tool for the D programming language. 3 * Copyright (C) 2014 Brian Schott 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 module dsymbol.conversion; 20 21 import dsymbol.cache_entry; 22 import dsymbol.conversion.first; 23 import dsymbol.conversion.second; 24 import dsymbol.modulecache; 25 import dsymbol.scope_; 26 import dsymbol.string_interning; 27 import dsymbol.symbol; 28 import dsymbol.semantic; 29 import dparse.ast; 30 import dparse.lexer; 31 import dparse.parser; 32 import dparse.rollback_allocator; 33 import stdx.allocator; 34 import std.typecons; 35 36 /** 37 * Used by autocompletion. 38 */ 39 ScopeSymbolPair generateAutocompleteTrees(const(Token)[] tokens, 40 IAllocator symbolAllocator, RollbackAllocator* parseAllocator, 41 size_t cursorPosition, ref ModuleCache cache) 42 { 43 Module m = parseModuleForAutocomplete(tokens, internString("stdin"), 44 parseAllocator, cursorPosition); 45 46 auto first = scoped!FirstPass(m, internString("stdin"), symbolAllocator, 47 symbolAllocator, true, &cache); 48 first.run(); 49 50 secondPass(first.rootSymbol, first.moduleScope, cache); 51 auto r = first.rootSymbol.acSymbol; 52 typeid(SemanticSymbol).destroy(first.rootSymbol); 53 return ScopeSymbolPair(r, first.moduleScope); 54 } 55 56 struct ScopeSymbolPair 57 { 58 void destroy() 59 { 60 typeid(DSymbol).destroy(symbol); 61 typeid(Scope).destroy(scope_); 62 } 63 64 DSymbol* symbol; 65 Scope* scope_; 66 } 67 68 /** 69 * Used by import symbol caching. 70 * 71 * Params: 72 * tokens = the tokens that compose the file 73 * fileName = the name of the file being parsed 74 * parseAllocator = the allocator to use for the AST 75 * Returns: the parsed module 76 */ 77 Module parseModuleSimple(const(Token)[] tokens, string fileName, RollbackAllocator* parseAllocator) 78 { 79 assert (parseAllocator !is null); 80 auto parser = scoped!SimpleParser(); 81 parser.fileName = fileName; 82 parser.tokens = tokens; 83 parser.messageFunction = &doesNothing; 84 parser.allocator = parseAllocator; 85 return parser.parseModule(); 86 } 87 88 private: 89 90 Module parseModuleForAutocomplete(const(Token)[] tokens, string fileName, 91 RollbackAllocator* parseAllocator, size_t cursorPosition) 92 { 93 auto parser = scoped!AutocompleteParser(); 94 parser.fileName = fileName; 95 parser.tokens = tokens; 96 parser.messageFunction = &doesNothing; 97 parser.allocator = parseAllocator; 98 parser.cursorPosition = cursorPosition; 99 return parser.parseModule(); 100 } 101 102 class AutocompleteParser : Parser 103 { 104 override BlockStatement parseBlockStatement() 105 { 106 if (!currentIs(tok!"{")) 107 return null; 108 if (current.index > cursorPosition) 109 { 110 BlockStatement bs = allocator.make!(BlockStatement); 111 bs.startLocation = current.index; 112 skipBraces(); 113 bs.endLocation = tokens[index - 1].index; 114 return bs; 115 } 116 immutable start = current.index; 117 auto b = setBookmark(); 118 skipBraces(); 119 if (tokens[index - 1].index < cursorPosition) 120 { 121 abandonBookmark(b); 122 BlockStatement bs = allocator.make!BlockStatement(); 123 bs.startLocation = start; 124 bs.endLocation = tokens[index - 1].index; 125 return bs; 126 } 127 else 128 { 129 goToBookmark(b); 130 return super.parseBlockStatement(); 131 } 132 } 133 134 private: 135 size_t cursorPosition; 136 } 137 138 class SimpleParser : Parser 139 { 140 override Unittest parseUnittest() 141 { 142 expect(tok!"unittest"); 143 if (currentIs(tok!"{")) 144 skipBraces(); 145 return allocator.make!Unittest; 146 } 147 148 override MissingFunctionBody parseMissingFunctionBody() 149 { 150 // Unlike many of the other parsing functions, it is valid and expected 151 // for this one to return `null` on valid code. Returning `null` in 152 // this function means that we are looking at a SpecifiedFunctionBody. 153 // The super-class will handle re-trying with the correct parsing 154 // function. 155 156 const bool needDo = skipContracts(); 157 if (needDo && moreTokens && (currentIs(tok!"do") || current.text == "body")) 158 return null; 159 if (currentIs(tok!";")) 160 advance(); 161 else 162 return null; 163 return allocator.make!MissingFunctionBody; 164 } 165 166 override SpecifiedFunctionBody parseSpecifiedFunctionBody() 167 { 168 if (currentIs(tok!"{")) 169 skipBraces(); 170 else 171 { 172 skipContracts(); 173 if (currentIs(tok!"do") || (currentIs(tok!"identifier") && current.text == "body")) 174 advance(); 175 if (currentIs(tok!"{")) 176 skipBraces(); 177 } 178 return allocator.make!SpecifiedFunctionBody; 179 } 180 181 /** 182 * Skip contracts, and return `true` if the type of contract used requires 183 * that the next token is `do`. 184 */ 185 private bool skipContracts() 186 { 187 bool needDo; 188 189 while (true) 190 { 191 if (currentIs(tok!"in")) 192 { 193 advance(); 194 if (currentIs(tok!"{")) 195 { 196 skipBraces(); 197 needDo = true; 198 } 199 if (currentIs(tok!"(")) 200 skipParens(); 201 } 202 else if (currentIs(tok!"out")) 203 { 204 advance(); 205 if (currentIs(tok!"(")) 206 { 207 immutable bool asExpr = peekIs(tok!";") 208 || (peekIs(tok!"identifier") 209 && index + 2 < tokens.length && tokens[index + 2].type == tok!";"); 210 skipParens(); 211 if (asExpr) 212 { 213 needDo = false; 214 continue; 215 } 216 } 217 if (currentIs(tok!"{")) 218 { 219 skipBraces(); 220 needDo = true; 221 } 222 } 223 else 224 break; 225 } 226 return needDo; 227 } 228 } 229 230 void doesNothing(string, size_t, size_t, string, bool) {}