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 35 /** 36 * Used by autocompletion. 37 */ 38 ScopeSymbolPair generateAutocompleteTrees(const(Token)[] tokens, 39 IAllocator symbolAllocator, RollbackAllocator* parseAllocator, 40 size_t cursorPosition, ref ModuleCache cache) 41 { 42 Module m = parseModuleForAutocomplete(tokens, internString("stdin"), 43 parseAllocator, cursorPosition); 44 45 scope first = new FirstPass(m, internString("stdin"), symbolAllocator, 46 symbolAllocator, true, &cache); 47 first.run(); 48 49 secondPass(first.rootSymbol, first.moduleScope, cache); 50 auto r = first.rootSymbol.acSymbol; 51 typeid(SemanticSymbol).destroy(first.rootSymbol); 52 return ScopeSymbolPair(r, first.moduleScope); 53 } 54 55 struct ScopeSymbolPair 56 { 57 void destroy() 58 { 59 typeid(DSymbol).destroy(symbol); 60 typeid(Scope).destroy(scope_); 61 } 62 63 DSymbol* symbol; 64 Scope* scope_; 65 } 66 67 /** 68 * Used by import symbol caching. 69 * 70 * Params: 71 * tokens = the tokens that compose the file 72 * fileName = the name of the file being parsed 73 * parseAllocator = the allocator to use for the AST 74 * Returns: the parsed module 75 */ 76 Module parseModuleSimple(const(Token)[] tokens, string fileName, RollbackAllocator* parseAllocator) 77 { 78 assert (parseAllocator !is null); 79 scope parser = new SimpleParser(); 80 parser.fileName = fileName; 81 parser.tokens = tokens; 82 parser.messageFunction = &doesNothing; 83 parser.allocator = parseAllocator; 84 return parser.parseModule(); 85 } 86 87 private: 88 89 Module parseModuleForAutocomplete(const(Token)[] tokens, string fileName, 90 RollbackAllocator* parseAllocator, size_t cursorPosition) 91 { 92 scope parser = new AutocompleteParser(); 93 parser.fileName = fileName; 94 parser.tokens = tokens; 95 parser.messageFunction = &doesNothing; 96 parser.allocator = parseAllocator; 97 parser.cursorPosition = cursorPosition; 98 return parser.parseModule(); 99 } 100 101 class AutocompleteParser : Parser 102 { 103 override BlockStatement parseBlockStatement() 104 { 105 if (!currentIs(tok!"{")) 106 return null; 107 if (current.index > cursorPosition) 108 { 109 BlockStatement bs = allocator.make!(BlockStatement); 110 bs.startLocation = current.index; 111 skipBraces(); 112 bs.endLocation = tokens[index - 1].index; 113 return bs; 114 } 115 immutable start = current.index; 116 auto b = setBookmark(); 117 skipBraces(); 118 if (tokens[index - 1].index < cursorPosition) 119 { 120 abandonBookmark(b); 121 BlockStatement bs = allocator.make!BlockStatement(); 122 bs.startLocation = start; 123 bs.endLocation = tokens[index - 1].index; 124 return bs; 125 } 126 else 127 { 128 goToBookmark(b); 129 return super.parseBlockStatement(); 130 } 131 } 132 133 private: 134 size_t cursorPosition; 135 } 136 137 class SimpleParser : Parser 138 { 139 override Unittest parseUnittest() 140 { 141 expect(tok!"unittest"); 142 if (currentIs(tok!"{")) 143 skipBraces(); 144 return allocator.make!Unittest; 145 } 146 147 override MissingFunctionBody parseMissingFunctionBody() 148 { 149 // Unlike many of the other parsing functions, it is valid and expected 150 // for this one to return `null` on valid code. Returning `null` in 151 // this function means that we are looking at a SpecifiedFunctionBody 152 // or ShortenedFunctionBody. 153 // 154 // The super-class will handle re-trying with the correct parsing 155 // function. 156 157 const bool needDo = skipContracts(); 158 if (needDo && moreTokens && (currentIs(tok!"do") || current.text == "body")) 159 return null; 160 if (currentIs(tok!";")) 161 advance(); 162 else 163 return null; 164 return allocator.make!MissingFunctionBody; 165 } 166 167 override SpecifiedFunctionBody parseSpecifiedFunctionBody() 168 { 169 if (currentIs(tok!"{")) 170 skipBraces(); 171 else 172 { 173 skipContracts(); 174 if (currentIs(tok!"do") || (currentIs(tok!"identifier") && current.text == "body")) 175 advance(); 176 if (currentIs(tok!"{")) 177 skipBraces(); 178 } 179 return allocator.make!SpecifiedFunctionBody; 180 } 181 182 override ShortenedFunctionBody parseShortenedFunctionBody() 183 { 184 skipContracts(); 185 if (currentIs(tok!"=>")) 186 { 187 while (!currentIs(tok!";") && moreTokens) 188 { 189 if (currentIs(tok!"{")) // potential function literal 190 skipBraces(); 191 else 192 advance(); 193 } 194 if (moreTokens) 195 advance(); 196 } 197 return allocator.make!ShortenedFunctionBody; 198 } 199 200 /** 201 * Skip contracts, and return `true` if the type of contract used requires 202 * that the next token is `do`. 203 */ 204 private bool skipContracts() 205 { 206 bool needDo; 207 208 while (true) 209 { 210 if (currentIs(tok!"in")) 211 { 212 advance(); 213 if (currentIs(tok!"{")) 214 { 215 skipBraces(); 216 needDo = true; 217 } 218 if (currentIs(tok!"(")) 219 skipParens(); 220 } 221 else if (currentIs(tok!"out")) 222 { 223 advance(); 224 if (currentIs(tok!"(")) 225 { 226 immutable bool asExpr = peekIs(tok!";") 227 || (peekIs(tok!"identifier") 228 && index + 2 < tokens.length && tokens[index + 2].type == tok!";"); 229 skipParens(); 230 if (asExpr) 231 { 232 needDo = false; 233 continue; 234 } 235 } 236 if (currentIs(tok!"{")) 237 { 238 skipBraces(); 239 needDo = true; 240 } 241 } 242 else 243 break; 244 } 245 return needDo; 246 } 247 } 248 249 void doesNothing(string, size_t, size_t, string, bool) {}