1 module workspaced.visitors.attributes; 2 3 import dparse.ast; 4 import dparse.formatter; 5 import dparse.lexer; 6 7 import std.algorithm; 8 import std.conv; 9 import std.range; 10 import std.string; 11 import std.variant; 12 13 class AttributesVisitor : ASTVisitor 14 { 15 bool sticky; 16 17 override void visit(const MemberFunctionAttribute attribute) 18 { 19 if (attribute.tokenType != IdType.init) 20 context.attributes ~= ASTContext.AnyAttributeInfo( 21 ASTContext.AnyAttribute(cast() attribute), sticky); 22 attribute.accept(this); 23 } 24 25 override void visit(const FunctionAttribute attribute) 26 { 27 if (attribute.token.type != IdType.init) 28 context.attributes ~= ASTContext.AnyAttributeInfo(ASTContext.AnyAttribute( 29 ASTContext.SimpleAttribute([cast() attribute.token])), sticky); 30 attribute.accept(this); 31 } 32 33 override void visit(const AtAttribute attribute) 34 { 35 context.attributes ~= ASTContext.AnyAttributeInfo( 36 ASTContext.AnyAttribute(cast() attribute), sticky); 37 attribute.accept(this); 38 } 39 40 override void visit(const PragmaExpression attribute) 41 { 42 context.attributes ~= ASTContext.AnyAttributeInfo( 43 ASTContext.AnyAttribute(cast() attribute), sticky); 44 attribute.accept(this); 45 } 46 47 override void visit(const Deprecated attribute) 48 { 49 context.attributes ~= ASTContext.AnyAttributeInfo( 50 ASTContext.AnyAttribute(cast() attribute), sticky); 51 attribute.accept(this); 52 } 53 54 override void visit(const AlignAttribute attribute) 55 { 56 context.attributes ~= ASTContext.AnyAttributeInfo( 57 ASTContext.AnyAttribute(cast() attribute), sticky); 58 attribute.accept(this); 59 } 60 61 override void visit(const LinkageAttribute attribute) 62 { 63 context.attributes ~= ASTContext.AnyAttributeInfo( 64 ASTContext.AnyAttribute(cast() attribute), sticky); 65 attribute.accept(this); 66 } 67 68 override void visit(const Attribute attribute) 69 { 70 Token[] tokens; 71 if (attribute.attribute.type != IdType.init) 72 tokens ~= attribute.attribute; 73 if (attribute.identifierChain) 74 tokens ~= attribute.identifierChain.identifiers; 75 if (tokens.length) 76 context.attributes ~= ASTContext.AnyAttributeInfo( 77 ASTContext.AnyAttribute(ASTContext.SimpleAttribute(tokens)), sticky); 78 attribute.accept(this); 79 } 80 81 override void visit(const StorageClass storage) 82 { 83 if (storage.token.type != IdType.init) 84 context.attributes ~= ASTContext.AnyAttributeInfo(ASTContext.AnyAttribute( 85 ASTContext.SimpleAttribute([cast() storage.token])), sticky); 86 storage.accept(this); 87 } 88 89 override void visit(const AttributeDeclaration dec) 90 { 91 sticky = true; 92 dec.accept(this); 93 sticky = false; 94 } 95 96 override void visit(const Declaration dec) 97 { 98 auto c = context.save; 99 // attribute ':' (private:) 100 bool attribDecl = !!dec.attributeDeclaration; 101 if (attribDecl) 102 { 103 sticky = true; 104 processDeclaration(dec); 105 sticky = false; 106 } 107 else 108 { 109 processDeclaration(dec); 110 context.restore(c); 111 } 112 } 113 114 override void visit(const InterfaceDeclaration dec) 115 { 116 auto c = context.save; 117 context.pushContainer(ASTContext.ContainerAttribute.Type.class_, dec.name.text); 118 super.visit(dec); 119 context.restore(c); 120 } 121 122 override void visit(const ClassDeclaration dec) 123 { 124 auto c = context.save; 125 context.pushContainer(ASTContext.ContainerAttribute.Type.class_, dec.name.text); 126 super.visit(dec); 127 context.restore(c); 128 } 129 130 override void visit(const StructDeclaration dec) 131 { 132 auto c = context.save; 133 context.pushContainer(ASTContext.ContainerAttribute.Type.struct_, dec.name.text); 134 super.visit(dec); 135 context.restore(c); 136 } 137 138 override void visit(const UnionDeclaration dec) 139 { 140 auto c = context.save; 141 context.pushContainer(ASTContext.ContainerAttribute.Type.union_, dec.name.text); 142 super.visit(dec); 143 context.restore(c); 144 } 145 146 override void visit(const EnumDeclaration dec) 147 { 148 auto c = context.save; 149 context.pushContainer(ASTContext.ContainerAttribute.Type.enum_, dec.name.text); 150 super.visit(dec); 151 context.restore(c); 152 } 153 154 override void visit(const TemplateDeclaration dec) 155 { 156 auto c = context.save; 157 context.pushContainer(ASTContext.ContainerAttribute.Type.template_, dec.name.text); 158 super.visit(dec); 159 context.restore(c); 160 } 161 162 void processDeclaration(const Declaration dec) 163 { 164 dec.accept(this); 165 } 166 167 ASTContext context; 168 169 alias visit = ASTVisitor.visit; 170 } 171 172 struct ASTContext 173 { 174 struct UserdataAttribute 175 { 176 string name; 177 Variant variant; 178 } 179 180 struct ContainerAttribute 181 { 182 enum Type 183 { 184 class_, 185 interface_, 186 struct_, 187 union_, 188 enum_, 189 template_ 190 } 191 192 Type type; 193 string name; 194 } 195 196 struct SimpleAttribute 197 { 198 Token[] attributes; 199 200 Token attribute() @property 201 { 202 if (attributes.length == 1) 203 return attributes[0]; 204 else 205 return Token.init; 206 } 207 208 Token firstAttribute() @property 209 { 210 if (attributes.length >= 1) 211 return attributes[0]; 212 else 213 return Token.init; 214 } 215 } 216 217 alias AnyAttribute = Algebraic!(PragmaExpression, Deprecated, AtAttribute, AlignAttribute, LinkageAttribute, 218 SimpleAttribute, MemberFunctionAttribute, ContainerAttribute, UserdataAttribute); 219 220 class AttributeWithInfo(T) 221 { 222 T value; 223 bool sticky; 224 225 alias value this; 226 227 this(T value, bool sticky) 228 { 229 this.value = value; 230 this.sticky = sticky; 231 } 232 233 auto opUnary(string op : "*")() 234 { 235 return this; 236 } 237 } 238 239 struct AnyAttributeInfo 240 { 241 AnyAttribute base; 242 // if true then this attribute is applying to all the following statements in the block (attribute ':') 243 bool sticky; 244 245 AttributeWithInfo!T peek(T)() 246 { 247 auto ret = base.peek!T; 248 if (!!ret) 249 return new AttributeWithInfo!T(*ret, sticky); 250 else 251 return null; 252 } 253 254 alias base this; 255 } 256 257 AnyAttributeInfo[] attributes; 258 259 /// Attributes only inside a container 260 auto localAttributes() @property 261 { 262 auto end = attributes.retro.countUntil!(a => !!a.peek!ContainerAttribute); 263 if (end == -1) 264 return attributes; 265 else 266 return attributes[$ - end .. $]; 267 } 268 269 auto attributeDescriptions() @property 270 { 271 return attributes.map!((a) { 272 if (auto prag = a.peek!PragmaExpression) 273 return "pragma(" ~ prag.identifier.text ~ ", ...[" 274 ~ prag.argumentList.items.length.to!string ~ "])"; 275 else if (auto depr = a.peek!Deprecated) return "deprecated"; 276 else if (auto at = a.peek!AtAttribute) return "@" ~ at.identifier.text; 277 else if (auto align_ = a.peek!AlignAttribute) return "align"; 278 else if (auto linkage = a.peek!LinkageAttribute) return "extern (" ~ linkage.identifier.text ~ ( 279 linkage.hasPlusPlus ? "++" : "") ~ ")"; 280 else if (auto simple = a.peek!SimpleAttribute) return simple.attributes.map!(a => a.text.length 281 ? a.text : str(a.type)).join("."); 282 else if (auto mfunAttr = a.peek!MemberFunctionAttribute) return "mfun<" ~ (mfunAttr.atAttribute 283 ? "@" ~ mfunAttr.atAttribute.identifier.text : "???") ~ ">"; 284 else if (auto container = a.peek!ContainerAttribute) return container.type.to!string[0 .. $ - 1] 285 ~ " " ~ container.name; 286 else if (auto user = a.peek!UserdataAttribute) return "user " ~ user.name; 287 else 288 return "Unknown type?!"; 289 }); 290 } 291 292 private static auto formatAttributes(T)(T attributes) 293 { 294 return attributes.map!((a) { 295 auto t = appender!string; 296 if (auto prag = a.peek!PragmaExpression) 297 format(t, *prag); 298 else if (auto depr = a.peek!Deprecated) 299 format(t, *depr); 300 else if (auto at = a.peek!AtAttribute) 301 format(t, *at); 302 else if (auto align_ = a.peek!AlignAttribute) 303 format(t, *align_); 304 else if (auto linkage = a.peek!LinkageAttribute) 305 format(t, *linkage); 306 else if (auto simple = a.peek!SimpleAttribute) 307 return simple.attributes.map!(a => a.text.length ? a.text : str(a.type)).join("."); 308 else if (auto mfunAttr = a.peek!MemberFunctionAttribute) 309 return str(mfunAttr.tokenType); 310 else if (auto container = a.peek!ContainerAttribute) 311 return null; 312 else if (auto user = a.peek!UserdataAttribute) 313 return null; 314 else 315 return "/* <<ERROR>> */"; 316 return t.data.strip; 317 }); 318 } 319 320 auto formattedAttributes() @property 321 { 322 return formatAttributes(attributes); 323 } 324 325 auto localFormattedAttributes() @property 326 { 327 return formatAttributes(localAttributes); 328 } 329 330 auto simpleAttributes() @property 331 { 332 return attributes.filter!(a => !!a.peek!SimpleAttribute) 333 .map!(a => *a.peek!SimpleAttribute); 334 } 335 336 auto simpleAttributesInContainer() @property 337 { 338 return localAttributes.filter!(a => !!a.peek!SimpleAttribute) 339 .map!(a => *a.peek!SimpleAttribute); 340 } 341 342 auto atAttributes() @property 343 { 344 return attributes.filter!(a => !!a.peek!AtAttribute) 345 .map!(a => *a.peek!AtAttribute); 346 } 347 348 auto memberFunctionAttributes() @property 349 { 350 return attributes.filter!(a => !!a.peek!MemberFunctionAttribute) 351 .map!(a => *a.peek!MemberFunctionAttribute); 352 } 353 354 auto memberFunctionAttributesInContainer() @property 355 { 356 return localAttributes.filter!(a => !!a.peek!MemberFunctionAttribute) 357 .map!(a => *a.peek!MemberFunctionAttribute); 358 } 359 360 auto userdataAttributes() @property 361 { 362 return attributes.filter!(a => !!a.peek!UserdataAttribute) 363 .map!(a => *a.peek!UserdataAttribute); 364 } 365 366 auto containerAttributes() @property 367 { 368 return attributes.filter!(a => !!a.peek!ContainerAttribute) 369 .map!(a => *a.peek!ContainerAttribute); 370 } 371 372 bool isToken(IdType t) @property 373 { 374 return memberFunctionAttributes.any!(a => a.tokenType == t) 375 || simpleAttributes.any!(a => a.attribute.type == t); 376 } 377 378 bool isTokenInContainer(IdType t) @property 379 { 380 return memberFunctionAttributesInContainer.any!(a => a.tokenType == t) 381 || simpleAttributesInContainer.any!(a => a.attribute.type == t); 382 } 383 384 bool isNothrow() @property 385 { 386 return isToken(tok!"nothrow"); 387 } 388 389 bool isNogc() @property 390 { 391 return atAttributes.any!(a => a.identifier.text == "nogc"); 392 } 393 394 bool isStatic() @property 395 { 396 return isToken(tok!"static"); 397 } 398 399 bool isFinal() @property 400 { 401 return isToken(tok!"final"); 402 } 403 404 bool isAbstract() @property 405 { 406 return isToken(tok!"abstract"); 407 } 408 409 bool isAbstractInContainer() @property 410 { 411 return isTokenInContainer(tok!"abstract"); 412 } 413 414 /// Returns: if a block needs implementations (virtual/abstract or interface methods) 415 /// 0 = must not be implemented (not in a container, private, static or final method) 416 /// 1 = optionally implementable, must be implemented if there is no function body 417 /// 9 = must be implemented 418 int requiredImplementationLevel() @property 419 { 420 auto container = containerAttributes; 421 if (container.empty || protectionType == tok!"private" || isStatic || isFinal) 422 return 0; 423 ContainerAttribute innerContainer = container.tail(1).front; 424 if (innerContainer.type == ContainerAttribute.Type.class_) 425 return isAbstractInContainer ? 9 : 1; 426 else // interface (or others) 427 return 9; 428 } 429 430 AttributeWithInfo!SimpleAttribute protectionAttribute() @property 431 { 432 auto prot = simpleAttributes.filter!(a => a.firstAttribute.type.among!(tok!"public", 433 tok!"private", tok!"protected", tok!"package")).tail(1); 434 if (prot.empty) 435 return null; 436 else 437 return prot.front; 438 } 439 440 IdType protectionType() @property 441 { 442 auto attr = protectionAttribute; 443 if (attr is null) 444 return IdType.init; 445 else 446 return attr.attributes[0].type; 447 } 448 449 void pushData(string name, Variant value, bool sticky = false) 450 { 451 attributes ~= AnyAttributeInfo(AnyAttribute(UserdataAttribute(name, value)), sticky); 452 } 453 454 void pushData(T)(string name, T value, bool sticky = false) 455 { 456 pushData(name, Variant(value), sticky); 457 } 458 459 void pushContainer(ContainerAttribute.Type type, string name) 460 { 461 attributes ~= AnyAttributeInfo(AnyAttribute(ContainerAttribute(type, name)), false); 462 } 463 464 ASTContext save() 465 { 466 return ASTContext(attributes[]); 467 } 468 469 void restore(ASTContext c) 470 { 471 attributes = c.attributes; 472 } 473 }