yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
9adac4069
master
1// capabilities-generator-main.cpp 2 3#include "../../source/compiler-core/slang-lexer.h" 4#include "../../source/compiler-core/slang-perfect-hash-codegen.h" 5#include "../../source/core/slang-file-system.h" 6#include "../../source/core/slang-io.h" 7#include "../../source/core/slang-secure-crt.h" 8#include "../../source/core/slang-string-util.h" 9#include "../../source/core/slang-uint-set.h" 10 11#include <stdio.h> 12 13using namespace Slang ; 14 15namespace Diagnostics 16{ 17#define DIAGNOSTIC (id ,severity ,name ,messageFormat ) \ 18 const DiagnosticInfo name = {id, Severity::severity, #name, messageFormat}; 19#include "slang-capability-diagnostic-defs.h" 20#undef DIAGNOSTIC 21}// namespace Diagnostics 22 23enum class CapabilityFlavor 24{ 25Normal , 26Abstract , 27Alias 28}; 29 30struct CapabilityDef ; 31 32struct CapabilityConjunctionExpr 33{ 34List < CapabilityDef *> atoms ; 35SourceLoc sourceLoc ; 36}; 37 38struct CapabilityDisjunctionExpr 39{ 40List < CapabilityConjunctionExpr > conjunctions ; 41}; 42 43struct SerializedArrayView 44{ 45Index first ; 46Index count ; 47}; 48 49struct CapabilitySharedContext 50{ 51CapabilityDef * ptrOfTarget = nullptr ; 52CapabilityDef * ptrOfStage = nullptr ; 53}; 54 55static void _removeFromOtherAtomsNotInThis ( 56HashSet < const CapabilityDef *> thisSet , 57HashSet < const CapabilityDef *> otherSet , 58List < const CapabilityDef *> atomsToRemove ) 59{ 60atomsToRemove .clear (); 61atomsToRemove .reserve (otherSet .getCount ()); 62for (auto keyAtom :otherSet ) 63 { 64if (thisSet .contains (keyAtom )) 65continue ; 66atomsToRemove .add (keyAtom ); 67 } 68 69for (auto atomToRemove :atomsToRemove ) 70otherSet .remove (atomToRemove ); 71} 72 73enum class AutoDocHeaderGroup :UInt 74{ 75Targets = 0 , 76Stages , 77Versions , 78Extensions , 79Compound , 80Other , 81Count , 82Invalid , 83}; 84 85UnownedStringSlice getHeaderNameFromAutoDocHeaderGroup (UInt headerGroup ) 86{ 87switch (headerGroup ) 88 { 89case (UInt )AutoDocHeaderGroup ::Targets : 90return UnownedStringSlice ("Targets" ); 91case (UInt )AutoDocHeaderGroup ::Stages : 92return UnownedStringSlice ("Stages" ); 93case (UInt )AutoDocHeaderGroup ::Extensions : 94return UnownedStringSlice ("Extensions" ); 95case (UInt )AutoDocHeaderGroup ::Versions : 96return UnownedStringSlice ("Versions" ); 97case (UInt )AutoDocHeaderGroup ::Compound : 98return UnownedStringSlice ("Compound Capabilities" ); 99case (UInt )AutoDocHeaderGroup ::Other : 100return UnownedStringSlice ("Other" ); 101default : 102SLANG_ASSERT ("Unknown `AutoDocHeaderGroup`" ); 103return UnownedStringSlice ("" ); 104 } 105} 106 107UnownedStringSlice getHeaderDescriptionFromAutoDocHeaderGroup (UInt headerGroup ) 108{ 109switch (headerGroup ) 110 { 111case (UInt )AutoDocHeaderGroup ::Targets : 112return UnownedStringSlice ( 113"Capabilities to specify code generation targets (`glsl`, `spirv`...)" ); 114case (UInt )AutoDocHeaderGroup ::Stages : 115return UnownedStringSlice ( 116"Capabilities to specify code generation stages (`vertex`, `fragment`...)" ); 117case (UInt )AutoDocHeaderGroup ::Extensions : 118return UnownedStringSlice ("Capabilities to specify extensions (`GL_EXT`, `SPV_EXT`...)" ); 119case (UInt )AutoDocHeaderGroup ::Versions : 120return UnownedStringSlice ("Capabilities to specify versions of a code generation " 121"target (`sm_5_0`, `GLSL_400`...)" ); 122case (UInt )AutoDocHeaderGroup ::Compound : 123return UnownedStringSlice ("Capabilities to specify capabilities created by other " 124"capabilities (`raytracing`, `meshshading`...)" ); 125case (UInt )AutoDocHeaderGroup ::Other : 126return UnownedStringSlice ("Capabilities which may be deprecated" ); 127default : 128SLANG_ASSERT ("Unknown `AutoDocHeaderGroup`" ); 129return UnownedStringSlice ("" ); 130 } 131} 132 133AutoDocHeaderGroup getAutoDocHeaderGroupFromTag ( 134DiagnosticSink * sink , 135UnownedStringSlice headerGroupName , 136SourceLoc loc ) 137{ 138if (headerGroupName .caseInsensitiveEquals (UnownedStringSlice ("Other" ))) 139return AutoDocHeaderGroup ::Other ; 140else if (headerGroupName .caseInsensitiveEquals (UnownedStringSlice ("Target" ))) 141return AutoDocHeaderGroup ::Targets ; 142else if (headerGroupName .caseInsensitiveEquals (UnownedStringSlice ("Stage" ))) 143return AutoDocHeaderGroup ::Stages ; 144else if (headerGroupName .caseInsensitiveEquals (UnownedStringSlice ("EXT" ))) 145return AutoDocHeaderGroup ::Extensions ; 146else if (headerGroupName .caseInsensitiveEquals (UnownedStringSlice ("Version" ))) 147return AutoDocHeaderGroup ::Versions ; 148else if (headerGroupName .caseInsensitiveEquals (UnownedStringSlice ("Compound" ))) 149return AutoDocHeaderGroup ::Compound ; 150else 151 { 152sink -> diagnose (loc ,Diagnostics ::invalidDocCommentHeader ,headerGroupName ); 153return AutoDocHeaderGroup ::Invalid ; 154 } 155} 156 157struct AutoDocInfo 158{ 159String comment ; 160AutoDocHeaderGroup headerGroup ; 161 162AutoDocInfo () 163 { 164comment = {}; 165headerGroup = AutoDocHeaderGroup ::Other ; 166 } 167}; 168 169struct CapabilityDef :public RefObject 170{ 171public : 172void operator= (const CapabilityDef & other ) 173 { 174this -> name = other .name ; 175this -> enumValue = other .enumValue ; 176this -> expr = other .expr ; 177this -> flavor = other .flavor ; 178this -> rank = other .rank ; 179this -> canonicalRepresentation = other .canonicalRepresentation ; 180this -> serializedCanonicalRepresentation = other .serializedCanonicalRepresentation ; 181this -> sourceLoc = other .sourceLoc ; 182this -> keyAtomsPresent = other .keyAtomsPresent ; 183this -> sharedContext = other .sharedContext ; 184this -> docComment = other .docComment ; 185 } 186 187String name ; 188Index enumValue ; 189CapabilityDisjunctionExpr expr ; 190CapabilityFlavor flavor ; 191/// optional, 0 is default rank. 192int rank = 0 ; 193List < List < CapabilityDef *>> canonicalRepresentation ; 194SerializedArrayView serializedCanonicalRepresentation ; 195SourceLoc sourceLoc ; 196AutoDocInfo docComment ; 197/// Stores key atoms a CapabilityDef refers to. 198/// Shared key atoms: key atoms shared between every individual set in a 199/// canonicalRepresentation, added together. 200HashSet < const CapabilityDef *> keyAtomsPresent ; 201 202CapabilitySharedContext * sharedContext ; 203 204CapabilityDef * getAbstractBase ()const 205 { 206if (flavor != CapabilityFlavor ::Normal ) 207return nullptr ; 208if (expr .conjunctions .getCount ()!= 1 ) 209return nullptr ; 210if (expr .conjunctions [0 ].atoms .getCount ()== 0 ) 211return nullptr ; 212if (expr .conjunctions [0 ].atoms [0 ]-> flavor != CapabilityFlavor ::Abstract ) 213return nullptr ; 214return expr .conjunctions [0 ].atoms [0 ]; 215 } 216 217void fillKeyAtomsPresentInCannonicalRepresentation () 218 { 219HashSet < const CapabilityDef *> sharedKeyAtomsInCanonicalSet_target ; 220HashSet < const CapabilityDef *> sharedKeyAtomsInCanonicalSet_stage ; 221HashSet < const CapabilityDef *> keyAtomsFound ; 222List < const CapabilityDef *> atomsToRemove ; 223for (auto & canonicalSet :canonicalRepresentation ) 224 { 225bool alreadySetTarget = false; 226bool alreadySetStage = false; 227sharedKeyAtomsInCanonicalSet_target .clear (); 228sharedKeyAtomsInCanonicalSet_stage .clear (); 229 230// find key atoms all atoms in a canonical set share. 231for (auto & atom :canonicalSet ) 232 { 233bool foundTarget = false; 234bool foundStage = false; 235for (auto otherkeyAtomsPresent :atom -> keyAtomsPresent ) 236 { 237auto base = otherkeyAtomsPresent -> getAbstractBase (); 238// add all `target` key atoms associated with atom in canonicalSet 239if (base == sharedContext -> ptrOfTarget ) 240 { 241foundTarget = true; 242if (!alreadySetTarget ) 243sharedKeyAtomsInCanonicalSet_target .add (otherkeyAtomsPresent ); 244 } 245// add all `stage` key atoms associated with atom in canonicalSet 246else if (base == sharedContext -> ptrOfStage ) 247 { 248foundStage = true; 249if (!alreadySetTarget ) 250sharedKeyAtomsInCanonicalSet_stage .add (otherkeyAtomsPresent ); 251 } 252// all key atoms associated with atom 253keyAtomsFound .add (otherkeyAtomsPresent ); 254 } 255 256// remove all not shared key atoms 257if (foundTarget ) 258 { 259alreadySetTarget = true; 260_removeFromOtherAtomsNotInThis ( 261keyAtomsFound , 262sharedKeyAtomsInCanonicalSet_target , 263atomsToRemove ); 264 } 265if (foundStage ) 266 { 267alreadySetStage = true; 268_removeFromOtherAtomsNotInThis ( 269keyAtomsFound , 270sharedKeyAtomsInCanonicalSet_stage , 271atomsToRemove ); 272 } 273keyAtomsFound .clear (); 274 } 275 276// add all shared key atoms 277for (auto keyAtom :sharedKeyAtomsInCanonicalSet_target ) 278this -> keyAtomsPresent .add (keyAtom ); 279for (auto keyAtom :sharedKeyAtomsInCanonicalSet_stage ) 280this -> keyAtomsPresent .add (keyAtom ); 281 } 282if (auto base = this -> getAbstractBase ()) 283keyAtomsPresent .add (this ); 284 } 285}; 286 287/// Advances through BlockComment/LineComment, otherwise, "advanceIf 'type' is the next token" 288enum class AdvanceOptions :UInt 289{ 290None = 0 <<0 , 291SkipComments = 1 <<0 , 292}; 293 294template < AdvanceOptions L ,AdvanceOptions R > 295constexpr bool ContainsOption () 296{ 297return (UInt )L & (UInt )R ; 298} 299 300static bool isInternalDef (RefPtr < CapabilityDef > def ) 301{ 302return def -> name .startsWith ("_" ); 303} 304 305struct CapabilityDefParser 306{ 307CapabilityDefParser (Lexer * lexer ,DiagnosticSink * sink ,CapabilitySharedContext & sharedContext ) 308 :m_lexer (lexer ),m_sink (sink ),m_sharedContext (sharedContext ) 309 { 310 } 311 312Lexer * m_lexer ; 313DiagnosticSink * m_sink ; 314 315Dictionary < String ,CapabilityDef *> m_mapNameToCapability ; 316List < RefPtr < CapabilityDef >> m_defs ; 317CapabilitySharedContext & m_sharedContext ; 318 319TokenReader m_tokenReader ; 320 321template < AdvanceOptions advanceOptions > 322bool advanceIf (TokenType type ) 323 { 324auto peekToken = m_tokenReader .peekTokenType (); 325if constexpr (ContainsOption < advanceOptions ,AdvanceOptions ::SkipComments > ()) 326 { 327while (peekToken == TokenType ::BlockComment || peekToken == TokenType ::LineComment ) 328 { 329m_tokenReader .advanceToken (); 330peekToken = m_tokenReader .peekTokenType (); 331 } 332 } 333if (peekToken == type ) 334 { 335m_tokenReader .advanceToken (); 336return true; 337 } 338return false; 339 } 340 341template < AdvanceOptions advanceOptions > 342SlangResult readToken (TokenType type ,Token & nextToken ) 343 { 344nextToken = m_tokenReader .advanceToken (); 345if constexpr (ContainsOption < advanceOptions ,AdvanceOptions ::SkipComments > ()) 346 { 347while (nextToken .type == TokenType ::BlockComment || 348nextToken .type == TokenType ::LineComment ) 349nextToken = m_tokenReader .advanceToken (); 350 } 351if (nextToken .type != type ) 352 { 353m_sink -> diagnose ( 354nextToken .loc , 355Diagnostics ::unexpectedTokenExpectedTokenType , 356nextToken , 357type ); 358return SLANG_FAIL ; 359 } 360return SLANG_OK ; 361 } 362 363template < AdvanceOptions advanceOptions > 364SlangResult readToken (TokenType type ) 365 { 366Token nextToken ; 367return readToken < advanceOptions > (type ,nextToken ); 368 } 369 370SlangResult parseConjunction (CapabilityConjunctionExpr & expr ) 371 { 372for (;;) 373 { 374Token nameToken ; 375SLANG_RETURN_ON_FAIL ( 376readToken < AdvanceOptions ::SkipComments > (TokenType ::Identifier ,nameToken )); 377CapabilityDef * def = nullptr ; 378if (m_mapNameToCapability .tryGetValue (nameToken .getContent (),def )) 379 { 380expr .atoms .add (def ); 381 } 382else 383 { 384m_sink -> diagnose (nameToken .loc ,Diagnostics ::undefinedIdentifier ,nameToken ); 385return SLANG_FAIL ; 386 } 387if (!(advanceIf < AdvanceOptions ::SkipComments > (TokenType ::OpAdd ))) 388break ; 389 } 390return SLANG_OK ; 391 } 392 393SlangResult parseExpr (CapabilityDisjunctionExpr & expr ) 394 { 395for (;;) 396 { 397CapabilityConjunctionExpr conjunction ; 398conjunction .sourceLoc = this -> m_tokenReader .m_cursor -> getLoc (); 399SLANG_RETURN_ON_FAIL (parseConjunction (conjunction )); 400expr .conjunctions .add (conjunction ); 401if (!advanceIf < AdvanceOptions ::SkipComments > (TokenType ::OpBitOr )) 402break ; 403 } 404return SLANG_OK ; 405 } 406 407void validateInternalAtomExternalAtomPair () 408 { 409// All `_Internal` atoms must have an `External` atom. 410// `External` atoms do not require to have an `_Internal` atom. 411// The following behavior ensures that if we error with 'atom' instead of 412// '_atom' a user may add the 'atom' capability to solve their error. This is 413// important because '_Internal' will only be for 1 target, 'External' will alias 414// to more than 1 target. We need to ensure users avoid 'Internal' when possible. 415 416Dictionary < String ,List < RefPtr < CapabilityDef >>> nameToInternalAndExternalAtom ; 417for (auto i :m_defs ) 418 { 419// 'abstract' atoms are not reported to a user and are ignored 420if (i -> flavor == CapabilityFlavor ::Abstract ) 421continue ; 422 423// Try to pack `_atom` and `atom` into the same per key List 424String name = i -> name ; 425if (i -> name .startsWith ("_" )) 426name = name .subString (1 ,name .getLength ()- 1 ); 427nameToInternalAndExternalAtom [name ].add (i ); 428 } 429for (auto i :nameToInternalAndExternalAtom ) 430 { 431SLANG_ASSERT (i .second .getCount () <=2 ); 432if (i .second .getCount ()!= 2 ) 433 { 434// If we only have a '_Internal' atom inside our name list there is a missing 435// 'External' atom 436if (i .second [0 ]-> name .startsWith ("_" )) 437m_sink -> diagnose ( 438i .second [0 ]-> sourceLoc , 439Diagnostics ::missingExternalInternalAtomPair , 440i .second [0 ]-> name ); 441 } 442 } 443 } 444 445bool isLineSuccessive (HumaneSourceLoc above ,HumaneSourceLoc below ) 446 { 447return above .line + 1 == below .line ; 448 } 449 450SlangResult parseDefs () 451 { 452auto tokens = m_lexer -> lexAllMarkupTokens (); 453m_tokenReader = TokenReader (tokens ); 454AutoDocInfo successiveComments = AutoDocInfo (); 455HumaneSourceLoc successiveCommentLine = {}; 456 457for (;;) 458 { 459auto nextToken = m_tokenReader .advanceToken (); 460 461if (!isLineSuccessive ( 462successiveCommentLine , 463m_lexer -> m_sourceView -> getHumaneLoc (nextToken .getLoc ()))) 464successiveComments = AutoDocInfo (); 465 466RefPtr < CapabilityDef > def = new CapabilityDef (); 467def -> sharedContext = & m_sharedContext ; 468def -> flavor = CapabilityFlavor ::Normal ; 469if (nextToken .getContent ()== "alias" ) 470 { 471def -> flavor = CapabilityFlavor ::Alias ; 472 } 473else if (nextToken .getContent ()== "abstract" ) 474 { 475def -> flavor = CapabilityFlavor ::Abstract ; 476 } 477else if (nextToken .getContent ()== "def" ) 478 { 479def -> flavor = CapabilityFlavor ::Normal ; 480 } 481else if (nextToken .type == TokenType ::BlockComment ) 482 { 483// Do not auto-document 484continue ; 485 } 486else if (nextToken .type == TokenType ::LineComment ) 487 { 488// Auto-document if the preceeding token to an identifier is '///' 489// complete rules described in `source\slang\slang-capabilities.capdef` 490auto commentContent = nextToken .getContent (); 491 492// remove "//" 493commentContent = commentContent .subString (2 ,commentContent .getLength ()- 2 ); 494if (commentContent .startsWith ("/" )) 495 { 496auto commentLine = m_lexer -> m_sourceView -> getHumaneLoc (nextToken .getLoc ()); 497 498// Reset the `successiveCommentLine` to our newest commentLine 499successiveCommentLine = commentLine ; 500 501// remove "/" from "///" 502commentContent = 503commentContent .subString (1 ,commentContent .getLength ()- 1 ).trim (); 504 505// Check if we have a `[header]` 506if (commentContent .startsWith ("[" )) 507 { 508// Make a substring of `header]` 509auto consumedLeftBracketOfHeader = 510commentContent .subString (1 ,commentContent .getLength ()- 1 ); 511// Find a `]` of `header]` if it exists 512auto indexOfHeaderEnd = consumedLeftBracketOfHeader .indexOf (']' ); 513if (indexOfHeaderEnd != -1 ) 514 { 515// We found our `header` 516auto headerName = 517consumedLeftBracketOfHeader .subString (0 ,indexOfHeaderEnd ); 518successiveComments .headerGroup = getAutoDocHeaderGroupFromTag ( 519m_sink , 520headerName , 521nextToken .getLoc ()); 522continue ; 523 } 524// If we did not find a header this is a regular comment 525 } 526successiveComments .comment .append ("> " ); 527successiveComments .comment .append (commentContent ); 528successiveComments .comment .append ("\n" ); 529 } 530continue ; 531 } 532else if (nextToken .type == TokenType ::EndOfFile ) 533 { 534break ; 535 } 536else 537 { 538m_sink -> diagnose (nextToken .loc ,Diagnostics ::unexpectedToken ,nextToken ); 539return SLANG_FAIL ; 540 } 541 542Token nameToken ; 543SLANG_RETURN_ON_FAIL ( 544readToken < AdvanceOptions ::SkipComments > (TokenType ::Identifier ,nameToken )); 545def -> name = nameToken .getContent (); 546 547if (def -> flavor == CapabilityFlavor ::Normal ) 548 { 549if (advanceIf < AdvanceOptions ::SkipComments > (TokenType ::Colon )) 550 { 551SLANG_RETURN_ON_FAIL (parseExpr (def -> expr )); 552 } 553if (advanceIf < AdvanceOptions ::SkipComments > (TokenType ::OpAssign )) 554 { 555Token rankToken ; 556SLANG_RETURN_ON_FAIL (readToken < AdvanceOptions ::SkipComments > ( 557TokenType ::IntegerLiteral , 558rankToken )); 559def -> rank = stringToInt (rankToken .getContent ()); 560 } 561def -> docComment = successiveComments ; 562if (def -> docComment .comment .getLength ()== 0 && !isInternalDef (def )) 563m_sink -> diagnose (nextToken .loc ,Diagnostics ::requiresDocComment ,def -> name ); 564 } 565else if (def -> flavor == CapabilityFlavor ::Alias ) 566 { 567SLANG_RETURN_ON_FAIL (readToken < AdvanceOptions ::SkipComments > (TokenType ::OpAssign )); 568SLANG_RETURN_ON_FAIL (parseExpr (def -> expr )); 569def -> docComment = successiveComments ; 570if (def -> docComment .comment .getLength ()== 0 && !isInternalDef (def )) 571m_sink -> diagnose (nextToken .loc ,Diagnostics ::requiresDocComment ,def -> name ); 572 } 573else if (def -> flavor == CapabilityFlavor ::Abstract ) 574 { 575if (advanceIf < AdvanceOptions ::SkipComments > (TokenType ::Colon )) 576 { 577SLANG_RETURN_ON_FAIL (parseExpr (def -> expr )); 578 } 579 } 580SLANG_RETURN_ON_FAIL (readToken < AdvanceOptions ::SkipComments > (TokenType ::Semicolon )); 581m_defs .add (def ); 582if (!m_mapNameToCapability .addIfNotExists (def -> name ,m_defs .getLast ())) 583 { 584m_sink -> diagnose (nextToken .loc ,Diagnostics ::redefinition ,def -> name ); 585return SLANG_FAIL ; 586 } 587 588// set abstract atom identifiers 589if (!m_sharedContext .ptrOfTarget && def -> name .equals ("target" )) 590m_sharedContext .ptrOfTarget = m_defs .getLast (); 591else if (!m_sharedContext .ptrOfStage && def -> name .equals ("stage" )) 592m_sharedContext .ptrOfStage = m_defs .getLast (); 593 594def -> sourceLoc = nameToken .loc ; 595 } 596validateInternalAtomExternalAtomPair (); 597return SLANG_OK ; 598 } 599}; 600 601struct CapabilityConjunction 602{ 603HashSet < CapabilityDef *> atoms ; 604 605String toString ()const 606 { 607bool first = true; 608String result = "[" ; 609for (auto atom :atoms ) 610 { 611if (!first ) 612 { 613result .append (" + " ); 614 } 615first = false; 616result .append (atom -> name ); 617 } 618result .appendChar (']' ); 619return result ; 620 } 621 622bool implies (const CapabilityConjunction & c )const 623 { 624for (auto & atom :c .atoms ) 625 { 626if (!atoms .contains (atom )) 627return false; 628 } 629return true; 630 } 631 632const CapabilityDef * getAbstractAtom (CapabilityDef * defToFilterFor )const 633 { 634for (auto * atom :this -> atoms ) 635 { 636for (auto present :atom -> keyAtomsPresent ) 637 { 638auto base = present -> getAbstractBase (); 639if (base != defToFilterFor ) 640continue ; 641return present ; 642 } 643 } 644return nullptr ; 645 } 646 647bool shareTargetAndStageAtom ( 648const CapabilityConjunction & other , 649CapabilitySharedContext & context ) 650 { 651// shared target means thisTarget==otherTarget 652// shared stage means either `nostage + ...` or `stage == stage` 653 654const CapabilityDef * thisTarget = this -> getAbstractAtom (context .ptrOfTarget ); 655const CapabilityDef * otherTarget = other .getAbstractAtom (context .ptrOfTarget ); 656 657if (thisTarget != otherTarget && thisTarget && otherTarget ) 658return false; 659 660const CapabilityDef * thisStage = this -> getAbstractAtom (context .ptrOfStage ); 661const CapabilityDef * otherStage = other .getAbstractAtom (context .ptrOfStage ); 662 663if (thisStage != otherStage && thisStage && otherStage ) 664return false; 665 666return true; 667 } 668 669bool isImpossible ()const 670 { 671// Keep a map from an abstract base to the concrete atom defined in this conjunction that 672// implements the base. 673Dictionary < CapabilityDef * ,CapabilityDef *> abstractKV ; 674 675for (auto & atom :atoms ) 676 { 677auto abstractBase = atom -> getAbstractBase (); 678if (!abstractBase ) 679continue ; 680 681// Have we already seen another concrete atom that implements the same abstract base of 682// the current atom? If so, we have a conflict and the conjunction is impossible. 683// 684CapabilityDef * value = nullptr ; 685if (abstractKV .tryGetValue (abstractBase ,value )) 686 { 687if (value != atom ) 688return true; 689 } 690else 691 { 692abstractKV [abstractBase ]= atom ; 693 } 694 } 695return false; 696 } 697}; 698 699struct CapabilityDisjunction 700{ 701List < CapabilityConjunction > conjunctions ; 702 703void addConjunction ( 704DiagnosticSink * sink , 705SourceLoc sourceLoc , 706CapabilitySharedContext & context , 707CapabilityConjunction & c ) 708 { 709if (c .isImpossible ()) 710return ; 711bool cImpliesThis = false; 712for (Index i = 0 ;i < conjunctions .getCount ();) 713 { 714// implied sets will be replaced 715if (c .implies (conjunctions [i ])) 716 { 717cImpliesThis = true; 718conjunctions .fastRemoveAt (i ); 719 } 720else 721i ++ ; 722 } 723if (cImpliesThis ) 724 { 725conjunctions .add (_Move (c )); 726return ; 727 } 728 729for (Index i = 0 ;i < conjunctions .getCount ();) 730 { 731if (conjunctions [i ].implies (c )) 732 { 733// subset is implied, we do not need to add it. 734return ; 735 } 736else 737 { 738// validate we are not creating a disjunction of same targets 739if (conjunctions [i ].shareTargetAndStageAtom (c ,context )) 740 { 741if (sink ) 742 { 743sink -> diagnose ( 744sourceLoc , 745Diagnostics ::unionWithSameKeyAtomButNotSubset , 746conjunctions [i ].toString (), 747c .toString ()); 748sink = nullptr ; 749 } 750 } 751i ++ ; 752 } 753 } 754conjunctions .add (_Move (c )); 755 } 756void removeImplied () 757 { 758for (Index i = 0 ;i < conjunctions .getCount ();i ++ ) 759 { 760for (Index ii = 0 ;ii < conjunctions .getCount ();ii ++ ) 761 { 762if (ii == i ) 763continue ; 764 765if (!conjunctions [i ].implies (conjunctions [ii ])) 766continue ; 767 768if (i < ii ) 769 { 770conjunctions .fastRemoveAt (ii ); 771 } 772else 773 { 774conjunctions .removeAt (ii ); 775i -- ; 776 } 777ii -- ; 778 } 779 } 780 } 781 782void inclusiveJoinConjunction ( 783CapabilitySharedContext & context , 784CapabilityConjunction & c , 785List < CapabilityConjunction >& toAddAfter ) 786 { 787if (c .isImpossible ()) 788return ; 789for (auto & conjunction :conjunctions ) 790 { 791if (conjunction .implies (c )) 792return ; 793 } 794for (Index i = 0 ;i < conjunctions .getCount ();) 795 { 796if (conjunctions [i ].shareTargetAndStageAtom (c ,context )) 797 { 798CapabilityConjunction toAddAfterSet ; 799for (auto atom :conjunctions [i ].atoms ) 800toAddAfterSet .atoms .add (atom ); 801for (auto atom :c .atoms ) 802toAddAfterSet .atoms .add (atom ); 803toAddAfter .add (toAddAfterSet ); 804return ; 805 } 806else 807 { 808i ++ ; 809 } 810 } 811conjunctions .add (_Move (c )); 812 } 813 814CapabilityDisjunction joinWith ( 815DiagnosticSink * sink , 816SourceLoc sourceLoc , 817CapabilitySharedContext & context , 818const CapabilityDisjunction & other ) 819 { 820if (conjunctions .getCount ()== 0 ) 821 { 822return other ; 823 } 824if (other .conjunctions .getCount ()== 0 ) 825 { 826return * this ; 827 } 828 829CapabilityDisjunction result ; 830 831for (auto & thisC :conjunctions ) 832 { 833for (auto & thatC :other .conjunctions ) 834 { 835CapabilityConjunction newC ; 836for (auto atom :thisC .atoms ) 837newC .atoms .add (atom ); 838for (auto atom :thatC .atoms ) 839newC .atoms .add (atom ); 840result .addConjunction (sink ,sourceLoc ,context ,newC ); 841 } 842 } 843 844// incompatible abstract atoms 845if (result .conjunctions .getCount ()== 0 ) 846sink -> diagnose (sourceLoc ,Diagnostics ::invalidJoinInGenerator ); 847 848return result ; 849 } 850 851List < List < CapabilityDef *>> canonicalize () 852 { 853List < List < CapabilityDef *>> result ; 854for (auto & c :conjunctions ) 855 { 856List < CapabilityDef *> atoms ; 857for (auto & atom :c .atoms ) 858atoms .add (atom ); 859atoms .sort ([](CapabilityDef * c1 ,CapabilityDef * c2 ) 860 {return c1 -> enumValue < c2 -> enumValue ; }); 861result .add (_Move (atoms )); 862 } 863result .sort ( 864 [](const List < CapabilityDef *>& c1 ,const List < CapabilityDef *>& c2 ) 865 { 866for (Index i = 0 ;i < Math ::Min (c1 .getCount (),c2 .getCount ());i ++ ) 867 { 868if (c1 [i ]-> enumValue < c2 [i ]-> enumValue ) 869return true; 870else if (c1 [i ]-> enumValue > c2 [i ]-> enumValue ) 871return false; 872 } 873return c1 .getCount ()< c2 .getCount (); 874 }); 875return result ; 876 } 877}; 878 879CapabilityDisjunction getCanonicalRepresentation (CapabilityDef * def ) 880{ 881CapabilityDisjunction result ; 882for (auto & c :def -> canonicalRepresentation ) 883 { 884CapabilityConjunction conj ; 885for (auto & atom :c ) 886conj .atoms .add (atom ); 887result .conjunctions .add (conj ); 888 } 889return result ; 890} 891 892CapabilityDisjunction evaluateConjunction ( 893DiagnosticSink * sink , 894SourceLoc sourceLoc , 895CapabilitySharedContext & context , 896const List < CapabilityDef *>& atoms ) 897{ 898CapabilityDisjunction result ; 899for (auto * def :atoms ) 900 { 901CapabilityDisjunction defCanonical = getCanonicalRepresentation (def ); 902result = result .joinWith (sink ,sourceLoc ,context ,defCanonical ); 903 } 904return result ; 905} 906 907void calcCanonicalRepresentation ( 908DiagnosticSink * sink , 909CapabilityDef * def , 910const List < CapabilityDef *>& mapEnumValueToDef ) 911{ 912CapabilityDisjunction disjunction ; 913if (def -> flavor == CapabilityFlavor ::Normal ) 914 { 915CapabilityConjunction c ; 916c .atoms .add (def ); 917disjunction .conjunctions .add (c ); 918 } 919CapabilityDisjunction exprVal ; 920for (auto & c :def -> expr .conjunctions ) 921 { 922CapabilityDisjunction evalD = 923evaluateConjunction (sink ,c .sourceLoc ,* def -> sharedContext ,c .atoms ); 924List < CapabilityConjunction > toAddAfter ; 925for (auto & cc :evalD .conjunctions ) 926 { 927exprVal .inclusiveJoinConjunction (* def -> sharedContext ,cc ,toAddAfter ); 928 } 929for (auto & i :toAddAfter ) 930exprVal .conjunctions .add (i ); 931if (toAddAfter .getCount ()> 0 ) 932exprVal .removeImplied (); 933 } 934disjunction = disjunction .joinWith (sink ,def -> sourceLoc ,* def -> sharedContext ,exprVal ); 935def -> canonicalRepresentation = disjunction .canonicalize (); 936def -> fillKeyAtomsPresentInCannonicalRepresentation (); 937} 938 939void calcCanonicalRepresentations ( 940DiagnosticSink * sink , 941List < RefPtr < CapabilityDef >>& defs , 942const List < CapabilityDef *>& mapEnumValueToDef ) 943{ 944for (auto def :defs ) 945calcCanonicalRepresentation (sink ,def ,mapEnumValueToDef ); 946} 947 948// Create a local UIntSet with data 949void outputLocalUIntSetBuffer ( 950const String & nameOfBuffer , 951StringBuilder & resultBuilder , 952UIntSet & set ) 953{ 954resultBuilder <<" CapabilityAtomSet " <<nameOfBuffer <<";\n" ; 955resultBuilder <<" " <<nameOfBuffer <<".resizeBackingBufferDirectly(" 956 <<set .getBuffer ().getCount () <<");\n" ; 957for (Index i = 0 ;i < set .getBuffer ().getCount ();i ++ ) 958 { 959resultBuilder <<" " <<nameOfBuffer <<".addRawElement(UIntSet::Element(" 960 <<set .getBuffer ()[i ] <<"UL), " <<i <<"); \n" ; 961 } 962} 963 964// Create function to generate a UIntSet with initial data 965void outputUIntSetGenerator ( 966const String & nameOfGenerator , 967StringBuilder & resultBuilder , 968UIntSet & set ) 969{ 970resultBuilder <<"inline static CapabilityAtomSet " <<nameOfGenerator <<"()\n" ; 971resultBuilder <<"{\n" ; 972auto nameOfBackingData = nameOfGenerator + "_data" ; 973outputLocalUIntSetBuffer (nameOfBackingData ,resultBuilder ,set ); 974resultBuilder <<" return " <<nameOfBackingData <<";\n" ; 975resultBuilder <<"}\n" ; 976} 977 978 979UIntSet atomSetToUIntSet (const List < CapabilityDef *>& atomSet ) 980{ 981UIntSet set {}; 982// Last element is generally a larger number. Start from there to minimize reallocations. 983for (Index i = atomSet .getCount ()- 1 ;i >=0 ;i -- ) 984set .add (atomSet [i ]-> enumValue ); 985return set ; 986} 987 988void printDocForCapabilityDef ( 989StringBuilder & sbDoc , 990RefPtr < CapabilityDef > def , 991List < StringBuilder >& sbDocSections ) 992{ 993if (isInternalDef (def )|| def -> flavor == CapabilityFlavor ::Abstract || 994def -> docComment .headerGroup == AutoDocHeaderGroup ::Invalid ) 995return ; 996 997auto & sbDocSection = sbDocSections [(UInt )def -> docComment .headerGroup ]; 998sbDocSection <<"\n" 999 <<"`" <<def -> name <<"`\n" ; 1000sbDocSection <<def -> docComment .comment ; 1001} 1002 1003List < StringBuilder > setupDocCommentHeaderStringBuilders () 1004{ 1005List < StringBuilder > sbDocSections ; 1006sbDocSections .setCount ((UInt )AutoDocHeaderGroup ::Count ); 1007for (UInt i = 0 ;i < (UInt )AutoDocHeaderGroup ::Count ;i ++ ) 1008 { 1009sbDocSections [i ] <<"\n" 1010 <<getHeaderNameFromAutoDocHeaderGroup (i ) <<"\n----------------------\n" ; 1011sbDocSections [i ] <<"*" <<getHeaderDescriptionFromAutoDocHeaderGroup (i ) <<"*\n" ; 1012 } 1013return sbDocSections ; 1014} 1015 1016/// "[Link Name](fileName#Link-Name)" 1017void addHyperLink (StringBuilder & sbDoc ,UnownedStringSlice suffix ) 1018{ 1019String suffixReformatted = "" ; 1020 1021for (auto i :suffix ) 1022 { 1023if (i == ' ' ) 1024 { 1025suffixReformatted .appendChar ('-' ); 1026continue ; 1027 } 1028suffixReformatted .appendChar (i ); 1029 } 1030sbDoc <<"[" <<suffix <<"](#" <<suffixReformatted .toLower () <<")" ; 1031} 1032 1033void setupDocumentationHeader (StringBuilder & sbDoc ,const String & outPath ) 1034{ 1035sbDoc <<R"( 1036--- 1037layout: user-guide 1038--- 1039 1040Capability Atoms 1041============================ 1042 1043### Sections: 1044 1045)" ; 1046 1047// Hyper-Links 1048for (UInt i = 0 ;i < (UInt )AutoDocHeaderGroup ::Count ;i ++ ) 1049 { 1050auto headerName = getHeaderNameFromAutoDocHeaderGroup (i ); 1051sbDoc <<i + 1 <<". " ;// "i. " 1052addHyperLink (sbDoc ,headerName ); 1053sbDoc <<"\n" ; 1054 } 1055} 1056 1057SlangResult generateDocumentation ( 1058DiagnosticSink * sink , 1059List < RefPtr < CapabilityDef >>& defs , 1060StringBuilder & sbDoc , 1061const String & outPath ) 1062{ 1063setupDocumentationHeader (sbDoc ,outPath ); 1064 1065List < StringBuilder > sbDocSections = setupDocCommentHeaderStringBuilders (); 1066 1067// Group capabilities by header group and sort alphabetically within each group 1068List < List < RefPtr < CapabilityDef >>> capabilitiesByHeaderGroup ; 1069capabilitiesByHeaderGroup .setCount ((UInt )AutoDocHeaderGroup ::Count ); 1070 1071// Collect capabilities into their respective header groups 1072for (auto def :defs ) 1073 { 1074if (!isInternalDef (def )&& def -> flavor != CapabilityFlavor ::Abstract && 1075def -> docComment .headerGroup != AutoDocHeaderGroup ::Invalid ) 1076 { 1077capabilitiesByHeaderGroup [(UInt )def -> docComment .headerGroup ].add (def ); 1078 } 1079 } 1080 1081// Sort capabilities within each header group alphabetically by name 1082for (auto & capabilitiesInGroup :capabilitiesByHeaderGroup ) 1083 { 1084capabilitiesInGroup .sort ([](const RefPtr < CapabilityDef >& a ,const RefPtr < CapabilityDef >& b ) 1085 {return a -> name < b -> name ; }); 1086 } 1087 1088// Add sorted capabilities to documentation sections 1089for (UInt headerGroupIndex = 0 ;headerGroupIndex < (UInt )AutoDocHeaderGroup ::Count ; 1090headerGroupIndex ++ ) 1091 { 1092for (auto def :capabilitiesByHeaderGroup [headerGroupIndex ]) 1093 { 1094printDocForCapabilityDef (sbDoc ,def ,sbDocSections ); 1095 } 1096 } 1097 1098for (auto stringBuilder :sbDocSections ) 1099sbDoc <<stringBuilder .toString (); 1100return 1 ; 1101} 1102SlangResult generateDefinitions ( 1103DiagnosticSink * sink , 1104List < RefPtr < CapabilityDef >>& defs , 1105StringBuilder & sbHeader , 1106StringBuilder & sbCpp ) 1107{ 1108 1109sbHeader <<"enum class CapabilityAtom\n{\n" ; 1110sbHeader <<" Invalid,\n" ; 1111for (auto def :defs ) 1112 { 1113if (def -> flavor == CapabilityFlavor ::Normal ) 1114 { 1115sbHeader <<" " <<def -> name <<",\n" ; 1116 } 1117 } 1118sbHeader <<" Count\n" ; 1119sbHeader <<"};\n" ; 1120 1121CapabilityDef * firstAbstractDef = nullptr ; 1122CapabilityDef * firstAliasDef = nullptr ; 1123sbHeader <<"enum class CapabilityName\n{\n" ; 1124sbHeader <<" Invalid,\n" ; 1125Index enumValueCounter = 1 ; 1126List < CapabilityDef *> mapEnumValueToDef ; 1127mapEnumValueToDef .add (nullptr );// For Invalid. 1128for (auto def :defs ) 1129 { 1130if (def -> flavor == CapabilityFlavor ::Normal ) 1131 { 1132def -> enumValue = enumValueCounter ; 1133++ enumValueCounter ; 1134mapEnumValueToDef .add (def ); 1135sbHeader <<" " <<def -> name <<" = (int)CapabilityAtom::" <<def -> name <<",\n" ; 1136 } 1137 } 1138for (auto def :defs ) 1139 { 1140if (def -> flavor == CapabilityFlavor ::Abstract ) 1141 { 1142if (firstAbstractDef == nullptr ) 1143firstAbstractDef = def ; 1144def -> enumValue = enumValueCounter ; 1145++ enumValueCounter ; 1146mapEnumValueToDef .add (def ); 1147sbHeader <<" " <<def -> name <<",\n" ; 1148 } 1149 } 1150for (auto def :defs ) 1151 { 1152if (def -> flavor == CapabilityFlavor ::Alias ) 1153 { 1154if (firstAliasDef == nullptr ) 1155firstAliasDef = def ; 1156def -> enumValue = enumValueCounter ; 1157++ enumValueCounter ; 1158mapEnumValueToDef .add (def ); 1159sbHeader <<" " <<def -> name <<",\n" ; 1160 } 1161 } 1162sbHeader <<" Count\n" ; 1163sbHeader <<"};\n" ; 1164 1165Index targetCount = 0 ; 1166Index stageCount = 0 ; 1167 1168UIntSet anyTargetAtomSet {}; 1169UIntSet anyStageAtomSet {}; 1170StringBuilder anyTargetUIntSetHash ; 1171StringBuilder anyStageUIntSetHash ; 1172 1173for (auto def :defs ) 1174 { 1175if (def -> getAbstractBase ()== def -> sharedContext -> ptrOfTarget ) 1176 { 1177targetCount ++ ; 1178anyTargetAtomSet .add (def -> enumValue ); 1179 } 1180else if (def -> getAbstractBase ()== def -> sharedContext -> ptrOfStage ) 1181 { 1182stageCount ++ ; 1183anyStageAtomSet .add (def -> enumValue ); 1184 } 1185 } 1186outputUIntSetGenerator ( 1187"generatorOf_kAnyTargetUIntSetBuffer" , 1188anyTargetUIntSetHash , 1189anyTargetAtomSet ); 1190anyTargetUIntSetHash <<"static CapabilityAtomSet kAnyTargetUIntSetBuffer = " 1191"generatorOf_kAnyTargetUIntSetBuffer();\n" ; 1192sbCpp <<anyTargetUIntSetHash ; 1193 1194outputUIntSetGenerator ( 1195"generatorOf_kAnyStageUIntSetBuffer" , 1196anyStageUIntSetHash , 1197anyStageAtomSet ); 1198anyStageUIntSetHash <<"static CapabilityAtomSet kAnyStageUIntSetBuffer = " 1199"generatorOf_kAnyStageUIntSetBuffer();\n" ; 1200sbCpp <<anyStageUIntSetHash ; 1201 1202sbHeader <<"\nenum {\n" ; 1203sbHeader <<" kCapabilityTargetCount = " <<targetCount <<",\n" ; 1204sbHeader <<" kCapabilityStageCount = " <<stageCount <<",\n" ; 1205sbHeader <<"};\n\n" ; 1206 1207calcCanonicalRepresentations (sink ,defs ,mapEnumValueToDef ); 1208 1209struct SerializedConjunction 1210 { 1211SerializedConjunction () {} 1212SerializedConjunction (const String & initFunctionName ,UIntSet & data ) 1213 :m_initFunctionName (initFunctionName ),m_data (data ) 1214 { 1215 } 1216String m_initFunctionName ; 1217UIntSet m_data ; 1218 }; 1219List < SerializedConjunction > serializedCapabilitesCache ; 1220 1221List < Index > serializedAtomDisjunctions ; 1222auto serializeConjunction = [& ](const List < CapabilityDef *>& capabilities , 1223CapabilityDef * parentDef , 1224Index conjunctionNumber )-> Index 1225 { 1226auto capabilitiesAsUIntSet = atomSetToUIntSet (capabilities ); 1227// Do we already have a serialized capability array that is the same the one we are trying 1228// to serialize? 1229for (Index i = 0 ;i < serializedCapabilitesCache .getCount ();i ++ ) 1230 { 1231auto & existingSet = serializedCapabilitesCache [i ].m_data ; 1232if (existingSet == capabilitiesAsUIntSet ) 1233 { 1234return i ; 1235 } 1236 } 1237auto initName = 1238"generatorOf_" + parentDef -> name + "_conjunction" + String (conjunctionNumber ); 1239outputUIntSetGenerator (initName ,sbCpp ,capabilitiesAsUIntSet ); 1240 1241auto result = serializedCapabilitesCache .getCount (); 1242serializedCapabilitesCache .add ( 1243SerializedConjunction (initName + "()" ,capabilitiesAsUIntSet )); 1244return result ; 1245 }; 1246auto serializeDisjunction = [& ](const List < Index >& conjunctions )-> SerializedArrayView 1247 { 1248SerializedArrayView result ; 1249result .first = serializedAtomDisjunctions .getCount (); 1250for (auto c :conjunctions ) 1251 { 1252serializedAtomDisjunctions .add (c ); 1253 } 1254result .count = conjunctions .getCount (); 1255return result ; 1256 }; 1257for (auto def :defs ) 1258 { 1259List < Index > conjunctions ; 1260for (auto & c :def -> canonicalRepresentation ) 1261conjunctions .add (serializeConjunction (c ,def ,conjunctions .getCount ())); 1262def -> serializedCanonicalRepresentation = serializeDisjunction (conjunctions ); 1263 } 1264 1265sbCpp <<"static CapabilityAtomSet kCapabilityArray[] = {\n" ; 1266Index arrayIndex = 0 ; 1267for (Index i = 0 ;i < serializedCapabilitesCache .getCount ();++ i ) 1268 { 1269sbCpp <<" " <<serializedCapabilitesCache [i ].m_initFunctionName <<",\n" ; 1270 } 1271sbCpp <<"};\n" ; 1272sbCpp <<"static CapabilityAtomSet* kCapabilityConjunctions[] = {\n" ; 1273for (auto c :serializedAtomDisjunctions ) 1274 { 1275sbCpp <<" kCapabilityArray + " <<c <<", \n" ; 1276 } 1277sbCpp <<"};\n" ; 1278 1279sbCpp 1280 <<"static const CapabilityAtomInfo kCapabilityNameInfos[int(CapabilityName::Count)] = {\n" ; 1281for (auto * def :mapEnumValueToDef ) 1282 { 1283if (!def ) 1284 { 1285sbCpp 1286 <<R"( { UnownedStringSlice::fromLiteral("Invalid"), CapabilityNameFlavor::Concrete, CapabilityName::Invalid, 0, {nullptr, 0} },)" 1287 <<"\n" ; 1288continue ; 1289 } 1290 1291// name. 1292sbCpp <<" { UnownedStringSlice::fromLiteral(\"" <<def -> name <<"\"), " ; 1293 1294// flavor. 1295switch (def -> flavor ) 1296 { 1297case CapabilityFlavor ::Normal : 1298sbCpp <<"CapabilityNameFlavor::Concrete" ; 1299break ; 1300case CapabilityFlavor ::Abstract : 1301sbCpp <<"CapabilityNameFlavor::Abstract" ; 1302break ; 1303case CapabilityFlavor ::Alias : 1304sbCpp <<"CapabilityNameFlavor::Alias" ; 1305break ; 1306 } 1307sbCpp <<", " ; 1308 1309// abstract base. 1310auto abstractBase = def -> getAbstractBase (); 1311if (abstractBase ) 1312 { 1313sbCpp <<"CapabilityName::" <<abstractBase -> name ; 1314 } 1315else 1316 { 1317sbCpp <<"CapabilityName::Invalid" ; 1318 } 1319sbCpp <<", " ; 1320 1321// rank 1322sbCpp <<def -> rank ; 1323sbCpp <<", " ; 1324 1325// canonnical representation. 1326sbCpp <<"{ kCapabilityConjunctions + " <<def -> serializedCanonicalRepresentation .first 1327 <<", " <<def -> serializedCanonicalRepresentation .count <<"} },\n" ; 1328 } 1329 1330sbCpp <<"};\n" ; 1331 1332sbCpp <<"void freeCapabilityDefs()\n" 1333 <<"{\n" 1334 <<" for (auto& cap : kCapabilityArray) { cap = CapabilityAtomSet(); }\n" 1335 <<" kAnyTargetUIntSetBuffer = CapabilityAtomSet();\n" 1336 <<" kAnyStageUIntSetBuffer = CapabilityAtomSet();\n" 1337 <<"}\n" ; 1338return SLANG_OK ; 1339} 1340 1341 1342SlangResult parseDefFile ( 1343DiagnosticSink * sink , 1344String inputPath , 1345List < RefPtr < CapabilityDef >>& outDefs , 1346CapabilitySharedContext & capabilitySharedContext ) 1347{ 1348auto sourceManager = sink -> getSourceManager (); 1349 1350String contents ; 1351SLANG_RETURN_ON_FAIL (File ::readAllText (inputPath ,contents )); 1352PathInfo pathInfo = PathInfo ::makeFromString (inputPath ); 1353SourceFile * sourceFile = sourceManager -> createSourceFileWithString (pathInfo ,contents ); 1354SourceView * sourceView = sourceManager -> createSourceView (sourceFile ,nullptr ,SourceLoc ()); 1355Lexer lexer ; 1356NamePool namePool ; 1357lexer .initialize (sourceView ,sink ,& namePool ,sourceManager -> getMemoryArena ()); 1358 1359CapabilityDefParser parser (& lexer ,sink ,capabilitySharedContext ); 1360 1361SLANG_RETURN_ON_FAIL (parser .parseDefs ()); 1362outDefs = _Move (parser .m_defs ); 1363return SLANG_OK ; 1364} 1365 1366void printDiagnostics (DiagnosticSink * sink ) 1367{ 1368ComPtr < ISlangBlob > blob ; 1369sink -> getBlobIfNeeded (blob .writeRef ()); 1370if (blob ) 1371 { 1372fprintf (stderr ,"%s" , (const char * )blob -> getBufferPointer ()); 1373 } 1374} 1375 1376void writeIfChanged (String fileName ,String content ) 1377{ 1378if (File ::exists (fileName )) 1379 { 1380String existingContent ; 1381File ::readAllText (fileName ,existingContent ); 1382if (existingContent .getUnownedSlice ().trim ()== content .getUnownedSlice ().trim ()) 1383return ; 1384 } 1385File ::writeAllText (fileName ,content ); 1386} 1387 1388int main (int argc ,const char * const * argv ) 1389{ 1390if (argc < 2 ) 1391 { 1392fprintf (stderr ,"Usage: %s\n" ,argc >=1 ?argv [0 ] :"slang-capabilities-generator" ); 1393return 1 ; 1394 } 1395String targetDir ,outDocPath ; 1396for (int i = 0 ;i < argc - 1 ;i ++ ) 1397 { 1398if (strcmp (argv [i ],"--target-directory" )== 0 ) 1399targetDir = argv [i + 1 ]; 1400if (strcmp (argv [i ],"--doc" )== 0 ) 1401outDocPath = argv [i + 1 ]; 1402 } 1403 1404String inPath = argv [1 ]; 1405if (targetDir .getLength ()== 0 ) 1406targetDir = Path ::getParentDirectory (inPath ); 1407 1408auto outCppPath = Path ::combine (targetDir ,"slang-generated-capability-defs-impl.h" ); 1409auto outHeaderPath = Path ::combine (targetDir ,"slang-generated-capability-defs.h" ); 1410auto outLookupPath = Path ::combine (targetDir ,"slang-lookup-capability-defs.cpp" ); 1411SourceManager sourceManager ; 1412sourceManager .initialize (nullptr ,OSFileSystem ::getExtSingleton ()); 1413DiagnosticSink sink (& sourceManager ,nullptr ); 1414List < RefPtr < CapabilityDef >> defs ; 1415CapabilitySharedContext capabilitySharedContext ; 1416if (SLANG_FAILED (parseDefFile (& sink ,inPath ,defs ,capabilitySharedContext ))) 1417 { 1418printDiagnostics (& sink ); 1419return 1 ; 1420 } 1421 1422StringBuilder sbHeader ,sbCpp ; 1423if (SLANG_FAILED (generateDefinitions (& sink ,defs ,sbHeader ,sbCpp ))) 1424 { 1425printDiagnostics (& sink ); 1426return 1 ; 1427 } 1428 1429if (!File ::exists (outDocPath )) 1430 { 1431sink .diagnose ( 1432SourceLoc (), 1433Diagnostics ::couldNotFindValidDocumentationOutputPath , 1434outDocPath ); 1435 } 1436 1437StringBuilder sbDoc ; 1438if (SLANG_FAILED (generateDocumentation (& sink ,defs ,sbDoc ,outDocPath ))) 1439 { 1440printDiagnostics (& sink ); 1441return 1 ; 1442 } 1443 1444writeIfChanged (outHeaderPath ,sbHeader .produceString ()); 1445writeIfChanged (outCppPath ,sbCpp .produceString ()); 1446writeIfChanged (outDocPath ,sbDoc .produceString ()); 1447 1448List < String > opnames ; 1449for (auto def :defs ) 1450 { 1451opnames .add (def -> name ); 1452 } 1453 1454if (SLANG_FAILED (writePerfectHashLookupCppFile ( 1455outLookupPath , 1456opnames , 1457"CapabilityName" , 1458"CapabilityName::" , 1459"slang-capability.h" , 1460& sink ))) 1461 { 1462printDiagnostics (& sink ); 1463return 1 ; 1464 } 1465printDiagnostics (& sink ); 1466return 0 ; 1467}