1 //===- Parser.h - Toy Language Parser -------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements the parser for the Toy language. It processes the Token 10 // provided by the Lexer and returns an AST. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef TOY_PARSER_H 15 #define TOY_PARSER_H 16 17 #include "toy/AST.h" 18 #include "toy/Lexer.h" 19 20 #include "llvm/ADT/STLExtras.h" 21 #include "llvm/ADT/StringExtras.h" 22 #include "llvm/Support/raw_ostream.h" 23 24 #include <map> 25 #include <utility> 26 #include <vector> 27 #include <optional> 28 29 namespace toy { 30 31 /// This is a simple recursive parser for the Toy language. It produces a well 32 /// formed AST from a stream of Token supplied by the Lexer. No semantic checks 33 /// or symbol resolution is performed. For example, variables are referenced by 34 /// string and the code could reference an undeclared variable and the parsing 35 /// succeeds. 36 class Parser { 37 public: 38 /// Create a Parser for the supplied lexer. Parser(Lexer & lexer)39 Parser(Lexer &lexer) : lexer(lexer) {} 40 41 /// Parse a full Module. A module is a list of function definitions. parseModule()42 std::unique_ptr<ModuleAST> parseModule() { 43 lexer.getNextToken(); // prime the lexer 44 45 // Parse functions and structs one at a time and accumulate in this vector. 46 std::vector<std::unique_ptr<RecordAST>> records; 47 while (true) { 48 std::unique_ptr<RecordAST> record; 49 switch (lexer.getCurToken()) { 50 case tok_eof: 51 break; 52 case tok_def: 53 record = parseDefinition(); 54 break; 55 case tok_struct: 56 record = parseStruct(); 57 break; 58 default: 59 return parseError<ModuleAST>("'def' or 'struct'", 60 "when parsing top level module records"); 61 } 62 if (!record) 63 break; 64 records.push_back(std::move(record)); 65 } 66 67 // If we didn't reach EOF, there was an error during parsing 68 if (lexer.getCurToken() != tok_eof) 69 return parseError<ModuleAST>("nothing", "at end of module"); 70 71 return std::make_unique<ModuleAST>(std::move(records)); 72 } 73 74 private: 75 Lexer &lexer; 76 77 /// Parse a return statement. 78 /// return :== return ; | return expr ; parseReturn()79 std::unique_ptr<ReturnExprAST> parseReturn() { 80 auto loc = lexer.getLastLocation(); 81 lexer.consume(tok_return); 82 83 // return takes an optional argument 84 std::optional<std::unique_ptr<ExprAST>> expr; 85 if (lexer.getCurToken() != ';') { 86 expr = parseExpression(); 87 if (!expr) 88 return nullptr; 89 } 90 return std::make_unique<ReturnExprAST>(std::move(loc), std::move(expr)); 91 } 92 93 /// Parse a literal number. 94 /// numberexpr ::= number parseNumberExpr()95 std::unique_ptr<ExprAST> parseNumberExpr() { 96 auto loc = lexer.getLastLocation(); 97 auto result = 98 std::make_unique<NumberExprAST>(std::move(loc), lexer.getValue()); 99 lexer.consume(tok_number); 100 return std::move(result); 101 } 102 103 /// Parse a literal array expression. 104 /// tensorLiteral ::= [ literalList ] | number 105 /// literalList ::= tensorLiteral | tensorLiteral, literalList parseTensorLiteralExpr()106 std::unique_ptr<ExprAST> parseTensorLiteralExpr() { 107 auto loc = lexer.getLastLocation(); 108 lexer.consume(Token('[')); 109 110 // Hold the list of values at this nesting level. 111 std::vector<std::unique_ptr<ExprAST>> values; 112 // Hold the dimensions for all the nesting inside this level. 113 std::vector<int64_t> dims; 114 do { 115 // We can have either another nested array or a number literal. 116 if (lexer.getCurToken() == '[') { 117 values.push_back(parseTensorLiteralExpr()); 118 if (!values.back()) 119 return nullptr; // parse error in the nested array. 120 } else { 121 if (lexer.getCurToken() != tok_number) 122 return parseError<ExprAST>("<num> or [", "in literal expression"); 123 values.push_back(parseNumberExpr()); 124 } 125 126 // End of this list on ']' 127 if (lexer.getCurToken() == ']') 128 break; 129 130 // Elements are separated by a comma. 131 if (lexer.getCurToken() != ',') 132 return parseError<ExprAST>("] or ,", "in literal expression"); 133 134 lexer.getNextToken(); // eat , 135 } while (true); 136 if (values.empty()) 137 return parseError<ExprAST>("<something>", "to fill literal expression"); 138 lexer.getNextToken(); // eat ] 139 140 /// Fill in the dimensions now. First the current nesting level: 141 dims.push_back(values.size()); 142 143 /// If there is any nested array, process all of them and ensure that 144 /// dimensions are uniform. 145 if (llvm::any_of(values, [](std::unique_ptr<ExprAST> &expr) { 146 return llvm::isa<LiteralExprAST>(expr.get()); 147 })) { 148 auto *firstLiteral = llvm::dyn_cast<LiteralExprAST>(values.front().get()); 149 if (!firstLiteral) 150 return parseError<ExprAST>("uniform well-nested dimensions", 151 "inside literal expression"); 152 153 // Append the nested dimensions to the current level 154 auto firstDims = firstLiteral->getDims(); 155 dims.insert(dims.end(), firstDims.begin(), firstDims.end()); 156 157 // Sanity check that shape is uniform across all elements of the list. 158 for (auto &expr : values) { 159 auto *exprLiteral = llvm::cast<LiteralExprAST>(expr.get()); 160 if (!exprLiteral) 161 return parseError<ExprAST>("uniform well-nested dimensions", 162 "inside literal expression"); 163 if (exprLiteral->getDims() != firstDims) 164 return parseError<ExprAST>("uniform well-nested dimensions", 165 "inside literal expression"); 166 } 167 } 168 return std::make_unique<LiteralExprAST>(std::move(loc), std::move(values), 169 std::move(dims)); 170 } 171 172 /// Parse a literal struct expression. 173 /// structLiteral ::= { (structLiteral | tensorLiteral)+ } parseStructLiteralExpr()174 std::unique_ptr<ExprAST> parseStructLiteralExpr() { 175 auto loc = lexer.getLastLocation(); 176 lexer.consume(Token('{')); 177 178 // Hold the list of values. 179 std::vector<std::unique_ptr<ExprAST>> values; 180 do { 181 // We can have either another nested array or a number literal. 182 if (lexer.getCurToken() == '[') { 183 values.push_back(parseTensorLiteralExpr()); 184 if (!values.back()) 185 return nullptr; 186 } else if (lexer.getCurToken() == tok_number) { 187 values.push_back(parseNumberExpr()); 188 if (!values.back()) 189 return nullptr; 190 } else { 191 if (lexer.getCurToken() != '{') 192 return parseError<ExprAST>("{, [, or number", 193 "in struct literal expression"); 194 values.push_back(parseStructLiteralExpr()); 195 } 196 197 // End of this list on '}' 198 if (lexer.getCurToken() == '}') 199 break; 200 201 // Elements are separated by a comma. 202 if (lexer.getCurToken() != ',') 203 return parseError<ExprAST>("} or ,", "in struct literal expression"); 204 205 lexer.getNextToken(); // eat , 206 } while (true); 207 if (values.empty()) 208 return parseError<ExprAST>("<something>", 209 "to fill struct literal expression"); 210 lexer.getNextToken(); // eat } 211 212 return std::make_unique<StructLiteralExprAST>(std::move(loc), 213 std::move(values)); 214 } 215 216 /// parenexpr ::= '(' expression ')' parseParenExpr()217 std::unique_ptr<ExprAST> parseParenExpr() { 218 lexer.getNextToken(); // eat (. 219 auto v = parseExpression(); 220 if (!v) 221 return nullptr; 222 223 if (lexer.getCurToken() != ')') 224 return parseError<ExprAST>(")", "to close expression with parentheses"); 225 lexer.consume(Token(')')); 226 return v; 227 } 228 229 /// Parse a call expression. parseCallExpr(llvm::StringRef name,const Location & loc)230 std::unique_ptr<ExprAST> parseCallExpr(llvm::StringRef name, 231 const Location &loc) { 232 lexer.consume(Token('(')); 233 std::vector<std::unique_ptr<ExprAST>> args; 234 if (lexer.getCurToken() != ')') { 235 while (true) { 236 if (auto arg = parseExpression()) 237 args.push_back(std::move(arg)); 238 else 239 return nullptr; 240 241 if (lexer.getCurToken() == ')') 242 break; 243 244 if (lexer.getCurToken() != ',') 245 return parseError<ExprAST>(", or )", "in argument list"); 246 lexer.getNextToken(); 247 } 248 } 249 lexer.consume(Token(')')); 250 251 // It can be a builtin call to print 252 if (name == "print") { 253 if (args.size() != 1) 254 return parseError<ExprAST>("<single arg>", "as argument to print()"); 255 256 return std::make_unique<PrintExprAST>(loc, std::move(args[0])); 257 } 258 259 // Call to a user-defined function 260 return std::make_unique<CallExprAST>(loc, std::string(name), 261 std::move(args)); 262 } 263 264 /// identifierexpr 265 /// ::= identifier 266 /// ::= identifier '(' expression ')' parseIdentifierExpr()267 std::unique_ptr<ExprAST> parseIdentifierExpr() { 268 std::string name(lexer.getId()); 269 270 auto loc = lexer.getLastLocation(); 271 lexer.getNextToken(); // eat identifier. 272 273 if (lexer.getCurToken() != '(') // Simple variable ref. 274 return std::make_unique<VariableExprAST>(std::move(loc), name); 275 276 // This is a function call. 277 return parseCallExpr(name, loc); 278 } 279 280 /// primary 281 /// ::= identifierexpr 282 /// ::= numberexpr 283 /// ::= parenexpr 284 /// ::= tensorliteral parsePrimary()285 std::unique_ptr<ExprAST> parsePrimary() { 286 switch (lexer.getCurToken()) { 287 default: 288 llvm::errs() << "unknown token '" << lexer.getCurToken() 289 << "' when expecting an expression\n"; 290 return nullptr; 291 case tok_identifier: 292 return parseIdentifierExpr(); 293 case tok_number: 294 return parseNumberExpr(); 295 case '(': 296 return parseParenExpr(); 297 case '[': 298 return parseTensorLiteralExpr(); 299 case '{': 300 return parseStructLiteralExpr(); 301 case ';': 302 return nullptr; 303 case '}': 304 return nullptr; 305 } 306 } 307 308 /// Recursively parse the right hand side of a binary expression, the ExprPrec 309 /// argument indicates the precedence of the current binary operator. 310 /// 311 /// binoprhs ::= ('+' primary)* parseBinOpRHS(int exprPrec,std::unique_ptr<ExprAST> lhs)312 std::unique_ptr<ExprAST> parseBinOpRHS(int exprPrec, 313 std::unique_ptr<ExprAST> lhs) { 314 // If this is a binop, find its precedence. 315 while (true) { 316 int tokPrec = getTokPrecedence(); 317 318 // If this is a binop that binds at least as tightly as the current binop, 319 // consume it, otherwise we are done. 320 if (tokPrec < exprPrec) 321 return lhs; 322 323 // Okay, we know this is a binop. 324 int binOp = lexer.getCurToken(); 325 lexer.consume(Token(binOp)); 326 auto loc = lexer.getLastLocation(); 327 328 // Parse the primary expression after the binary operator. 329 auto rhs = parsePrimary(); 330 if (!rhs) 331 return parseError<ExprAST>("expression", "to complete binary operator"); 332 333 // If BinOp binds less tightly with rhs than the operator after rhs, let 334 // the pending operator take rhs as its lhs. 335 int nextPrec = getTokPrecedence(); 336 if (tokPrec < nextPrec) { 337 rhs = parseBinOpRHS(tokPrec + 1, std::move(rhs)); 338 if (!rhs) 339 return nullptr; 340 } 341 342 // Merge lhs/RHS. 343 lhs = std::make_unique<BinaryExprAST>(std::move(loc), binOp, 344 std::move(lhs), std::move(rhs)); 345 } 346 } 347 348 /// expression::= primary binop rhs parseExpression()349 std::unique_ptr<ExprAST> parseExpression() { 350 auto lhs = parsePrimary(); 351 if (!lhs) 352 return nullptr; 353 354 return parseBinOpRHS(0, std::move(lhs)); 355 } 356 357 /// type ::= < shape_list > 358 /// shape_list ::= num | num , shape_list parseType()359 std::unique_ptr<VarType> parseType() { 360 if (lexer.getCurToken() != '<') 361 return parseError<VarType>("<", "to begin type"); 362 lexer.getNextToken(); // eat < 363 364 auto type = std::make_unique<VarType>(); 365 366 while (lexer.getCurToken() == tok_number) { 367 type->shape.push_back(lexer.getValue()); 368 lexer.getNextToken(); 369 if (lexer.getCurToken() == ',') 370 lexer.getNextToken(); 371 } 372 373 if (lexer.getCurToken() != '>') 374 return parseError<VarType>(">", "to end type"); 375 lexer.getNextToken(); // eat > 376 return type; 377 } 378 379 /// Parse either a variable declaration or a call expression. parseDeclarationOrCallExpr()380 std::unique_ptr<ExprAST> parseDeclarationOrCallExpr() { 381 auto loc = lexer.getLastLocation(); 382 std::string id(lexer.getId()); 383 lexer.consume(tok_identifier); 384 385 // Check for a call expression. 386 if (lexer.getCurToken() == '(') 387 return parseCallExpr(id, loc); 388 389 // Otherwise, this is a variable declaration. 390 return parseTypedDeclaration(id, /*requiresInitializer=*/true, loc); 391 } 392 393 /// Parse a typed variable declaration. 394 std::unique_ptr<VarDeclExprAST> parseTypedDeclaration(llvm::StringRef typeName,bool requiresInitializer,const Location & loc)395 parseTypedDeclaration(llvm::StringRef typeName, bool requiresInitializer, 396 const Location &loc) { 397 // Parse the variable name. 398 if (lexer.getCurToken() != tok_identifier) 399 return parseError<VarDeclExprAST>("name", "in variable declaration"); 400 std::string id(lexer.getId()); 401 lexer.getNextToken(); // eat id 402 403 // Parse the initializer. 404 std::unique_ptr<ExprAST> expr; 405 if (requiresInitializer) { 406 if (lexer.getCurToken() != '=') 407 return parseError<VarDeclExprAST>("initializer", 408 "in variable declaration"); 409 lexer.consume(Token('=')); 410 expr = parseExpression(); 411 } 412 413 VarType type; 414 type.name = std::string(typeName); 415 return std::make_unique<VarDeclExprAST>(loc, std::move(id), std::move(type), 416 std::move(expr)); 417 } 418 419 /// Parse a variable declaration, for either a tensor value or a struct value, 420 /// with an optionally required initializer. 421 /// decl ::= var identifier [ type ] (= expr)? 422 /// decl ::= identifier identifier (= expr)? parseDeclaration(bool requiresInitializer)423 std::unique_ptr<VarDeclExprAST> parseDeclaration(bool requiresInitializer) { 424 // Check to see if this is a 'var' declaration. 425 if (lexer.getCurToken() == tok_var) 426 return parseVarDeclaration(requiresInitializer); 427 428 // Parse the type name. 429 if (lexer.getCurToken() != tok_identifier) 430 return parseError<VarDeclExprAST>("type name", "in variable declaration"); 431 auto loc = lexer.getLastLocation(); 432 std::string typeName(lexer.getId()); 433 lexer.getNextToken(); // eat id 434 435 // Parse the rest of the declaration. 436 return parseTypedDeclaration(typeName, requiresInitializer, loc); 437 } 438 439 /// Parse a variable declaration, it starts with a `var` keyword followed by 440 /// and identifier and an optional type (shape specification) before the 441 /// optionally required initializer. 442 /// decl ::= var identifier [ type ] (= expr)? 443 std::unique_ptr<VarDeclExprAST> parseVarDeclaration(bool requiresInitializer)444 parseVarDeclaration(bool requiresInitializer) { 445 if (lexer.getCurToken() != tok_var) 446 return parseError<VarDeclExprAST>("var", "to begin declaration"); 447 auto loc = lexer.getLastLocation(); 448 lexer.getNextToken(); // eat var 449 450 if (lexer.getCurToken() != tok_identifier) 451 return parseError<VarDeclExprAST>("identified", 452 "after 'var' declaration"); 453 std::string id(lexer.getId()); 454 lexer.getNextToken(); // eat id 455 456 std::unique_ptr<VarType> type; // Type is optional, it can be inferred 457 if (lexer.getCurToken() == '<') { 458 type = parseType(); 459 if (!type) 460 return nullptr; 461 } 462 if (!type) 463 type = std::make_unique<VarType>(); 464 465 std::unique_ptr<ExprAST> expr; 466 if (requiresInitializer) { 467 lexer.consume(Token('=')); 468 expr = parseExpression(); 469 } 470 return std::make_unique<VarDeclExprAST>(std::move(loc), std::move(id), 471 std::move(*type), std::move(expr)); 472 } 473 474 /// Parse a block: a list of expression separated by semicolons and wrapped in 475 /// curly braces. 476 /// 477 /// block ::= { expression_list } 478 /// expression_list ::= block_expr ; expression_list 479 /// block_expr ::= decl | "return" | expr parseBlock()480 std::unique_ptr<ExprASTList> parseBlock() { 481 if (lexer.getCurToken() != '{') 482 return parseError<ExprASTList>("{", "to begin block"); 483 lexer.consume(Token('{')); 484 485 auto exprList = std::make_unique<ExprASTList>(); 486 487 // Ignore empty expressions: swallow sequences of semicolons. 488 while (lexer.getCurToken() == ';') 489 lexer.consume(Token(';')); 490 491 while (lexer.getCurToken() != '}' && lexer.getCurToken() != tok_eof) { 492 if (lexer.getCurToken() == tok_identifier) { 493 // Variable declaration or call 494 auto expr = parseDeclarationOrCallExpr(); 495 if (!expr) 496 return nullptr; 497 exprList->push_back(std::move(expr)); 498 } else if (lexer.getCurToken() == tok_var) { 499 // Variable declaration 500 auto varDecl = parseDeclaration(/*requiresInitializer=*/true); 501 if (!varDecl) 502 return nullptr; 503 exprList->push_back(std::move(varDecl)); 504 } else if (lexer.getCurToken() == tok_return) { 505 // Return statement 506 auto ret = parseReturn(); 507 if (!ret) 508 return nullptr; 509 exprList->push_back(std::move(ret)); 510 } else { 511 // General expression 512 auto expr = parseExpression(); 513 if (!expr) 514 return nullptr; 515 exprList->push_back(std::move(expr)); 516 } 517 // Ensure that elements are separated by a semicolon. 518 if (lexer.getCurToken() != ';') 519 return parseError<ExprASTList>(";", "after expression"); 520 521 // Ignore empty expressions: swallow sequences of semicolons. 522 while (lexer.getCurToken() == ';') 523 lexer.consume(Token(';')); 524 } 525 526 if (lexer.getCurToken() != '}') 527 return parseError<ExprASTList>("}", "to close block"); 528 529 lexer.consume(Token('}')); 530 return exprList; 531 } 532 533 /// prototype ::= def id '(' decl_list ')' 534 /// decl_list ::= identifier | identifier, decl_list parsePrototype()535 std::unique_ptr<PrototypeAST> parsePrototype() { 536 auto loc = lexer.getLastLocation(); 537 538 if (lexer.getCurToken() != tok_def) 539 return parseError<PrototypeAST>("def", "in prototype"); 540 lexer.consume(tok_def); 541 542 if (lexer.getCurToken() != tok_identifier) 543 return parseError<PrototypeAST>("function name", "in prototype"); 544 545 std::string fnName(lexer.getId()); 546 lexer.consume(tok_identifier); 547 548 if (lexer.getCurToken() != '(') 549 return parseError<PrototypeAST>("(", "in prototype"); 550 lexer.consume(Token('(')); 551 552 std::vector<std::unique_ptr<VarDeclExprAST>> args; 553 if (lexer.getCurToken() != ')') { 554 do { 555 VarType type; 556 std::string name; 557 558 // Parse either the name of the variable, or its type. 559 std::string nameOrType(lexer.getId()); 560 auto loc = lexer.getLastLocation(); 561 lexer.consume(tok_identifier); 562 563 // If the next token is an identifier, we just parsed the type. 564 if (lexer.getCurToken() == tok_identifier) { 565 type.name = std::move(nameOrType); 566 567 // Parse the name. 568 name = std::string(lexer.getId()); 569 lexer.consume(tok_identifier); 570 } else { 571 // Otherwise, we just parsed the name. 572 name = std::move(nameOrType); 573 } 574 575 args.push_back( 576 std::make_unique<VarDeclExprAST>(std::move(loc), name, type)); 577 if (lexer.getCurToken() != ',') 578 break; 579 lexer.consume(Token(',')); 580 if (lexer.getCurToken() != tok_identifier) 581 return parseError<PrototypeAST>( 582 "identifier", "after ',' in function parameter list"); 583 } while (true); 584 } 585 if (lexer.getCurToken() != ')') 586 return parseError<PrototypeAST>(")", "to end function prototype"); 587 588 // success. 589 lexer.consume(Token(')')); 590 return std::make_unique<PrototypeAST>(std::move(loc), fnName, 591 std::move(args)); 592 } 593 594 /// Parse a function definition, we expect a prototype initiated with the 595 /// `def` keyword, followed by a block containing a list of expressions. 596 /// 597 /// definition ::= prototype block parseDefinition()598 std::unique_ptr<FunctionAST> parseDefinition() { 599 auto proto = parsePrototype(); 600 if (!proto) 601 return nullptr; 602 603 if (auto block = parseBlock()) 604 return std::make_unique<FunctionAST>(std::move(proto), std::move(block)); 605 return nullptr; 606 } 607 608 /// Parse a struct definition, we expect a struct initiated with the 609 /// `struct` keyword, followed by a block containing a list of variable 610 /// declarations. 611 /// 612 /// definition ::= `struct` identifier `{` decl+ `}` parseStruct()613 std::unique_ptr<StructAST> parseStruct() { 614 auto loc = lexer.getLastLocation(); 615 lexer.consume(tok_struct); 616 if (lexer.getCurToken() != tok_identifier) 617 return parseError<StructAST>("name", "in struct definition"); 618 std::string name(lexer.getId()); 619 lexer.consume(tok_identifier); 620 621 // Parse: '{' 622 if (lexer.getCurToken() != '{') 623 return parseError<StructAST>("{", "in struct definition"); 624 lexer.consume(Token('{')); 625 626 // Parse: decl+ 627 std::vector<std::unique_ptr<VarDeclExprAST>> decls; 628 do { 629 auto decl = parseDeclaration(/*requiresInitializer=*/false); 630 if (!decl) 631 return nullptr; 632 decls.push_back(std::move(decl)); 633 634 if (lexer.getCurToken() != ';') 635 return parseError<StructAST>(";", 636 "after variable in struct definition"); 637 lexer.consume(Token(';')); 638 } while (lexer.getCurToken() != '}'); 639 640 // Parse: '}' 641 lexer.consume(Token('}')); 642 return std::make_unique<StructAST>(loc, name, std::move(decls)); 643 } 644 645 /// Get the precedence of the pending binary operator token. getTokPrecedence()646 int getTokPrecedence() { 647 if (!isascii(lexer.getCurToken())) 648 return -1; 649 650 // 1 is lowest precedence. 651 switch (static_cast<char>(lexer.getCurToken())) { 652 case '-': 653 return 20; 654 case '+': 655 return 20; 656 case '*': 657 return 40; 658 case '.': 659 return 60; 660 default: 661 return -1; 662 } 663 } 664 665 /// Helper function to signal errors while parsing, it takes an argument 666 /// indicating the expected token and another argument giving more context. 667 /// Location is retrieved from the lexer to enrich the error message. 668 template <typename R, typename T, typename U = const char *> 669 std::unique_ptr<R> parseError(T &&expected, U &&context = "") { 670 auto curToken = lexer.getCurToken(); 671 llvm::errs() << "Parse error (" << lexer.getLastLocation().line << ", " 672 << lexer.getLastLocation().col << "): expected '" << expected 673 << "' " << context << " but has Token " << curToken; 674 if (isprint(curToken)) 675 llvm::errs() << " '" << (char)curToken << "'"; 676 llvm::errs() << "\n"; 677 return nullptr; 678 } 679 }; 680 681 } // namespace toy 682 683 #endif // TOY_PARSER_H 684