yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
b118451e3
master
1// slang-json-lexer.cpp 2#include "slang-json-lexer.h" 3 4#include "../core/slang-char-util.h" 5#include "slang-json-diagnostics.h" 6 7/* 8https://www.json.org/json-en.html 9*/ 10 11namespace Slang 12{ 13 14/* static */ UnownedStringSlice JSONLexer ::calcLexemeLocation (const UnownedStringSlice & text ) 15{ 16SourceManager sourceManager ; 17sourceManager .initialize (nullptr ,nullptr ); 18DiagnosticSink sink ; 19sink .init (& sourceManager ,nullptr ); 20 21String contents (text ); 22SourceFile * sourceFile = 23sourceManager .createSourceFileWithString (PathInfo ::makeUnknown (),contents ); 24SourceView * sourceView = sourceManager .createSourceView (sourceFile ,nullptr ,SourceLoc ()); 25 26JSONLexer lexer ; 27 28lexer .init (sourceView ,& sink ); 29 30if (lexer .peekType ()!= JSONTokenType ::Invalid ) 31 { 32// Get the start offset 33auto offset = sourceView -> getRange ().getOffset (lexer .peekLoc ()); 34 35return text .subString (offset ,lexer .peekLexeme ().getLength ()); 36 } 37else 38 { 39return text .head (0 ); 40 } 41} 42 43SlangResult JSONLexer ::init (SourceView * sourceView ,DiagnosticSink * sink ) 44{ 45m_sourceView = sourceView ; 46m_sink = sink ; 47 48SourceFile * sourceFile = sourceView -> getSourceFile (); 49 50// Note that the content must be null terminated (because of other requirements) 51SLANG_ASSERT (sourceFile && sourceFile -> hasContent ()); 52 53m_contentStart = sourceFile -> getContent ().begin (); 54 55m_startLoc = sourceView -> getRange ().begin ; 56 57m_lexemeStart = m_contentStart ; 58m_cursor = m_lexemeStart ; 59 60// We need to prime the first token 61advance (); 62 63return SLANG_OK ; 64} 65 66SLANG_FORCE_INLINE static const char * _handleEndOfLine (char c ,const char * cursor ) 67{ 68SLANG_ASSERT (c == '\n' || c == '\r' ); 69const char d = * cursor ; 70return cursor + Index ((c ^d )== ('\n' ^'\r' )); 71} 72 73JSONTokenType JSONLexer ::_setInvalidToken () 74{ 75return _setToken (JSONTokenType ::Invalid ,m_lexemeStart ); 76} 77 78SlangResult JSONLexer ::expect (JSONTokenType type ) 79{ 80if (type != peekType ()) 81 { 82m_sink -> diagnose ( 83m_token .loc , 84JSONDiagnostics ::unexpectedTokenExpectedTokenType , 85getJSONTokenAsText (peekType ()), 86getJSONTokenAsText (type )); 87return SLANG_FAIL ; 88 } 89 90advance (); 91return SLANG_OK ; 92} 93 94SlangResult JSONLexer ::expect (JSONTokenType type ,JSONToken & out ) 95{ 96if (type != peekType ()) 97 { 98m_sink -> diagnose ( 99m_token .loc , 100JSONDiagnostics ::unexpectedTokenExpectedTokenType , 101getJSONTokenAsText (peekType ()), 102getJSONTokenAsText (type )); 103return SLANG_FAIL ; 104 } 105 106out = m_token ; 107advance (); 108return SLANG_OK ; 109} 110 111bool JSONLexer ::advanceIf (JSONTokenType type ) 112{ 113if (type == peekType ()) 114 { 115advance (); 116return true; 117 } 118return false; 119} 120 121bool JSONLexer ::advanceIf (JSONTokenType type ,JSONToken & out ) 122{ 123if (type == peekType ()) 124 { 125out = m_token ; 126advance (); 127return true; 128 } 129return false; 130} 131 132UnownedStringSlice JSONLexer ::getLexeme (const JSONToken & tok )const 133{ 134auto offset = m_sourceView -> getRange ().getOffset (tok .loc ); 135return UnownedStringSlice (m_sourceView -> getContent ().begin ()+ offset ,tok .length ); 136} 137 138JSONTokenType JSONLexer ::advance () 139{ 140const char * cursor = m_cursor ; 141 142while (true) 143 { 144m_lexemeStart = cursor ; 145 146const char c = * cursor ++ ; 147 148switch (c ) 149 { 150case 0 : 151return _setToken (JSONTokenType ::EndOfFile ,cursor - 1 ); 152case '"' : 153 { 154cursor = _lexString (cursor ); 155if (cursor == nullptr ) 156 { 157return _setInvalidToken (); 158 } 159return _setToken (JSONTokenType ::StringLiteral ,cursor ); 160 } 161case '/' : 162 { 163// We allow comments 164const char nextChar = * m_cursor ; 165 166if (nextChar == '/' ) 167 { 168// Line comment 169cursor = _lexLineComment (cursor ); 170 } 171else if (nextChar == '*' ) 172 { 173cursor = _lexBlockComment (cursor ); 174// Can fail... 175if (cursor == nullptr ) 176 { 177return _setInvalidToken (); 178 } 179 } 180else 181 { 182return _setInvalidToken (); 183 } 184break ; 185 } 186case ' ' : 187case '\t' : 188case '\n' : 189case '\r' : 190 { 191cursor = _lexWhitespace (cursor ); 192break ; 193 } 194case ':' : 195return _setToken (JSONTokenType ::Colon ,cursor ); 196case ',' : 197return _setToken (JSONTokenType ::Comma ,cursor ); 198case '[' : 199return _setToken (JSONTokenType ::LBracket ,cursor ); 200case ']' : 201return _setToken (JSONTokenType ::RBracket ,cursor ); 202case '{' : 203return _setToken (JSONTokenType ::LBrace ,cursor ); 204case '}' : 205return _setToken (JSONTokenType ::RBrace ,cursor ); 206 207case '-' : 208case '0' : 209case '1' : 210case '2' : 211case '3' : 212case '4' : 213case '5' : 214case '6' : 215case '7' : 216case '8' : 217case '9' : 218 { 219LexResult res = _lexNumber (cursor - 1 ); 220if (res .cursor == nullptr ) 221 { 222return _setToken (JSONTokenType ::Invalid ,m_lexemeStart ); 223 } 224return _setToken (res .type ,res .cursor ); 225 } 226case 't' : 227 { 228if (cursor [0 ]== 'r' && cursor [1 ]== 'u' && cursor [2 ]== 'e' ) 229 { 230return _setToken (JSONTokenType ::True ,cursor + 3 ); 231 } 232m_sink -> diagnose (_getLoc (m_lexemeStart ),JSONDiagnostics ::expectingValueName ); 233return _setInvalidToken (); 234 } 235case 'f' : 236 { 237if (cursor [0 ]== 'a' && cursor [1 ]== 'l' && cursor [2 ]== 's' && cursor [3 ]== 'e' ) 238 { 239return _setToken (JSONTokenType ::False ,cursor + 4 ); 240 } 241m_sink -> diagnose (_getLoc (m_lexemeStart ),JSONDiagnostics ::expectingValueName ); 242return _setInvalidToken (); 243 } 244case 'n' : 245 { 246if (cursor [0 ]== 'u' && cursor [1 ]== 'l' && cursor [2 ]== 'l' ) 247 { 248return _setToken (JSONTokenType ::Null ,cursor + 3 ); 249 } 250m_sink -> diagnose (_getLoc (m_lexemeStart ),JSONDiagnostics ::expectingValueName ); 251return _setInvalidToken (); 252 } 253default : 254 { 255StringBuilder buf ; 256if (c <=' ' || c >=0x7e ) 257 { 258static const char s_hex []= "0123456789abcdef" ; 259 260char hexBuf [5 ]= "0x" ; 261 262uint32_t value = c ; 263hexBuf [2 ]= s_hex [((value >>4 )& 0xf )]; 264hexBuf [3 ]= s_hex [(value & 0xf )]; 265hexBuf [4 ]= 0 ; 266 267buf <<hexBuf ; 268 } 269else 270 { 271buf <<c ; 272 } 273 274m_sink -> diagnose (_getLoc (m_lexemeStart ),JSONDiagnostics ::unexpectedCharacter ); 275return _setInvalidToken (); 276 } 277 } 278 } 279} 280 281JSONLexer ::LexResult JSONLexer ::_lexNumber (const char * cursor ) 282{ 283JSONTokenType tokenType = JSONTokenType ::IntegerLiteral ; 284 285if (* cursor == '-' ) 286 { 287cursor ++ ; 288 } 289 290if (* cursor == '0' ) 291 { 292// Can only be followed by . exponent, or nothing 293cursor ++ ; 294 } 295else if (* cursor >='1' && * cursor <='9' ) 296 { 297cursor ++ ; 298while (CharUtil ::isDigit (* cursor )) 299 { 300cursor ++ ; 301 } 302 } 303 304// Theres a fraction 305if (* cursor == '.' ) 306 { 307tokenType = JSONTokenType ::FloatLiteral ; 308// Skip the dot 309cursor ++ ; 310// Must have at least one digit 311if (!CharUtil ::isDigit (* cursor )) 312 { 313m_sink -> diagnose (_getLoc (cursor ),JSONDiagnostics ::expectingADigit ); 314return LexResult {JSONTokenType ::Invalid ,nullptr }; 315 } 316// Skip the digit 317cursor ++ ; 318// Skip any more digits 319while (CharUtil ::isDigit (* cursor )) 320cursor ++ ; 321 } 322 323// Theres an exponent 324if (* cursor == 'e' || * cursor == 'E' ) 325 { 326tokenType = JSONTokenType ::FloatLiteral ; 327 328// Has an exponent 329cursor ++ ; 330 331// Skip +/- if has one 332if (* cursor == '+' || * cursor == '-' ) 333 { 334cursor ++ ; 335 } 336 337// Must have one digit 338if (!CharUtil ::isDigit (* cursor )) 339 { 340m_sink -> diagnose (_getLoc (cursor ),JSONDiagnostics ::expectingADigit ); 341return LexResult {JSONTokenType ::Invalid ,nullptr }; 342 } 343 344// Skip the digit 345cursor ++ ; 346// Skip any more digits 347while (CharUtil ::isDigit (* cursor )) 348cursor ++ ; 349 } 350 351return LexResult {tokenType ,cursor }; 352} 353 354const char * JSONLexer ::_lexString (const char * cursor ) 355{ 356// We've skipped the first " 357while (true) 358 { 359const char c = * cursor ++ ; 360 361switch (c ) 362 { 363case 0 : 364 { 365m_sink -> diagnose (_getLoc (cursor - 1 ),JSONDiagnostics ::endOfFileInLiteral ); 366return nullptr ; 367 } 368case '"' : 369 { 370return cursor ; 371 } 372case '\\' : 373 { 374const char nextC = * cursor ; 375switch (nextC ) 376 { 377case '"' : 378case '\\' : 379case '/' : 380case 'b' : 381case 'f' : 382case 'n' : 383case 'r' : 384case 't' : 385 { 386++ cursor ; 387break ; 388 } 389case 'u' : 390 { 391cursor ++ ; 392for (Index i = 0 ;i < 4 ;++ i ) 393 { 394if (!CharUtil ::isHexDigit (cursor [i ])) 395 { 396m_sink -> diagnose ( 397_getLoc (cursor ), 398JSONDiagnostics ::expectingAHexDigit ); 399return nullptr ; 400 } 401 } 402cursor += 4 ; 403break ; 404 } 405 } 406 } 407// Somewhat surprisingly it appears it's valid to have \r\n inside of quotes. 408default : 409break ; 410 } 411 } 412} 413 414const char * JSONLexer ::_lexLineComment (const char * cursor ) 415{ 416for (;;) 417 { 418const char c = * cursor ++ ; 419 420switch (c ) 421 { 422case '\n' : 423case '\r' : 424 { 425// We need to skip to the next line 426return _handleEndOfLine (c ,cursor ); 427 } 428case 0 : 429 { 430return cursor - 1 ; 431 } 432 } 433 } 434} 435 436const char * JSONLexer ::_lexBlockComment (const char * cursor ) 437{ 438for (;;) 439 { 440const char c = * cursor ++ ; 441switch (c ) 442 { 443case 0 : 444 { 445m_sink -> diagnose (_getLoc (cursor ),JSONDiagnostics ::endOfFileInComment ); 446return nullptr ; 447 } 448case '*' : 449 { 450if (* cursor == '/' ) 451 { 452return cursor + 1 ; 453 } 454break ; 455 } 456default : 457break ; 458 } 459 } 460} 461 462const char * JSONLexer ::_lexWhitespace (const char * cursor ) 463{ 464while (true) 465 { 466const char c = * cursor ; 467 468// Might want to use CharUtil::isWhitespace... 469 470switch (c ) 471 { 472case ' ' : 473case '\n' : 474case '\r' : 475case '\t' : 476 { 477cursor ++ ; 478break ; 479 } 480default : 481 { 482// Hit non white space 483return cursor ; 484 } 485 } 486 } 487} 488 489UnownedStringSlice getJSONTokenAsText (JSONTokenType type ) 490{ 491switch (type ) 492 { 493case JSONTokenType ::Invalid : 494return UnownedStringSlice ::fromLiteral ("invalid" ); 495case JSONTokenType ::IntegerLiteral : 496return UnownedStringSlice ::fromLiteral ("integer literal" ); 497case JSONTokenType ::FloatLiteral : 498return UnownedStringSlice ::fromLiteral ("float literal" ); 499case JSONTokenType ::StringLiteral : 500return UnownedStringSlice ::fromLiteral ("string literal" ); 501case JSONTokenType ::LBracket : 502return UnownedStringSlice ::fromLiteral ("[" ); 503case JSONTokenType ::RBracket : 504return UnownedStringSlice ::fromLiteral ("]" ); 505case JSONTokenType ::LBrace : 506return UnownedStringSlice ::fromLiteral ("{" ); 507case JSONTokenType ::RBrace : 508return UnownedStringSlice ::fromLiteral ("}" ); 509case JSONTokenType ::Comma : 510return UnownedStringSlice ::fromLiteral ("," ); 511case JSONTokenType ::Colon : 512return UnownedStringSlice ::fromLiteral (":" ); 513case JSONTokenType ::True : 514return UnownedStringSlice ::fromLiteral ("true" ); 515case JSONTokenType ::False : 516return UnownedStringSlice ::fromLiteral ("false" ); 517case JSONTokenType ::Null : 518return UnownedStringSlice ::fromLiteral ("null" ); 519case JSONTokenType ::EndOfFile : 520return UnownedStringSlice ::fromLiteral ("end of file" ); 521default : 522break ; 523 } 524SLANG_UNEXPECTED ("JSONTokenType not known" ); 525} 526 527}// namespace Slang