// 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/StringUtils.h" #include "Luau/DenseHash.h" #include "Luau/Common.h" #include #include namespace Luau { class ParseError : public std::exception { public: ParseError(const Location& location, const std::string& message); virtual const char* what() const throw(); const Location& getLocation() const; const std::string& getMessage() const; static LUAU_NORETURN void raise(const Location& location, const char* format, ...) LUAU_PRINTF_ATTR(2, 3); private: Location location; std::string message; }; class ParseErrors : public std::exception { public: ParseErrors(std::vector errors); virtual const char* what() const throw(); const std::vector& getErrors() const; private: std::vector errors; std::string message; }; 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_; }; struct Comment { Lexeme::Type type; // Comment, BlockComment, or BrokenComment Location location; }; struct ParseResult { AstStatBlock* root; std::vector hotcomments; std::vector errors; std::vector commentLocations; }; class Parser { public: static ParseResult parse( const char* buffer, std::size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options = ParseOptions()); static constexpr const char* errorName = "%error-id%"; private: struct Name; struct Binding; Parser(const char* buffer, std::size_t bufferSize, AstNameTable& names, Allocator& allocator); 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 | // function funcname funcbody | // 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(); // function funcname funcbody | // funcname ::= Name {`.' Name} [`:' Name] AstStat* parseFunctionStat(); // local function Name funcbody | // local namelist [`=' explist] AstStat* parseLocal(); // return [explist] AstStat* parseReturn(); // type Name `=' typeannotation AstStat* parseTypeAlias(const Location& start, bool exported); AstDeclaredClassProp parseDeclaredClassMethod(); // `declare global' Name: typeannotation | // `declare function' Name`(' [parlist] `)' [`:` TypeAnnotation] AstStat* parseDeclaration(const Location& start); // varlist `=' explist AstStat* parseAssignment(AstExpr* initial); // var [`+=' | `-=' | `*=' | `/=' | `%=' | `^=' | `..='] exp AstStat* parseCompoundAssignment(AstExpr* initial, AstExprBinary::Op op); // funcbody ::= `(' [parlist] `)' block end // parlist ::= namelist [`,' `...'] | `...' std::pair parseFunctionBody( bool hasself, const Lexeme& matchFunction, const AstName& debugname, std::optional localName); // explist ::= {exp `,'} exp void parseExprList(TempVector& result); // binding ::= Name [`:` TypeAnnotation] Binding parseBinding(); // bindinglist ::= (binding | `...') {`,' bindinglist} // Returns the location of the vararg ..., or std::nullopt if the function is not vararg. std::pair, AstTypePack*> parseBindingList(TempVector& result, bool allowDot3 = false); AstType* parseOptionalTypeAnnotation(); // TypeList ::= TypeAnnotation [`,' TypeList] // ReturnType ::= TypeAnnotation | `(' TypeList `)' // TableProp ::= Name `:' TypeAnnotation // TableIndexer ::= `[' TypeAnnotation `]' `:' TypeAnnotation // PropList ::= (TableProp | TableIndexer) [`,' PropList] // TypeAnnotation // ::= Name // | `nil` // | `{' [PropList] `}' // | `(' [TypeList] `)' `->` ReturnType // Returns the variadic annotation, if it exists. AstTypePack* parseTypeList(TempVector& result, TempVector>& resultNames); std::optional parseOptionalReturnTypeAnnotation(); std::pair parseReturnTypeAnnotation(); AstTableIndexer* parseTableIndexerAnnotation(); AstTypeOrPack parseFunctionTypeAnnotation(bool allowPack); AstType* parseFunctionTypeAnnotationTail(const Lexeme& begin, AstArray generics, AstArray genericPacks, AstArray& params, AstArray>& paramNames, AstTypePack* varargAnnotation); AstType* parseTableTypeAnnotation(); AstTypeOrPack parseSimpleTypeAnnotation(bool allowPack); AstTypeOrPack parseTypeOrPackAnnotation(); AstType* parseTypeAnnotation(TempVector& parts, const Location& begin); AstType* parseTypeAnnotation(); AstTypePack* parseTypePackAnnotation(); AstTypePack* parseVariadicArgumentAnnotation(); 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 [`::' typeAnnotation] AstExpr* parseAssertionExpr(); // simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | FUNCTION body | primaryexp AstExpr* parseSimpleExpr(); // args ::= `(' [explist] `)' | tableconstructor | String AstExpr* parseFunctionArgs(AstExpr* func, bool self, const Location& selfLocation); // tableconstructor ::= `{' [fieldlist] `}' // fieldlist ::= field {fieldsep field} [fieldsep] // field ::= `[' exp `]' `=' exp | Name `=' exp | exp // fieldsep ::= `,' | `;' AstExpr* parseTableConstructor(); // TODO: Add grammar rules here? AstExpr* parseIfElseExpr(); // 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(); // `<' typeAnnotation[, ...] `>' AstArray parseTypeParams(); std::optional> parseCharArray(); AstExpr* parseString(); 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); bool expectMatchAndConsume(char value, const Lexeme& begin, bool searchForMissing = false); void expectMatchAndConsumeFail(Lexeme::Type type, const Lexeme& begin, const char* extra = nullptr); bool expectMatchEndAndConsume(Lexeme::Type type, const Lexeme& begin); void expectMatchEndAndConsumeFail(Lexeme::Type type, const Lexeme& 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* reportTypeAnnotationError(const Location& location, const AstArray& types, bool isMissing, const char* format, ...) LUAU_PRINTF_ATTR(5, 6); const Lexeme& 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; unsigned int recursionCounter; AstName nameSelf; AstName nameNumber; AstName nameError; AstName nameNil; Lexeme endMismatchSuspect; std::vector functionStack; DenseHashMap localMap; std::vector localStack; std::vector parseErrors; std::vector matchRecoveryStopOnToken; std::vector scratchStat; std::vector scratchExpr; std::vector scratchExprAux; std::vector scratchName; std::vector scratchPackName; std::vector scratchBinding; std::vector scratchLocal; std::vector scratchTableTypeProps; std::vector scratchAnnotation; std::vector scratchTypeOrPackAnnotation; std::vector scratchDeclaredClassProps; std::vector scratchItem; std::vector scratchArgName; std::vector> scratchOptArgName; std::string scratchData; }; } // namespace Luau