char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
e8c54893f
char/aliceai-t5-moe
1#ifndef PRE_WGSL_HPP 2#define PRE_WGSL_HPP 3 4#include <cctype> 5#include <fstream> 6#include <sstream> 7#include <stdexcept> 8#include <string> 9#include <string_view> 10#include <unordered_map> 11#include <unordered_set> 12#include <vector> 13 14namespace pre_wgsl { 15 16//============================================================== 17// Options 18//============================================================== 19struct Options { 20 std::string include_path = "." ; 21 std::vector < std::string > macros ; 22}; 23 24//============================================================== 25// Utility: trim 26//============================================================== 27static std::string trim (const std::string & s ) { 28size_t a = 0 ; 29while (a < s .size ()&& std::isspace ((unsigned char )s [a ])) { 30a ++ ; 31 } 32size_t b = s .size (); 33while (b > a && std::isspace ((unsigned char )s [b - 1 ])) { 34b -- ; 35 } 36return s .substr (a ,b - a ); 37} 38 39static std::string trim_value (std::istream & is ) { 40 std::ostringstream ss ; 41ss <<is .rdbuf (); 42return trim (ss .str ()); 43} 44 45static bool isIdentChar (char c ) { 46return std::isalnum (static_cast < unsigned char > (c ))|| c == '_' ; 47} 48 49static bool endsWithContinuation (const std::string & line ) { 50size_t i = line .size (); 51while (i > 0 && std::isspace ((unsigned char )line [i - 1 ])) { 52i -- ; 53 } 54return i > 0 && line [i - 1 ]== '\\' ; 55} 56 57static void stripContinuation (std::string & line ) { 58size_t i = line .size (); 59while (i > 0 && std::isspace ((unsigned char )line [i - 1 ])) { 60i -- ; 61 } 62if (i > 0 && line [i - 1 ]== '\\' ) { 63line .erase (i - 1 ); 64 } 65} 66 67static std::string expandMacrosRecursiveInternal (const std::string & line , 68const std::unordered_map < std::string , std::string > & macros , 69 std::unordered_set < std::string > & visiting ); 70 71static std::string expandMacroValue (const std::string & name , 72const std::unordered_map < std::string , std::string > & macros , 73 std::unordered_set < std::string > & visiting ) { 74if (visiting .count (name )) { 75throw std::runtime_error ("Recursive macro: " + name ); 76 } 77visiting .insert (name ); 78 79auto it = macros .find (name ); 80if (it == macros .end ()) { 81visiting .erase (name ); 82return name ; 83 } 84 85const std::string & value = it -> second ; 86if (value .empty ()) { 87visiting .erase (name ); 88return "" ; 89 } 90 91 std::string expanded = expandMacrosRecursiveInternal (value ,macros ,visiting ); 92visiting .erase (name ); 93return expanded ; 94} 95 96static std::string expandMacrosRecursiveInternal (const std::string & line , 97const std::unordered_map < std::string , std::string > & macros , 98 std::unordered_set < std::string > & visiting ) { 99 std::string result ; 100result .reserve (line .size ()); 101 102size_t i = 0 ; 103while (i < line .size ()) { 104if (isIdentChar (line [i ])) { 105size_t start = i ; 106while (i < line .size ()&& isIdentChar (line [i ])) { 107i ++ ; 108 } 109 std::string token = line .substr (start ,i - start ); 110 111auto it = macros .find (token ); 112if (it != macros .end ()) { 113result += expandMacroValue (token ,macros ,visiting ); 114 }else { 115result += token ; 116 } 117 }else { 118result += line [i ]; 119i ++ ; 120 } 121 } 122 123return result ; 124} 125 126static std::string expandMacrosRecursive (const std::string & line , 127const std::unordered_map < std::string , std::string > & macros ) { 128 std::unordered_set < std::string > visiting ; 129return expandMacrosRecursiveInternal (line ,macros ,visiting ); 130} 131 132//============================================================== 133// Tokenizer for expressions in #if/#elif 134//============================================================== 135class ExprLexer { 136public : 137enum Kind {END ,IDENT ,NUMBER ,OP ,LPAREN ,RPAREN }; 138 139struct Tok { 140Kind kind ; 141 std::string text ; 142 }; 143 144explicit ExprLexer (std::string_view sv ) :src (sv ),pos (0 ) {} 145 146Tok next () { 147skipWS (); 148if (pos >=src .size ()) { 149return {END ,"" }; 150 } 151 152char c = src [pos ]; 153 154// number 155if (std::isdigit ((unsigned char )c )) { 156size_t start = pos ; 157while (pos < src .size ()&& std::isdigit ((unsigned char )src [pos ])) { 158pos ++ ; 159 } 160return {NUMBER , std::string (src .substr (start ,pos - start )) }; 161 } 162 163// identifier 164if (std::isalpha ((unsigned char )c )|| c == '_' ) { 165size_t start = pos ; 166while (pos < src .size ()&& (std::isalnum ((unsigned char )src [pos ])|| src [pos ]== '_' )) { 167pos ++ ; 168 } 169return {IDENT , std::string (src .substr (start ,pos - start )) }; 170 } 171 172if (c == '(' ) { 173pos ++ ; 174return {LPAREN ,"(" }; 175 } 176if (c == ')' ) { 177pos ++ ; 178return {RPAREN ,")" }; 179 } 180 181// multi-char operators 182static const char * two_ops []= {"==" ,"!=" ,"<=" ,">=" ,"&&" ,"||" ,"<<" ,">>" }; 183for (auto op :two_ops ) { 184if (src .substr (pos ,2 )== op ) { 185pos += 2 ; 186return {OP , std::string (op ) }; 187 } 188 } 189 190// single-char operators 191if (std::string ("+-*/%<>!" ).find (c )!= std::string::npos ) { 192pos ++ ; 193return {OP , std::string (1 ,c ) }; 194 } 195 196// unexpected 197pos ++ ; 198return {END ,"" }; 199 } 200 201private : 202 std::string_view src ; 203size_t pos ; 204 205void skipWS () { 206while (pos < src .size ()&& std::isspace ((unsigned char )src [pos ])) { 207pos ++ ; 208 } 209 } 210}; 211 212//============================================================== 213// Expression Parser (recursive descent) 214//============================================================== 215class ExprParser { 216public : 217ExprParser (std::string_view expr , 218const std::unordered_map < std::string , std::string > & macros , 219 std::unordered_set < std::string > & visiting ) : 220lex (expr ), 221macros (macros ), 222visiting (visiting ) { 223advance (); 224 } 225 226int parse () {return parseLogicalOr (); } 227 228private : 229ExprLexer lex ; 230ExprLexer ::Tok tok ; 231const std::unordered_map < std::string , std::string > & macros ; 232 std::unordered_set < std::string > & visiting ; 233 234void advance () {tok = lex .next (); } 235 236bool acceptOp (const std::string & s ) { 237if (tok .kind == ExprLexer ::OP && tok .text == s ) { 238advance (); 239return true; 240 } 241return false; 242 } 243 244bool acceptKind (ExprLexer ::Kind k ) { 245if (tok .kind == k ) { 246advance (); 247return true; 248 } 249return false; 250 } 251 252int parseLogicalOr () { 253int v = parseLogicalAnd (); 254while (acceptOp ("||" )) { 255int rhs = parseLogicalAnd (); 256v = (v || rhs ); 257 } 258return v ; 259 } 260 261int parseLogicalAnd () { 262int v = parseEquality (); 263while (acceptOp ("&&" )) { 264int rhs = parseEquality (); 265v = (v && rhs ); 266 } 267return v ; 268 } 269 270int parseEquality () { 271int v = parseRelational (); 272for (;;) { 273if (acceptOp ("==" )) { 274int rhs = parseRelational (); 275v = (v == rhs ); 276 }else if (acceptOp ("!=" )) { 277int rhs = parseRelational (); 278v = (v != rhs ); 279 }else { 280break ; 281 } 282 } 283return v ; 284 } 285 286int parseRelational () { 287int v = parseShift (); 288for (;;) { 289if (acceptOp ("<" )) { 290int rhs = parseShift (); 291v = (v < rhs ); 292 }else if (acceptOp (">" )) { 293int rhs = parseShift (); 294v = (v > rhs ); 295 }else if (acceptOp ("<=" )) { 296int rhs = parseShift (); 297v = (v <=rhs ); 298 }else if (acceptOp (">=" )) { 299int rhs = parseShift (); 300v = (v >=rhs ); 301 }else { 302break ; 303 } 304 } 305return v ; 306 } 307 308int parseShift () { 309int v = parseAdd (); 310for (;;) { 311if (acceptOp ("<<" )) { 312int rhs = parseAdd (); 313v = (v <<rhs ); 314 }else if (acceptOp (">>" )) { 315int rhs = parseAdd (); 316v = (v >>rhs ); 317 }else { 318break ; 319 } 320 } 321return v ; 322 } 323 324int parseAdd () { 325int v = parseMult (); 326for (;;) { 327if (acceptOp ("+" )) { 328int rhs = parseMult (); 329v = (v + rhs ); 330 }else if (acceptOp ("-" )) { 331int rhs = parseMult (); 332v = (v - rhs ); 333 }else { 334break ; 335 } 336 } 337return v ; 338 } 339 340int parseMult () { 341int v = parseUnary (); 342for (;;) { 343if (acceptOp ("*" )) { 344int rhs = parseUnary (); 345v = (v * rhs ); 346 }else if (acceptOp ("/" )) { 347int rhs = parseUnary (); 348v = (rhs == 0 ?0 :v /rhs ); 349 }else if (acceptOp ("%" )) { 350int rhs = parseUnary (); 351v = (rhs == 0 ?0 :v %rhs ); 352 }else { 353break ; 354 } 355 } 356return v ; 357 } 358 359int parseUnary () { 360if (acceptOp ("!" )) { 361return !parseUnary (); 362 } 363if (acceptOp ("-" )) { 364return - parseUnary (); 365 } 366if (acceptOp ("+" )) { 367return + parseUnary (); 368 } 369return parsePrimary (); 370 } 371 372int parsePrimary () { 373// '(' expr ')' 374if (acceptKind (ExprLexer ::LPAREN )) { 375int v = parse (); 376if (!acceptKind (ExprLexer ::RPAREN )) { 377throw std::runtime_error ("missing ')'" ); 378 } 379return v ; 380 } 381 382// number 383if (tok .kind == ExprLexer ::NUMBER ) { 384int v = std::stoi (tok .text ); 385advance (); 386return v ; 387 } 388 389// defined(identifier) 390if (tok .kind == ExprLexer ::IDENT && tok .text == "defined" ) { 391advance (); 392if (acceptKind (ExprLexer ::LPAREN )) { 393if (tok .kind != ExprLexer ::IDENT ) { 394throw std::runtime_error ("expected identifier in defined()" ); 395 } 396 std::string name = tok .text ; 397advance (); 398if (!acceptKind (ExprLexer ::RPAREN )) { 399throw std::runtime_error ("missing ) in defined()" ); 400 } 401return macros .count (name ) ?1 :0 ; 402 }else { 403// defined NAME 404if (tok .kind != ExprLexer ::IDENT ) { 405throw std::runtime_error ("expected identifier in defined NAME" ); 406 } 407 std::string name = tok .text ; 408advance (); 409return macros .count (name ) ?1 :0 ; 410 } 411 } 412 413// identifier -> treat as integer, if defined use its value else 0 414if (tok .kind == ExprLexer ::IDENT ) { 415 std::string name = tok .text ; 416advance (); 417auto it = macros .find (name ); 418if (it == macros .end ()) { 419return 0 ; 420 } 421if (it -> second .empty ()) { 422return 1 ; 423 } 424return evalMacroExpression (name ,it -> second ); 425 } 426 427// unexpected 428return 0 ; 429 } 430 431int evalMacroExpression (const std::string & name ,const std::string & value ) { 432if (visiting .count (name )) { 433throw std::runtime_error ("Recursive macro: " + name ); 434 } 435 436visiting .insert (name ); 437ExprParser ep (value ,macros ,visiting ); 438int v = ep .parse (); 439visiting .erase (name ); 440return v ; 441 } 442}; 443 444//============================================================== 445// Preprocessor 446//============================================================== 447class Preprocessor { 448public : 449explicit Preprocessor (Options opts = {}) : opts_ (std:: move ( opts )) { 450// Treat empty include path as current directory 451if ( opts_ . include_path . empty ()) { 452opts_ . include_path = "." ; 453} 454parseMacroDefinitions ( opts_ . macros ); 455} 456 457std::string preprocess_file ( const std::string & filename , const std::vector < std::string > & additional_macros = {}) { 458std::unordered_map < std::string, std::string > macros ; 459std::unordered_set < std::string > predefined ; 460std::unordered_set < std::string > include_stack ; 461buildMacros ( additional_macros , macros , predefined ); 462 463std::string result = processFile ( filename , macros , predefined , include_stack , DirectiveMode:: All ); 464return result ; 465} 466 467std::string preprocess ( const std::string & contents , const std::vector < std::string > & additional_macros = {}) { 468std::unordered_map < std::string, std::string > macros ; 469std::unordered_set < std::string > predefined ; 470std::unordered_set < std::string > include_stack ; 471buildMacros ( additional_macros , macros , predefined ); 472 473std::string result = processString ( contents , macros , predefined , include_stack , DirectiveMode:: All ); 474return result ; 475} 476 477std::string preprocess_includes_file ( const std::string & filename ) { 478std::unordered_map < std::string, std::string > macros ; 479std::unordered_set < std::string > predefined ; 480std::unordered_set < std::string > include_stack ; 481std::string result = processFile ( filename , macros , predefined , include_stack , DirectiveMode:: IncludesOnly ); 482return result ; 483} 484 485std::string preprocess_includes ( const std::string & contents ) { 486std::unordered_map < std::string, std::string > macros ; 487std::unordered_set < std::string > predefined ; 488std::unordered_set < std::string > include_stack ; 489std::string result = processString ( contents , macros , predefined , include_stack , DirectiveMode:: IncludesOnly ); 490return result ; 491} 492 493private : 494Options opts_ ; 495std::unordered_map < std::string, std::string > global_macros ; 496 497enum class DirectiveMode { All , IncludesOnly }; 498 499struct Cond { 500bool parent_active ; 501bool active ; 502bool taken ; 503}; 504 505//---------------------------------------------------------- 506// Parse macro definitions into global_macros 507//---------------------------------------------------------- 508void parseMacroDefinitions ( const std::vector < std::string > & macro_defs ) { 509for ( const auto & def : macro_defs ) { 510size_t eq_pos = def . find ( '=' ); 511if ( eq_pos != std::string:: npos ) { 512// Format: NAME=VALUE 513std::string name = trim ( def . substr ( 0 , eq_pos )); 514std::string value = trim ( def . substr ( eq_pos + 1 )); 515global_macros [ name ] = value ; 516} else { 517// Format: NAME 518std::string name = trim ( def ); 519global_macros [ name ] = "" ; 520} 521} 522} 523 524//---------------------------------------------------------- 525// Build combined macro map and predefined set for a preprocessing operation 526//---------------------------------------------------------- 527void buildMacros ( const std::vector < std::string > & additional_macros , 528std::unordered_map < std::string, std::string > & macros , 529std::unordered_set < std::string > & predefined ) { 530macros = global_macros ; 531predefined . clear (); 532 533for ( const auto & [ name , value ] : global_macros ) { 534predefined . insert ( name ); 535} 536 537for ( const auto & def : additional_macros ) { 538size_t eq_pos = def . find ( '=' ); 539std::string name , value ; 540if ( eq_pos != std::string:: npos ) { 541name = trim ( def . substr ( 0 , eq_pos )); 542value = trim ( def . substr ( eq_pos + 1 )); 543} else { 544name = trim ( def ); 545value = "" ; 546} 547 548// Add to macros map (will override global if same name) 549macros [ name ] = value ; 550predefined . insert ( name ); 551} 552} 553 554//---------------------------------------------------------- 555// Helpers 556//---------------------------------------------------------- 557std::string loadFile ( const std::string & fname ) { 558std::ifstream f (fname); 559if (! f . is_open ()) { 560throw std:: runtime_error ( "Could not open file: " + fname ); 561} 562std::stringstream ss ; 563ss << f . rdbuf (); 564return ss . str (); 565} 566 567bool condActive ( const std::vector < Cond > & cond ) const { 568if ( cond . empty ()) { 569return true; 570} 571return cond . back (). active ; 572} 573 574//---------------------------------------------------------- 575// Process a file 576//---------------------------------------------------------- 577std::string processFile ( const std::string & name , 578std::unordered_map < std::string, std::string > & macros , 579const std::unordered_set < std::string > & predefined_macros , 580std::unordered_set < std::string > & include_stack , 581DirectiveMode mode ) { 582if ( include_stack . count ( name )) { 583throw std:: runtime_error ( "Recursive include: " + name ); 584} 585 586include_stack . insert ( name ); 587std::string shader_code = loadFile ( name ); 588std::string out = processString ( shader_code , macros , predefined_macros , include_stack , mode ); 589include_stack . erase ( name ); 590return out ; 591} 592 593std::string processIncludeFile ( const std::string & fname , 594std::unordered_map < std::string, std::string > & macros , 595const std::unordered_set < std::string > & predefined_macros , 596std::unordered_set < std::string > & include_stack , 597DirectiveMode mode ) { 598std::string full_path = opts_ . include_path + "/" + fname ; 599return processFile ( full_path , macros , predefined_macros , include_stack , mode ); 600} 601 602//---------------------------------------------------------- 603// Process text 604//---------------------------------------------------------- 605std::string processString ( const std::string & shader_code , 606std::unordered_map < std::string, std::string > & macros , 607const std::unordered_set < std::string > & predefined_macros , 608std::unordered_set < std::string > & include_stack , 609DirectiveMode mode ) { 610std::vector < Cond > cond ; // Conditional stack for this shader 611std::stringstream out ; 612std::istringstream in (shader_code); 613std::string line ; 614 615while (std:: getline ( in , line )) { 616std::string logical = line ; 617std::string t = trim ( logical ); 618if (! t . empty () && t [ 0 ] == '#' ) { 619while ( endsWithContinuation ( logical )) { 620stripContinuation ( logical ); 621if (!std:: getline ( in , line )) { 622break ; 623} 624logical += "\n" ; 625logical += line ; 626} 627t = trim ( logical ); 628} 629 630if (! t . empty () && t [ 0 ] == '#' ) { 631bool handled = handleDirective ( t , out , macros , predefined_macros , cond , include_stack , mode ); 632if ( mode == DirectiveMode:: IncludesOnly && ! handled ) { 633out << logical << "\n" ; 634} 635} else { 636if ( mode == DirectiveMode:: IncludesOnly ) { 637out << logical << "\n" ; 638} else if ( condActive ( cond )) { 639// Expand macros in the line before outputting 640std::string expanded = expandMacrosRecursive ( logical , macros ); 641out << expanded << "\n" ; 642} 643} 644} 645 646if ( mode == DirectiveMode:: All && ! cond . empty ()) { 647throw std:: runtime_error ( "Unclosed #if directive" ); 648} 649 650return out . str (); 651} 652 653//---------------------------------------------------------- 654// Directive handler 655//---------------------------------------------------------- 656bool handleDirective ( const std::string & t , 657std::stringstream & out , 658std::unordered_map < std::string, std::string > & macros , 659const std::unordered_set < std::string > & predefined_macros , 660std::vector < Cond > & cond , 661std::unordered_set < std::string > & include_stack , 662DirectiveMode mode ) { 663// split into tokens 664std::string body = t . substr ( 1 ); 665std::istringstream iss (body); 666std::string cmd ; 667iss >> cmd ; 668 669if ( cmd == "include" ) { 670if ( mode == DirectiveMode:: All && ! condActive ( cond )) { 671return true; 672} 673std::string file ; 674iss >> file ; 675if ( file . size () >= 2 && file . front () == '"' && file . back () == '"' ) { 676file = file . substr ( 1 , file . size () - 2 ); 677} 678out << processIncludeFile ( file , macros , predefined_macros , include_stack , mode ); 679return true; 680} 681 682if ( mode == DirectiveMode:: IncludesOnly ) { 683return false; 684} 685 686if ( cmd == "define" ) { 687if (! condActive ( cond )) { 688return true; 689} 690std::string name ; 691iss >> name ; 692// Don't override predefined macros from options 693if ( predefined_macros . count ( name )) { 694return true; 695} 696std::string value = trim_value ( iss ); 697macros [ name ] = value ; 698return true; 699} 700 701if ( cmd == "undef" ) { 702if (! condActive ( cond )) { 703return true; 704} 705std::string name ; 706iss >> name ; 707// Don't undef predefined macros from options 708if ( predefined_macros . count ( name )) { 709return true; 710} 711macros . erase ( name ); 712return true; 713} 714 715if ( cmd == "ifdef" ) { 716std::string name ; 717iss >> name ; 718bool p = condActive ( cond ); 719bool v = macros . count ( name ); 720cond . push_back ({ p , p && v , p && v }); 721return true; 722} 723 724if ( cmd == "ifndef" ) { 725std::string name ; 726iss >> name ; 727bool p = condActive ( cond ); 728bool v = ! macros . count ( name ); 729cond . push_back ({ p , p && v , p && v }); 730return true; 731} 732 733if ( cmd == "if" ) { 734std::string expr = trim_value ( iss ); 735bool p = condActive ( cond ); 736bool v = false; 737if ( p ) { 738std::unordered_set < std::string > visiting ; 739ExprParser ep (expr, macros, visiting); 740v = ep . parse () != 0 ; 741} 742cond . push_back ({ p , p && v , p && v }); 743return true; 744} 745 746if ( cmd == "elif" ) { 747std::string expr = trim_value ( iss ); 748 749if ( cond . empty ()) { 750throw std:: runtime_error ( "#elif without #if" ); 751} 752 753Cond & c = cond . back (); 754if (! c . parent_active ) { 755c . active = false; 756return true; 757} 758 759if ( c . taken ) { 760c . active = false; 761return true; 762} 763 764std::unordered_set < std::string > visiting ; 765ExprParser ep (expr, macros, visiting); 766bool v = ep . parse () != 0 ; 767c . active = v ; 768if ( v ) { 769c . taken = true; 770} 771return true; 772} 773 774if ( cmd == "else" ) { 775if ( cond . empty ()) { 776throw std:: runtime_error ( "#else without #if" ); 777} 778 779Cond & c = cond . back (); 780if (! c . parent_active ) { 781c . active = false; 782return true; 783} 784if ( c . taken ) { 785c . active = false; 786} else { 787c . active = true; 788c . taken = true; 789} 790return true; 791} 792 793if ( cmd == "endif" ) { 794if ( cond . empty ()) { 795throw std:: runtime_error ( "#endif without #if" ); 796} 797cond . pop_back (); 798return true; 799} 800 801// Unknown directive 802throw std:: runtime_error ( "Unknown directive: #" + cmd ); 803} 804}; 805 806} // namespace pre_wgsl 807 808#endif // PRE_WGSL_HPP