// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #pragma once #include "Luau/Ast.h" #include "Luau/Lexer.h" #include "Luau/ParseOptions.h" #include "Luau/ParseResult.h" #include "Luau/StringUtils.h" #include "Luau/DenseHash.h" #include "Luau/Common.h" #include #include #include namespace Luau { template class TempVector { public: explicit TempVector(std::vector& storage); ~TempVector(); const T& operator[](std::size_t index) const; const T& front() const; const T& back() const; bool empty() const; std::size_t size() const; void push_back(const T& item); typename std::vector::const_iterator begin() const { return storage.begin() + offset; } typename std::vector::const_iterator end() const { return storage.begin() + offset + size_; } private: std::vector& storage; size_t offset; size_t size_; }; class Parser { public: static ParseResult parse( const char* buffer, std::size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options = ParseOptions() ); private: struct Name; struct Binding; Parser(const char* buffer, std::size_t bufferSize, AstNameTable& names, Allocator& allocator, const ParseOptions& options); bool blockFollow(const Lexeme& l); AstStatBlock* parseChunk(); // chunk ::= {stat [`;']} [laststat [`;']] // block ::= chunk AstStatBlock* parseBlock(); AstStatBlock* parseBlockNoScope(); // stat ::= // varlist `=' explist | // functioncall | // do block end | // while exp do block end | // repeat block until exp | // if exp then block {elseif exp then block} [else block] end | // for Name `=' exp `,' exp [`,' exp] do block end | // for namelist in explist do block end | // [attributes] function funcname funcbody | // [attributes] local function Name funcbody | // local namelist [`=' explist] // laststat ::= return [explist] | break AstStat* parseStat(); // if exp then block {elseif exp then block} [else block] end AstStat* parseIf(); // while exp do block end AstStat* parseWhile(); // repeat block until exp AstStat* parseRepeat(); // do block end AstStat* parseDo(); // break AstStat* parseBreak(); // continue AstStat* parseContinue(const Location& start); // for Name `=' exp `,' exp [`,' exp] do block end | // for namelist in explist do block end | AstStat* parseFor(); // funcname ::= Name {`.' Name} [`:' Name] AstExpr* parseFunctionName(Location start, bool& hasself, AstName& debugname); // function funcname funcbody LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray& attributes = {nullptr, 0}); std::pair validateAttribute(const char* attributeName, const TempVector& attributes); // attribute ::= '@' NAME void parseAttribute(TempVector& attribute); // attributes ::= {attribute} AstArray parseAttributes(); // attributes local function Name funcbody // attributes function funcname funcbody // attributes `declare function' Name`(' [parlist] `)' [`:` Type] // declare Name '{' Name ':' attributes `(' [parlist] `)' [`:` Type] '}' AstStat* parseAttributeStat(); // local function Name funcbody | // local namelist [`=' explist] AstStat* parseLocal(const AstArray& attributes); // return [explist] AstStat* parseReturn(); // type Name `=' Type AstStat* parseTypeAlias(const Location& start, bool exported); // type function Name ... end AstStat* parseTypeFunction(const Location& start, bool exported); AstDeclaredClassProp parseDeclaredClassMethod(); // `declare global' Name: Type | // `declare function' Name`(' [parlist] `)' [`:` Type] AstStat* parseDeclaration(const Location& start, const AstArray& attributes); // varlist `=' explist AstStat* parseAssignment(AstExpr* initial); // var [`+=' | `-=' | `*=' | `/=' | `%=' | `^=' | `..='] exp AstStat* parseCompoundAssignment(AstExpr* initial, AstExprBinary::Op op); std::pair> prepareFunctionArguments(const Location& start, bool hasself, const TempVector& args); // funcbodyhead ::= `(' [namelist [`,' `...'] | `...'] `)' [`:` Type] // funcbody ::= funcbodyhead block end std::pair parseFunctionBody( bool hasself, const Lexeme& matchFunction, const AstName& debugname, const Name* localName, const AstArray& attributes ); // explist ::= {exp `,'} exp void parseExprList(TempVector& result); // binding ::= Name [`:` Type] Binding parseBinding(); // bindinglist ::= (binding | `...') {`,' bindinglist} // Returns the location of the vararg ..., or std::nullopt if the function is not vararg. std::tuple parseBindingList(TempVector& result, bool allowDot3 = false); AstType* parseOptionalType(); // TypeList ::= Type [`,' TypeList] // ReturnType ::= Type | `(' TypeList `)' // TableProp ::= Name `:' Type // TableIndexer ::= `[' Type `]' `:' Type // PropList ::= (TableProp | TableIndexer) [`,' PropList] // Type // ::= Name // | `nil` // | `{' [PropList] `}' // | `(' [TypeList] `)' `->` ReturnType // Returns the variadic annotation, if it exists. AstTypePack* parseTypeList(TempVector& result, TempVector>& resultNames); std::optional parseOptionalReturnType(); std::pair parseReturnType(); AstTableIndexer* parseTableIndexer(AstTableAccess access, std::optional accessLocation); AstTypeOrPack parseFunctionType(bool allowPack, const AstArray& attributes); AstType* parseFunctionTypeTail( const Lexeme& begin, const AstArray& attributes, AstArray generics, AstArray genericPacks, AstArray params, AstArray> paramNames, AstTypePack* varargAnnotation ); AstType* parseTableType(bool inDeclarationContext = false); AstTypeOrPack parseSimpleType(bool allowPack, bool inDeclarationContext = false); AstTypeOrPack parseSimpleTypeOrPack(); AstType* parseType(bool inDeclarationContext = false); AstTypePack* parseTypePack(); AstTypePack* parseVariadicArgumentTypePack(); AstType* parseTypeSuffix(AstType* type, const Location& begin); static std::optional parseUnaryOp(const Lexeme& l); static std::optional parseBinaryOp(const Lexeme& l); static std::optional parseCompoundOp(const Lexeme& l); struct BinaryOpPriority { unsigned char left, right; }; std::optional checkUnaryConfusables(); std::optional checkBinaryConfusables(const BinaryOpPriority binaryPriority[], unsigned int limit); // subexpr -> (asexp | unop subexpr) { binop subexpr } // where `binop' is any binary operator with a priority higher than `limit' AstExpr* parseExpr(unsigned int limit = 0); // NAME AstExpr* parseNameExpr(const char* context = nullptr); // prefixexp -> NAME | '(' expr ')' AstExpr* parsePrefixExpr(); // primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } AstExpr* parsePrimaryExpr(bool asStatement); // asexp -> simpleexp [`::' Type] AstExpr* parseAssertionExpr(); // simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | [attributes] FUNCTION body | primaryexp AstExpr* parseSimpleExpr(); // args ::= `(' [explist] `)' | tableconstructor | String AstExpr* parseFunctionArgs(AstExpr* func, bool self); // tableconstructor ::= `{' [fieldlist] `}' // fieldlist ::= field {fieldsep field} [fieldsep] // field ::= `[' exp `]' `=' exp | Name `=' exp | exp // fieldsep ::= `,' | `;' AstExpr* parseTableConstructor(); // TODO: Add grammar rules here? AstExpr* parseIfElseExpr(); // stringinterp ::= exp { exp} AstExpr* parseInterpString(); // Name std::optional parseNameOpt(const char* context = nullptr); Name parseName(const char* context = nullptr); Name parseIndexName(const char* context, const Position& previous); // `<' namelist `>' std::pair, AstArray> parseGenericTypeList(bool withDefaultValues); // `<' Type[, ...] `>' AstArray parseTypeParams(); std::optional> parseCharArray(); AstExpr* parseString(); AstExpr* parseNumber(); AstLocal* pushLocal(const Binding& binding); unsigned int saveLocals(); void restoreLocals(unsigned int offset); // check that parser is at lexeme/symbol, move to next lexeme/symbol on success, report failure and continue on failure bool expectAndConsume(char value, const char* context = nullptr); bool expectAndConsume(Lexeme::Type type, const char* context = nullptr); void expectAndConsumeFail(Lexeme::Type type, const char* context); struct MatchLexeme { MatchLexeme(const Lexeme& l) : type(l.type) , position(l.location.begin) { } Lexeme::Type type; Position position; }; bool expectMatchAndConsume(char value, const MatchLexeme& begin, bool searchForMissing = false); void expectMatchAndConsumeFail(Lexeme::Type type, const MatchLexeme& begin, const char* extra = nullptr); bool expectMatchAndConsumeRecover(char value, const MatchLexeme& begin, bool searchForMissing); bool expectMatchEndAndConsume(Lexeme::Type type, const MatchLexeme& begin); void expectMatchEndAndConsumeFail(Lexeme::Type type, const MatchLexeme& begin); template AstArray copy(const T* data, std::size_t size); template AstArray copy(const TempVector& data); template AstArray copy(std::initializer_list data); AstArray copy(const std::string& data); void incrementRecursionCounter(const char* context); void report(const Location& location, const char* format, va_list args); void report(const Location& location, const char* format, ...) LUAU_PRINTF_ATTR(3, 4); void reportNameError(const char* context); AstStatError* reportStatError( const Location& location, const AstArray& expressions, const AstArray& statements, const char* format, ... ) LUAU_PRINTF_ATTR(5, 6); AstExprError* reportExprError(const Location& location, const AstArray& expressions, const char* format, ...) LUAU_PRINTF_ATTR(4, 5); AstTypeError* reportTypeError(const Location& location, const AstArray& types, const char* format, ...) LUAU_PRINTF_ATTR(4, 5); // `parseErrorLocation` is associated with the parser error // `astErrorLocation` is associated with the AstTypeError created // It can be useful to have different error locations so that the parse error can include the next lexeme, while the AstTypeError can precisely // define the location (possibly of zero size) where a type annotation is expected. AstTypeError* reportMissingTypeError(const Location& parseErrorLocation, const Location& astErrorLocation, const char* format, ...) LUAU_PRINTF_ATTR(4, 5); AstExpr* reportFunctionArgsError(AstExpr* func, bool self); void reportAmbiguousCallError(); void nextLexeme(); struct Function { bool vararg; unsigned int loopDepth; Function() : vararg(false) , loopDepth(0) { } }; struct Local { AstLocal* local; unsigned int offset; Local() : local(nullptr) , offset(0) { } }; struct Name { AstName name; Location location; Name(const AstName& name, const Location& location) : name(name) , location(location) { } }; struct Binding { Name name; AstType* annotation; explicit Binding(const Name& name, AstType* annotation = nullptr) : name(name) , annotation(annotation) { } }; ParseOptions options; Lexer lexer; Allocator& allocator; std::vector commentLocations; std::vector hotcomments; bool hotcommentHeader = true; unsigned int recursionCounter; AstName nameSelf; AstName nameNumber; AstName nameError; AstName nameNil; MatchLexeme endMismatchSuspect; std::vector functionStack; size_t typeFunctionDepth = 0; DenseHashMap localMap; std::vector localStack; std::vector parseErrors; std::vector matchRecoveryStopOnToken; std::vector scratchAttr; std::vector scratchStat; std::vector> scratchString; std::vector scratchExpr; std::vector scratchExprAux; std::vector scratchName; std::vector scratchPackName; std::vector scratchBinding; std::vector scratchLocal; std::vector scratchTableTypeProps; std::vector scratchType; std::vector scratchTypeOrPack; std::vector scratchDeclaredClassProps; std::vector scratchItem; std::vector scratchArgName; std::vector scratchGenericTypes; std::vector scratchGenericTypePacks; std::vector> scratchOptArgName; std::string scratchData; }; } // namespace Luau