yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
b118451e3
master
1#include "slang-token-reader.h" 2 3namespace Slang 4{ 5namespace Misc 6{ 7 8enum class TokenizeErrorType 9{ 10InvalidCharacter , 11InvalidEscapeSequence 12}; 13 14enum class State 15{ 16Start , 17Identifier , 18Operator , 19Int , 20Hex , 21Fixed , 22Double , 23Char , 24String , 25MultiComment , 26SingleComment 27}; 28 29enum class LexDerivative 30{ 31None , 32Line , 33File 34}; 35 36inline bool IsLetter (char ch ) 37{ 38return ((ch >='a' && ch <='z' )|| (ch >='A' && ch <='Z' )|| ch == '_' ); 39} 40 41inline bool IsDigit (char ch ) 42{ 43return ch >='0' && ch <='9' ; 44} 45 46inline bool IsPunctuation (char ch ) 47{ 48return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%' || ch == '!' || 49ch == '^' || ch == '&' || ch == '(' || ch == ')' || ch == '=' || ch == '{' || 50ch == '}' || ch == '[' || ch == ']' || ch == '|' || ch == ';' || ch == ',' || 51ch == '.' || ch == '<' || ch == '>' || ch == '~' || ch == '@' || ch == ':' || 52ch == '?' || ch == '#' ; 53} 54 55inline bool IsWhiteSpace (char ch ) 56{ 57return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' ); 58} 59 60void ParseOperators ( 61const String & str , 62List < Token >& tokens , 63TokenFlags & tokenFlags , 64int line , 65int col , 66int startPos , 67String fileName ) 68{ 69Index pos = 0 ; 70while (pos < str .getLength ()) 71 { 72wchar_t curChar = str [pos ]; 73wchar_t nextChar = (pos < str .getLength ()- 1 ) ?str [pos + 1 ] :'\0' ; 74wchar_t nextNextChar = (pos < str .getLength ()- 2 ) ?str [pos + 2 ] :'\0' ; 75auto InsertToken = [& ](TokenType type ,const String & ct ) 76 { 77tokens .add ( 78Token (type ,ct ,line ,int (col + pos ),int (pos + startPos ),fileName ,tokenFlags )); 79tokenFlags = 0 ; 80 }; 81switch (curChar ) 82 { 83case '+' : 84if (nextChar == '+' ) 85 { 86InsertToken (TokenType ::OpInc ,"++" ); 87pos += 2 ; 88 } 89else if (nextChar == '=' ) 90 { 91InsertToken (TokenType ::OpAddAssign ,"+=" ); 92pos += 2 ; 93 } 94else 95 { 96InsertToken (TokenType ::OpAdd ,"+" ); 97pos ++ ; 98 } 99break ; 100case '-' : 101if (nextChar == '-' ) 102 { 103InsertToken (TokenType ::OpDec ,"--" ); 104pos += 2 ; 105 } 106else if (nextChar == '=' ) 107 { 108InsertToken (TokenType ::OpSubAssign ,"-=" ); 109pos += 2 ; 110 } 111else if (nextChar == '>' ) 112 { 113InsertToken (TokenType ::RightArrow ,"->" ); 114pos += 2 ; 115 } 116else 117 { 118InsertToken (TokenType ::OpSub ,"-" ); 119pos ++ ; 120 } 121break ; 122case '*' : 123if (nextChar == '=' ) 124 { 125InsertToken (TokenType ::OpMulAssign ,"*=" ); 126pos += 2 ; 127 } 128else 129 { 130InsertToken (TokenType ::OpMul ,"*" ); 131pos ++ ; 132 } 133break ; 134case '/' : 135if (nextChar == '=' ) 136 { 137InsertToken (TokenType ::OpDivAssign ,"/=" ); 138pos += 2 ; 139 } 140else 141 { 142InsertToken (TokenType ::OpDiv ,"/" ); 143pos ++ ; 144 } 145break ; 146case '%' : 147if (nextChar == '=' ) 148 { 149InsertToken (TokenType ::OpModAssign ,"%=" ); 150pos += 2 ; 151 } 152else 153 { 154InsertToken (TokenType ::OpMod ,"%" ); 155pos ++ ; 156 } 157break ; 158case '|' : 159if (nextChar == '|' ) 160 { 161InsertToken (TokenType ::OpOr ,"||" ); 162pos += 2 ; 163 } 164else if (nextChar == '=' ) 165 { 166InsertToken (TokenType ::OpOrAssign ,"|=" ); 167pos += 2 ; 168 } 169else 170 { 171InsertToken (TokenType ::OpBitOr ,"|" ); 172pos ++ ; 173 } 174break ; 175case '&' : 176if (nextChar == '&' ) 177 { 178InsertToken (TokenType ::OpAnd ,"&&" ); 179pos += 2 ; 180 } 181else if (nextChar == '=' ) 182 { 183InsertToken (TokenType ::OpAndAssign ,"&=" ); 184pos += 2 ; 185 } 186else 187 { 188InsertToken (TokenType ::OpBitAnd ,"&" ); 189pos ++ ; 190 } 191break ; 192case '^' : 193if (nextChar == '=' ) 194 { 195InsertToken (TokenType ::OpXorAssign ,"^=" ); 196pos += 2 ; 197 } 198else 199 { 200InsertToken (TokenType ::OpBitXor ,"^" ); 201pos ++ ; 202 } 203break ; 204case '>' : 205if (nextChar == '>' ) 206 { 207if (nextNextChar == '=' ) 208 { 209InsertToken (TokenType ::OpShrAssign ,">>=" ); 210pos += 3 ; 211 } 212else 213 { 214InsertToken (TokenType ::OpRsh ,">>" ); 215pos += 2 ; 216 } 217 } 218else if (nextChar == '=' ) 219 { 220InsertToken (TokenType ::OpGeq ,">=" ); 221pos += 2 ; 222 } 223else 224 { 225InsertToken (TokenType ::OpGreater ,">" ); 226pos ++ ; 227 } 228break ; 229case '<' : 230if (nextChar == '<' ) 231 { 232if (nextNextChar == '=' ) 233 { 234InsertToken (TokenType ::OpShlAssign ,"<<=" ); 235pos += 3 ; 236 } 237else 238 { 239InsertToken (TokenType ::OpLsh ,"<<" ); 240pos += 2 ; 241 } 242 } 243else if (nextChar == '=' ) 244 { 245InsertToken (TokenType ::OpLeq ,"<=" ); 246pos += 2 ; 247 } 248else 249 { 250InsertToken (TokenType ::OpLess ,"<" ); 251pos ++ ; 252 } 253break ; 254case '=' : 255if (nextChar == '=' ) 256 { 257InsertToken (TokenType ::OpEql ,"==" ); 258pos += 2 ; 259 } 260else 261 { 262InsertToken (TokenType ::OpAssign ,"=" ); 263pos ++ ; 264 } 265break ; 266case '!' : 267if (nextChar == '=' ) 268 { 269InsertToken (TokenType ::OpNeq ,"!=" ); 270pos += 2 ; 271 } 272else 273 { 274InsertToken (TokenType ::OpNot ,"!" ); 275pos ++ ; 276 } 277break ; 278case '?' : 279InsertToken (TokenType ::QuestionMark ,"?" ); 280pos ++ ; 281break ; 282case '@' : 283InsertToken (TokenType ::At ,"@" ); 284pos ++ ; 285break ; 286case '#' : 287if (nextChar == '#' ) 288 { 289InsertToken (TokenType ::PoundPound ,"##" ); 290pos += 2 ; 291 } 292else 293 { 294InsertToken (TokenType ::Pound ,"#" ); 295pos ++ ; 296 } 297pos ++ ; 298break ; 299case ':' : 300InsertToken (TokenType ::Colon ,":" ); 301pos ++ ; 302break ; 303case '~' : 304InsertToken (TokenType ::OpBitNot ,"~" ); 305pos ++ ; 306break ; 307case ';' : 308InsertToken (TokenType ::Semicolon ,";" ); 309pos ++ ; 310break ; 311case ',' : 312InsertToken (TokenType ::Comma ,"," ); 313pos ++ ; 314break ; 315case '.' : 316InsertToken (TokenType ::Dot ,"." ); 317pos ++ ; 318break ; 319case '{' : 320InsertToken (TokenType ::LBrace ,"{" ); 321pos ++ ; 322break ; 323case '}' : 324InsertToken (TokenType ::RBrace ,"}" ); 325pos ++ ; 326break ; 327case '[' : 328InsertToken (TokenType ::LBracket ,"[" ); 329pos ++ ; 330break ; 331case ']' : 332InsertToken (TokenType ::RBracket ,"]" ); 333pos ++ ; 334break ; 335case '(' : 336InsertToken (TokenType ::LParent ,"(" ); 337pos ++ ; 338break ; 339case ')' : 340InsertToken (TokenType ::RParent ,")" ); 341pos ++ ; 342break ; 343 } 344 } 345} 346 347List < Token > TokenizeText (const String & fileName ,const String & text ) 348{ 349Index lastPos = 0 ,pos = 0 ; 350int line = 1 ,col = 0 ; 351String file = fileName ; 352State state = State ::Start ; 353StringBuilder tokenBuilder ; 354int tokenLine ,tokenCol ; 355List < Token > tokenList ; 356LexDerivative derivative = LexDerivative ::None ; 357TokenFlags tokenFlags = TokenFlag ::AtStartOfLine ; 358auto InsertToken = [& ](TokenType type ) 359 { 360derivative = LexDerivative ::None ; 361tokenList .add ( 362Token (type ,tokenBuilder .toString (),tokenLine ,tokenCol ,int (pos ),file ,tokenFlags )); 363tokenFlags = 0 ; 364tokenBuilder .clear (); 365 }; 366auto ProcessTransferChar = [& ](char nextChar ) 367 { 368switch (nextChar ) 369 { 370case '\\' : 371case '\"' : 372case '\'' : 373tokenBuilder .append (nextChar ); 374break ; 375case 't' : 376tokenBuilder .append ('\t' ); 377break ; 378case 's' : 379tokenBuilder .append (' ' ); 380break ; 381case 'n' : 382tokenBuilder .append ('\n' ); 383break ; 384case 'r' : 385tokenBuilder .append ('\r' ); 386break ; 387case 'b' : 388tokenBuilder .append ('\b' ); 389break ; 390 } 391 }; 392while (pos <=text .getLength ()) 393 { 394char curChar = (pos < text .getLength () ?text [pos ] :' ' ); 395char nextChar = (pos < text .getLength ()- 1 ) ?text [pos + 1 ] :'\0' ; 396if (lastPos != pos ) 397 { 398if (curChar == '\n' ) 399 { 400line ++ ; 401col = 0 ; 402 } 403else 404col ++ ; 405lastPos = pos ; 406 } 407 408switch (state ) 409 { 410case State ::Start : 411if (IsLetter (curChar )) 412 { 413state = State ::Identifier ; 414tokenLine = line ; 415tokenCol = col ; 416 } 417else if (IsDigit (curChar )) 418 { 419state = State ::Int ; 420tokenLine = line ; 421tokenCol = col ; 422 } 423else if (curChar == '\'' ) 424 { 425state = State ::Char ; 426pos ++ ; 427tokenLine = line ; 428tokenCol = col ; 429 } 430else if (curChar == '"' ) 431 { 432state = State ::String ; 433pos ++ ; 434tokenLine = line ; 435tokenCol = col ; 436 } 437else if (curChar == '\r' || curChar == '\n' ) 438 { 439tokenFlags |=TokenFlag ::AtStartOfLine |TokenFlag ::AfterWhitespace ; 440pos ++ ; 441 } 442else if ( 443curChar == ' ' || curChar == '\t' || curChar == '\xC2' || 444curChar == '\xA0' )// -62/-96:non-break space 445 { 446tokenFlags |=TokenFlag ::AfterWhitespace ; 447pos ++ ; 448 } 449else if (curChar == '/' && nextChar == '/' ) 450 { 451state = State ::SingleComment ; 452pos += 2 ; 453 } 454else if (curChar == '/' && nextChar == '*' ) 455 { 456pos += 2 ; 457state = State ::MultiComment ; 458 } 459else if (curChar == '.' && IsDigit (nextChar )) 460 { 461tokenBuilder .append ("0." ); 462state = State ::Fixed ; 463pos ++ ; 464 } 465else if (IsPunctuation (curChar )) 466 { 467state = State ::Operator ; 468tokenLine = line ; 469tokenCol = col ; 470 } 471else 472 { 473pos ++ ; 474 } 475break ; 476case State ::Identifier : 477if (IsLetter (curChar )|| IsDigit (curChar )) 478 { 479tokenBuilder .append (curChar ); 480pos ++ ; 481 } 482else 483 { 484auto tokenStr = tokenBuilder .toString (); 485#if 0 486if (tokenStr == "#line_reset#" ) 487 { 488line = 0 ; 489col = 0 ; 490tokenBuilder .clear (); 491 } 492else if (tokenStr == "#line" ) 493 { 494derivative = LexDerivative ::Line ; 495tokenBuilder .clear (); 496 } 497else if (tokenStr == "#file" ) 498 { 499derivative = LexDerivative ::File ; 500tokenBuilder .clear (); 501line = 0 ; 502col = 0 ; 503 } 504else 505#endif 506InsertToken (TokenType ::Identifier ); 507state = State ::Start ; 508 } 509break ; 510case State ::Operator : 511if (IsPunctuation (curChar )&& 512 !((curChar == '/' && nextChar == '/' )|| (curChar == '/' && nextChar == '*' ))) 513 { 514tokenBuilder .append (curChar ); 515pos ++ ; 516 } 517else 518 { 519// do token analyze 520ParseOperators ( 521tokenBuilder .toString (), 522tokenList , 523tokenFlags , 524tokenLine , 525tokenCol , 526 (int )(pos - tokenBuilder .getLength ()), 527file ); 528tokenBuilder .clear (); 529state = State ::Start ; 530 } 531break ; 532case State ::Int : 533if (IsDigit (curChar )) 534 { 535tokenBuilder .append (curChar ); 536pos ++ ; 537 } 538else if (curChar == '.' ) 539 { 540state = State ::Fixed ; 541tokenBuilder .append (curChar ); 542pos ++ ; 543 } 544else if (curChar == 'e' || curChar == 'E' ) 545 { 546state = State ::Double ; 547tokenBuilder .append (curChar ); 548if (nextChar == '-' || nextChar == '+' ) 549 { 550tokenBuilder .append (nextChar ); 551pos ++ ; 552 } 553pos ++ ; 554 } 555else if (curChar == 'x' ) 556 { 557state = State ::Hex ; 558tokenBuilder .append (curChar ); 559pos ++ ; 560 } 561else if (curChar == 'u' ) 562 { 563pos ++ ; 564tokenBuilder .append (curChar ); 565InsertToken (TokenType ::IntLiteral ); 566state = State ::Start ; 567 } 568else 569 { 570if (derivative == LexDerivative ::Line ) 571 { 572derivative = LexDerivative ::None ; 573line = stringToInt (tokenBuilder .toString ())- 1 ; 574col = 0 ; 575tokenBuilder .clear (); 576 } 577else 578 { 579InsertToken (TokenType ::IntLiteral ); 580 } 581state = State ::Start ; 582 } 583break ; 584case State ::Hex : 585if (IsDigit (curChar )|| (curChar >='a' && curChar <='f' )|| 586 (curChar >='A' && curChar <='F' )) 587 { 588tokenBuilder .append (curChar ); 589pos ++ ; 590 } 591else 592 { 593InsertToken (TokenType ::IntLiteral ); 594state = State ::Start ; 595 } 596break ; 597case State ::Fixed : 598if (IsDigit (curChar )) 599 { 600tokenBuilder .append (curChar ); 601pos ++ ; 602 } 603else if (curChar == 'e' || curChar == 'E' ) 604 { 605state = State ::Double ; 606tokenBuilder .append (curChar ); 607if (nextChar == '-' || nextChar == '+' ) 608 { 609tokenBuilder .append (nextChar ); 610pos ++ ; 611 } 612pos ++ ; 613 } 614else 615 { 616if (curChar == 'f' ) 617pos ++ ; 618InsertToken (TokenType ::DoubleLiteral ); 619state = State ::Start ; 620 } 621break ; 622case State ::Double : 623if (IsDigit (curChar )) 624 { 625tokenBuilder .append (curChar ); 626pos ++ ; 627 } 628else 629 { 630if (curChar == 'f' ) 631pos ++ ; 632InsertToken (TokenType ::DoubleLiteral ); 633state = State ::Start ; 634 } 635break ; 636case State ::String : 637if (curChar != '"' ) 638 { 639if (curChar == '\\' ) 640 { 641ProcessTransferChar (nextChar ); 642pos ++ ; 643 } 644else 645tokenBuilder .append (curChar ); 646 } 647else 648 { 649if (derivative == LexDerivative ::File ) 650 { 651derivative = LexDerivative ::None ; 652file = tokenBuilder .toString (); 653tokenBuilder .clear (); 654 } 655else 656 { 657InsertToken (TokenType ::StringLiteral ); 658 } 659state = State ::Start ; 660 } 661pos ++ ; 662break ; 663case State ::Char : 664if (curChar != '\'' ) 665 { 666if (curChar == '\\' ) 667 { 668ProcessTransferChar (nextChar ); 669pos ++ ; 670 } 671else 672tokenBuilder .append (curChar ); 673 } 674else 675 { 676InsertToken (TokenType ::CharLiteral ); 677state = State ::Start ; 678 } 679pos ++ ; 680break ; 681case State ::SingleComment : 682if (curChar == '\n' ) 683 { 684state = State ::Start ; 685tokenFlags |=TokenFlag ::AtStartOfLine |TokenFlag ::AfterWhitespace ; 686 } 687pos ++ ; 688break ; 689case State ::MultiComment : 690if (curChar == '*' && nextChar == '/' ) 691 { 692state = State ::Start ; 693tokenFlags |=TokenFlag ::AfterWhitespace ; 694pos += 2 ; 695 } 696else 697pos ++ ; 698break ; 699 } 700 } 701return tokenList ; 702} 703List < Token > TokenizeText (const String & text ) 704{ 705return TokenizeText ("" ,text ); 706} 707 708TokenReader ::TokenReader (String text ) 709{ 710this -> tokens = TokenizeText ("" ,text ); 711tokenPtr = 0 ; 712} 713 714}// namespace Misc 715}// namespace Slang