From 0c14c734fa32014fd24297ccdbed927016185ffd Mon Sep 17 00:00:00 2001 From: ubsan Date: Sat, 24 Dec 2016 19:07:08 -0700 Subject: No more tears! No more DOS line endings or trailing whitespace either --- src/parse/common.hpp | 140 +- src/parse/expr.cpp | 2602 ++++++++++++------------ src/parse/interpolated_fragment.hpp | 12 +- src/parse/lex.cpp | 40 +- src/parse/lex.hpp | 4 +- src/parse/parseerror.cpp | 182 +- src/parse/parseerror.hpp | 80 +- src/parse/paths.cpp | 28 +- src/parse/pattern.cpp | 68 +- src/parse/root.cpp | 3758 +++++++++++++++++------------------ src/parse/token.cpp | 12 +- src/parse/token.hpp | 18 +- src/parse/tokenstream.cpp | 8 +- src/parse/tokenstream.hpp | 18 +- src/parse/tokentree.hpp | 122 +- src/parse/ttstream.hpp | 4 +- src/parse/types.cpp | 30 +- 17 files changed, 3563 insertions(+), 3563 deletions(-) (limited to 'src/parse') diff --git a/src/parse/common.hpp b/src/parse/common.hpp index 57d6f24d..613290f2 100644 --- a/src/parse/common.hpp +++ b/src/parse/common.hpp @@ -1,70 +1,70 @@ -/* - * MRustC - Rust Compiler - * - By John Hodge (Mutabah/thePowersGang) - * - * parse/common.hpp - * - Common definitions used by the parser - */ -#ifndef PARSE_COMMON_HPP_INCLUDED -#define PARSE_COMMON_HPP_INCLUDED -#include -#include "tokenstream.hpp" -#include "../ast/ast.hpp" - -#define GET_TOK(tok, lex) ((tok = lex.getToken()).type()) -#define PUTBACK(tok, lex) lex.putback( ::std::move(tok) ) -#define LOOK_AHEAD(lex) (lex.lookahead(0)) -#define GET_CHECK_TOK(tok, lex, exp) do {\ - if((tok = lex.getToken()).type() != exp) { \ - DEBUG("GET_CHECK_TOK " << __FILE__ << ":" << __LINE__); \ - throw ParseError::Unexpected(lex, tok, Token(exp));\ - }\ -} while(0) -#define CHECK_TOK(tok, exp) do {\ - if(tok.type() != exp) { \ - DEBUG("CHECK_TOK " << __FILE__ << ":" << __LINE__); \ - throw ParseError::Unexpected(lex, tok, Token(exp));\ - } \ -} while(0) - -// --- path.cpp -enum eParsePathGenericMode -{ - PATH_GENERIC_NONE, - PATH_GENERIC_EXPR, - PATH_GENERIC_TYPE -}; -extern AST::Path Parse_Path(TokenStream& lex, eParsePathGenericMode generic_mode); // Auto-determines -extern AST::Path Parse_Path(TokenStream& lex, bool is_abs, eParsePathGenericMode generic_mode); -extern ::std::vector Parse_PathNodes(TokenStream& lex, eParsePathGenericMode generic_mode); -extern AST::PathParams Parse_Path_GenericList(TokenStream& lex); - - -extern ::std::vector< ::std::string> Parse_HRB(TokenStream& lex); -extern AST::MetaItem Parse_MetaItem(TokenStream& lex); -extern ::AST::MacroInvocation Parse_MacroInvocation(ProtoSpan ps, ::std::string name, TokenStream& lex); -extern TypeRef Parse_Type(TokenStream& lex, bool allow_trait_list = true); -extern AST::Pattern Parse_Pattern(TokenStream& lex, bool is_refutable); - -extern void Parse_Impl_Item(TokenStream& lex, AST::Impl& impl); -extern void Parse_Mod_Item(TokenStream& lex, AST::Module& mod, AST::MetaItems meta_items); -extern ::AST::Named<::AST::Item> Parse_Mod_Item_S(TokenStream& lex, const AST::Module::FileInfo& mod_fileinfo, const ::AST::Path& mod_path, AST::MetaItems meta_items); -extern void Parse_ModRoot_Items(TokenStream& lex, AST::Module& mod); - - -extern AST::Expr Parse_Expr(TokenStream& lex); -extern AST::Expr Parse_ExprBlock(TokenStream& lex); -extern AST::ExprNodeP Parse_Expr0(TokenStream& lex); -extern AST::ExprNodeP Parse_ExprVal(TokenStream& lex); -extern AST::ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe=false); -extern AST::ExprNodeP Parse_ExprBlockLine(TokenStream& lex, bool *add_silence); -extern AST::ExprNodeP Parse_ExprBlockLine_WithItems(TokenStream& lex, ::std::shared_ptr& local_mod, bool& add_silence_if_end); -extern AST::ExprNodeP Parse_Stmt(TokenStream& lex); - -// unwrapped = Exclude the enclosing brackets (used by macro parse code) -extern TokenTree Parse_TT(TokenStream& lex, bool unwrapped); - - -extern bool Parse_IsTokValue(eTokenType tok_type); - -#endif // PARSE_COMMON_HPP_INCLUDED +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * parse/common.hpp + * - Common definitions used by the parser + */ +#ifndef PARSE_COMMON_HPP_INCLUDED +#define PARSE_COMMON_HPP_INCLUDED +#include +#include "tokenstream.hpp" +#include "../ast/ast.hpp" + +#define GET_TOK(tok, lex) ((tok = lex.getToken()).type()) +#define PUTBACK(tok, lex) lex.putback( ::std::move(tok) ) +#define LOOK_AHEAD(lex) (lex.lookahead(0)) +#define GET_CHECK_TOK(tok, lex, exp) do {\ + if((tok = lex.getToken()).type() != exp) { \ + DEBUG("GET_CHECK_TOK " << __FILE__ << ":" << __LINE__); \ + throw ParseError::Unexpected(lex, tok, Token(exp));\ + }\ +} while(0) +#define CHECK_TOK(tok, exp) do {\ + if(tok.type() != exp) { \ + DEBUG("CHECK_TOK " << __FILE__ << ":" << __LINE__); \ + throw ParseError::Unexpected(lex, tok, Token(exp));\ + } \ +} while(0) + +// --- path.cpp +enum eParsePathGenericMode +{ + PATH_GENERIC_NONE, + PATH_GENERIC_EXPR, + PATH_GENERIC_TYPE +}; +extern AST::Path Parse_Path(TokenStream& lex, eParsePathGenericMode generic_mode); // Auto-determines +extern AST::Path Parse_Path(TokenStream& lex, bool is_abs, eParsePathGenericMode generic_mode); +extern ::std::vector Parse_PathNodes(TokenStream& lex, eParsePathGenericMode generic_mode); +extern AST::PathParams Parse_Path_GenericList(TokenStream& lex); + + +extern ::std::vector< ::std::string> Parse_HRB(TokenStream& lex); +extern AST::MetaItem Parse_MetaItem(TokenStream& lex); +extern ::AST::MacroInvocation Parse_MacroInvocation(ProtoSpan ps, ::std::string name, TokenStream& lex); +extern TypeRef Parse_Type(TokenStream& lex, bool allow_trait_list = true); +extern AST::Pattern Parse_Pattern(TokenStream& lex, bool is_refutable); + +extern void Parse_Impl_Item(TokenStream& lex, AST::Impl& impl); +extern void Parse_Mod_Item(TokenStream& lex, AST::Module& mod, AST::MetaItems meta_items); +extern ::AST::Named<::AST::Item> Parse_Mod_Item_S(TokenStream& lex, const AST::Module::FileInfo& mod_fileinfo, const ::AST::Path& mod_path, AST::MetaItems meta_items); +extern void Parse_ModRoot_Items(TokenStream& lex, AST::Module& mod); + + +extern AST::Expr Parse_Expr(TokenStream& lex); +extern AST::Expr Parse_ExprBlock(TokenStream& lex); +extern AST::ExprNodeP Parse_Expr0(TokenStream& lex); +extern AST::ExprNodeP Parse_ExprVal(TokenStream& lex); +extern AST::ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe=false); +extern AST::ExprNodeP Parse_ExprBlockLine(TokenStream& lex, bool *add_silence); +extern AST::ExprNodeP Parse_ExprBlockLine_WithItems(TokenStream& lex, ::std::shared_ptr& local_mod, bool& add_silence_if_end); +extern AST::ExprNodeP Parse_Stmt(TokenStream& lex); + +// unwrapped = Exclude the enclosing brackets (used by macro parse code) +extern TokenTree Parse_TT(TokenStream& lex, bool unwrapped); + + +extern bool Parse_IsTokValue(eTokenType tok_type); + +#endif // PARSE_COMMON_HPP_INCLUDED diff --git a/src/parse/expr.cpp b/src/parse/expr.cpp index c71d2b4a..eb82bae7 100644 --- a/src/parse/expr.cpp +++ b/src/parse/expr.cpp @@ -1,1301 +1,1301 @@ -/* - * MRustC - Rust Compiler - * - By John Hodge (Mutabah/thePowersGang) - * - * parse/expr.cpp - * - Expression (i.e. code) parsing - * - * Start points: - * - Parse_ExprBlockNode : Parses a block - * - Parse_Stmt : Parse a single statement - * - Parse_Expr0 : Parse a single expression - */ -#include "parseerror.hpp" -#include -#include -#include "common.hpp" -#include -#include "tokentree.hpp" -#include "interpolated_fragment.hpp" - -using AST::ExprNode; -using AST::ExprNodeP; -static inline ExprNodeP mk_exprnodep(const TokenStream& lex, AST::ExprNode* en){en->set_pos(lex.getPosition()); return ExprNodeP(en); } -#define NEWNODE(type, ...) mk_exprnodep(lex, new type(__VA_ARGS__)) - -//ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe=false); // common.hpp -//ExprNodeP Parse_ExprBlockLine_WithItems(TokenStream& lex, ::std::shared_ptr& local_mod, bool& add_silence_if_end); -//ExprNodeP Parse_ExprBlockLine(TokenStream& lex, bool *add_silence); -ExprNodeP Parse_ExprBlockLine_Stmt(TokenStream& lex, bool& has_semicolon); -//ExprNodeP Parse_Stmt(TokenStream& lex); // common.hpp -ExprNodeP Parse_Stmt_Let(TokenStream& lex); -ExprNodeP Parse_Expr0(TokenStream& lex); -ExprNodeP Parse_IfStmt(TokenStream& lex); -ExprNodeP Parse_WhileStmt(TokenStream& lex, ::std::string lifetime); -ExprNodeP Parse_ForStmt(TokenStream& lex, ::std::string lifetime); -ExprNodeP Parse_Expr_Match(TokenStream& lex); -ExprNodeP Parse_Expr1(TokenStream& lex); -ExprNodeP Parse_ExprMacro(TokenStream& lex, Token tok); - -AST::Expr Parse_Expr(TokenStream& lex) -{ - return ::AST::Expr( Parse_Expr0(lex) ); -} - -AST::Expr Parse_ExprBlock(TokenStream& lex) -{ - return ::AST::Expr( Parse_ExprBlockNode(lex) ); -} - -ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe/*=false*/) -{ - TRACE_FUNCTION; - Token tok; - - bool yields_final_value = true; - ::std::vector nodes; - - ::std::shared_ptr local_mod; - - GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); - - while( LOOK_AHEAD(lex) != TOK_BRACE_CLOSE ) - { - DEBUG("tok = " << tok); - - // NOTE: Doc comments can appear within a function and apply to the function - // TODO: Use these attributes - while( GET_TOK(tok, lex) == TOK_CATTR_OPEN ) - { - /*node_attrs.push_back(*/ Parse_MetaItem(lex) /*)*/; - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - PUTBACK(tok, lex); - if( LOOK_AHEAD(lex) == TOK_BRACE_CLOSE ) - break; - - bool add_silence_if_end = false; - auto rv = Parse_ExprBlockLine_WithItems(lex, local_mod, add_silence_if_end); - if( rv ) - { - // Set to TRUE if there was no semicolon after a statement - if( LOOK_AHEAD(lex) == TOK_BRACE_CLOSE && add_silence_if_end ) - { - DEBUG("End of block, and add_silence_if_end == true - doesn't yeild"); - yields_final_value = false; - // Since the next token is TOK_BRACE_CLOSE, the loop will terminate - } - nodes.push_back( mv$(rv) ); - } - else { - assert( !add_silence_if_end ); - } - } - GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); - - return NEWNODE( AST::ExprNode_Block, is_unsafe, yields_final_value, mv$(nodes), mv$(local_mod) ); -} - -/// Parse a single line in a block, handling items added to the local module -/// -/// - If an item was parsed, this returns an empty ExprNodeP -ExprNodeP Parse_ExprBlockLine_WithItems(TokenStream& lex, ::std::shared_ptr& local_mod, bool& add_silence_if_end) -{ - Token tok; - - AST::MetaItems item_attrs; - while( GET_TOK(tok, lex) == TOK_ATTR_OPEN ) - { - item_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - - switch(tok.type()) - { - // Items: - case TOK_RWORD_PUB: - // NOTE: Allowed, but doesn't do much - case TOK_RWORD_TYPE: - case TOK_RWORD_USE: - case TOK_RWORD_EXTERN: - case TOK_RWORD_CONST: - case TOK_RWORD_STATIC: - case TOK_RWORD_STRUCT: - case TOK_RWORD_ENUM: - case TOK_RWORD_TRAIT: - case TOK_RWORD_IMPL: - case TOK_RWORD_FN: - case TOK_RWORD_MOD: - PUTBACK(tok, lex); - if( !local_mod ) { - local_mod = lex.parse_state().get_current_mod().add_anon(); - } - Parse_Mod_Item(lex, *local_mod, mv$(item_attrs)); - return ExprNodeP(); - // 'unsafe' - Check if the next token isn't a `{`, if so it's an item. Otherwise, fall through - case TOK_RWORD_UNSAFE: - if( LOOK_AHEAD(lex) != TOK_BRACE_OPEN ) - { - PUTBACK(tok, lex); - if( !local_mod ) { - local_mod = lex.parse_state().get_current_mod().add_anon(); - } - Parse_Mod_Item(lex, *local_mod, mv$(item_attrs)); - return ExprNodeP(); - } - // fall - default: { - PUTBACK(tok, lex); - auto rv = Parse_ExprBlockLine(lex, &add_silence_if_end); - if( rv ) { - rv->set_attrs( mv$(item_attrs) ); - } - else if( item_attrs.m_items.size() > 0 ) { - // TODO: Is this an error? - Attributes on a expression that didn't yeild a node. - } - else { - } - return rv; - } break; - } -} - -/// Parse a single line from a block -/// -/// Handles: -/// - Block-level constructs (with lifetime annotations) -/// - use/extern/const/let -ExprNodeP Parse_ExprBlockLine(TokenStream& lex, bool *add_silence) -{ - Token tok; - ExprNodeP ret; - - if( GET_TOK(tok, lex) == TOK_LIFETIME ) - { - // Lifetimes can only precede loops... and blocks? - ::std::string lifetime = tok.str(); - GET_CHECK_TOK(tok, lex, TOK_COLON); - - switch( GET_TOK(tok, lex) ) - { - case TOK_RWORD_LOOP: - return NEWNODE( AST::ExprNode_Loop, lifetime, Parse_ExprBlockNode(lex) ); - case TOK_RWORD_WHILE: - return Parse_WhileStmt(lex, lifetime); - case TOK_RWORD_FOR: - return Parse_ForStmt(lex, lifetime); - //case TOK_RWORD_IF: - // return Parse_IfStmt(lex); - //case TOK_RWORD_MATCH: - // return Parse_Expr_Match(lex); - //case TOK_BRACE_OPEN: - // PUTBACK(tok, lex); - // return Parse_ExprBlockNode(lex); - - default: - throw ParseError::Unexpected(lex, tok); - } - } - else - { - switch( tok.type() ) - { - case TOK_INTERPOLATED_BLOCK: - return tok.take_frag_node(); - case TOK_SEMICOLON: - // Return a NULL expression, nothing here. - return nullptr; - - // let binding - case TOK_RWORD_LET: - ret = Parse_Stmt_Let(lex); - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - return ret; - - // Blocks that don't need semicolons - // NOTE: If these are followed by a small set of tokens (`.` and `?`) then they are actually the start of an expression - // HACK: Parse here, but if the next token is one of the set store in a TOK_INTERPOLATED_EXPR and invoke the statement parser - case TOK_RWORD_LOOP: - ret = NEWNODE( AST::ExprNode_Loop, "", Parse_ExprBlockNode(lex) ); - if(0) - case TOK_RWORD_WHILE: - ret = Parse_WhileStmt(lex, ""); - if(0) - case TOK_RWORD_FOR: - ret = Parse_ForStmt(lex, ""); - if(0) - case TOK_RWORD_IF: - ret = Parse_IfStmt(lex); - if(0) - case TOK_RWORD_MATCH: - ret = Parse_Expr_Match(lex); - if(0) - case TOK_RWORD_UNSAFE: - ret = Parse_ExprBlockNode(lex, true); - if(0) - case TOK_BRACE_OPEN: - { PUTBACK(tok, lex); ret = Parse_ExprBlockNode(lex); } - - if( lex.lookahead(0) == TOK_DOT || lex.lookahead(0) == TOK_QMARK ) { - lex.putback( Token(Token::TagTakeIP(), InterpolatedFragment(InterpolatedFragment::EXPR, ret.release())) ); - return Parse_ExprBlockLine_Stmt(lex, *add_silence); - } - - if( LOOK_AHEAD(lex) == TOK_SEMICOLON ) { - GET_TOK(tok, lex); - *add_silence = true; - } - - return ret; - - // Flow control - case TOK_RWORD_RETURN: - case TOK_RWORD_CONTINUE: - case TOK_RWORD_BREAK: { - PUTBACK(tok, lex); - auto ret = Parse_Stmt(lex); - if( LOOK_AHEAD(lex) == TOK_EOF ) { - } - else if( GET_TOK(tok, lex) != TOK_SEMICOLON ) { - CHECK_TOK(tok, TOK_BRACE_CLOSE); - PUTBACK(tok, lex); - } - else { - // return/continue/break don't need silencing - } - return ret; - } - - case TOK_MACRO: - // If a braced macro invocation is the first part of a statement, don't expect a semicolon - if( LOOK_AHEAD(lex) == TOK_BRACE_OPEN || (lex.lookahead(0) == TOK_IDENT && lex.lookahead(1) == TOK_BRACE_OPEN) ) { - return Parse_ExprMacro(lex, tok); - } - // Fall through to the statement code - default: - PUTBACK(tok, lex); - return Parse_ExprBlockLine_Stmt(lex, *add_silence); - } - } -} - -ExprNodeP Parse_ExprBlockLine_Stmt(TokenStream& lex, bool& has_semicolon) -{ - Token tok; - auto ret = Parse_Stmt(lex); - // If this expression statement wasn't followed by a semicolon, then it's yielding its value out of the block. - // - I.e. The block should be ending - if( GET_TOK(tok, lex) != TOK_SEMICOLON ) { - // - Allow TOK_EOF for macro expansion. - if( tok.type() == TOK_EOF ) - ; - else - CHECK_TOK(tok, TOK_BRACE_CLOSE); - PUTBACK(tok, lex); - } - else { - has_semicolon = true; - } - return ret; -} - -/// While loop (either as a statement, or as part of an expression) -ExprNodeP Parse_WhileStmt(TokenStream& lex, ::std::string lifetime) -{ - Token tok; - - if( GET_TOK(tok, lex) == TOK_RWORD_LET ) { - auto pat = Parse_Pattern(lex, true); // Refutable pattern - GET_CHECK_TOK(tok, lex, TOK_EQUAL); - ExprNodeP val; - { - SET_PARSE_FLAG(lex, disallow_struct_literal); - val = Parse_Expr0(lex); - } - return NEWNODE( AST::ExprNode_Loop, lifetime, AST::ExprNode_Loop::WHILELET, - ::std::move(pat), ::std::move(val), Parse_ExprBlockNode(lex) ); - } - else { - PUTBACK(tok, lex); - ExprNodeP cnd; - { - SET_PARSE_FLAG(lex, disallow_struct_literal); - cnd = Parse_Expr1(lex); - } - return NEWNODE( AST::ExprNode_Loop, lifetime, ::std::move(cnd), Parse_ExprBlockNode(lex) ); - } -} -/// For loop (either as a statement, or as part of an expression) -ExprNodeP Parse_ForStmt(TokenStream& lex, ::std::string lifetime) -{ - Token tok; - - // Irrefutable pattern - AST::Pattern pat = Parse_Pattern(lex, false); - GET_CHECK_TOK(tok, lex, TOK_RWORD_IN); - ExprNodeP val; - { - SET_PARSE_FLAG(lex, disallow_struct_literal); - val = Parse_Expr0(lex); - } - return NEWNODE( AST::ExprNode_Loop, lifetime, AST::ExprNode_Loop::FOR, - ::std::move(pat), ::std::move(val), Parse_ExprBlockNode(lex) ); -} -/// Parse an 'if' statement -// Note: TOK_RWORD_IF has already been eaten -ExprNodeP Parse_IfStmt(TokenStream& lex) -{ - TRACE_FUNCTION; - - Token tok; - ExprNodeP cond; - AST::Pattern pat; - bool if_let = false; - - { - SET_PARSE_FLAG(lex, disallow_struct_literal); - if( GET_TOK(tok, lex) == TOK_RWORD_LET ) { - if_let = true; - // Refutable pattern - pat = Parse_Pattern(lex, true); - GET_CHECK_TOK(tok, lex, TOK_EQUAL); - cond = Parse_Expr0(lex); - } - else { - PUTBACK(tok, lex); - cond = Parse_Expr0(lex); - } - } - - // Contents - ExprNodeP code = Parse_ExprBlockNode(lex); - - // Handle else: - ExprNodeP altcode; - if( GET_TOK(tok, lex) == TOK_RWORD_ELSE ) - { - // Recurse for 'else if' - if( GET_TOK(tok, lex) == TOK_RWORD_IF ) { - altcode = Parse_IfStmt(lex); - } - // - or get block - else { - PUTBACK(tok, lex); - altcode = Parse_ExprBlockNode(lex); - } - } - // - or nothing - else { - PUTBACK(tok, lex); - } - - if( if_let ) - return NEWNODE( AST::ExprNode_IfLet, ::std::move(pat), ::std::move(cond), ::std::move(code), ::std::move(altcode) ); - else - return NEWNODE( AST::ExprNode_If, ::std::move(cond), ::std::move(code), ::std::move(altcode) ); -} -/// "match" block -ExprNodeP Parse_Expr_Match(TokenStream& lex) -{ - TRACE_FUNCTION; - Token tok; - - CLEAR_PARSE_FLAG(lex, disallow_struct_literal); - // 1. Get expression - ExprNodeP switch_val; - { - SET_PARSE_FLAG(lex, disallow_struct_literal); - switch_val = Parse_Expr1(lex); - } - //ASSERT(lex, !CHECK_PARSE_FLAG(lex, disallow_struct_literal) ); - GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); - - ::std::vector< AST::ExprNode_Match_Arm > arms; - do { - if( GET_TOK(tok, lex) == TOK_BRACE_CLOSE ) - break; - PUTBACK(tok, lex); - AST::ExprNode_Match_Arm arm; - - ::AST::MetaItems arm_attrs; - while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) { - GET_TOK(tok, lex); - arm_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - arm.m_attrs = mv$(arm_attrs); - - do { - // Refutable pattern - arm.m_patterns.push_back( Parse_Pattern(lex, true) ); - } while( GET_TOK(tok, lex) == TOK_PIPE ); - - if( tok.type() == TOK_RWORD_IF ) - { - arm.m_cond = Parse_Expr1(lex); - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_FATARROW); - - arm.m_code = Parse_Stmt(lex); - - arms.push_back( ::std::move(arm) ); - - if( GET_TOK(tok, lex) == TOK_COMMA ) - continue; - PUTBACK(tok, lex); - - } while( 1 ); - CHECK_TOK(tok, TOK_BRACE_CLOSE); - - return NEWNODE( AST::ExprNode_Match, ::std::move(switch_val), ::std::move(arms) ); -} - -/// Parses the 'stmt' fragment specifier -/// - Flow control -/// - Expressions -ExprNodeP Parse_Stmt(TokenStream& lex) -{ - TRACE_FUNCTION; - Token tok; - - switch(GET_TOK(tok, lex)) - { - case TOK_INTERPOLATED_STMT: - return tok.take_frag_node(); - // Duplicated here for the :stmt pattern fragment. - case TOK_RWORD_LET: - return Parse_Stmt_Let(lex); - case TOK_RWORD_RETURN: { - ExprNodeP val; - switch(LOOK_AHEAD(lex)) - { - case TOK_SEMICOLON: - case TOK_COMMA: - case TOK_BRACE_CLOSE: - case TOK_PAREN_CLOSE: - case TOK_SQUARE_CLOSE: - break; - default: - val = Parse_Expr0(lex); - break; - } - return NEWNODE( AST::ExprNode_Flow, AST::ExprNode_Flow::RETURN, "", ::std::move(val) ); - } - case TOK_RWORD_CONTINUE: - case TOK_RWORD_BREAK: - { - AST::ExprNode_Flow::Type type; - switch(tok.type()) - { - case TOK_RWORD_CONTINUE: type = AST::ExprNode_Flow::CONTINUE; break; - case TOK_RWORD_BREAK: type = AST::ExprNode_Flow::BREAK; break; - default: throw ParseError::BugCheck(/*lex,*/ "continue/break"); - } - ::std::string lifetime; - if( GET_TOK(tok, lex) == TOK_LIFETIME ) - { - lifetime = tok.str(); - GET_TOK(tok, lex); - } - ExprNodeP val; - switch(tok.type()) - { - case TOK_SEMICOLON: - case TOK_COMMA: - case TOK_BRACE_OPEN: - case TOK_BRACE_CLOSE: - case TOK_PAREN_CLOSE: - case TOK_SQUARE_CLOSE: - PUTBACK(tok, lex); - break; - default: - PUTBACK(tok, lex); - val = Parse_Expr1(lex); - break; - } - return NEWNODE( AST::ExprNode_Flow, type, lifetime, ::std::move(val) ); - } - case TOK_BRACE_OPEN: - PUTBACK(tok, lex); - return Parse_ExprBlockNode(lex); - default: - PUTBACK(tok, lex); - return Parse_Expr0(lex); - } -} - -ExprNodeP Parse_Stmt_Let(TokenStream& lex) -{ - Token tok; - AST::Pattern pat = Parse_Pattern(lex, false); // irrefutable - TypeRef type { lex.getPosition() }; - if( GET_TOK(tok, lex) == TOK_COLON ) { - type = Parse_Type(lex); - GET_TOK(tok, lex); - } - ExprNodeP val; - if( tok.type() == TOK_EQUAL ) { - val = Parse_Expr0(lex); - } - else { - PUTBACK(tok, lex); - } - return NEWNODE( AST::ExprNode_LetBinding, ::std::move(pat), mv$(type), ::std::move(val) ); -} - -::std::vector Parse_ParenList(TokenStream& lex) -{ - TRACE_FUNCTION; - Token tok; - - CLEAR_PARSE_FLAG(lex, disallow_struct_literal); - - ::std::vector rv; - GET_CHECK_TOK(tok, lex, TOK_PAREN_OPEN); - if( GET_TOK(tok, lex) != TOK_PAREN_CLOSE ) - { - PUTBACK(tok, lex); - do { - if( LOOK_AHEAD(lex) == TOK_PAREN_CLOSE ) { - GET_TOK(tok, lex); - break; - } - rv.push_back( Parse_Expr0(lex) ); - } while( GET_TOK(tok, lex) == TOK_COMMA ); - CHECK_TOK(tok, TOK_PAREN_CLOSE); - } - return rv; -} - -// 0: Assign -ExprNodeP Parse_Expr0(TokenStream& lex) -{ - //TRACE_FUNCTION; - Token tok; - - ::AST::MetaItems expr_attrs; - while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) - { - GET_TOK(tok, lex); - expr_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - - ExprNodeP rv = Parse_Expr1(lex); - auto op = AST::ExprNode_Assign::NONE; - switch( GET_TOK(tok, lex) ) - { - case TOK_PLUS_EQUAL: - op = AST::ExprNode_Assign::ADD; if(0) - case TOK_DASH_EQUAL: - op = AST::ExprNode_Assign::SUB; if(0) - case TOK_STAR_EQUAL: - op = AST::ExprNode_Assign::MUL; if(0) - case TOK_SLASH_EQUAL: - op = AST::ExprNode_Assign::DIV; if(0) - case TOK_PERCENT_EQUAL: - op = AST::ExprNode_Assign::MOD; if(0) - - case TOK_AMP_EQUAL: - op = AST::ExprNode_Assign::AND; if(0) - case TOK_PIPE_EQUAL: - op = AST::ExprNode_Assign::OR ; if(0) - case TOK_CARET_EQUAL: - op = AST::ExprNode_Assign::XOR; if(0) - - case TOK_DOUBLE_GT_EQUAL: - op = AST::ExprNode_Assign::SHR; if(0) - case TOK_DOUBLE_LT_EQUAL: - op = AST::ExprNode_Assign::SHL; if(0) - - case TOK_EQUAL: - op = AST::ExprNode_Assign::NONE; - rv = NEWNODE( AST::ExprNode_Assign, op, ::std::move(rv), Parse_Expr0(lex) ); - rv->set_attrs(mv$(expr_attrs)); - return rv; - - default: - PUTBACK(tok, lex); - rv->set_attrs(mv$(expr_attrs)); - return rv; - } -} - - -#define LEFTASSOC(cur, _next, cases) \ -ExprNodeP _next(TokenStream& lex); \ -ExprNodeP cur(TokenStream& lex) \ -{ \ - ExprNodeP (*next)(TokenStream&) = _next;\ - ExprNodeP rv = next(lex); \ - while(true) \ - { \ - Token tok; \ - switch((tok = lex.getToken()).type()) \ - { \ - cases \ - default: \ - /*::std::cout << "<<" << #cur << ::std::endl; */\ - PUTBACK(tok, lex); \ - return rv; \ - } \ - } \ -} -bool Parse_IsTokValue(eTokenType tok_type) -{ - switch( tok_type ) - { - case TOK_DOUBLE_COLON: - case TOK_IDENT: - case TOK_INTEGER: - case TOK_FLOAT: - case TOK_STRING: - case TOK_RWORD_TRUE: - case TOK_RWORD_FALSE: - case TOK_RWORD_SELF: - case TOK_RWORD_SUPER: - case TOK_RWORD_BOX: - case TOK_RWORD_IN: - case TOK_PAREN_OPEN: - case TOK_SQUARE_OPEN: - - case TOK_MACRO: - - case TOK_PIPE: - case TOK_EXCLAM: - case TOK_DASH: - case TOK_STAR: - case TOK_AMP: - return true; - default: - return false; - } - -} -ExprNodeP Parse_Expr1_1(TokenStream& lex); -// Very evil handling for '..' -ExprNodeP Parse_Expr1(TokenStream& lex) -{ - Token tok; - ExprNodeP (*next)(TokenStream&) = Parse_Expr1_1; - ExprNodeP left, right; - - // Inclusive range to a value - if( GET_TOK(tok, lex) == TOK_TRIPLE_DOT ) { - right = next(lex); - return NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::RANGE_INC, nullptr, mv$(right) ); - } - else { - PUTBACK(tok, lex); - } - - // Exclusive ranges - // - If NOT `.. `, parse a leading value - if( GET_TOK(tok, lex) != TOK_DOUBLE_DOT ) - { - PUTBACK(tok, lex); - - left = next(lex); - - // - If NOT ` ..`, return the value - if( GET_TOK(tok, lex) != TOK_DOUBLE_DOT ) - { - PUTBACK(tok, lex); - return ::std::move(left); - } - } - assert( tok.type() == TOK_DOUBLE_DOT ); - // If the next token is part of a value, parse that value - if( Parse_IsTokValue( LOOK_AHEAD(lex) ) ) - { - right = next(lex); - } - else - { - // Otherwise, leave `right` as nullptr - } - - return NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::RANGE, ::std::move(left), ::std::move(right) ); -} -// TODO: Is this left associative? -LEFTASSOC(Parse_Expr1_1, Parse_Expr1_5, - case TOK_TRIPLE_DOT: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::RANGE_INC, mv$(rv), next(lex) ); - break; -) -// 1: Bool OR -LEFTASSOC(Parse_Expr1_5, Parse_Expr2, - case TOK_DOUBLE_PIPE: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BOOLOR, ::std::move(rv), next(lex)); - break; -) -// 2: Bool AND -LEFTASSOC(Parse_Expr2, Parse_Expr3, - case TOK_DOUBLE_AMP: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BOOLAND, ::std::move(rv), next(lex)); - break; -) -// 3: (In)Equality -LEFTASSOC(Parse_Expr3, Parse_Expr4, - case TOK_DOUBLE_EQUAL: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPEQU, ::std::move(rv), next(lex)); - break; - case TOK_EXCLAM_EQUAL: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPNEQU, ::std::move(rv), next(lex)); - break; -) -// 4: Comparisons -LEFTASSOC(Parse_Expr4, Parse_Expr5, - case TOK_LT: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPLT, ::std::move(rv), next(lex)); - break; - case TOK_GT: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPGT, ::std::move(rv), next(lex)); - break; - case TOK_LTE: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPLTE, ::std::move(rv), next(lex)); - break; - case TOK_GTE: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPGTE, ::std::move(rv), next(lex)); - break; -) -// 5: Bit OR -LEFTASSOC(Parse_Expr5, Parse_Expr6, - case TOK_PIPE: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BITOR, ::std::move(rv), next(lex)); - break; -) -// 6: Bit XOR -LEFTASSOC(Parse_Expr6, Parse_Expr7, - case TOK_CARET: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BITXOR, ::std::move(rv), next(lex)); - break; -) -// 7: Bit AND -LEFTASSOC(Parse_Expr7, Parse_Expr8, - case TOK_AMP: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BITAND, ::std::move(rv), next(lex)); - break; -) -// 8: Bit Shifts -LEFTASSOC(Parse_Expr8, Parse_Expr9, - case TOK_DOUBLE_LT: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::SHL, ::std::move(rv), next(lex)); - break; - case TOK_DOUBLE_GT: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::SHR, ::std::move(rv), next(lex)); - break; -) -// 9: Add / Subtract -LEFTASSOC(Parse_Expr9, Parse_Expr10, - case TOK_PLUS: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::ADD, ::std::move(rv), next(lex)); - break; - case TOK_DASH: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::SUB, ::std::move(rv), next(lex)); - break; -) -// 10: Times / Divide / Modulo -LEFTASSOC(Parse_Expr10, Parse_Expr11, - case TOK_STAR: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::MULTIPLY, ::std::move(rv), next(lex)); - break; - case TOK_SLASH: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::DIVIDE, ::std::move(rv), next(lex)); - break; - case TOK_PERCENT: - rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::MODULO, ::std::move(rv), next(lex)); - break; -) -// 11: Cast -LEFTASSOC(Parse_Expr11, Parse_Expr12, - case TOK_RWORD_AS: - rv = NEWNODE( AST::ExprNode_Cast, ::std::move(rv), Parse_Type(lex, false) ); - break; -) -// 12: Type Ascription -ExprNodeP Parse_Expr13(TokenStream& lex); -ExprNodeP Parse_Expr12(TokenStream& lex) -{ - Token tok; - auto rv = Parse_Expr13(lex); - if(GET_TOK(tok, lex) == TOK_COLON) - { - rv = NEWNODE( AST::ExprNode_TypeAnnotation, mv$(rv), Parse_Type(lex) ); - } - else - { - PUTBACK(tok, lex); - } - return rv; -} -// 13: Unaries -ExprNodeP Parse_ExprFC(TokenStream& lex); -ExprNodeP Parse_Expr13(TokenStream& lex) -{ - Token tok; - switch(GET_TOK(tok, lex)) - { - case TOK_DASH: - return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::NEGATE, Parse_Expr12(lex) ); - case TOK_EXCLAM: - return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::INVERT, Parse_Expr12(lex) ); - case TOK_STAR: - return NEWNODE( AST::ExprNode_Deref, Parse_Expr12(lex) ); - case TOK_RWORD_BOX: - return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::BOX, Parse_Expr12(lex) ); - case TOK_RWORD_IN: { - ExprNodeP dest; - { - SET_PARSE_FLAG(lex, disallow_struct_literal); - dest = Parse_Expr1(lex); - } - auto val = Parse_ExprBlockNode(lex); - return NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::PLACE_IN, mv$(dest), mv$(val)); - } - case TOK_DOUBLE_AMP: - // HACK: Split && into & & - lex.putback( Token(TOK_AMP) ); - case TOK_AMP: - if( GET_TOK(tok, lex) == TOK_RWORD_MUT ) - return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::REFMUT, Parse_Expr12(lex) ); - else { - PUTBACK(tok, lex); - return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::REF, Parse_Expr12(lex) ); - } - default: - PUTBACK(tok, lex); - return Parse_ExprFC(lex); - } -} - -ExprNodeP Parse_ExprVal(TokenStream& lex); -ExprNodeP Parse_ExprFC(TokenStream& lex) -{ - ExprNodeP val = Parse_ExprVal(lex); - while(true) - { - Token tok; - switch(GET_TOK(tok, lex)) - { - case TOK_QMARK: - val = NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::QMARK, mv$(val) ); - break; - - case TOK_PAREN_OPEN: - // Expression method call - PUTBACK(tok, lex); - val = NEWNODE( AST::ExprNode_CallObject, ::std::move(val), Parse_ParenList(lex) ); - break; - case TOK_SQUARE_OPEN: - val = NEWNODE( AST::ExprNode_Index, ::std::move(val), Parse_Expr0(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - break; - case TOK_DOT: - // Field access / method call - // TODO: What about tuple indexing? - switch(GET_TOK(tok, lex)) - { - case TOK_IDENT: { - AST::PathNode path( mv$(tok.str()) , {}); - switch( GET_TOK(tok, lex) ) - { - case TOK_PAREN_OPEN: - PUTBACK(tok, lex); - val = NEWNODE( AST::ExprNode_CallMethod, ::std::move(val), ::std::move(path), Parse_ParenList(lex) ); - break; - case TOK_DOUBLE_COLON: - GET_CHECK_TOK(tok, lex, TOK_LT); - path.args() = Parse_Path_GenericList(lex); - val = NEWNODE( AST::ExprNode_CallMethod, ::std::move(val), ::std::move(path), Parse_ParenList(lex) ); - break; - default: - val = NEWNODE( AST::ExprNode_Field, ::std::move(val), ::std::string(path.name()) ); - PUTBACK(tok, lex); - break; - } - break; } - case TOK_INTEGER: - val = NEWNODE( AST::ExprNode_Field, ::std::move(val), FMT(tok.intval()) ); - break; - default: - throw ParseError::Unexpected(lex, mv$(tok)); - } - break; - default: - PUTBACK(tok, lex); - return val; - } - } -} - -ExprNodeP Parse_ExprVal_StructLiteral(TokenStream& lex, AST::Path path) -{ - TRACE_FUNCTION; - Token tok; - - // #![feature(relaxed_adts)] - if( LOOK_AHEAD(lex) == TOK_INTEGER ) - { - ::std::map nodes; - while( GET_TOK(tok, lex) == TOK_INTEGER ) - { - unsigned int ofs = tok.intval(); - GET_CHECK_TOK(tok, lex, TOK_COLON); - ExprNodeP val = Parse_Stmt(lex); - if( ! nodes.insert( ::std::make_pair(ofs, mv$(val)) ).second ) { - ERROR(lex.getPosition(), E0000, "Duplicate index"); - } - - if( GET_TOK(tok,lex) == TOK_BRACE_CLOSE ) - break; - CHECK_TOK(tok, TOK_COMMA); - } - CHECK_TOK(tok, TOK_BRACE_CLOSE); - - ::std::vector items; - unsigned int i = 0; - for(auto& p : nodes) - { - if( p.first != i ) { - ERROR(lex.getPosition(), E0000, "Missing index " << i); - } - items.push_back( mv$(p.second) ); - i ++; - } - - return NEWNODE( AST::ExprNode_CallPath, mv$(path), mv$(items) ); - } - - // Braced structure literal - // - A series of 0 or more pairs of : , - // - '..' - ::std::vector< ::std::pair< ::std::string, ::std::unique_ptr> > items; - while( GET_TOK(tok, lex) == TOK_IDENT ) - { - auto name = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_COLON); - ExprNodeP val = Parse_Stmt(lex); - items.push_back( ::std::make_pair(::std::move(name), ::std::move(val)) ); - - if( GET_TOK(tok,lex) == TOK_BRACE_CLOSE ) - break; - CHECK_TOK(tok, TOK_COMMA); - } - ExprNodeP base_val; - if( tok.type() == TOK_DOUBLE_DOT ) - { - // default - base_val = Parse_Expr0(lex); - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_BRACE_CLOSE); - - return NEWNODE( AST::ExprNode_StructLiteral, path, ::std::move(base_val), ::std::move(items) ); -} - -ExprNodeP Parse_ExprVal_Closure(TokenStream& lex, bool is_move) -{ - TRACE_FUNCTION; - Token tok; - - ::std::vector< ::std::pair > args; - - while( GET_TOK(tok, lex) != TOK_PIPE ) - { - PUTBACK(tok, lex); - // Irrefutable pattern - AST::Pattern pat = Parse_Pattern(lex, false); - - TypeRef type { lex.getPosition() }; - if( GET_TOK(tok, lex) == TOK_COLON ) - type = Parse_Type(lex); - else - PUTBACK(tok, lex); - - args.push_back( ::std::make_pair( ::std::move(pat), ::std::move(type) ) ); - - if( GET_TOK(tok, lex) != TOK_COMMA ) - break; - } - CHECK_TOK(tok, TOK_PIPE); - - auto rt = TypeRef(lex.getPosition()); - if( GET_TOK(tok, lex) == TOK_THINARROW ) { - - if( GET_TOK(tok, lex) == TOK_EXCLAM ) { - rt = TypeRef(TypeRef::TagInvalid(), Span(tok.get_pos())); - } - else { - PUTBACK(tok, lex); - rt = Parse_Type(lex); - } - } - else - PUTBACK(tok, lex); - - auto code = Parse_Expr0(lex); - - return NEWNODE( AST::ExprNode_Closure, ::std::move(args), ::std::move(rt), ::std::move(code), is_move ); -} - -ExprNodeP Parse_ExprVal(TokenStream& lex) -{ - TRACE_FUNCTION; - - Token tok; - AST::Path path; - switch( GET_TOK(tok, lex) ) - { - case TOK_BRACE_OPEN: - PUTBACK(tok, lex); - return Parse_ExprBlockNode(lex); - - case TOK_INTERPOLATED_EXPR: - case TOK_INTERPOLATED_BLOCK: - return tok.take_frag_node(); - - - // TODO: Return/break/continue/... here? - case TOK_RWORD_RETURN: - case TOK_RWORD_CONTINUE: - case TOK_RWORD_BREAK: - PUTBACK(tok, lex); - return Parse_Stmt(lex); - - case TOK_RWORD_LOOP: - return NEWNODE( AST::ExprNode_Loop, "", Parse_ExprBlockNode(lex) ); - case TOK_RWORD_WHILE: - return Parse_WhileStmt(lex, ""); - case TOK_RWORD_FOR: - return Parse_ForStmt(lex, ""); - case TOK_RWORD_MATCH: - return Parse_Expr_Match(lex); - case TOK_RWORD_IF: - return Parse_IfStmt(lex); - case TOK_RWORD_UNSAFE: - return Parse_ExprBlockNode(lex, true); - - // UFCS - case TOK_DOUBLE_LT: - case TOK_LT: - PUTBACK(tok, lex); - path = Parse_Path(lex, PATH_GENERIC_EXPR); - // Skip down to method - if(0) - case TOK_RWORD_SELF: - { - if( LOOK_AHEAD(lex) != TOK_DOUBLE_COLON ) { - return NEWNODE( AST::ExprNode_NamedValue, AST::Path(AST::Path::TagLocal(), "self") ); - } - else - { - PUTBACK(tok, lex); - path = Parse_Path(lex, PATH_GENERIC_EXPR); - } - } - if(0) - case TOK_RWORD_SUPER: - { - PUTBACK(tok, lex); - path = Parse_Path(lex, PATH_GENERIC_EXPR); - } - if(0) - case TOK_IDENT: - // Get path - { - PUTBACK(tok, lex); - path = Parse_Path(lex, false, PATH_GENERIC_EXPR); - } - if(0) - case TOK_INTERPOLATED_PATH: - { - path = mv$(tok.frag_path()); - } - if(0) - case TOK_DOUBLE_COLON: - path = Parse_Path(lex, true, PATH_GENERIC_EXPR); - // SKIP TARGET - switch( GET_TOK(tok, lex) ) - { - case TOK_PAREN_OPEN: - // Function call - PUTBACK(tok, lex); - return NEWNODE( AST::ExprNode_CallPath, ::std::move(path), Parse_ParenList(lex) ); - case TOK_BRACE_OPEN: - if( !CHECK_PARSE_FLAG(lex, disallow_struct_literal) ) - return Parse_ExprVal_StructLiteral(lex, ::std::move(path)); - else - DEBUG("Not parsing struct literal"); - default: - // Value - PUTBACK(tok, lex); - return NEWNODE( AST::ExprNode_NamedValue, ::std::move(path) ); - } - case TOK_RWORD_MOVE: - // TODO: Annotate closure as move - GET_TOK(tok, lex); - if(tok.type() == TOK_PIPE) - return Parse_ExprVal_Closure(lex, true); - else if(tok.type() == TOK_DOUBLE_PIPE) { - lex.putback(Token(TOK_PIPE)); - return Parse_ExprVal_Closure(lex, true); - } - else { - CHECK_TOK(tok, TOK_PIPE); - } - case TOK_DOUBLE_PIPE: - lex.putback(Token(TOK_PIPE)); - case TOK_PIPE: - return Parse_ExprVal_Closure(lex, false); - case TOK_INTEGER: - return NEWNODE( AST::ExprNode_Integer, tok.intval(), tok.datatype() ); - case TOK_FLOAT: - return NEWNODE( AST::ExprNode_Float, tok.floatval(), tok.datatype() ); - case TOK_STRING: - return NEWNODE( AST::ExprNode_String, tok.str() ); - case TOK_BYTESTRING: - return NEWNODE( AST::ExprNode_ByteString, tok.str() ); - case TOK_RWORD_TRUE: - return NEWNODE( AST::ExprNode_Bool, true ); - case TOK_RWORD_FALSE: - return NEWNODE( AST::ExprNode_Bool, false ); - case TOK_PAREN_OPEN: - if( GET_TOK(tok, lex) == TOK_PAREN_CLOSE ) - { - DEBUG("Unit"); - return NEWNODE( AST::ExprNode_Tuple, ::std::vector() ); - } - else - { - CLEAR_PARSE_FLAG(lex, disallow_struct_literal); - PUTBACK(tok, lex); - - ExprNodeP rv = Parse_Expr0(lex); - if( GET_TOK(tok, lex) == TOK_COMMA ) { - ::std::vector ents; - ents.push_back( ::std::move(rv) ); - do { - if( GET_TOK(tok, lex) == TOK_PAREN_CLOSE ) - break; - PUTBACK(tok, lex); - ents.push_back( Parse_Expr0(lex) ); - } while( GET_TOK(tok, lex) == TOK_COMMA ); - rv = NEWNODE( AST::ExprNode_Tuple, ::std::move(ents) ); - } - CHECK_TOK(tok, TOK_PAREN_CLOSE); - return rv; - } - case TOK_SQUARE_OPEN: - if( GET_TOK(tok, lex) == TOK_SQUARE_CLOSE ) - { - // Empty literal - return NEWNODE( AST::ExprNode_Array, ::std::vector() ); - } - else - { - PUTBACK(tok, lex); - auto first = Parse_Expr0(lex); - if( GET_TOK(tok, lex) == TOK_SEMICOLON ) - { - // Repetiion - auto count = Parse_Expr0(lex); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - return NEWNODE( AST::ExprNode_Array, ::std::move(first), ::std::move(count) ); - } - else - { - ::std::vector items; - items.push_back( ::std::move(first) ); - while( tok.type() == TOK_COMMA ) - { - if( GET_TOK(tok, lex) == TOK_SQUARE_CLOSE ) - break; - else - PUTBACK(tok, lex); - items.push_back( Parse_Expr0(lex) ); - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_SQUARE_CLOSE); - return NEWNODE( AST::ExprNode_Array, ::std::move(items) ); - } - } - throw ParseError::BugCheck(lex, "Array literal fell"); - case TOK_MACRO: - return Parse_ExprMacro(lex, mv$(tok)); - default: - throw ParseError::Unexpected(lex, tok); - } -} -ExprNodeP Parse_ExprMacro(TokenStream& lex, Token tok) -{ - ::std::string name = tok.str(); - ::std::string ident; - if( GET_TOK(tok, lex) == TOK_IDENT ) { - ident = mv$(tok.str()); - } - else { - PUTBACK(tok, lex); - } - TokenTree tt = Parse_TT(lex, true); - if( tt.is_token() ) { - throw ParseError::Unexpected(lex, tt.tok()); - } - return NEWNODE(AST::ExprNode_Macro, mv$(name), mv$(ident), mv$(tt)); -} - -// Token Tree Parsing -TokenTree Parse_TT(TokenStream& lex, bool unwrapped) -{ - TokenTree rv; - TRACE_FUNCTION_FR("", rv); - - Token tok = lex.getToken(); - eTokenType closer = TOK_PAREN_CLOSE; - switch(tok.type()) - { - case TOK_PAREN_OPEN: - closer = TOK_PAREN_CLOSE; - break; - case TOK_SQUARE_OPEN: - closer = TOK_SQUARE_CLOSE; - break; - case TOK_BRACE_OPEN: - closer = TOK_BRACE_CLOSE; - break; - // HACK! mrustc parses #[ and #![ as composite tokens - // TODO: Split these into their component tokens. - case TOK_ATTR_OPEN: - case TOK_CATTR_OPEN: - if( unwrapped ) - throw ParseError::Unexpected(lex, tok); - closer = TOK_SQUARE_CLOSE; - break; - - case TOK_EOF: - case TOK_NULL: - case TOK_PAREN_CLOSE: - case TOK_SQUARE_CLOSE: - case TOK_BRACE_CLOSE: - throw ParseError::Unexpected(lex, tok); - default: - rv = TokenTree(lex.getHygiene(), mv$(tok) ); - return rv; - } - - ::std::vector items; - if( !unwrapped ) - items.push_back( TokenTree(lex.getHygiene(), mv$(tok)) ); - while(GET_TOK(tok, lex) != closer && tok.type() != TOK_EOF) - { - if( tok.type() == TOK_NULL ) - throw ParseError::Unexpected(lex, tok); - PUTBACK(tok, lex); - items.push_back(Parse_TT(lex, false)); - } - if( !unwrapped ) - items.push_back( TokenTree(lex.getHygiene(), mv$(tok)) ); - rv = TokenTree(lex.getHygiene(), mv$(items)); - return rv; -} +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * parse/expr.cpp + * - Expression (i.e. code) parsing + * + * Start points: + * - Parse_ExprBlockNode : Parses a block + * - Parse_Stmt : Parse a single statement + * - Parse_Expr0 : Parse a single expression + */ +#include "parseerror.hpp" +#include +#include +#include "common.hpp" +#include +#include "tokentree.hpp" +#include "interpolated_fragment.hpp" + +using AST::ExprNode; +using AST::ExprNodeP; +static inline ExprNodeP mk_exprnodep(const TokenStream& lex, AST::ExprNode* en){en->set_pos(lex.getPosition()); return ExprNodeP(en); } +#define NEWNODE(type, ...) mk_exprnodep(lex, new type(__VA_ARGS__)) + +//ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe=false); // common.hpp +//ExprNodeP Parse_ExprBlockLine_WithItems(TokenStream& lex, ::std::shared_ptr& local_mod, bool& add_silence_if_end); +//ExprNodeP Parse_ExprBlockLine(TokenStream& lex, bool *add_silence); +ExprNodeP Parse_ExprBlockLine_Stmt(TokenStream& lex, bool& has_semicolon); +//ExprNodeP Parse_Stmt(TokenStream& lex); // common.hpp +ExprNodeP Parse_Stmt_Let(TokenStream& lex); +ExprNodeP Parse_Expr0(TokenStream& lex); +ExprNodeP Parse_IfStmt(TokenStream& lex); +ExprNodeP Parse_WhileStmt(TokenStream& lex, ::std::string lifetime); +ExprNodeP Parse_ForStmt(TokenStream& lex, ::std::string lifetime); +ExprNodeP Parse_Expr_Match(TokenStream& lex); +ExprNodeP Parse_Expr1(TokenStream& lex); +ExprNodeP Parse_ExprMacro(TokenStream& lex, Token tok); + +AST::Expr Parse_Expr(TokenStream& lex) +{ + return ::AST::Expr( Parse_Expr0(lex) ); +} + +AST::Expr Parse_ExprBlock(TokenStream& lex) +{ + return ::AST::Expr( Parse_ExprBlockNode(lex) ); +} + +ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe/*=false*/) +{ + TRACE_FUNCTION; + Token tok; + + bool yields_final_value = true; + ::std::vector nodes; + + ::std::shared_ptr local_mod; + + GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); + + while( LOOK_AHEAD(lex) != TOK_BRACE_CLOSE ) + { + DEBUG("tok = " << tok); + + // NOTE: Doc comments can appear within a function and apply to the function + // TODO: Use these attributes + while( GET_TOK(tok, lex) == TOK_CATTR_OPEN ) + { + /*node_attrs.push_back(*/ Parse_MetaItem(lex) /*)*/; + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + PUTBACK(tok, lex); + if( LOOK_AHEAD(lex) == TOK_BRACE_CLOSE ) + break; + + bool add_silence_if_end = false; + auto rv = Parse_ExprBlockLine_WithItems(lex, local_mod, add_silence_if_end); + if( rv ) + { + // Set to TRUE if there was no semicolon after a statement + if( LOOK_AHEAD(lex) == TOK_BRACE_CLOSE && add_silence_if_end ) + { + DEBUG("End of block, and add_silence_if_end == true - doesn't yeild"); + yields_final_value = false; + // Since the next token is TOK_BRACE_CLOSE, the loop will terminate + } + nodes.push_back( mv$(rv) ); + } + else { + assert( !add_silence_if_end ); + } + } + GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); + + return NEWNODE( AST::ExprNode_Block, is_unsafe, yields_final_value, mv$(nodes), mv$(local_mod) ); +} + +/// Parse a single line in a block, handling items added to the local module +/// +/// - If an item was parsed, this returns an empty ExprNodeP +ExprNodeP Parse_ExprBlockLine_WithItems(TokenStream& lex, ::std::shared_ptr& local_mod, bool& add_silence_if_end) +{ + Token tok; + + AST::MetaItems item_attrs; + while( GET_TOK(tok, lex) == TOK_ATTR_OPEN ) + { + item_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + + switch(tok.type()) + { + // Items: + case TOK_RWORD_PUB: + // NOTE: Allowed, but doesn't do much + case TOK_RWORD_TYPE: + case TOK_RWORD_USE: + case TOK_RWORD_EXTERN: + case TOK_RWORD_CONST: + case TOK_RWORD_STATIC: + case TOK_RWORD_STRUCT: + case TOK_RWORD_ENUM: + case TOK_RWORD_TRAIT: + case TOK_RWORD_IMPL: + case TOK_RWORD_FN: + case TOK_RWORD_MOD: + PUTBACK(tok, lex); + if( !local_mod ) { + local_mod = lex.parse_state().get_current_mod().add_anon(); + } + Parse_Mod_Item(lex, *local_mod, mv$(item_attrs)); + return ExprNodeP(); + // 'unsafe' - Check if the next token isn't a `{`, if so it's an item. Otherwise, fall through + case TOK_RWORD_UNSAFE: + if( LOOK_AHEAD(lex) != TOK_BRACE_OPEN ) + { + PUTBACK(tok, lex); + if( !local_mod ) { + local_mod = lex.parse_state().get_current_mod().add_anon(); + } + Parse_Mod_Item(lex, *local_mod, mv$(item_attrs)); + return ExprNodeP(); + } + // fall + default: { + PUTBACK(tok, lex); + auto rv = Parse_ExprBlockLine(lex, &add_silence_if_end); + if( rv ) { + rv->set_attrs( mv$(item_attrs) ); + } + else if( item_attrs.m_items.size() > 0 ) { + // TODO: Is this an error? - Attributes on a expression that didn't yeild a node. + } + else { + } + return rv; + } break; + } +} + +/// Parse a single line from a block +/// +/// Handles: +/// - Block-level constructs (with lifetime annotations) +/// - use/extern/const/let +ExprNodeP Parse_ExprBlockLine(TokenStream& lex, bool *add_silence) +{ + Token tok; + ExprNodeP ret; + + if( GET_TOK(tok, lex) == TOK_LIFETIME ) + { + // Lifetimes can only precede loops... and blocks? + ::std::string lifetime = tok.str(); + GET_CHECK_TOK(tok, lex, TOK_COLON); + + switch( GET_TOK(tok, lex) ) + { + case TOK_RWORD_LOOP: + return NEWNODE( AST::ExprNode_Loop, lifetime, Parse_ExprBlockNode(lex) ); + case TOK_RWORD_WHILE: + return Parse_WhileStmt(lex, lifetime); + case TOK_RWORD_FOR: + return Parse_ForStmt(lex, lifetime); + //case TOK_RWORD_IF: + // return Parse_IfStmt(lex); + //case TOK_RWORD_MATCH: + // return Parse_Expr_Match(lex); + //case TOK_BRACE_OPEN: + // PUTBACK(tok, lex); + // return Parse_ExprBlockNode(lex); + + default: + throw ParseError::Unexpected(lex, tok); + } + } + else + { + switch( tok.type() ) + { + case TOK_INTERPOLATED_BLOCK: + return tok.take_frag_node(); + case TOK_SEMICOLON: + // Return a NULL expression, nothing here. + return nullptr; + + // let binding + case TOK_RWORD_LET: + ret = Parse_Stmt_Let(lex); + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + return ret; + + // Blocks that don't need semicolons + // NOTE: If these are followed by a small set of tokens (`.` and `?`) then they are actually the start of an expression + // HACK: Parse here, but if the next token is one of the set store in a TOK_INTERPOLATED_EXPR and invoke the statement parser + case TOK_RWORD_LOOP: + ret = NEWNODE( AST::ExprNode_Loop, "", Parse_ExprBlockNode(lex) ); + if(0) + case TOK_RWORD_WHILE: + ret = Parse_WhileStmt(lex, ""); + if(0) + case TOK_RWORD_FOR: + ret = Parse_ForStmt(lex, ""); + if(0) + case TOK_RWORD_IF: + ret = Parse_IfStmt(lex); + if(0) + case TOK_RWORD_MATCH: + ret = Parse_Expr_Match(lex); + if(0) + case TOK_RWORD_UNSAFE: + ret = Parse_ExprBlockNode(lex, true); + if(0) + case TOK_BRACE_OPEN: + { PUTBACK(tok, lex); ret = Parse_ExprBlockNode(lex); } + + if( lex.lookahead(0) == TOK_DOT || lex.lookahead(0) == TOK_QMARK ) { + lex.putback( Token(Token::TagTakeIP(), InterpolatedFragment(InterpolatedFragment::EXPR, ret.release())) ); + return Parse_ExprBlockLine_Stmt(lex, *add_silence); + } + + if( LOOK_AHEAD(lex) == TOK_SEMICOLON ) { + GET_TOK(tok, lex); + *add_silence = true; + } + + return ret; + + // Flow control + case TOK_RWORD_RETURN: + case TOK_RWORD_CONTINUE: + case TOK_RWORD_BREAK: { + PUTBACK(tok, lex); + auto ret = Parse_Stmt(lex); + if( LOOK_AHEAD(lex) == TOK_EOF ) { + } + else if( GET_TOK(tok, lex) != TOK_SEMICOLON ) { + CHECK_TOK(tok, TOK_BRACE_CLOSE); + PUTBACK(tok, lex); + } + else { + // return/continue/break don't need silencing + } + return ret; + } + + case TOK_MACRO: + // If a braced macro invocation is the first part of a statement, don't expect a semicolon + if( LOOK_AHEAD(lex) == TOK_BRACE_OPEN || (lex.lookahead(0) == TOK_IDENT && lex.lookahead(1) == TOK_BRACE_OPEN) ) { + return Parse_ExprMacro(lex, tok); + } + // Fall through to the statement code + default: + PUTBACK(tok, lex); + return Parse_ExprBlockLine_Stmt(lex, *add_silence); + } + } +} + +ExprNodeP Parse_ExprBlockLine_Stmt(TokenStream& lex, bool& has_semicolon) +{ + Token tok; + auto ret = Parse_Stmt(lex); + // If this expression statement wasn't followed by a semicolon, then it's yielding its value out of the block. + // - I.e. The block should be ending + if( GET_TOK(tok, lex) != TOK_SEMICOLON ) { + // - Allow TOK_EOF for macro expansion. + if( tok.type() == TOK_EOF ) + ; + else + CHECK_TOK(tok, TOK_BRACE_CLOSE); + PUTBACK(tok, lex); + } + else { + has_semicolon = true; + } + return ret; +} + +/// While loop (either as a statement, or as part of an expression) +ExprNodeP Parse_WhileStmt(TokenStream& lex, ::std::string lifetime) +{ + Token tok; + + if( GET_TOK(tok, lex) == TOK_RWORD_LET ) { + auto pat = Parse_Pattern(lex, true); // Refutable pattern + GET_CHECK_TOK(tok, lex, TOK_EQUAL); + ExprNodeP val; + { + SET_PARSE_FLAG(lex, disallow_struct_literal); + val = Parse_Expr0(lex); + } + return NEWNODE( AST::ExprNode_Loop, lifetime, AST::ExprNode_Loop::WHILELET, + ::std::move(pat), ::std::move(val), Parse_ExprBlockNode(lex) ); + } + else { + PUTBACK(tok, lex); + ExprNodeP cnd; + { + SET_PARSE_FLAG(lex, disallow_struct_literal); + cnd = Parse_Expr1(lex); + } + return NEWNODE( AST::ExprNode_Loop, lifetime, ::std::move(cnd), Parse_ExprBlockNode(lex) ); + } +} +/// For loop (either as a statement, or as part of an expression) +ExprNodeP Parse_ForStmt(TokenStream& lex, ::std::string lifetime) +{ + Token tok; + + // Irrefutable pattern + AST::Pattern pat = Parse_Pattern(lex, false); + GET_CHECK_TOK(tok, lex, TOK_RWORD_IN); + ExprNodeP val; + { + SET_PARSE_FLAG(lex, disallow_struct_literal); + val = Parse_Expr0(lex); + } + return NEWNODE( AST::ExprNode_Loop, lifetime, AST::ExprNode_Loop::FOR, + ::std::move(pat), ::std::move(val), Parse_ExprBlockNode(lex) ); +} +/// Parse an 'if' statement +// Note: TOK_RWORD_IF has already been eaten +ExprNodeP Parse_IfStmt(TokenStream& lex) +{ + TRACE_FUNCTION; + + Token tok; + ExprNodeP cond; + AST::Pattern pat; + bool if_let = false; + + { + SET_PARSE_FLAG(lex, disallow_struct_literal); + if( GET_TOK(tok, lex) == TOK_RWORD_LET ) { + if_let = true; + // Refutable pattern + pat = Parse_Pattern(lex, true); + GET_CHECK_TOK(tok, lex, TOK_EQUAL); + cond = Parse_Expr0(lex); + } + else { + PUTBACK(tok, lex); + cond = Parse_Expr0(lex); + } + } + + // Contents + ExprNodeP code = Parse_ExprBlockNode(lex); + + // Handle else: + ExprNodeP altcode; + if( GET_TOK(tok, lex) == TOK_RWORD_ELSE ) + { + // Recurse for 'else if' + if( GET_TOK(tok, lex) == TOK_RWORD_IF ) { + altcode = Parse_IfStmt(lex); + } + // - or get block + else { + PUTBACK(tok, lex); + altcode = Parse_ExprBlockNode(lex); + } + } + // - or nothing + else { + PUTBACK(tok, lex); + } + + if( if_let ) + return NEWNODE( AST::ExprNode_IfLet, ::std::move(pat), ::std::move(cond), ::std::move(code), ::std::move(altcode) ); + else + return NEWNODE( AST::ExprNode_If, ::std::move(cond), ::std::move(code), ::std::move(altcode) ); +} +/// "match" block +ExprNodeP Parse_Expr_Match(TokenStream& lex) +{ + TRACE_FUNCTION; + Token tok; + + CLEAR_PARSE_FLAG(lex, disallow_struct_literal); + // 1. Get expression + ExprNodeP switch_val; + { + SET_PARSE_FLAG(lex, disallow_struct_literal); + switch_val = Parse_Expr1(lex); + } + //ASSERT(lex, !CHECK_PARSE_FLAG(lex, disallow_struct_literal) ); + GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); + + ::std::vector< AST::ExprNode_Match_Arm > arms; + do { + if( GET_TOK(tok, lex) == TOK_BRACE_CLOSE ) + break; + PUTBACK(tok, lex); + AST::ExprNode_Match_Arm arm; + + ::AST::MetaItems arm_attrs; + while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) { + GET_TOK(tok, lex); + arm_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + arm.m_attrs = mv$(arm_attrs); + + do { + // Refutable pattern + arm.m_patterns.push_back( Parse_Pattern(lex, true) ); + } while( GET_TOK(tok, lex) == TOK_PIPE ); + + if( tok.type() == TOK_RWORD_IF ) + { + arm.m_cond = Parse_Expr1(lex); + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_FATARROW); + + arm.m_code = Parse_Stmt(lex); + + arms.push_back( ::std::move(arm) ); + + if( GET_TOK(tok, lex) == TOK_COMMA ) + continue; + PUTBACK(tok, lex); + + } while( 1 ); + CHECK_TOK(tok, TOK_BRACE_CLOSE); + + return NEWNODE( AST::ExprNode_Match, ::std::move(switch_val), ::std::move(arms) ); +} + +/// Parses the 'stmt' fragment specifier +/// - Flow control +/// - Expressions +ExprNodeP Parse_Stmt(TokenStream& lex) +{ + TRACE_FUNCTION; + Token tok; + + switch(GET_TOK(tok, lex)) + { + case TOK_INTERPOLATED_STMT: + return tok.take_frag_node(); + // Duplicated here for the :stmt pattern fragment. + case TOK_RWORD_LET: + return Parse_Stmt_Let(lex); + case TOK_RWORD_RETURN: { + ExprNodeP val; + switch(LOOK_AHEAD(lex)) + { + case TOK_SEMICOLON: + case TOK_COMMA: + case TOK_BRACE_CLOSE: + case TOK_PAREN_CLOSE: + case TOK_SQUARE_CLOSE: + break; + default: + val = Parse_Expr0(lex); + break; + } + return NEWNODE( AST::ExprNode_Flow, AST::ExprNode_Flow::RETURN, "", ::std::move(val) ); + } + case TOK_RWORD_CONTINUE: + case TOK_RWORD_BREAK: + { + AST::ExprNode_Flow::Type type; + switch(tok.type()) + { + case TOK_RWORD_CONTINUE: type = AST::ExprNode_Flow::CONTINUE; break; + case TOK_RWORD_BREAK: type = AST::ExprNode_Flow::BREAK; break; + default: throw ParseError::BugCheck(/*lex,*/ "continue/break"); + } + ::std::string lifetime; + if( GET_TOK(tok, lex) == TOK_LIFETIME ) + { + lifetime = tok.str(); + GET_TOK(tok, lex); + } + ExprNodeP val; + switch(tok.type()) + { + case TOK_SEMICOLON: + case TOK_COMMA: + case TOK_BRACE_OPEN: + case TOK_BRACE_CLOSE: + case TOK_PAREN_CLOSE: + case TOK_SQUARE_CLOSE: + PUTBACK(tok, lex); + break; + default: + PUTBACK(tok, lex); + val = Parse_Expr1(lex); + break; + } + return NEWNODE( AST::ExprNode_Flow, type, lifetime, ::std::move(val) ); + } + case TOK_BRACE_OPEN: + PUTBACK(tok, lex); + return Parse_ExprBlockNode(lex); + default: + PUTBACK(tok, lex); + return Parse_Expr0(lex); + } +} + +ExprNodeP Parse_Stmt_Let(TokenStream& lex) +{ + Token tok; + AST::Pattern pat = Parse_Pattern(lex, false); // irrefutable + TypeRef type { lex.getPosition() }; + if( GET_TOK(tok, lex) == TOK_COLON ) { + type = Parse_Type(lex); + GET_TOK(tok, lex); + } + ExprNodeP val; + if( tok.type() == TOK_EQUAL ) { + val = Parse_Expr0(lex); + } + else { + PUTBACK(tok, lex); + } + return NEWNODE( AST::ExprNode_LetBinding, ::std::move(pat), mv$(type), ::std::move(val) ); +} + +::std::vector Parse_ParenList(TokenStream& lex) +{ + TRACE_FUNCTION; + Token tok; + + CLEAR_PARSE_FLAG(lex, disallow_struct_literal); + + ::std::vector rv; + GET_CHECK_TOK(tok, lex, TOK_PAREN_OPEN); + if( GET_TOK(tok, lex) != TOK_PAREN_CLOSE ) + { + PUTBACK(tok, lex); + do { + if( LOOK_AHEAD(lex) == TOK_PAREN_CLOSE ) { + GET_TOK(tok, lex); + break; + } + rv.push_back( Parse_Expr0(lex) ); + } while( GET_TOK(tok, lex) == TOK_COMMA ); + CHECK_TOK(tok, TOK_PAREN_CLOSE); + } + return rv; +} + +// 0: Assign +ExprNodeP Parse_Expr0(TokenStream& lex) +{ + //TRACE_FUNCTION; + Token tok; + + ::AST::MetaItems expr_attrs; + while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) + { + GET_TOK(tok, lex); + expr_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + + ExprNodeP rv = Parse_Expr1(lex); + auto op = AST::ExprNode_Assign::NONE; + switch( GET_TOK(tok, lex) ) + { + case TOK_PLUS_EQUAL: + op = AST::ExprNode_Assign::ADD; if(0) + case TOK_DASH_EQUAL: + op = AST::ExprNode_Assign::SUB; if(0) + case TOK_STAR_EQUAL: + op = AST::ExprNode_Assign::MUL; if(0) + case TOK_SLASH_EQUAL: + op = AST::ExprNode_Assign::DIV; if(0) + case TOK_PERCENT_EQUAL: + op = AST::ExprNode_Assign::MOD; if(0) + + case TOK_AMP_EQUAL: + op = AST::ExprNode_Assign::AND; if(0) + case TOK_PIPE_EQUAL: + op = AST::ExprNode_Assign::OR ; if(0) + case TOK_CARET_EQUAL: + op = AST::ExprNode_Assign::XOR; if(0) + + case TOK_DOUBLE_GT_EQUAL: + op = AST::ExprNode_Assign::SHR; if(0) + case TOK_DOUBLE_LT_EQUAL: + op = AST::ExprNode_Assign::SHL; if(0) + + case TOK_EQUAL: + op = AST::ExprNode_Assign::NONE; + rv = NEWNODE( AST::ExprNode_Assign, op, ::std::move(rv), Parse_Expr0(lex) ); + rv->set_attrs(mv$(expr_attrs)); + return rv; + + default: + PUTBACK(tok, lex); + rv->set_attrs(mv$(expr_attrs)); + return rv; + } +} + + +#define LEFTASSOC(cur, _next, cases) \ +ExprNodeP _next(TokenStream& lex); \ +ExprNodeP cur(TokenStream& lex) \ +{ \ + ExprNodeP (*next)(TokenStream&) = _next;\ + ExprNodeP rv = next(lex); \ + while(true) \ + { \ + Token tok; \ + switch((tok = lex.getToken()).type()) \ + { \ + cases \ + default: \ + /*::std::cout << "<<" << #cur << ::std::endl; */\ + PUTBACK(tok, lex); \ + return rv; \ + } \ + } \ +} +bool Parse_IsTokValue(eTokenType tok_type) +{ + switch( tok_type ) + { + case TOK_DOUBLE_COLON: + case TOK_IDENT: + case TOK_INTEGER: + case TOK_FLOAT: + case TOK_STRING: + case TOK_RWORD_TRUE: + case TOK_RWORD_FALSE: + case TOK_RWORD_SELF: + case TOK_RWORD_SUPER: + case TOK_RWORD_BOX: + case TOK_RWORD_IN: + case TOK_PAREN_OPEN: + case TOK_SQUARE_OPEN: + + case TOK_MACRO: + + case TOK_PIPE: + case TOK_EXCLAM: + case TOK_DASH: + case TOK_STAR: + case TOK_AMP: + return true; + default: + return false; + } + +} +ExprNodeP Parse_Expr1_1(TokenStream& lex); +// Very evil handling for '..' +ExprNodeP Parse_Expr1(TokenStream& lex) +{ + Token tok; + ExprNodeP (*next)(TokenStream&) = Parse_Expr1_1; + ExprNodeP left, right; + + // Inclusive range to a value + if( GET_TOK(tok, lex) == TOK_TRIPLE_DOT ) { + right = next(lex); + return NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::RANGE_INC, nullptr, mv$(right) ); + } + else { + PUTBACK(tok, lex); + } + + // Exclusive ranges + // - If NOT `.. `, parse a leading value + if( GET_TOK(tok, lex) != TOK_DOUBLE_DOT ) + { + PUTBACK(tok, lex); + + left = next(lex); + + // - If NOT ` ..`, return the value + if( GET_TOK(tok, lex) != TOK_DOUBLE_DOT ) + { + PUTBACK(tok, lex); + return ::std::move(left); + } + } + assert( tok.type() == TOK_DOUBLE_DOT ); + // If the next token is part of a value, parse that value + if( Parse_IsTokValue( LOOK_AHEAD(lex) ) ) + { + right = next(lex); + } + else + { + // Otherwise, leave `right` as nullptr + } + + return NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::RANGE, ::std::move(left), ::std::move(right) ); +} +// TODO: Is this left associative? +LEFTASSOC(Parse_Expr1_1, Parse_Expr1_5, + case TOK_TRIPLE_DOT: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::RANGE_INC, mv$(rv), next(lex) ); + break; +) +// 1: Bool OR +LEFTASSOC(Parse_Expr1_5, Parse_Expr2, + case TOK_DOUBLE_PIPE: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BOOLOR, ::std::move(rv), next(lex)); + break; +) +// 2: Bool AND +LEFTASSOC(Parse_Expr2, Parse_Expr3, + case TOK_DOUBLE_AMP: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BOOLAND, ::std::move(rv), next(lex)); + break; +) +// 3: (In)Equality +LEFTASSOC(Parse_Expr3, Parse_Expr4, + case TOK_DOUBLE_EQUAL: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPEQU, ::std::move(rv), next(lex)); + break; + case TOK_EXCLAM_EQUAL: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPNEQU, ::std::move(rv), next(lex)); + break; +) +// 4: Comparisons +LEFTASSOC(Parse_Expr4, Parse_Expr5, + case TOK_LT: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPLT, ::std::move(rv), next(lex)); + break; + case TOK_GT: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPGT, ::std::move(rv), next(lex)); + break; + case TOK_LTE: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPLTE, ::std::move(rv), next(lex)); + break; + case TOK_GTE: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::CMPGTE, ::std::move(rv), next(lex)); + break; +) +// 5: Bit OR +LEFTASSOC(Parse_Expr5, Parse_Expr6, + case TOK_PIPE: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BITOR, ::std::move(rv), next(lex)); + break; +) +// 6: Bit XOR +LEFTASSOC(Parse_Expr6, Parse_Expr7, + case TOK_CARET: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BITXOR, ::std::move(rv), next(lex)); + break; +) +// 7: Bit AND +LEFTASSOC(Parse_Expr7, Parse_Expr8, + case TOK_AMP: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::BITAND, ::std::move(rv), next(lex)); + break; +) +// 8: Bit Shifts +LEFTASSOC(Parse_Expr8, Parse_Expr9, + case TOK_DOUBLE_LT: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::SHL, ::std::move(rv), next(lex)); + break; + case TOK_DOUBLE_GT: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::SHR, ::std::move(rv), next(lex)); + break; +) +// 9: Add / Subtract +LEFTASSOC(Parse_Expr9, Parse_Expr10, + case TOK_PLUS: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::ADD, ::std::move(rv), next(lex)); + break; + case TOK_DASH: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::SUB, ::std::move(rv), next(lex)); + break; +) +// 10: Times / Divide / Modulo +LEFTASSOC(Parse_Expr10, Parse_Expr11, + case TOK_STAR: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::MULTIPLY, ::std::move(rv), next(lex)); + break; + case TOK_SLASH: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::DIVIDE, ::std::move(rv), next(lex)); + break; + case TOK_PERCENT: + rv = NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::MODULO, ::std::move(rv), next(lex)); + break; +) +// 11: Cast +LEFTASSOC(Parse_Expr11, Parse_Expr12, + case TOK_RWORD_AS: + rv = NEWNODE( AST::ExprNode_Cast, ::std::move(rv), Parse_Type(lex, false) ); + break; +) +// 12: Type Ascription +ExprNodeP Parse_Expr13(TokenStream& lex); +ExprNodeP Parse_Expr12(TokenStream& lex) +{ + Token tok; + auto rv = Parse_Expr13(lex); + if(GET_TOK(tok, lex) == TOK_COLON) + { + rv = NEWNODE( AST::ExprNode_TypeAnnotation, mv$(rv), Parse_Type(lex) ); + } + else + { + PUTBACK(tok, lex); + } + return rv; +} +// 13: Unaries +ExprNodeP Parse_ExprFC(TokenStream& lex); +ExprNodeP Parse_Expr13(TokenStream& lex) +{ + Token tok; + switch(GET_TOK(tok, lex)) + { + case TOK_DASH: + return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::NEGATE, Parse_Expr12(lex) ); + case TOK_EXCLAM: + return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::INVERT, Parse_Expr12(lex) ); + case TOK_STAR: + return NEWNODE( AST::ExprNode_Deref, Parse_Expr12(lex) ); + case TOK_RWORD_BOX: + return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::BOX, Parse_Expr12(lex) ); + case TOK_RWORD_IN: { + ExprNodeP dest; + { + SET_PARSE_FLAG(lex, disallow_struct_literal); + dest = Parse_Expr1(lex); + } + auto val = Parse_ExprBlockNode(lex); + return NEWNODE( AST::ExprNode_BinOp, AST::ExprNode_BinOp::PLACE_IN, mv$(dest), mv$(val)); + } + case TOK_DOUBLE_AMP: + // HACK: Split && into & & + lex.putback( Token(TOK_AMP) ); + case TOK_AMP: + if( GET_TOK(tok, lex) == TOK_RWORD_MUT ) + return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::REFMUT, Parse_Expr12(lex) ); + else { + PUTBACK(tok, lex); + return NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::REF, Parse_Expr12(lex) ); + } + default: + PUTBACK(tok, lex); + return Parse_ExprFC(lex); + } +} + +ExprNodeP Parse_ExprVal(TokenStream& lex); +ExprNodeP Parse_ExprFC(TokenStream& lex) +{ + ExprNodeP val = Parse_ExprVal(lex); + while(true) + { + Token tok; + switch(GET_TOK(tok, lex)) + { + case TOK_QMARK: + val = NEWNODE( AST::ExprNode_UniOp, AST::ExprNode_UniOp::QMARK, mv$(val) ); + break; + + case TOK_PAREN_OPEN: + // Expression method call + PUTBACK(tok, lex); + val = NEWNODE( AST::ExprNode_CallObject, ::std::move(val), Parse_ParenList(lex) ); + break; + case TOK_SQUARE_OPEN: + val = NEWNODE( AST::ExprNode_Index, ::std::move(val), Parse_Expr0(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + break; + case TOK_DOT: + // Field access / method call + // TODO: What about tuple indexing? + switch(GET_TOK(tok, lex)) + { + case TOK_IDENT: { + AST::PathNode path( mv$(tok.str()) , {}); + switch( GET_TOK(tok, lex) ) + { + case TOK_PAREN_OPEN: + PUTBACK(tok, lex); + val = NEWNODE( AST::ExprNode_CallMethod, ::std::move(val), ::std::move(path), Parse_ParenList(lex) ); + break; + case TOK_DOUBLE_COLON: + GET_CHECK_TOK(tok, lex, TOK_LT); + path.args() = Parse_Path_GenericList(lex); + val = NEWNODE( AST::ExprNode_CallMethod, ::std::move(val), ::std::move(path), Parse_ParenList(lex) ); + break; + default: + val = NEWNODE( AST::ExprNode_Field, ::std::move(val), ::std::string(path.name()) ); + PUTBACK(tok, lex); + break; + } + break; } + case TOK_INTEGER: + val = NEWNODE( AST::ExprNode_Field, ::std::move(val), FMT(tok.intval()) ); + break; + default: + throw ParseError::Unexpected(lex, mv$(tok)); + } + break; + default: + PUTBACK(tok, lex); + return val; + } + } +} + +ExprNodeP Parse_ExprVal_StructLiteral(TokenStream& lex, AST::Path path) +{ + TRACE_FUNCTION; + Token tok; + + // #![feature(relaxed_adts)] + if( LOOK_AHEAD(lex) == TOK_INTEGER ) + { + ::std::map nodes; + while( GET_TOK(tok, lex) == TOK_INTEGER ) + { + unsigned int ofs = tok.intval(); + GET_CHECK_TOK(tok, lex, TOK_COLON); + ExprNodeP val = Parse_Stmt(lex); + if( ! nodes.insert( ::std::make_pair(ofs, mv$(val)) ).second ) { + ERROR(lex.getPosition(), E0000, "Duplicate index"); + } + + if( GET_TOK(tok,lex) == TOK_BRACE_CLOSE ) + break; + CHECK_TOK(tok, TOK_COMMA); + } + CHECK_TOK(tok, TOK_BRACE_CLOSE); + + ::std::vector items; + unsigned int i = 0; + for(auto& p : nodes) + { + if( p.first != i ) { + ERROR(lex.getPosition(), E0000, "Missing index " << i); + } + items.push_back( mv$(p.second) ); + i ++; + } + + return NEWNODE( AST::ExprNode_CallPath, mv$(path), mv$(items) ); + } + + // Braced structure literal + // - A series of 0 or more pairs of : , + // - '..' + ::std::vector< ::std::pair< ::std::string, ::std::unique_ptr> > items; + while( GET_TOK(tok, lex) == TOK_IDENT ) + { + auto name = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_COLON); + ExprNodeP val = Parse_Stmt(lex); + items.push_back( ::std::make_pair(::std::move(name), ::std::move(val)) ); + + if( GET_TOK(tok,lex) == TOK_BRACE_CLOSE ) + break; + CHECK_TOK(tok, TOK_COMMA); + } + ExprNodeP base_val; + if( tok.type() == TOK_DOUBLE_DOT ) + { + // default + base_val = Parse_Expr0(lex); + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_BRACE_CLOSE); + + return NEWNODE( AST::ExprNode_StructLiteral, path, ::std::move(base_val), ::std::move(items) ); +} + +ExprNodeP Parse_ExprVal_Closure(TokenStream& lex, bool is_move) +{ + TRACE_FUNCTION; + Token tok; + + ::std::vector< ::std::pair > args; + + while( GET_TOK(tok, lex) != TOK_PIPE ) + { + PUTBACK(tok, lex); + // Irrefutable pattern + AST::Pattern pat = Parse_Pattern(lex, false); + + TypeRef type { lex.getPosition() }; + if( GET_TOK(tok, lex) == TOK_COLON ) + type = Parse_Type(lex); + else + PUTBACK(tok, lex); + + args.push_back( ::std::make_pair( ::std::move(pat), ::std::move(type) ) ); + + if( GET_TOK(tok, lex) != TOK_COMMA ) + break; + } + CHECK_TOK(tok, TOK_PIPE); + + auto rt = TypeRef(lex.getPosition()); + if( GET_TOK(tok, lex) == TOK_THINARROW ) { + + if( GET_TOK(tok, lex) == TOK_EXCLAM ) { + rt = TypeRef(TypeRef::TagInvalid(), Span(tok.get_pos())); + } + else { + PUTBACK(tok, lex); + rt = Parse_Type(lex); + } + } + else + PUTBACK(tok, lex); + + auto code = Parse_Expr0(lex); + + return NEWNODE( AST::ExprNode_Closure, ::std::move(args), ::std::move(rt), ::std::move(code), is_move ); +} + +ExprNodeP Parse_ExprVal(TokenStream& lex) +{ + TRACE_FUNCTION; + + Token tok; + AST::Path path; + switch( GET_TOK(tok, lex) ) + { + case TOK_BRACE_OPEN: + PUTBACK(tok, lex); + return Parse_ExprBlockNode(lex); + + case TOK_INTERPOLATED_EXPR: + case TOK_INTERPOLATED_BLOCK: + return tok.take_frag_node(); + + + // TODO: Return/break/continue/... here? + case TOK_RWORD_RETURN: + case TOK_RWORD_CONTINUE: + case TOK_RWORD_BREAK: + PUTBACK(tok, lex); + return Parse_Stmt(lex); + + case TOK_RWORD_LOOP: + return NEWNODE( AST::ExprNode_Loop, "", Parse_ExprBlockNode(lex) ); + case TOK_RWORD_WHILE: + return Parse_WhileStmt(lex, ""); + case TOK_RWORD_FOR: + return Parse_ForStmt(lex, ""); + case TOK_RWORD_MATCH: + return Parse_Expr_Match(lex); + case TOK_RWORD_IF: + return Parse_IfStmt(lex); + case TOK_RWORD_UNSAFE: + return Parse_ExprBlockNode(lex, true); + + // UFCS + case TOK_DOUBLE_LT: + case TOK_LT: + PUTBACK(tok, lex); + path = Parse_Path(lex, PATH_GENERIC_EXPR); + // Skip down to method + if(0) + case TOK_RWORD_SELF: + { + if( LOOK_AHEAD(lex) != TOK_DOUBLE_COLON ) { + return NEWNODE( AST::ExprNode_NamedValue, AST::Path(AST::Path::TagLocal(), "self") ); + } + else + { + PUTBACK(tok, lex); + path = Parse_Path(lex, PATH_GENERIC_EXPR); + } + } + if(0) + case TOK_RWORD_SUPER: + { + PUTBACK(tok, lex); + path = Parse_Path(lex, PATH_GENERIC_EXPR); + } + if(0) + case TOK_IDENT: + // Get path + { + PUTBACK(tok, lex); + path = Parse_Path(lex, false, PATH_GENERIC_EXPR); + } + if(0) + case TOK_INTERPOLATED_PATH: + { + path = mv$(tok.frag_path()); + } + if(0) + case TOK_DOUBLE_COLON: + path = Parse_Path(lex, true, PATH_GENERIC_EXPR); + // SKIP TARGET + switch( GET_TOK(tok, lex) ) + { + case TOK_PAREN_OPEN: + // Function call + PUTBACK(tok, lex); + return NEWNODE( AST::ExprNode_CallPath, ::std::move(path), Parse_ParenList(lex) ); + case TOK_BRACE_OPEN: + if( !CHECK_PARSE_FLAG(lex, disallow_struct_literal) ) + return Parse_ExprVal_StructLiteral(lex, ::std::move(path)); + else + DEBUG("Not parsing struct literal"); + default: + // Value + PUTBACK(tok, lex); + return NEWNODE( AST::ExprNode_NamedValue, ::std::move(path) ); + } + case TOK_RWORD_MOVE: + // TODO: Annotate closure as move + GET_TOK(tok, lex); + if(tok.type() == TOK_PIPE) + return Parse_ExprVal_Closure(lex, true); + else if(tok.type() == TOK_DOUBLE_PIPE) { + lex.putback(Token(TOK_PIPE)); + return Parse_ExprVal_Closure(lex, true); + } + else { + CHECK_TOK(tok, TOK_PIPE); + } + case TOK_DOUBLE_PIPE: + lex.putback(Token(TOK_PIPE)); + case TOK_PIPE: + return Parse_ExprVal_Closure(lex, false); + case TOK_INTEGER: + return NEWNODE( AST::ExprNode_Integer, tok.intval(), tok.datatype() ); + case TOK_FLOAT: + return NEWNODE( AST::ExprNode_Float, tok.floatval(), tok.datatype() ); + case TOK_STRING: + return NEWNODE( AST::ExprNode_String, tok.str() ); + case TOK_BYTESTRING: + return NEWNODE( AST::ExprNode_ByteString, tok.str() ); + case TOK_RWORD_TRUE: + return NEWNODE( AST::ExprNode_Bool, true ); + case TOK_RWORD_FALSE: + return NEWNODE( AST::ExprNode_Bool, false ); + case TOK_PAREN_OPEN: + if( GET_TOK(tok, lex) == TOK_PAREN_CLOSE ) + { + DEBUG("Unit"); + return NEWNODE( AST::ExprNode_Tuple, ::std::vector() ); + } + else + { + CLEAR_PARSE_FLAG(lex, disallow_struct_literal); + PUTBACK(tok, lex); + + ExprNodeP rv = Parse_Expr0(lex); + if( GET_TOK(tok, lex) == TOK_COMMA ) { + ::std::vector ents; + ents.push_back( ::std::move(rv) ); + do { + if( GET_TOK(tok, lex) == TOK_PAREN_CLOSE ) + break; + PUTBACK(tok, lex); + ents.push_back( Parse_Expr0(lex) ); + } while( GET_TOK(tok, lex) == TOK_COMMA ); + rv = NEWNODE( AST::ExprNode_Tuple, ::std::move(ents) ); + } + CHECK_TOK(tok, TOK_PAREN_CLOSE); + return rv; + } + case TOK_SQUARE_OPEN: + if( GET_TOK(tok, lex) == TOK_SQUARE_CLOSE ) + { + // Empty literal + return NEWNODE( AST::ExprNode_Array, ::std::vector() ); + } + else + { + PUTBACK(tok, lex); + auto first = Parse_Expr0(lex); + if( GET_TOK(tok, lex) == TOK_SEMICOLON ) + { + // Repetiion + auto count = Parse_Expr0(lex); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + return NEWNODE( AST::ExprNode_Array, ::std::move(first), ::std::move(count) ); + } + else + { + ::std::vector items; + items.push_back( ::std::move(first) ); + while( tok.type() == TOK_COMMA ) + { + if( GET_TOK(tok, lex) == TOK_SQUARE_CLOSE ) + break; + else + PUTBACK(tok, lex); + items.push_back( Parse_Expr0(lex) ); + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_SQUARE_CLOSE); + return NEWNODE( AST::ExprNode_Array, ::std::move(items) ); + } + } + throw ParseError::BugCheck(lex, "Array literal fell"); + case TOK_MACRO: + return Parse_ExprMacro(lex, mv$(tok)); + default: + throw ParseError::Unexpected(lex, tok); + } +} +ExprNodeP Parse_ExprMacro(TokenStream& lex, Token tok) +{ + ::std::string name = tok.str(); + ::std::string ident; + if( GET_TOK(tok, lex) == TOK_IDENT ) { + ident = mv$(tok.str()); + } + else { + PUTBACK(tok, lex); + } + TokenTree tt = Parse_TT(lex, true); + if( tt.is_token() ) { + throw ParseError::Unexpected(lex, tt.tok()); + } + return NEWNODE(AST::ExprNode_Macro, mv$(name), mv$(ident), mv$(tt)); +} + +// Token Tree Parsing +TokenTree Parse_TT(TokenStream& lex, bool unwrapped) +{ + TokenTree rv; + TRACE_FUNCTION_FR("", rv); + + Token tok = lex.getToken(); + eTokenType closer = TOK_PAREN_CLOSE; + switch(tok.type()) + { + case TOK_PAREN_OPEN: + closer = TOK_PAREN_CLOSE; + break; + case TOK_SQUARE_OPEN: + closer = TOK_SQUARE_CLOSE; + break; + case TOK_BRACE_OPEN: + closer = TOK_BRACE_CLOSE; + break; + // HACK! mrustc parses #[ and #![ as composite tokens + // TODO: Split these into their component tokens. + case TOK_ATTR_OPEN: + case TOK_CATTR_OPEN: + if( unwrapped ) + throw ParseError::Unexpected(lex, tok); + closer = TOK_SQUARE_CLOSE; + break; + + case TOK_EOF: + case TOK_NULL: + case TOK_PAREN_CLOSE: + case TOK_SQUARE_CLOSE: + case TOK_BRACE_CLOSE: + throw ParseError::Unexpected(lex, tok); + default: + rv = TokenTree(lex.getHygiene(), mv$(tok) ); + return rv; + } + + ::std::vector items; + if( !unwrapped ) + items.push_back( TokenTree(lex.getHygiene(), mv$(tok)) ); + while(GET_TOK(tok, lex) != closer && tok.type() != TOK_EOF) + { + if( tok.type() == TOK_NULL ) + throw ParseError::Unexpected(lex, tok); + PUTBACK(tok, lex); + items.push_back(Parse_TT(lex, false)); + } + if( !unwrapped ) + items.push_back( TokenTree(lex.getHygiene(), mv$(tok)) ); + rv = TokenTree(lex.getHygiene(), mv$(items)); + return rv; +} diff --git a/src/parse/interpolated_fragment.hpp b/src/parse/interpolated_fragment.hpp index b0be2efe..a3e72816 100644 --- a/src/parse/interpolated_fragment.hpp +++ b/src/parse/interpolated_fragment.hpp @@ -25,18 +25,18 @@ public: PAT, PATH, TYPE, - + EXPR, STMT, BLOCK, - + META, ITEM, } m_type; - + // Owned type-pruned pointer void* m_ptr; - + InterpolatedFragment(InterpolatedFragment&& ); InterpolatedFragment& operator=(InterpolatedFragment&& ); //InterpolatedFragment(const InterpolatedFragment& ); @@ -48,9 +48,9 @@ public: InterpolatedFragment(::AST::Named ); ~InterpolatedFragment(); InterpolatedFragment(Type , ::AST::ExprNode*); - + TokenTree& as_tt() { assert(m_type == TT); return *reinterpret_cast(m_ptr); } const TokenTree& as_tt() const { assert(m_type == TT); return *reinterpret_cast(m_ptr); } - + friend ::std::ostream& operator<<(::std::ostream& os, const InterpolatedFragment& x); }; diff --git a/src/parse/lex.cpp b/src/parse/lex.cpp index dfd2a2d3..9b09b1d4 100644 --- a/src/parse/lex.cpp +++ b/src/parse/lex.cpp @@ -275,7 +275,7 @@ Token Lexer::getTokenInt() try { Codepoint ch = this->getc(); - + if( ch == '#' && m_line == 1 && m_line_ofs == 1 ) { switch( (ch = this->getc()).v ) { @@ -326,8 +326,8 @@ Token Lexer::getTokenInt() DEC, HEX, } num_mode = DEC; - - + + // Handle integers/floats uint64_t val = 0; if( ch == '0' ) { @@ -390,7 +390,7 @@ Token Lexer::getTokenInt() if( ch == '.' ) { ch = this->getc(); - + // Double/Triple Dot if( ch == '.' ) { @@ -403,7 +403,7 @@ Token Lexer::getTokenInt() } return Token(val, CORETYPE_ANY); } - + // Single dot followed by a non-digit, could be a float or an integer with a method/field access if( !ch.isdigit() ) { @@ -428,8 +428,8 @@ Token Lexer::getTokenInt() } if( num_mode != DEC ) TODO(this->getPosition(), "Non-decimal floats"); - - + + this->ungetc(); double fval = this->parseFloat(val); if( issym(ch = this->getc()) ) @@ -441,7 +441,7 @@ Token Lexer::getTokenInt() ch = this->getc(); } this->ungetc(); - + if(0) ; else if(suffix == "f32") num_type = CORETYPE_F32; else if(suffix == "f64") num_type = CORETYPE_F64; @@ -464,7 +464,7 @@ Token Lexer::getTokenInt() ch = this->getc(); } this->ungetc(); - + if(0) ; else if(suffix == "i8") num_type = CORETYPE_I8; else if(suffix == "i16") num_type = CORETYPE_I16; @@ -495,13 +495,13 @@ Token Lexer::getTokenInt() is_byte = true; ch = this->getc(); } - + if(ch == 'r') { return this->getTokenInt_RawString(is_byte); } else { assert(is_byte); - + // Byte string if( ch == '"' ) { ::std::string str; @@ -579,7 +579,7 @@ Token Lexer::getTokenInt() while(true) { ch = this->getc(); - + if( ch == '/' ) { str += ch; ch = this->getc(); @@ -704,7 +704,7 @@ Token Lexer::getTokenInt_RawString(bool is_byte) catch( Lexer::EndOfFile e ) { throw ParseError::Generic(*this, "EOF reached in raw string"); } - + if( terminating_hashes > 0 ) { assert(terminating_hashes > 0); @@ -715,7 +715,7 @@ Token Lexer::getTokenInt_RawString(bool is_byte) terminating_hashes += 1; } terminating_hashes = 0; - + this->ungetc(); } else { @@ -800,9 +800,9 @@ double Lexer::parseFloat(uint64_t whole) } this->ungetc(); buf[ofs] = 0; - + DEBUG("buf = " << buf << ", ch = '" << ch << "'"); - + return ::std::strtod(buf, NULL); } @@ -920,7 +920,7 @@ Codepoint Lexer::getc_cp() // Two bytes uint8_t e1 = this->getc_byte(); if( (e1 & 0xC0) != 0x80 ) return {0xFFFE}; - + uint32_t outval = ((v1 & 0x1F) << 6) | ((e1 & 0x3F) <<0) @@ -933,7 +933,7 @@ Codepoint Lexer::getc_cp() if( (e1 & 0xC0) != 0x80 ) return {0xFFFE}; uint8_t e2 = this->getc_byte(); if( (e2 & 0xC0) != 0x80 ) return {0xFFFE}; - + uint32_t outval = ((v1 & 0x0F) << 12) | ((e1 & 0x3F) << 6) @@ -949,7 +949,7 @@ Codepoint Lexer::getc_cp() if( (e2 & 0xC0) != 0x80 ) return {0xFFFE}; uint8_t e3 = this->getc_byte(); if( (e3 & 0xC0) != 0x80 ) return {0xFFFE}; - + uint32_t outval = ((v1 & 0x07) << 18) | ((e1 & 0x3F) << 12) @@ -1004,7 +1004,7 @@ bool Codepoint::isxdigit() const { s += (char)cp.v; } else if( cp.v < (0x1F+1)<<(1*6) ) { - + s += (char)(0xC0 | ((cp.v >> 6) & 0x1F)); s += (char)(0x80 | ((cp.v >> 0) & 0x3F)); } diff --git a/src/parse/lex.hpp b/src/parse/lex.hpp index 572846da..871ab29a 100644 --- a/src/parse/lex.hpp +++ b/src/parse/lex.hpp @@ -40,7 +40,7 @@ class Lexer: bool m_last_char_valid; Codepoint m_last_char; Token m_next_token; // Used when lexing generated two tokens - + Ident::Hygiene m_hygiene; public: Lexer(const ::std::string& filename); @@ -51,7 +51,7 @@ public: private: Token getTokenInt(); - + signed int getSymbol(); Token getTokenInt_RawString(bool is_byte); Token getTokenInt_Identifier(Codepoint ch, Codepoint ch2='\0'); diff --git a/src/parse/parseerror.cpp b/src/parse/parseerror.cpp index 116776a5..3c5d41fe 100644 --- a/src/parse/parseerror.cpp +++ b/src/parse/parseerror.cpp @@ -1,91 +1,91 @@ -/* - * MRustC - Rust Compiler - * - By John Hodge (Mutabah/thePowersGang) - * - * parse/parseerror.cpp - * - Exceptions thrown for different types of parsing errors - */ -#include "parseerror.hpp" -#include - -CompileError::Base::~Base() throw() -{ -} - -CompileError::Generic::Generic(::std::string message): - m_message(message) -{ - ::std::cout << "Generic(" << message << ")" << ::std::endl; -} -CompileError::Generic::Generic(const TokenStream& lex, ::std::string message) -{ - ::std::cout << lex.getPosition() << ": Generic(" << message << ")" << ::std::endl; -} - -CompileError::BugCheck::BugCheck(const TokenStream& lex, ::std::string message): - m_message(message) -{ - ::std::cout << lex.getPosition() << "BugCheck(" << message << ")" << ::std::endl; -} -CompileError::BugCheck::BugCheck(::std::string message): - m_message(message) -{ - ::std::cout << "BugCheck(" << message << ")" << ::std::endl; -} - -CompileError::Todo::Todo(::std::string message): - m_message(message) -{ - ::std::cout << "Todo(" << message << ")" << ::std::endl; -} -CompileError::Todo::Todo(const TokenStream& lex, ::std::string message): - m_message(message) -{ - ::std::cout << lex.getPosition() << ": Todo(" << message << ")" << ::std::endl; -} -CompileError::Todo::~Todo() throw() -{ -} - -ParseError::BadChar::BadChar(const TokenStream& lex, char character) -{ - ::std::cout << lex.getPosition() << ": BadChar(" << character << ")" << ::std::endl; -} -ParseError::BadChar::~BadChar() throw() -{ -} - -ParseError::Unexpected::Unexpected(const TokenStream& lex, const Token& tok)//: -// m_tok( mv$(tok) ) -{ - auto pos = tok.get_pos(); - if(pos.filename == "") - pos = lex.getPosition(); - ::std::cout << pos << ": Unexpected(" << tok << ")" << ::std::endl; -} -ParseError::Unexpected::Unexpected(const TokenStream& lex, const Token& tok, Token exp)//: -// m_tok( mv$(tok) ) -{ - auto pos = tok.get_pos(); - if(pos.filename == "") - pos = lex.getPosition(); - ::std::cout << pos << ": Unexpected(" << tok << ", " << exp << ")" << ::std::endl; -} -ParseError::Unexpected::Unexpected(const TokenStream& lex, const Token& tok, ::std::vector exp) -{ - auto pos = tok.get_pos(); - if(pos.filename == "") - pos = lex.getPosition(); - ::std::cout << pos << ": Unexpected " << tok << ", expected "; - bool f = true; - for(auto v: exp) { - if(!f) - ::std::cout << " or "; - f = false; - ::std::cout << Token::typestr(v); - } - ::std::cout << ::std::endl; -} -ParseError::Unexpected::~Unexpected() throw() -{ -} +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * parse/parseerror.cpp + * - Exceptions thrown for different types of parsing errors + */ +#include "parseerror.hpp" +#include + +CompileError::Base::~Base() throw() +{ +} + +CompileError::Generic::Generic(::std::string message): + m_message(message) +{ + ::std::cout << "Generic(" << message << ")" << ::std::endl; +} +CompileError::Generic::Generic(const TokenStream& lex, ::std::string message) +{ + ::std::cout << lex.getPosition() << ": Generic(" << message << ")" << ::std::endl; +} + +CompileError::BugCheck::BugCheck(const TokenStream& lex, ::std::string message): + m_message(message) +{ + ::std::cout << lex.getPosition() << "BugCheck(" << message << ")" << ::std::endl; +} +CompileError::BugCheck::BugCheck(::std::string message): + m_message(message) +{ + ::std::cout << "BugCheck(" << message << ")" << ::std::endl; +} + +CompileError::Todo::Todo(::std::string message): + m_message(message) +{ + ::std::cout << "Todo(" << message << ")" << ::std::endl; +} +CompileError::Todo::Todo(const TokenStream& lex, ::std::string message): + m_message(message) +{ + ::std::cout << lex.getPosition() << ": Todo(" << message << ")" << ::std::endl; +} +CompileError::Todo::~Todo() throw() +{ +} + +ParseError::BadChar::BadChar(const TokenStream& lex, char character) +{ + ::std::cout << lex.getPosition() << ": BadChar(" << character << ")" << ::std::endl; +} +ParseError::BadChar::~BadChar() throw() +{ +} + +ParseError::Unexpected::Unexpected(const TokenStream& lex, const Token& tok)//: +// m_tok( mv$(tok) ) +{ + auto pos = tok.get_pos(); + if(pos.filename == "") + pos = lex.getPosition(); + ::std::cout << pos << ": Unexpected(" << tok << ")" << ::std::endl; +} +ParseError::Unexpected::Unexpected(const TokenStream& lex, const Token& tok, Token exp)//: +// m_tok( mv$(tok) ) +{ + auto pos = tok.get_pos(); + if(pos.filename == "") + pos = lex.getPosition(); + ::std::cout << pos << ": Unexpected(" << tok << ", " << exp << ")" << ::std::endl; +} +ParseError::Unexpected::Unexpected(const TokenStream& lex, const Token& tok, ::std::vector exp) +{ + auto pos = tok.get_pos(); + if(pos.filename == "") + pos = lex.getPosition(); + ::std::cout << pos << ": Unexpected " << tok << ", expected "; + bool f = true; + for(auto v: exp) { + if(!f) + ::std::cout << " or "; + f = false; + ::std::cout << Token::typestr(v); + } + ::std::cout << ::std::endl; +} +ParseError::Unexpected::~Unexpected() throw() +{ +} diff --git a/src/parse/parseerror.hpp b/src/parse/parseerror.hpp index 0a00f60a..d6bcab6b 100644 --- a/src/parse/parseerror.hpp +++ b/src/parse/parseerror.hpp @@ -1,40 +1,40 @@ -#ifndef PARSEERROR_HPP_INCLUDED -#define PARSEERROR_HPP_INCLUDED - -#include -#include "tokenstream.hpp" -#include - -namespace ParseError { - -using CompileError::Generic; -using CompileError::BugCheck; -using CompileError::Todo; - -class BadChar: - public CompileError::Base -{ - //char m_char; -public: - BadChar(const TokenStream& lex, char character); - virtual ~BadChar() throw (); - -}; - -class Unexpected: - public CompileError::Base -{ - Token m_tok; -public: - Unexpected(const TokenStream& lex, const Token& tok); - Unexpected(const TokenStream& lex, const Token& tok, Token exp); - Unexpected(const TokenStream& lex, const Token& tok, ::std::vector exp); - virtual ~Unexpected() throw (); - -}; - -#define ASSERT(lex, cnd) do { if( !(cnd) ) throw CompileError::BugCheck(lex, "Assertion failed: " __FILE__ " - " #cnd); } while(0) - -} - -#endif // PARSEERROR_HPP_INCLUDED +#ifndef PARSEERROR_HPP_INCLUDED +#define PARSEERROR_HPP_INCLUDED + +#include +#include "tokenstream.hpp" +#include + +namespace ParseError { + +using CompileError::Generic; +using CompileError::BugCheck; +using CompileError::Todo; + +class BadChar: + public CompileError::Base +{ + //char m_char; +public: + BadChar(const TokenStream& lex, char character); + virtual ~BadChar() throw (); + +}; + +class Unexpected: + public CompileError::Base +{ + Token m_tok; +public: + Unexpected(const TokenStream& lex, const Token& tok); + Unexpected(const TokenStream& lex, const Token& tok, Token exp); + Unexpected(const TokenStream& lex, const Token& tok, ::std::vector exp); + virtual ~Unexpected() throw (); + +}; + +#define ASSERT(lex, cnd) do { if( !(cnd) ) throw CompileError::BugCheck(lex, "Assertion failed: " __FILE__ " - " #cnd); } while(0) + +} + +#endif // PARSEERROR_HPP_INCLUDED diff --git a/src/parse/paths.cpp b/src/parse/paths.cpp index e600e51e..360c630f 100644 --- a/src/parse/paths.cpp +++ b/src/parse/paths.cpp @@ -17,17 +17,17 @@ AST::PathParams Parse_Path_GenericList(TokenStream& lex); AST::Path Parse_Path(TokenStream& lex, eParsePathGenericMode generic_mode) { TRACE_FUNCTION_F("generic_mode="< Parse_PathNodes(TokenStream& lex, eParsePathGenericMode generic_mode) { TRACE_FUNCTION_F("generic_mode="< ret; @@ -122,7 +122,7 @@ AST::Path Parse_Path(TokenStream& lex, bool is_abs, eParsePathGenericMode generi // HACK! Handle breaking << into < < if( tok.type() == TOK_DOUBLE_LT ) lex.putback( Token(TOK_LT) ); - + // Type-mode generics "::path::to::Type" params = Parse_Path_GenericList(lex); GET_TOK(tok, lex); @@ -146,7 +146,7 @@ AST::Path Parse_Path(TokenStream& lex, bool is_abs, eParsePathGenericMode generi } while( GET_TOK(tok, lex) == TOK_COMMA ); } CHECK_TOK(tok, TOK_PAREN_CLOSE); - + TypeRef ret_type = TypeRef( TypeRef::TagUnit(), Span(tok.get_pos()) ); if( GET_TOK(tok, lex) == TOK_THINARROW ) { ret_type = Parse_Type(lex, false); @@ -155,14 +155,14 @@ AST::Path Parse_Path(TokenStream& lex, bool is_abs, eParsePathGenericMode generi PUTBACK(tok, lex); } DEBUG("- Fn("<"<C into Fn<(A,B),Ret=C> params = ::AST::PathParams { {}, ::make_vec1( TypeRef(TypeRef::TagTuple(), lex.end_span(ps), mv$(args)) ), ::make_vec1( ::std::make_pair( ::std::string("Output"), mv$(ret_type) ) ) }; - + GET_TOK(tok, lex); } else @@ -179,7 +179,7 @@ AST::Path Parse_Path(TokenStream& lex, bool is_abs, eParsePathGenericMode generi // HACK! Handle breaking << into < < if( tok.type() == TOK_DOUBLE_LT ) lex.putback( Token(TOK_LT) ); - + // Expr-mode generics "::path::to::function::(arg1, arg2)" params = Parse_Path_GenericList(lex); if( GET_TOK(tok, lex) != TOK_DOUBLE_COLON ) { @@ -205,7 +205,7 @@ AST::Path Parse_Path(TokenStream& lex, bool is_abs, eParsePathGenericMode generi ::std::vector types; ::std::vector< ::std::string> lifetimes; ::std::vector< ::std::pair< ::std::string, TypeRef > > assoc_bounds; - + do { if( LOOK_AHEAD(lex) == TOK_GT || LOOK_AHEAD(lex) == TOK_DOUBLE_GT || LOOK_AHEAD(lex) == TOK_GTE || LOOK_AHEAD(lex) == TOK_DOUBLE_GT_EQUAL ) { GET_TOK(tok, lex); @@ -244,7 +244,7 @@ AST::Path Parse_Path(TokenStream& lex, bool is_abs, eParsePathGenericMode generi else { CHECK_TOK(tok, TOK_GT); } - + return ::AST::PathParams { mv$( lifetimes ), mv$( types ), diff --git a/src/parse/pattern.cpp b/src/parse/pattern.cpp index b0149ddb..699e7fae 100644 --- a/src/parse/pattern.cpp +++ b/src/parse/pattern.cpp @@ -42,7 +42,7 @@ AST::Pattern Parse_Pattern(TokenStream& lex, bool is_refutable) Token tok; tok = lex.getToken(); - + if( tok.type() == TOK_MACRO ) { return AST::Pattern( AST::Pattern::TagMacro(), box$(Parse_MacroInvocation(ps, tok.str(), lex))); @@ -51,7 +51,7 @@ AST::Pattern Parse_Pattern(TokenStream& lex, bool is_refutable) { return mv$(tok.frag_pattern()); } - + bool expect_bind = false; auto bind_type = AST::PatternBinding::Type::MOVE; bool is_mut = false; @@ -80,7 +80,7 @@ AST::Pattern Parse_Pattern(TokenStream& lex, bool is_refutable) { // Fall through } - + AST::PatternBinding binding; // If a 'ref' or 'mut' annotation was seen, the next name must be a binding name if( expect_bind ) @@ -94,7 +94,7 @@ AST::Pattern Parse_Pattern(TokenStream& lex, bool is_refutable) return AST::Pattern(AST::Pattern::TagBind(), mv$(bind_name), bind_type, is_mut); } binding = AST::PatternBinding( mv$(bind_name), bind_type, is_mut ); - + // '@' consumed, move on to next token GET_TOK(tok, lex); } @@ -138,7 +138,7 @@ AST::Pattern Parse_Pattern(TokenStream& lex, bool is_refutable) { // Otherwise, fall through } - + PUTBACK(tok, lex); auto pat = Parse_PatternReal(lex, is_refutable); pat.binding() = mv$(binding); @@ -159,13 +159,13 @@ AST::Pattern Parse_PatternReal(TokenStream& lex, bool is_refutable) if( !ret.data().is_Value() ) throw ParseError::Generic(lex, "Using '...' with a non-value on left"); auto& ret_v = ret.data().as_Value(); - + auto right_pat = Parse_PatternReal1(lex, is_refutable); if( !right_pat.data().is_Value() ) throw ParseError::Generic(lex, "Using '...' with a non-value on right"); auto rightval = mv$( right_pat.data().as_Value().start ); ret_v.end = mv$(rightval); - + return ret; } else @@ -177,10 +177,10 @@ AST::Pattern Parse_PatternReal(TokenStream& lex, bool is_refutable) AST::Pattern Parse_PatternReal1(TokenStream& lex, bool is_refutable) { TRACE_FUNCTION; - + Token tok; AST::Path path; - + switch( GET_TOK(tok, lex) ) { case TOK_UNDERSCORE: @@ -260,7 +260,7 @@ AST::Pattern Parse_PatternReal1(TokenStream& lex, bool is_refutable) TODO(lex.getPosition(), "Convert :expr into a pattern value - " << *e); } } break; - + case TOK_PAREN_OPEN: return AST::Pattern( AST::Pattern::TagTuple(), Parse_PatternTuple(lex, is_refutable) ); case TOK_SQUARE_OPEN: @@ -272,7 +272,7 @@ AST::Pattern Parse_PatternReal1(TokenStream& lex, bool is_refutable) AST::Pattern Parse_PatternReal_Path(TokenStream& lex, AST::Path path, bool is_refutable) { Token tok; - + switch( GET_TOK(tok, lex) ) { case TOK_PAREN_OPEN: @@ -289,12 +289,12 @@ AST::Pattern Parse_PatternReal_Slice(TokenStream& lex, bool is_refutable) { auto sp = lex.start_span(); Token tok; - + ::std::vector< ::AST::Pattern> leading; ::std::vector< ::AST::Pattern> trailing; ::AST::PatternBinding inner_binding; bool is_split = false; - + while(GET_TOK(tok, lex) != TOK_SQUARE_CLOSE) { bool has_binding = true; @@ -316,11 +316,11 @@ AST::Pattern Parse_PatternReal_Slice(TokenStream& lex, bool is_refutable) else { has_binding = false; } - + if( has_binding ) { if(is_split) ERROR(lex.end_span(sp), E0000, "Multiple instances of .. in a slice pattern"); - + inner_binding = mv$(binding); is_split = true; GET_TOK(tok, lex); // TOK_DOUBLE_DOT @@ -334,12 +334,12 @@ AST::Pattern Parse_PatternReal_Slice(TokenStream& lex, bool is_refutable) trailing.push_back( Parse_Pattern(lex, is_refutable) ); } } - + if( GET_TOK(tok, lex) != TOK_COMMA ) break; } CHECK_TOK(tok, TOK_SQUARE_CLOSE); - + if( is_split ) { return ::AST::Pattern( ::AST::Pattern::Data::make_SplitSlice({ mv$(leading), mv$(inner_binding), mv$(trailing) }) ); @@ -357,34 +357,34 @@ AST::Pattern Parse_PatternReal_Slice(TokenStream& lex, bool is_refutable) TRACE_FUNCTION; auto sp = lex.start_span(); Token tok; - + ::std::vector leading; while( LOOK_AHEAD(lex) != TOK_PAREN_CLOSE && LOOK_AHEAD(lex) != TOK_DOUBLE_DOT ) { leading.push_back( Parse_Pattern(lex, is_refutable) ); - + if( GET_TOK(tok, lex) != TOK_COMMA ) { CHECK_TOK(tok, TOK_PAREN_CLOSE); return AST::Pattern::TuplePat { mv$(leading), false, {} }; } } - + if( LOOK_AHEAD(lex) != TOK_DOUBLE_DOT ) { GET_TOK(tok, lex); - + CHECK_TOK(tok, TOK_PAREN_CLOSE); return AST::Pattern::TuplePat { mv$(leading), false, {} }; } GET_CHECK_TOK(tok, lex, TOK_DOUBLE_DOT); - + ::std::vector trailing; if( GET_TOK(tok, lex) == TOK_COMMA ) { while( LOOK_AHEAD(lex) != TOK_PAREN_CLOSE ) { trailing.push_back( Parse_Pattern(lex, is_refutable) ); - + if( GET_TOK(tok, lex) != TOK_COMMA ) { PUTBACK(tok, lex); break; @@ -392,7 +392,7 @@ AST::Pattern Parse_PatternReal_Slice(TokenStream& lex, bool is_refutable) } GET_TOK(tok, lex); } - + CHECK_TOK(tok, TOK_PAREN_CLOSE); return ::AST::Pattern::TuplePat { mv$(leading), true, mv$(trailing) }; } @@ -401,7 +401,7 @@ AST::Pattern Parse_PatternStruct(TokenStream& lex, AST::Path path, bool is_refut { TRACE_FUNCTION; Token tok; - + // #![feature(relaxed_adts)] if( LOOK_AHEAD(lex) == TOK_INTEGER ) { @@ -415,7 +415,7 @@ AST::Pattern Parse_PatternStruct(TokenStream& lex, AST::Path path, bool is_refut if( ! pats.insert( ::std::make_pair(ofs, mv$(val)) ).second ) { ERROR(lex.getPosition(), E0000, "Duplicate index"); } - + if( GET_TOK(tok,lex) == TOK_BRACE_CLOSE ) break; CHECK_TOK(tok, TOK_COMMA); @@ -425,7 +425,7 @@ AST::Pattern Parse_PatternStruct(TokenStream& lex, AST::Path path, bool is_refut GET_TOK(tok, lex); } CHECK_TOK(tok, TOK_BRACE_CLOSE); - + bool has_split = false; ::std::vector leading; ::std::vector trailing; @@ -447,10 +447,10 @@ AST::Pattern Parse_PatternStruct(TokenStream& lex, AST::Path path, bool is_refut } i ++; } - + return AST::Pattern(AST::Pattern::TagNamedTuple(), mv$(path), AST::Pattern::TuplePat { mv$(leading), has_split, mv$(trailing) }); } - + bool is_exhaustive = true; ::std::vector< ::std::pair< ::std::string, AST::Pattern> > subpats; do { @@ -463,7 +463,7 @@ AST::Pattern Parse_PatternStruct(TokenStream& lex, AST::Path path, bool is_refut GET_TOK(tok, lex); break; } - + bool is_short_bind = false; bool is_box = false; auto bind_type = AST::PatternBinding::Type::MOVE; @@ -489,12 +489,12 @@ AST::Pattern Parse_PatternStruct(TokenStream& lex, AST::Path path, bool is_refut is_short_bind = true; GET_TOK(tok, lex); } - + CHECK_TOK(tok, TOK_IDENT); auto field_ident = lex.get_ident(mv$(tok)); ::std::string field_name; GET_TOK(tok, lex); - + AST::Pattern pat; if( is_short_bind || tok.type() != TOK_COLON ) { PUTBACK(tok, lex); @@ -511,11 +511,11 @@ AST::Pattern Parse_PatternStruct(TokenStream& lex, AST::Path path, bool is_refut field_name = mv$(field_ident.name); pat = Parse_Pattern(lex, is_refutable); } - + subpats.push_back( ::std::make_pair(mv$(field_name), mv$(pat)) ); } while( GET_TOK(tok, lex) == TOK_COMMA ); CHECK_TOK(tok, TOK_BRACE_CLOSE); - + return AST::Pattern(AST::Pattern::TagStruct(), ::std::move(path), ::std::move(subpats), is_exhaustive); } diff --git a/src/parse/root.cpp b/src/parse/root.cpp index 5a244543..e5f5b5f4 100644 --- a/src/parse/root.cpp +++ b/src/parse/root.cpp @@ -1,1879 +1,1879 @@ -/* - * MRustC - Rust Compiler - * - By John Hodge (Mutabah/thePowersGang) - * - * parse/root.cpp - * - Parsing at the module level (highest-level parsing) - * - * Entrypoint: - * - Parse_Crate : Handles crate attrbutes, and passes on to Parse_ModRoot - * - Parse_ModRoot - */ -#include -#include -#include "parseerror.hpp" -#include "common.hpp" -#include -#include // ABI_RUST - TODO: Move elsewhere? -#include // check_cfg - for `mod nonexistant;` -#include // Used by directory path -#include "lex.hpp" // New file lexer - -template -Spanned get_spanned(TokenStream& lex, ::std::function f) { - auto ps = lex.start_span(); - auto v = f(); - return Spanned { - lex.end_span(ps), - mv$(v) - }; -} -#define GET_SPANNED(type, lex, val) get_spanned< type >(lex, [&](){ return val; }) - -// Check the next two tokens -#define LOOKAHEAD2(lex, tok1, tok2) ((lex).lookahead(0) == (tok1) && (lex).lookahead(1) == (tok2)) - -::std::string dirname(::std::string input) { - while( input.size() > 0 && input.back() != '/' ) { - input.pop_back(); - } - return input; -} - -AST::MetaItem Parse_MetaItem(TokenStream& lex); -void Parse_ModRoot(TokenStream& lex, AST::Module& mod, AST::MetaItems& mod_attrs); - -::std::vector< ::std::string> Parse_HRB(TokenStream& lex) -{ - TRACE_FUNCTION; - Token tok; - - ::std::vector< ::std::string> lifetimes; - GET_CHECK_TOK(tok, lex, TOK_LT); - do { - GET_TOK(tok, lex); - - ::AST::MetaItems attrs; - while(tok.type() == TOK_ATTR_OPEN) - { - attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - GET_TOK(tok, lex); - } - (void)attrs; // TODO: Attributes on generic params - - switch(tok.type()) - { - case TOK_LIFETIME: - lifetimes.push_back(tok.str()); - break; - default: - throw ParseError::Unexpected(lex, tok, Token(TOK_LIFETIME)); - } - } while( GET_TOK(tok, lex) == TOK_COMMA ); - CHECK_TOK(tok, TOK_GT); - return lifetimes; -} -/// Parse type parameters in a definition -void Parse_TypeBound(TokenStream& lex, AST::GenericParams& ret, TypeRef checked_type, ::std::vector< ::std::string> lifetimes = {}) -{ - TRACE_FUNCTION; - Token tok; - - do - { - ::std::vector< ::std::string> hrls; - if(GET_TOK(tok, lex) == TOK_LIFETIME) { - ret.add_bound(AST::GenericBound::make_TypeLifetime( { - checked_type.clone(), tok.str() - } )); - } - else if( tok.type() == TOK_QMARK ) { - ret.add_bound(AST::GenericBound::make_MaybeTrait( { - checked_type.clone(), Parse_Path(lex, PATH_GENERIC_TYPE) - } )); - } - else { - if( tok.type() == TOK_RWORD_FOR ) - { - hrls = Parse_HRB(lex); - } - else { - PUTBACK(tok, lex); - } - (void)hrls; // TODO: HRLs - - ret.add_bound( AST::GenericBound::make_IsTrait({ - checked_type.clone(), mv$(lifetimes), Parse_Path(lex, PATH_GENERIC_TYPE) - }) ); - } - } while( GET_TOK(tok, lex) == TOK_PLUS ); - PUTBACK(tok, lex); -} - -/// Parse type parameters within '<' and '>' (definition) -AST::GenericParams Parse_GenericParams(TokenStream& lex) -{ - TRACE_FUNCTION; - - AST::GenericParams ret; - Token tok; - do { - if( GET_TOK(tok, lex) == TOK_GT ) { - break ; - } - - ::AST::MetaItems attrs; - while(tok.type() == TOK_ATTR_OPEN) - { - attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - GET_TOK(tok, lex); - } - (void)attrs; // TODO: Attributes on generic params - - if( tok.type() == TOK_IDENT ) - { - // TODO: Hygine - ::std::string param_name = mv$(tok.str()); - ret.add_ty_param( AST::TypeParam( param_name ) ); - - auto param_ty = TypeRef(lex.getPosition(), param_name); - if( GET_TOK(tok, lex) == TOK_COLON ) - { - Parse_TypeBound(lex, ret, mv$(param_ty)); - GET_TOK(tok, lex); - } - - if( tok.type() == TOK_EQUAL ) - { - ret.ty_params().back().setDefault( Parse_Type(lex) ); - GET_TOK(tok, lex); - } - } - else if( tok.type() == TOK_LIFETIME ) - { - // TODO: Should lifetimes have hygine? - ::std::string param_name = tok.str(); - ret.add_lft_param( param_name ); - if( GET_TOK(tok, lex) == TOK_COLON ) - { - do { - GET_CHECK_TOK(tok, lex, TOK_LIFETIME); - ret.add_bound(AST::GenericBound::make_Lifetime( {param_name, tok.str()} )); - } while( GET_TOK(tok, lex) == TOK_PLUS ); - } - } - else - { - throw ParseError::Unexpected(lex, tok, {TOK_IDENT, TOK_LIFETIME}); - } - } while( tok.type() == TOK_COMMA ); - PUTBACK(tok, lex); - return ret; -} - - -/// Parse the contents of a 'where' clause -void Parse_WhereClause(TokenStream& lex, AST::GenericParams& params) -{ - TRACE_FUNCTION; - Token tok; - - do { - GET_TOK(tok, lex); - if( tok.type() == TOK_BRACE_OPEN ) { - break; - } - - if( tok.type() == TOK_LIFETIME ) - { - auto lhs = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_COLON); - do { - GET_CHECK_TOK(tok, lex, TOK_LIFETIME); - auto rhs = mv$(tok.str()); - params.add_bound( AST::GenericBound::make_Lifetime({lhs, rhs}) ); - } while( GET_TOK(tok, lex) == TOK_PLUS ); - PUTBACK(tok, lex); - } - // Higher-ranked types/lifetimes - else if( tok.type() == TOK_RWORD_FOR ) - { - ::std::vector< ::std::string> lifetimes = Parse_HRB(lex); - - TypeRef type = Parse_Type(lex); - GET_CHECK_TOK(tok, lex, TOK_COLON); - Parse_TypeBound(lex,params, mv$(type), mv$(lifetimes)); - } - else - { - PUTBACK(tok, lex); - TypeRef type = Parse_Type(lex); - GET_CHECK_TOK(tok, lex, TOK_COLON); - Parse_TypeBound(lex, params, mv$(type)); - } - } while( GET_TOK(tok, lex) == TOK_COMMA ); - PUTBACK(tok, lex); -} - -// Parse a single function argument -::std::pair< AST::Pattern, TypeRef> Parse_Function_Arg(TokenStream& lex, bool expect_named) -{ - TRACE_FUNCTION_F("expect_named = " << expect_named); - Token tok; - - AST::Pattern pat; - - // If any of the following - // - Expecting a named parameter (i.e. defining a function in root or impl) - // - Next token is an underscore (only valid as a pattern here) - // - Next token is 'mut' (a mutable parameter slot) - // - Next two are ':' (a trivial named parameter) - // NOTE: When not expecting a named param, destructuring patterns are not allowed - if( expect_named - || LOOK_AHEAD(lex) == TOK_UNDERSCORE - || LOOK_AHEAD(lex) == TOK_RWORD_MUT - || (LOOK_AHEAD(lex) == TOK_IDENT && lex.lookahead(1) == TOK_COLON) - ) - { - // Function args can't be refuted - pat = Parse_Pattern(lex, false); - GET_CHECK_TOK(tok, lex, TOK_COLON); - } - - TypeRef type = Parse_Type(lex); - - - return ::std::make_pair( ::std::move(pat), ::std::move(type) ); -} - -/// Parse a function definition (after the 'fn ') -AST::Function Parse_FunctionDef(TokenStream& lex, ::std::string abi, bool allow_self, bool can_be_prototype, bool is_unsafe, bool is_const) -{ - TRACE_FUNCTION; - ProtoSpan ps = lex.start_span(); - - Token tok; - - // Parameters - AST::GenericParams params; - if( GET_TOK(tok, lex) == TOK_LT ) - { - params = Parse_GenericParams(lex); - GET_CHECK_TOK(tok, lex, TOK_GT); - } - else { - PUTBACK(tok, lex); - } - - AST::Function::Arglist args; - - GET_CHECK_TOK(tok, lex, TOK_PAREN_OPEN); - GET_TOK(tok, lex); - - // Handle self - if( tok.type() == TOK_AMP ) - { - // By-reference method? - // TODO: If a lifetime is seen (and not a prototype), it is definitely a self binding - - unsigned int ofs = 0; - if( lex.lookahead(0) == TOK_LIFETIME ) - ofs ++; - - if( lex.lookahead(ofs) == TOK_RWORD_SELF || (lex.lookahead(ofs) == TOK_RWORD_MUT && lex.lookahead(ofs+1) == TOK_RWORD_SELF) ) - { - auto ps = lex.start_span(); - ::std::string lifetime; - if( GET_TOK(tok, lex) == TOK_LIFETIME ) { - lifetime = tok.str(); - GET_TOK(tok, lex); - } - auto ty_sp = lex.end_span(ps); - - if( tok.type() == TOK_RWORD_MUT ) - { - GET_CHECK_TOK(tok, lex, TOK_RWORD_SELF); - args.push_back( ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "self"), TypeRef(TypeRef::TagReference(), ty_sp, true, TypeRef(ty_sp, "Self", 0xFFFF))) ); - } - else - { - args.push_back( ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "self"), TypeRef(TypeRef::TagReference(), ty_sp, false, TypeRef(ty_sp, "Self", 0xFFFF))) ); - } - DEBUG("TODO: UFCS / self lifetimes"); - if( allow_self == false ) - throw ParseError::Generic(lex, "Self binding not expected"); - //args.push_back( ::std::make_pair( - // AST::Pattern(), - // TypeRef(TypeRef::TagReference(), lifetime, (fcn_class == AST::Function::CLASS_MUTMETHOD), ) - //) ); - - // Prime tok for next step - GET_TOK(tok, lex); - } - else - { - // Unbound method - } - } - else if( tok.type() == TOK_RWORD_MUT ) - { - if( LOOK_AHEAD(lex) == TOK_RWORD_SELF ) - { - GET_TOK(tok, lex); - if( allow_self == false ) - throw ParseError::Generic(lex, "Self binding not expected"); - TypeRef ty = TypeRef( lex.getPosition(), "Self", 0xFFFF ); - if( GET_TOK(tok, lex) == TOK_COLON ) { - // Typed mut self - ty = Parse_Type(lex); - } - else { - PUTBACK(tok, lex); - } - args.push_back( ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "self"), mv$(ty)) ); - GET_TOK(tok, lex); - } - } - else if( tok.type() == TOK_RWORD_SELF ) - { - // By-value method - if( allow_self == false ) - throw ParseError::Generic(lex, "Self binding not expected"); - TypeRef ty = TypeRef( lex.getPosition(), "Self", 0xFFFF ); - if( GET_TOK(tok, lex) == TOK_COLON ) { - // Typed mut self - ty = Parse_Type(lex); - } - else { - PUTBACK(tok, lex); - } - args.push_back( ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "self"), mv$(ty)) ); - GET_TOK(tok, lex); - } - else - { - // Unbound method - } - - bool is_variadic = false; - if( tok.type() != TOK_PAREN_CLOSE ) - { - // Comma after self - if( args.size() ) - { - CHECK_TOK(tok, TOK_COMMA); - } - else { - PUTBACK(tok, lex); - } - - // Argument list - do { - if( LOOK_AHEAD(lex) == TOK_PAREN_CLOSE ) { - GET_TOK(tok, lex); - break; - } - if( LOOK_AHEAD(lex) == TOK_TRIPLE_DOT ) { - GET_TOK(tok, lex); - is_variadic = true; - GET_TOK(tok, lex); - break; - } - args.push_back( Parse_Function_Arg(lex, !can_be_prototype) ); - } while( GET_TOK(tok, lex) == TOK_COMMA ); - CHECK_TOK(tok, TOK_PAREN_CLOSE); - } - else { - // Eat 'tok', negative comparison - } - - TypeRef ret_type = TypeRef(TypeRef::TagUnit(), Span(tok.get_pos())); - if( GET_TOK(tok, lex) == TOK_THINARROW ) - { - // Return type - ret_type = Parse_Type(lex); - } - else - { - PUTBACK(tok, lex); - } - - if( GET_TOK(tok, lex) == TOK_RWORD_WHERE ) - { - Parse_WhereClause(lex, params); - } - else { - PUTBACK(tok, lex); - } - - return AST::Function(lex.end_span(ps), mv$(params), mv$(abi), is_unsafe, is_const, is_variadic, mv$(ret_type), mv$(args)); -} - -AST::Function Parse_FunctionDefWithCode(TokenStream& lex, ::std::string abi, bool allow_self, bool is_unsafe, bool is_const) -{ - Token tok; - auto ret = Parse_FunctionDef(lex, abi, allow_self, false, is_unsafe, is_const); - GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); - PUTBACK(tok, lex); - ret.set_code( Parse_ExprBlock(lex) ); - return ret; -} - -AST::TypeAlias Parse_TypeAlias(TokenStream& lex) -{ - TRACE_FUNCTION; - - Token tok; - - // Params - AST::GenericParams params; - if( GET_TOK(tok, lex) == TOK_LT ) - { - params = Parse_GenericParams(lex); - GET_CHECK_TOK(tok, lex, TOK_GT); - GET_TOK(tok, lex); - } - - if( tok.type() == TOK_RWORD_WHERE ) - { - Parse_WhereClause(lex, params); - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_EQUAL); - - // Type - TypeRef type = Parse_Type(lex); - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - - return AST::TypeAlias( ::std::move(params), ::std::move(type) ); -} - -AST::Struct Parse_Struct(TokenStream& lex, const AST::MetaItems& meta_items) -{ - TRACE_FUNCTION; - - Token tok; - - tok = lex.getToken(); - AST::GenericParams params; - if( tok.type() == TOK_LT ) - { - params = Parse_GenericParams(lex); - GET_CHECK_TOK(tok, lex, TOK_GT); - if(GET_TOK(tok, lex) == TOK_RWORD_WHERE) - { - Parse_WhereClause(lex, params); - tok = lex.getToken(); - } - } - if(tok.type() == TOK_PAREN_OPEN) - { - // Tuple structs - ::std::vector refs; - while(GET_TOK(tok, lex) != TOK_PAREN_CLOSE) - { - AST::MetaItems item_attrs; - while( tok.type() == TOK_ATTR_OPEN ) - { - item_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - GET_TOK(tok, lex); - } - SET_ATTRS(lex, item_attrs); - - bool is_pub = false; - if(tok.type() == TOK_RWORD_PUB) - is_pub = true; - else - PUTBACK(tok, lex); - - refs.push_back( AST::TupleItem( mv$(item_attrs), is_pub, Parse_Type(lex) ) ); - if( GET_TOK(tok, lex) != TOK_COMMA ) - break; - } - CHECK_TOK(tok, TOK_PAREN_CLOSE); - - if(LOOK_AHEAD(lex) == TOK_RWORD_WHERE) - { - GET_TOK(tok, lex); - Parse_WhereClause(lex, params); - } - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - //if( refs.size() == 0 ) - // WARNING( , W000, "Use 'struct Name;' instead of 'struct Name();' ... ning-nong"); - return AST::Struct(mv$(params), mv$(refs)); - } - else if(tok.type() == TOK_SEMICOLON) - { - // Unit-like struct - return AST::Struct(mv$(params), ::std::vector()); - } - else if(tok.type() == TOK_BRACE_OPEN) - { - ::std::vector items; - while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) - { - AST::MetaItems item_attrs; - while( tok.type() == TOK_ATTR_OPEN ) - { - item_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - GET_TOK(tok, lex); - } - SET_ATTRS(lex, item_attrs); - - bool is_pub = false; - if(tok.type() == TOK_RWORD_PUB) { - is_pub = true; - GET_TOK(tok, lex); - } - - CHECK_TOK(tok, TOK_IDENT); - auto name = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_COLON); - TypeRef type = Parse_Type(lex); - - items.push_back( AST::StructItem( mv$(item_attrs), is_pub, mv$(name), mv$(type) ) ); - if(GET_TOK(tok, lex) == TOK_BRACE_CLOSE) - break; - CHECK_TOK(tok, TOK_COMMA); - } - //if( items.size() == 0 ) - // WARNING( , W000, "Use 'struct Name;' instead of 'struct Nam { };' ... ning-nong"); - return AST::Struct(::std::move(params), ::std::move(items)); - } - else - { - throw ParseError::Unexpected(lex, tok); - } -} - -AST::Trait Parse_TraitDef(TokenStream& lex, const AST::MetaItems& meta_items) -{ - TRACE_FUNCTION; - - Token tok; - - AST::GenericParams params; - if( GET_TOK(tok, lex) == TOK_LT ) - { - params = Parse_GenericParams(lex); - GET_CHECK_TOK(tok, lex, TOK_GT); - tok = lex.getToken(); - } - - // Trait bounds "trait Trait : 'lifetime + OtherTrait + OtherTrait2" - ::std::vector > supertraits; - if(tok.type() == TOK_COLON) - { - do { - if( GET_TOK(tok, lex) == TOK_LIFETIME ) { - // TODO: Need a better way of indiciating 'static than just an invalid path - supertraits.push_back( make_spanned( Span(tok.get_pos()), AST::Path() ) ); - } - else { - PUTBACK(tok, lex); - supertraits.push_back( GET_SPANNED(::AST::Path, lex, Parse_Path(lex, PATH_GENERIC_TYPE)) ); - } - } while( GET_TOK(tok, lex) == TOK_PLUS ); - } - - // TODO: Support "for Sized?" - if(tok.type() == TOK_RWORD_WHERE) - { - //if( params.ty_params().size() == 0 ) - // throw ParseError::Generic("Where clause with no generic params"); - Parse_WhereClause(lex, params); - tok = lex.getToken(); - } - - - AST::Trait trait( mv$(params), mv$(supertraits) ); - - CHECK_TOK(tok, TOK_BRACE_OPEN); - while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) - { - - AST::MetaItems item_attrs; - while( tok.type() == TOK_ATTR_OPEN ) - { - item_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - GET_TOK(tok, lex); - } - SET_ATTRS(lex, item_attrs); - - auto ps = lex.start_span(); - if( tok.type() == TOK_MACRO ) { - auto inv = Parse_MacroInvocation( ps, mv$(tok.str()), lex ); - // - Silently consume ';' after the macro - if( GET_TOK(tok, lex) != TOK_SEMICOLON ) - PUTBACK(tok, lex); - - trait.items().push_back( AST::Named("", AST::Item(mv$(inv)), false) ); - continue ; - } - - bool is_specialisable = false; - if( tok.type() == TOK_IDENT && tok.str() == "default" ) { - is_specialisable = true; - GET_TOK(tok, lex); - } - // TODO: Mark specialisation - (void)is_specialisable; - - bool fn_is_const = false; - bool fn_is_unsafe = false; - ::std::string abi = ABI_RUST; - switch(tok.type()) - { - case TOK_RWORD_STATIC: { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - auto name = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_COLON); - auto ty = Parse_Type(lex); - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - - ::AST::Expr val; - if(GET_TOK(tok, lex) == TOK_EQUAL) { - val = Parse_Expr(lex); - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_SEMICOLON); - - // TODO: Attributes on associated statics - trait.add_static( mv$(name), ::AST::Static(AST::Static::STATIC, mv$(ty), val)/*, mv$(item_attrs)*/ ); - break; } - case TOK_RWORD_CONST: { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - auto name = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_COLON); - auto ty = Parse_Type(lex); - - ::AST::Expr val; - if(GET_TOK(tok, lex) == TOK_EQUAL) { - val = Parse_Expr(lex); - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_SEMICOLON); - - // TODO: Attributes on associated constants - trait.add_static( mv$(name), ::AST::Static(AST::Static::CONST, mv$(ty), val)/*, mv$(item_attrs)*/ ); - break; } - // Associated type - case TOK_RWORD_TYPE: { - auto atype_params = ::AST::GenericParams { }; - GET_CHECK_TOK(tok, lex, TOK_IDENT); - auto name = mv$(tok.str()); - if( GET_TOK(tok, lex) == TOK_COLON ) - { - // Bounded associated type - Parse_TypeBound(lex, atype_params, TypeRef(lex.getPosition(), "Self", 0xFFFF)); - GET_TOK(tok, lex); - } - if( tok.type() == TOK_RWORD_WHERE ) { - throw ParseError::Todo(lex, "Where clause on associated type"); - } - - TypeRef default_type = TypeRef( lex.getPosition() ); - if( tok.type() == TOK_EQUAL ) { - default_type = Parse_Type(lex); - GET_TOK(tok, lex); - } - - CHECK_TOK(tok, TOK_SEMICOLON); - trait.add_type( ::std::move(name), ::std::move(default_type) ); - trait.items().back().data.as_Type().params() = mv$(atype_params); - break; } - - // Functions (possibly unsafe) - // TODO: Const? - case TOK_RWORD_UNSAFE: - fn_is_unsafe = true; - if( GET_TOK(tok, lex) == TOK_RWORD_EXTERN ) - case TOK_RWORD_EXTERN: - { - abi = "C"; - if( GET_TOK(tok, lex) == TOK_STRING ) - abi = tok.str(); - else - PUTBACK(tok, lex); - - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_RWORD_FN); - case TOK_RWORD_FN: { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - ::std::string name = mv$(tok.str()); - // Self allowed, prototype-form allowed (optional names and no code) - auto fcn = Parse_FunctionDef(lex, abi, true, true, fn_is_unsafe, fn_is_const); - if( GET_TOK(tok, lex) == TOK_BRACE_OPEN ) - { - PUTBACK(tok, lex); - fcn.set_code( Parse_ExprBlock(lex) ); - } - else if( tok.type() == TOK_SEMICOLON ) - { - // Accept it - } - else - { - throw ParseError::Unexpected(lex, tok); - } - // TODO: Store `item_attrs` - trait.add_function( ::std::move(name), /*mv$(item_attrs),*/ ::std::move(fcn) ); - break; } - default: - throw ParseError::Unexpected(lex, tok); - } - } - - return trait; -} - -AST::Enum Parse_EnumDef(TokenStream& lex, const AST::MetaItems& meta_items) -{ - TRACE_FUNCTION; - - Token tok; - - tok = lex.getToken(); - // Type params supporting "where" - AST::GenericParams params; - if( tok.type() == TOK_LT ) - { - params = Parse_GenericParams(lex); - GET_CHECK_TOK(tok, lex, TOK_GT); - if(GET_TOK(tok, lex) == TOK_RWORD_WHERE) - { - Parse_WhereClause(lex, params); - tok = lex.getToken(); - } - } - - // Body - CHECK_TOK(tok, TOK_BRACE_OPEN); - ::std::vector variants; - while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) - { - auto sp = lex.start_span(); - - AST::MetaItems item_attrs; - while( tok.type() == TOK_ATTR_OPEN ) - { - item_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - GET_TOK(tok, lex); - } - SET_ATTRS(lex, item_attrs); - - CHECK_TOK(tok, TOK_IDENT); - ::std::string name = mv$(tok.str()); - if( GET_TOK(tok, lex) == TOK_PAREN_OPEN ) - { - ::std::vector types; - // Get type list - do - { - if(LOOK_AHEAD(lex) == TOK_PAREN_CLOSE) - { - GET_TOK(tok, lex); - break; - } - - AST::MetaItems field_attrs; - while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) - { - GET_TOK(tok, lex); - field_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - - types.push_back( Parse_Type(lex) ); - } while( GET_TOK(tok, lex) == TOK_COMMA ); - CHECK_TOK(tok, TOK_PAREN_CLOSE); - GET_TOK(tok, lex); - variants.push_back( AST::EnumVariant(mv$(item_attrs), mv$(name), mv$(types)) ); - } - else if( tok.type() == TOK_BRACE_OPEN ) - { - ::std::vector<::AST::StructItem> fields; - do - { - if(LOOK_AHEAD(lex) == TOK_BRACE_CLOSE) - { - GET_TOK(tok, lex); - break; - } - - AST::MetaItems field_attrs; - while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) - { - GET_TOK(tok, lex); - field_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - - GET_CHECK_TOK(tok, lex, TOK_IDENT); - auto name = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_COLON); - auto ty = Parse_Type(lex); - fields.push_back( ::AST::StructItem(mv$(field_attrs), true, mv$(name), mv$(ty)) ); - } while( GET_TOK(tok, lex) == TOK_COMMA ); - CHECK_TOK(tok, TOK_BRACE_CLOSE); - GET_TOK(tok, lex); - - variants.push_back( AST::EnumVariant(mv$(item_attrs), mv$(name), mv$(fields)) ); - } - else if( tok.type() == TOK_EQUAL ) - { - auto node = Parse_Expr(lex); - variants.push_back( AST::EnumVariant(mv$(item_attrs), mv$(name), mv$(node)) ); - GET_TOK(tok, lex); - } - else - { - variants.push_back( AST::EnumVariant(mv$(item_attrs), mv$(name), ::AST::Expr()) ); - } - - if( tok.type() != TOK_COMMA ) - break; - } - CHECK_TOK(tok, TOK_BRACE_CLOSE); - - - return AST::Enum( mv$(params), mv$(variants) ); -} - -::AST::Union Parse_Union(TokenStream& lex, AST::MetaItems& meta_items) -{ - Token tok; - - TRACE_FUNCTION; - - AST::GenericParams params; - if( GET_TOK(tok, lex) == TOK_LT ) - { - params = Parse_GenericParams(lex); - GET_CHECK_TOK(tok, lex, TOK_GT); - if(GET_TOK(tok, lex) == TOK_RWORD_WHERE) - { - Parse_WhereClause(lex, params); - tok = lex.getToken(); - } - } - - ::std::vector< ::AST::StructItem> variants; - - CHECK_TOK(tok, TOK_BRACE_OPEN); - do { - if( LOOK_AHEAD(lex) == TOK_BRACE_CLOSE ) { - GET_TOK(tok, lex); - break ; - } - - AST::MetaItems item_attrs; - while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) - { - GET_TOK(tok, lex); - item_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - SET_ATTRS(lex, item_attrs); - - bool is_pub = false; - if( LOOK_AHEAD(lex) == TOK_RWORD_PUB ) { - is_pub = true; - GET_TOK(tok, lex); - } - - GET_CHECK_TOK(tok, lex, TOK_IDENT); - auto name = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_COLON); - - auto ty = Parse_Type(lex); - - variants.push_back( ::AST::StructItem( mv$(item_attrs), is_pub, mv$(name), mv$(ty) ) ); - - } while( GET_TOK(tok, lex) == TOK_COMMA ); - CHECK_TOK(tok, TOK_BRACE_CLOSE); - - return ::AST::Union( mv$(params), mv$(variants) ); -} - -/// Parse a meta-item declaration (either #![ or #[) -AST::MetaItem Parse_MetaItem(TokenStream& lex) -{ - TRACE_FUNCTION; - Token tok; - GET_TOK(tok, lex); - - if( tok.type() == TOK_INTERPOLATED_META ) { - return mv$(tok.frag_meta()); - } - - CHECK_TOK(tok, TOK_IDENT); - ::std::string name = mv$(tok.str()); - switch(GET_TOK(tok, lex)) - { - case TOK_EQUAL: - GET_CHECK_TOK(tok, lex, TOK_STRING); - return AST::MetaItem(name, tok.str()); - case TOK_PAREN_OPEN: { - ::std::vector items; - do { - if(LOOK_AHEAD(lex) == TOK_PAREN_CLOSE) { - GET_TOK(tok, lex); - break; - } - items.push_back(Parse_MetaItem(lex)); - } while(GET_TOK(tok, lex) == TOK_COMMA); - CHECK_TOK(tok, TOK_PAREN_CLOSE); - return AST::MetaItem(name, mv$(items)); } - default: - PUTBACK(tok, lex); - return AST::MetaItem(name); - } -} - -::AST::Item Parse_Impl(TokenStream& lex, AST::MetaItems attrs, bool is_unsafe=false) -{ - TRACE_FUNCTION; - Token tok; - auto ps = lex.start_span(); - - AST::GenericParams params; - // 1. (optional) type parameters - if( GET_TOK(tok, lex) == TOK_LT ) - { - params = Parse_GenericParams(lex); - GET_CHECK_TOK(tok, lex, TOK_GT); - } - else { - PUTBACK(tok, lex); - } - // 2. Either a trait name (with type params), or the type to impl - - Spanned trait_path; - - // - Handle negative impls specially, which must be a trait - // "impl !Trait for Type {}" - if( GET_TOK(tok, lex) == TOK_EXCLAM ) - { - trait_path = GET_SPANNED(::AST::Path, lex, Parse_Path(lex, PATH_GENERIC_TYPE)); - GET_CHECK_TOK(tok, lex, TOK_RWORD_FOR); - auto impl_type = Parse_Type(lex, true); - - if( GET_TOK(tok, lex) == TOK_RWORD_WHERE ) - { - Parse_WhereClause(lex, params); - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_BRACE_OPEN); - // negative impls can't have any content - GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); - - return ::AST::Item::make_NegImpl( AST::ImplDef(lex.end_span(ps), mv$(attrs), mv$(params), mv$(trait_path), mv$(impl_type) ) ); - } - - // - Don't care which at this stage - PUTBACK(tok, lex); - - auto impl_type = Parse_Type(lex, true); - - if( GET_TOK(tok, lex) == TOK_RWORD_FOR ) - { - // Trickery! All traits parse as valid types, so this works. - if( !impl_type.is_path() ) - throw ParseError::Generic(lex, "Trait was not a path"); - trait_path = Spanned< AST::Path> { - impl_type.span(), - mv$(impl_type.path()) - }; - // Implementing a trait for another type, get the target type - if( GET_TOK(tok, lex) == TOK_DOUBLE_DOT ) - { - // Default impl - impl_type = TypeRef(TypeRef::TagInvalid(), lex.getPosition()); - } - else - { - PUTBACK(tok, lex); - impl_type = Parse_Type(lex, true); - } - } - else { - PUTBACK(tok, lex); - } - - // Where clause - if( GET_TOK(tok, lex) == TOK_RWORD_WHERE ) - { - Parse_WhereClause(lex, params); - } - else { - PUTBACK(tok, lex); - } - GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); - - while( LOOK_AHEAD(lex) == TOK_CATTR_OPEN ) - { - GET_TOK(tok, lex); - attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - - AST::Impl impl( AST::ImplDef( lex.end_span(ps), mv$(attrs), mv$(params), mv$(trait_path), mv$(impl_type) ) ); - - // A sequence of method implementations - while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) - { - auto ps = lex.start_span(); - if( tok.type() == TOK_MACRO ) - { - impl.add_macro_invocation( Parse_MacroInvocation( ps, mv$(tok.str()), lex ) ); - // - Silently consume ';' after the macro - if( GET_TOK(tok, lex) != TOK_SEMICOLON ) - PUTBACK(tok, lex); - } - else - { - PUTBACK(tok, lex); - Parse_Impl_Item(lex, impl); - } - } - - return ::AST::Item::make_Impl( mv$(impl) ); -} - -void Parse_Impl_Item(TokenStream& lex, AST::Impl& impl) -{ - TRACE_FUNCTION; - Token tok; - - GET_TOK(tok, lex); - - AST::MetaItems item_attrs; - while( tok.type() == TOK_ATTR_OPEN ) - { - item_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - GET_TOK(tok, lex); - } - SET_ATTRS(lex, item_attrs); - - auto ps = lex.start_span(); - - bool is_public = false; - if(tok.type() == TOK_RWORD_PUB) { - is_public = true; - GET_TOK(tok, lex); - } - - bool is_specialisable = false; - if( tok.type() == TOK_IDENT && tok.str() == "default" ) { - is_specialisable = true; - GET_TOK(tok, lex); - } - - ::std::string abi = ABI_RUST; - bool fn_is_unsafe = false; - bool fn_is_const = false; - switch(tok.type()) - { - case TOK_RWORD_TYPE: { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - auto name = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_EQUAL); - impl.add_type(is_public, is_specialisable, name, Parse_Type(lex)); - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - break; } - case TOK_RWORD_UNSAFE: - fn_is_unsafe = true; - GET_TOK(tok, lex); - if( tok.type() == TOK_RWORD_CONST ) - case TOK_RWORD_CONST: - { - GET_TOK(tok, lex); - if( tok.type() != TOK_RWORD_FN && tok.type() != TOK_RWORD_UNSAFE && !fn_is_unsafe ) - { - CHECK_TOK(tok, TOK_IDENT); - auto name = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_COLON); - auto ty = Parse_Type(lex); - GET_CHECK_TOK(tok, lex, TOK_EQUAL); - auto val = Parse_Expr(lex); - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - - auto i = ::AST::Static(AST::Static::CONST, mv$(ty), mv$(val)); - // TODO: Attributes on associated constants - impl.add_static( is_public, is_specialisable, mv$(name), mv$(i) /*, mv$(item_attrs)*/ ); - break ; - } - else if( tok.type() == TOK_RWORD_UNSAFE ) - { - fn_is_unsafe = true; - GET_CHECK_TOK(tok, lex, TOK_RWORD_FN); - } - fn_is_const = true; - } - if( tok.type() == TOK_RWORD_EXTERN ) - // FALL - case TOK_RWORD_EXTERN: - { - abi = "C"; - if( GET_TOK(tok, lex) == TOK_STRING ) - abi = tok.str(); - else - PUTBACK(tok, lex); - - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_RWORD_FN); - // FALL - case TOK_RWORD_FN: { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - // TODO: Hygine on function names? - Not in impl blocks? - ::std::string name = mv$(tok.str()); - DEBUG("Function " << name); - // - Self allowed, can't be prototype-form - auto fcn = Parse_FunctionDefWithCode(lex, abi, true, fn_is_unsafe, fn_is_const); - impl.add_function(is_public, is_specialisable, mv$(name), mv$(fcn)); - break; } - - default: - throw ParseError::Unexpected(lex, tok); - } - - impl.items().back().data->span = lex.end_span(ps); - impl.items().back().data->attrs = mv$(item_attrs); // Empty for functions -} - -AST::ExternBlock Parse_ExternBlock(TokenStream& lex, ::std::string abi, ::AST::MetaItems& block_attrs) -{ - TRACE_FUNCTION; - Token tok; - - while( GET_TOK(tok, lex) == TOK_CATTR_OPEN ) - { - block_attrs.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - PUTBACK(tok, lex); - - AST::ExternBlock rv { abi }; - - while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) - { - AST::MetaItems meta_items; - while( tok.type() == TOK_ATTR_OPEN ) - { - meta_items.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - GET_TOK(tok, lex); - } - SET_ATTRS(lex, meta_items); - - auto ps = lex.start_span(); - - bool is_public = false; - if( tok.type() == TOK_RWORD_PUB ) { - is_public = true; - GET_TOK(tok, lex); - } - switch(tok.type()) - { - case TOK_RWORD_FN: { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - auto name = mv$(tok.str()); - // parse function as prototype - // - no self, is prototype, is unsafe and not const - auto i = ::AST::Item( Parse_FunctionDef(lex, abi, false, true, true,false) ); - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - - i.attrs = mv$(meta_items); - i.span = lex.end_span(ps); - - rv.add_item( AST::Named { mv$(name), mv$(i), is_public } ); - break; } - case TOK_RWORD_STATIC: { - bool is_mut = false; - if( GET_TOK(tok, lex) == TOK_RWORD_MUT ) - is_mut = true; - else - PUTBACK(tok, lex); - GET_CHECK_TOK(tok, lex, TOK_IDENT); - auto name = mv$(tok.str()); - GET_CHECK_TOK(tok, lex, TOK_COLON); - auto type = Parse_Type(lex); - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - - auto i = ::AST::Item(::AST::Static( (is_mut ? ::AST::Static::MUT : ::AST::Static::STATIC), mv$(type), ::AST::Expr() )); - i.attrs = mv$(meta_items); - i.span = lex.end_span(ps); - rv.add_item( AST::Named { mv$(name), mv$(i), is_public } ); - break; } - default: - throw ParseError::Unexpected(lex, tok, {TOK_RWORD_FN, TOK_RWORD_STATIC}); - } - } - - return rv; -} - -void Parse_Use_Wildcard(Span sp, AST::Path base_path, ::std::function fcn) -{ - fcn( AST::UseStmt(mv$(sp), mv$(base_path)), "" ); // HACK! Empty path indicates wilcard import -} -void Parse_Use_Set(TokenStream& lex, const ProtoSpan& ps, const AST::Path& base_path, ::std::function fcn) -{ - TRACE_FUNCTION; - - Token tok; - do { - AST::Path path; - ::std::string name; - if( GET_TOK(tok, lex) == TOK_RWORD_SELF ) { - path = ::AST::Path(base_path); - name = base_path[base_path.size()-1].name(); - } - else if( tok.type() == TOK_BRACE_CLOSE ) { - break ; - } - else { - CHECK_TOK(tok, TOK_IDENT); - path = base_path + AST::PathNode(tok.str(), {}); - name = mv$(tok.str()); - } - - if( GET_TOK(tok, lex) == TOK_RWORD_AS ) { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - name = mv$(tok.str()); - } - else { - PUTBACK(tok, lex); - } - fcn(AST::UseStmt(lex.end_span(ps), mv$(path)), mv$(name)); - } while( GET_TOK(tok, lex) == TOK_COMMA ); - PUTBACK(tok, lex); -} - -void Parse_Use(TokenStream& lex, ::std::function fcn) -{ - TRACE_FUNCTION; - - Token tok; - AST::Path path = AST::Path("", {}); - ::std::vector nodes; - ProtoSpan span_start = lex.start_span(); - - switch( GET_TOK(tok, lex) ) - { - case TOK_RWORD_SELF: - path = AST::Path( AST::Path::TagSelf(), {} ); // relative path - break; - case TOK_RWORD_SUPER: { - unsigned int count = 1; - while( LOOK_AHEAD(lex) == TOK_DOUBLE_COLON && lex.lookahead(1) == TOK_RWORD_SUPER ) { - GET_CHECK_TOK(tok, lex, TOK_DOUBLE_COLON); - GET_CHECK_TOK(tok, lex, TOK_RWORD_SUPER); - count += 1; - } - path = AST::Path( AST::Path::TagSuper(), count, {} ); - break; } - case TOK_IDENT: - path.append( AST::PathNode(mv$(tok.str()), {}) ); - break; - // Leading :: is allowed and ignored for the $crate feature - case TOK_DOUBLE_COLON: - // Absolute path - // HACK! mrustc emits $crate as `::"crate-name"` - if( LOOK_AHEAD(lex) == TOK_STRING ) - { - GET_CHECK_TOK(tok, lex, TOK_STRING); - path = ::AST::Path(tok.str(), {}); - GET_CHECK_TOK(tok, lex, TOK_DOUBLE_COLON); - } - else { - PUTBACK(tok, lex); - } - break; - case TOK_BRACE_OPEN: - Parse_Use_Set(lex, span_start, path, fcn); - GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); - return; - default: - throw ParseError::Unexpected(lex, tok); - } - while( GET_TOK(tok, lex) == TOK_DOUBLE_COLON ) - { - if( GET_TOK(tok, lex) == TOK_IDENT ) - { - path.append( AST::PathNode( mv$(tok.str()), {}) ); - } - else - { - //path.set_span( lex.end_span(span_start) ); - switch( tok.type() ) - { - case TOK_BRACE_OPEN: - Parse_Use_Set(lex, span_start, mv$(path), fcn); - GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); - break ; - case TOK_STAR: - Parse_Use_Wildcard( lex.end_span(span_start), mv$(path), fcn ); - break ; - default: - throw ParseError::Unexpected(lex, tok); - } - // early return - This branch is either the end of the use statement, or a syntax error - return ; - } - } - //path.set_span( lex.end_span(span_start) ); - - ::std::string name; - // This should only be allowed if the last token was an ident - // - Above checks ensure this - if( tok.type() == TOK_RWORD_AS ) - { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - name = mv$(tok.str()); - } - else - { - PUTBACK(tok, lex); - assert(path.nodes().size() > 0); - name = path.nodes().back().name(); - } - - fcn( AST::UseStmt(lex.end_span(span_start), mv$(path)), name); -} - - -::AST::MacroInvocation Parse_MacroInvocation(ProtoSpan span_start, ::std::string name, TokenStream& lex) -{ - Token tok; - ::std::string ident; - if( GET_TOK(tok, lex) == TOK_IDENT ) { - ident = mv$(tok.str()); - } - else { - PUTBACK(tok, lex); - } - DEBUG("name=" << name << ", ident=" << ident); - TokenTree tt = Parse_TT(lex, true); - return ::AST::MacroInvocation( lex.end_span(span_start), mv$(name), mv$(ident), mv$(tt)); -} - -::AST::Named<::AST::Item> Parse_Mod_Item_S(TokenStream& lex, const AST::Module::FileInfo& mod_fileinfo, const ::AST::Path& mod_path, AST::MetaItems meta_items) -{ - TRACE_FUNCTION_F("mod_path="<" fn ...` - // `extern "" { ...` - case TOK_STRING: { - ::std::string abi = tok.str(); - switch(GET_TOK(tok, lex)) - { - // `extern "" fn ...` - case TOK_RWORD_FN: { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, abi, false, false,false) ); - break; } - // `extern "ABI" {` - case TOK_BRACE_OPEN: - item_name = ""; - item_data = ::AST::Item( Parse_ExternBlock(lex, mv$(abi), meta_items) ); - break; - default: - throw ParseError::Unexpected(lex, tok, {TOK_RWORD_FN, TOK_BRACE_OPEN}); - } - break; } - // `extern fn ...` - case TOK_RWORD_FN: - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, "C", false, false,false) ); - break; - - // NOTE: `extern { ...` is handled in caller - case TOK_BRACE_OPEN: - item_name = ""; - item_data = ::AST::Item( Parse_ExternBlock(lex, "C", meta_items) ); - break; - - // `extern crate "crate-name" as crate_name;` - // `extern crate crate_name;` - // `extern crate crate_name as other_name;` - case TOK_RWORD_CRATE: - switch( GET_TOK(tok, lex) ) - { - // `extern crate "crate-name" as crate_name;` - // NOTE: rustc doesn't allow this, keep in mrustc for for reparse support - case TOK_STRING: - item_data = ::AST::Item::make_Crate({ tok.str() }); - GET_CHECK_TOK(tok, lex, TOK_RWORD_AS); - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - break; - // `extern crate crate_name;` - // `extern crate crate_name as other_name;` - case TOK_IDENT: - item_name = mv$(tok.str()); - if(GET_TOK(tok, lex) == TOK_RWORD_AS) { - item_data = ::AST::Item::make_Crate({ mv$(item_name) }); - - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - } - else { - PUTBACK(tok, lex); - item_data = ::AST::Item::make_Crate({ item_name }); - } - break; - default: - throw ParseError::Unexpected(lex, tok, {TOK_STRING, TOK_IDENT}); - } - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - break; - default: - throw ParseError::Unexpected(lex, tok, {TOK_STRING, TOK_RWORD_FN, TOK_BRACE_OPEN, TOK_RWORD_CRATE}); - } - break; - - // `const NAME` - // `const [unsafe] fn` - case TOK_RWORD_CONST: - switch( GET_TOK(tok, lex) ) - { - case TOK_IDENT: { - item_name = mv$(tok.str()); - - GET_CHECK_TOK(tok, lex, TOK_COLON); - TypeRef type = Parse_Type(lex); - GET_CHECK_TOK(tok, lex, TOK_EQUAL); - AST::Expr val = Parse_Expr(lex); - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - item_data = ::AST::Item( ::AST::Static(AST::Static::CONST, mv$(type), mv$(val)) ); - break; } - case TOK_RWORD_UNSAFE: - GET_CHECK_TOK(tok, lex, TOK_RWORD_FN); - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, ABI_RUST, false, true,true/*unsafe,const*/) ); - break; - case TOK_RWORD_FN: - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - // - self not allowed, not prototype - item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, ABI_RUST, false, false,true/*unsafe,const*/) ); - break; - default: - throw ParseError::Unexpected(lex, tok, {TOK_IDENT, TOK_RWORD_FN}); - } - break; - // `static NAME` - // `static mut NAME` - case TOK_RWORD_STATIC: { - bool is_mut = false; - if(GET_TOK(tok, lex) == TOK_RWORD_MUT) { - is_mut = true; - GET_TOK(tok, lex); - } - CHECK_TOK(tok, TOK_IDENT); - item_name = mv$(tok.str()); - - GET_CHECK_TOK(tok, lex, TOK_COLON); - TypeRef type = Parse_Type(lex); - - GET_CHECK_TOK(tok, lex, TOK_EQUAL); - - AST::Expr val = Parse_Expr(lex); - - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - item_data = ::AST::Item( ::AST::Static( (is_mut ? AST::Static::MUT : AST::Static::STATIC), mv$(type), mv$(val)) ); - break; } - - // `unsafe fn` - // `unsafe trait` - // `unsafe impl` - case TOK_RWORD_UNSAFE: - meta_items.push_back( AST::MetaItem("#UNSAFE") ); - switch(GET_TOK(tok, lex)) - { - // `unsafe extern fn` - case TOK_RWORD_EXTERN: { - ::std::string abi = "C"; - if(GET_TOK(tok, lex) == TOK_STRING) { - abi = mv$(tok.str()); - } - else { - PUTBACK(tok, lex); - } - GET_CHECK_TOK(tok, lex, TOK_RWORD_FN); - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, abi, false, true,false/*unsafe,const*/) ); - break; } - // `unsafe fn` - case TOK_RWORD_FN: - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - // - self not allowed, not prototype - item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, ABI_RUST, false, true,false/*unsafe,const*/) ); - break; - // `unsafe trait` - case TOK_RWORD_TRAIT: - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - // TODO: Mark as unsafe - meta_items.push_back( AST::MetaItem("#UNSAFE") ); - item_data = ::AST::Item( Parse_TraitDef(lex, meta_items) ); - break; - // `unsafe impl` - case TOK_RWORD_IMPL: - return ::AST::Named< ::AST::Item> { "", Parse_Impl(lex, mv$(meta_items), true), false }; - default: - throw ParseError::Unexpected(lex, tok, {TOK_RWORD_FN, TOK_RWORD_TRAIT, TOK_RWORD_IMPL}); - } - break; - // `fn` - case TOK_RWORD_FN: - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - // - self not allowed, not prototype - item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, ABI_RUST, false, false,false/*unsafe,const*/) ); - break; - // `type` - case TOK_RWORD_TYPE: - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - item_data = ::AST::Item( Parse_TypeAlias(lex) ); - break; - // `struct` - case TOK_RWORD_STRUCT: - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - item_data = ::AST::Item( Parse_Struct(lex, meta_items) ); - break; - // `enum` - case TOK_RWORD_ENUM: - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - item_data = ::AST::Item( Parse_EnumDef(lex, meta_items) ); - break; - // Contextual keywords - case TOK_IDENT: - if( tok.str() == "union" ) { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - item_data = ::AST::Item( Parse_Union(lex, meta_items) ); - } - else { - throw ParseError::Unexpected(lex, tok); - } - break; - // `impl` - case TOK_RWORD_IMPL: - return ::AST::Named< ::AST::Item> { "", Parse_Impl(lex, mv$(meta_items)), false }; - // `trait` - case TOK_RWORD_TRAIT: - GET_CHECK_TOK(tok, lex, TOK_IDENT); - item_name = mv$(tok.str()); - item_data = ::AST::Item( Parse_TraitDef(lex, meta_items) ); - break; - - case TOK_RWORD_MOD: { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - auto name = mv$(tok.str()); - DEBUG("Sub module '" << name << "'"); - AST::Module submod( mod_path + name ); - - // Rules for external files (/ path handling): - // - IF using stdin (path='-') - Disallow and propagate '-' as path - // - IF a #[path] attribute was passed, allow - // - IF in crate root or mod.rs, allow (input flag) - // - else, disallow and set flag - ::std::string path_attr = (meta_items.has("path") ? meta_items.get("path")->string() : ""); - - //submod.m_file_info = get_submod_file(lex.end_span(ps), mod_fileinfo, name, path_attr, LOOK_AHEAD(lex) == TOK_SEMICOLON); - - ::std::string sub_path; - bool sub_file_controls_dir = true; - if( mod_fileinfo.path == "-" ) { - if( path_attr.size() ) { - ERROR(lex.getPosition(), E0000, "Cannot load module from file when reading stdin"); - } - sub_path = "-"; - } - else if( path_attr.size() > 0 ) - { - sub_path = dirname(mod_fileinfo.path) + path_attr; - } - else if( mod_fileinfo.controls_dir ) - { - sub_path = dirname(mod_fileinfo.path) + name; - } - else - { - sub_path = mod_fileinfo.path; - sub_file_controls_dir = false; - } - DEBUG("Mod '" << name << "', sub_path = " << sub_path); - - submod.m_file_info.path = sub_path; - submod.m_file_info.controls_dir = sub_file_controls_dir; - - // Check #[cfg] and don't load if it fails - struct H { - static bool check_item_cfg(const ::AST::MetaItems& attrs) - { - for(const auto& at : attrs.m_items) { - if( at.name() == "cfg" && !check_cfg(attrs.m_span, at) ) { - return false; - } - } - return true; - } - }; - - switch( GET_TOK(tok, lex) ) - { - case TOK_BRACE_OPEN: - submod.m_file_info.path = sub_path + "/"; - Parse_ModRoot(lex, submod, meta_items); - GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); - break; - case TOK_SEMICOLON: - if( sub_path == "-" ) { - ERROR(lex.getPosition(), E0000, "Cannot load module from file when reading stdin"); - } - else if( path_attr.size() == 0 && ! mod_fileinfo.controls_dir ) - { - ERROR(lex.getPosition(), E0000, "Can't load from files outside of mod.rs or crate root"); - } - else if( !H::check_item_cfg(meta_items) ) { - // Ignore - emit Item::None - item_name = mv$(name); - item_data = ::AST::Item( ); - break ; - } - else - { - ::std::string newpath_dir = sub_path + "/"; - ::std::string newpath_file = path_attr.size() > 0 ? sub_path : sub_path + ".rs"; - DEBUG("newpath_dir = '" << newpath_dir << "', newpath_file = '" << newpath_file << "'"); - ::std::ifstream ifs_dir (newpath_dir + "mod.rs"); - ::std::ifstream ifs_file(newpath_file); - if( ifs_dir.is_open() && ifs_file.is_open() ) - { - // Collision - ERROR(lex.getPosition(), E0000, "Both modname.rs and modname/mod.rs exist"); - } - else if( ifs_dir.is_open() ) - { - // Load from dir - submod.m_file_info.path = newpath_dir + "mod.rs"; - } - else if( ifs_file.is_open() ) - { - submod.m_file_info.path = newpath_file; - } - else - { - // Can't find file - ERROR(lex.getPosition(), E0000, "Can't find file for '" << name << "' in '" << mod_fileinfo.path << "'"); - } - DEBUG("- path = " << submod.m_file_info.path); - Lexer sub_lex(submod.m_file_info.path); - Parse_ModRoot(sub_lex, submod, meta_items); - GET_CHECK_TOK(tok, sub_lex, TOK_EOF); - } - break; - default: - throw ParseError::Generic("Expected { or ; after module name"); - } - item_name = mv$(name); - item_data = ::AST::Item( mv$(submod) ); - break; } - - default: - throw ParseError::Unexpected(lex, tok); - } - - item_data.attrs = mv$(meta_items); - item_data.span = lex.end_span(ps); - - return ::AST::Named< ::AST::Item> { mv$(item_name), mv$(item_data), is_public }; -} - -void Parse_Mod_Item(TokenStream& lex, AST::Module& mod, AST::MetaItems meta_items) -{ - SET_MODULE(lex, mod); - lex.parse_state().parent_attrs = &meta_items; - - //TRACE_FUNCTION; - Token tok; - - // `use ...` - if( LOOK_AHEAD(lex) == TOK_RWORD_USE || (lex.lookahead(0) == TOK_RWORD_PUB && lex.lookahead(1) == TOK_RWORD_USE) ) - { - bool is_public = false; - if( GET_TOK(tok, lex) == TOK_RWORD_PUB ) { - is_public = true; - GET_TOK(tok, lex); - } - - Parse_Use(lex, [&mod,is_public,&meta_items](AST::UseStmt p, std::string s) { - DEBUG(mod.path() << " - use " << p << " as '" << s << "'"); - mod.add_alias(is_public, mv$(p), s, meta_items.clone()); - }); - GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); - } - else - { - mod.add_item( Parse_Mod_Item_S(lex, mod.m_file_info, mod.path(), mv$(meta_items)) ); - } -} - -void Parse_ModRoot_Items(TokenStream& lex, AST::Module& mod) -{ - Token tok; - - for(;;) - { - // Check 1 - End of module (either via a closing brace, or EOF) - switch(GET_TOK(tok, lex)) - { - case TOK_BRACE_CLOSE: - case TOK_EOF: - PUTBACK(tok, lex); - return; - default: - PUTBACK(tok, lex); - break; - } - - // Attributes on the following item - AST::MetaItems meta_items; - while( GET_TOK(tok, lex) == TOK_ATTR_OPEN ) - { - meta_items.push_back( Parse_MetaItem(lex) ); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - } - PUTBACK(tok, lex); - DEBUG("meta_items = " << meta_items); - - Parse_Mod_Item(lex, mod, mv$(meta_items)); - } -} - -void Parse_ModRoot(TokenStream& lex, AST::Module& mod, AST::MetaItems& mod_attrs) -{ - TRACE_FUNCTION; - - Token tok; - - // Attributes on module/crate (will continue loop) - while( GET_TOK(tok, lex) == TOK_CATTR_OPEN ) - { - AST::MetaItem item = Parse_MetaItem(lex); - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - - mod_attrs.push_back( mv$(item) ); - } - PUTBACK(tok, lex); - - Parse_ModRoot_Items(lex, mod); -} - -AST::Crate Parse_Crate(::std::string mainfile) -{ - Token tok; - - Lexer lex(mainfile); - - size_t p = mainfile.find_last_of('/'); - ::std::string mainpath = (p != ::std::string::npos ? ::std::string(mainfile.begin(), mainfile.begin()+p+1) : "./"); - - AST::Crate crate; - - crate.root_module().m_file_info.path = mainpath; - crate.root_module().m_file_info.controls_dir = true; - - Parse_ModRoot(lex, crate.root_module(), crate.m_attrs); - - return crate; -} +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * parse/root.cpp + * - Parsing at the module level (highest-level parsing) + * + * Entrypoint: + * - Parse_Crate : Handles crate attrbutes, and passes on to Parse_ModRoot + * - Parse_ModRoot + */ +#include +#include +#include "parseerror.hpp" +#include "common.hpp" +#include +#include // ABI_RUST - TODO: Move elsewhere? +#include // check_cfg - for `mod nonexistant;` +#include // Used by directory path +#include "lex.hpp" // New file lexer + +template +Spanned get_spanned(TokenStream& lex, ::std::function f) { + auto ps = lex.start_span(); + auto v = f(); + return Spanned { + lex.end_span(ps), + mv$(v) + }; +} +#define GET_SPANNED(type, lex, val) get_spanned< type >(lex, [&](){ return val; }) + +// Check the next two tokens +#define LOOKAHEAD2(lex, tok1, tok2) ((lex).lookahead(0) == (tok1) && (lex).lookahead(1) == (tok2)) + +::std::string dirname(::std::string input) { + while( input.size() > 0 && input.back() != '/' ) { + input.pop_back(); + } + return input; +} + +AST::MetaItem Parse_MetaItem(TokenStream& lex); +void Parse_ModRoot(TokenStream& lex, AST::Module& mod, AST::MetaItems& mod_attrs); + +::std::vector< ::std::string> Parse_HRB(TokenStream& lex) +{ + TRACE_FUNCTION; + Token tok; + + ::std::vector< ::std::string> lifetimes; + GET_CHECK_TOK(tok, lex, TOK_LT); + do { + GET_TOK(tok, lex); + + ::AST::MetaItems attrs; + while(tok.type() == TOK_ATTR_OPEN) + { + attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + GET_TOK(tok, lex); + } + (void)attrs; // TODO: Attributes on generic params + + switch(tok.type()) + { + case TOK_LIFETIME: + lifetimes.push_back(tok.str()); + break; + default: + throw ParseError::Unexpected(lex, tok, Token(TOK_LIFETIME)); + } + } while( GET_TOK(tok, lex) == TOK_COMMA ); + CHECK_TOK(tok, TOK_GT); + return lifetimes; +} +/// Parse type parameters in a definition +void Parse_TypeBound(TokenStream& lex, AST::GenericParams& ret, TypeRef checked_type, ::std::vector< ::std::string> lifetimes = {}) +{ + TRACE_FUNCTION; + Token tok; + + do + { + ::std::vector< ::std::string> hrls; + if(GET_TOK(tok, lex) == TOK_LIFETIME) { + ret.add_bound(AST::GenericBound::make_TypeLifetime( { + checked_type.clone(), tok.str() + } )); + } + else if( tok.type() == TOK_QMARK ) { + ret.add_bound(AST::GenericBound::make_MaybeTrait( { + checked_type.clone(), Parse_Path(lex, PATH_GENERIC_TYPE) + } )); + } + else { + if( tok.type() == TOK_RWORD_FOR ) + { + hrls = Parse_HRB(lex); + } + else { + PUTBACK(tok, lex); + } + (void)hrls; // TODO: HRLs + + ret.add_bound( AST::GenericBound::make_IsTrait({ + checked_type.clone(), mv$(lifetimes), Parse_Path(lex, PATH_GENERIC_TYPE) + }) ); + } + } while( GET_TOK(tok, lex) == TOK_PLUS ); + PUTBACK(tok, lex); +} + +/// Parse type parameters within '<' and '>' (definition) +AST::GenericParams Parse_GenericParams(TokenStream& lex) +{ + TRACE_FUNCTION; + + AST::GenericParams ret; + Token tok; + do { + if( GET_TOK(tok, lex) == TOK_GT ) { + break ; + } + + ::AST::MetaItems attrs; + while(tok.type() == TOK_ATTR_OPEN) + { + attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + GET_TOK(tok, lex); + } + (void)attrs; // TODO: Attributes on generic params + + if( tok.type() == TOK_IDENT ) + { + // TODO: Hygine + ::std::string param_name = mv$(tok.str()); + ret.add_ty_param( AST::TypeParam( param_name ) ); + + auto param_ty = TypeRef(lex.getPosition(), param_name); + if( GET_TOK(tok, lex) == TOK_COLON ) + { + Parse_TypeBound(lex, ret, mv$(param_ty)); + GET_TOK(tok, lex); + } + + if( tok.type() == TOK_EQUAL ) + { + ret.ty_params().back().setDefault( Parse_Type(lex) ); + GET_TOK(tok, lex); + } + } + else if( tok.type() == TOK_LIFETIME ) + { + // TODO: Should lifetimes have hygine? + ::std::string param_name = tok.str(); + ret.add_lft_param( param_name ); + if( GET_TOK(tok, lex) == TOK_COLON ) + { + do { + GET_CHECK_TOK(tok, lex, TOK_LIFETIME); + ret.add_bound(AST::GenericBound::make_Lifetime( {param_name, tok.str()} )); + } while( GET_TOK(tok, lex) == TOK_PLUS ); + } + } + else + { + throw ParseError::Unexpected(lex, tok, {TOK_IDENT, TOK_LIFETIME}); + } + } while( tok.type() == TOK_COMMA ); + PUTBACK(tok, lex); + return ret; +} + + +/// Parse the contents of a 'where' clause +void Parse_WhereClause(TokenStream& lex, AST::GenericParams& params) +{ + TRACE_FUNCTION; + Token tok; + + do { + GET_TOK(tok, lex); + if( tok.type() == TOK_BRACE_OPEN ) { + break; + } + + if( tok.type() == TOK_LIFETIME ) + { + auto lhs = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_COLON); + do { + GET_CHECK_TOK(tok, lex, TOK_LIFETIME); + auto rhs = mv$(tok.str()); + params.add_bound( AST::GenericBound::make_Lifetime({lhs, rhs}) ); + } while( GET_TOK(tok, lex) == TOK_PLUS ); + PUTBACK(tok, lex); + } + // Higher-ranked types/lifetimes + else if( tok.type() == TOK_RWORD_FOR ) + { + ::std::vector< ::std::string> lifetimes = Parse_HRB(lex); + + TypeRef type = Parse_Type(lex); + GET_CHECK_TOK(tok, lex, TOK_COLON); + Parse_TypeBound(lex,params, mv$(type), mv$(lifetimes)); + } + else + { + PUTBACK(tok, lex); + TypeRef type = Parse_Type(lex); + GET_CHECK_TOK(tok, lex, TOK_COLON); + Parse_TypeBound(lex, params, mv$(type)); + } + } while( GET_TOK(tok, lex) == TOK_COMMA ); + PUTBACK(tok, lex); +} + +// Parse a single function argument +::std::pair< AST::Pattern, TypeRef> Parse_Function_Arg(TokenStream& lex, bool expect_named) +{ + TRACE_FUNCTION_F("expect_named = " << expect_named); + Token tok; + + AST::Pattern pat; + + // If any of the following + // - Expecting a named parameter (i.e. defining a function in root or impl) + // - Next token is an underscore (only valid as a pattern here) + // - Next token is 'mut' (a mutable parameter slot) + // - Next two are ':' (a trivial named parameter) + // NOTE: When not expecting a named param, destructuring patterns are not allowed + if( expect_named + || LOOK_AHEAD(lex) == TOK_UNDERSCORE + || LOOK_AHEAD(lex) == TOK_RWORD_MUT + || (LOOK_AHEAD(lex) == TOK_IDENT && lex.lookahead(1) == TOK_COLON) + ) + { + // Function args can't be refuted + pat = Parse_Pattern(lex, false); + GET_CHECK_TOK(tok, lex, TOK_COLON); + } + + TypeRef type = Parse_Type(lex); + + + return ::std::make_pair( ::std::move(pat), ::std::move(type) ); +} + +/// Parse a function definition (after the 'fn ') +AST::Function Parse_FunctionDef(TokenStream& lex, ::std::string abi, bool allow_self, bool can_be_prototype, bool is_unsafe, bool is_const) +{ + TRACE_FUNCTION; + ProtoSpan ps = lex.start_span(); + + Token tok; + + // Parameters + AST::GenericParams params; + if( GET_TOK(tok, lex) == TOK_LT ) + { + params = Parse_GenericParams(lex); + GET_CHECK_TOK(tok, lex, TOK_GT); + } + else { + PUTBACK(tok, lex); + } + + AST::Function::Arglist args; + + GET_CHECK_TOK(tok, lex, TOK_PAREN_OPEN); + GET_TOK(tok, lex); + + // Handle self + if( tok.type() == TOK_AMP ) + { + // By-reference method? + // TODO: If a lifetime is seen (and not a prototype), it is definitely a self binding + + unsigned int ofs = 0; + if( lex.lookahead(0) == TOK_LIFETIME ) + ofs ++; + + if( lex.lookahead(ofs) == TOK_RWORD_SELF || (lex.lookahead(ofs) == TOK_RWORD_MUT && lex.lookahead(ofs+1) == TOK_RWORD_SELF) ) + { + auto ps = lex.start_span(); + ::std::string lifetime; + if( GET_TOK(tok, lex) == TOK_LIFETIME ) { + lifetime = tok.str(); + GET_TOK(tok, lex); + } + auto ty_sp = lex.end_span(ps); + + if( tok.type() == TOK_RWORD_MUT ) + { + GET_CHECK_TOK(tok, lex, TOK_RWORD_SELF); + args.push_back( ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "self"), TypeRef(TypeRef::TagReference(), ty_sp, true, TypeRef(ty_sp, "Self", 0xFFFF))) ); + } + else + { + args.push_back( ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "self"), TypeRef(TypeRef::TagReference(), ty_sp, false, TypeRef(ty_sp, "Self", 0xFFFF))) ); + } + DEBUG("TODO: UFCS / self lifetimes"); + if( allow_self == false ) + throw ParseError::Generic(lex, "Self binding not expected"); + //args.push_back( ::std::make_pair( + // AST::Pattern(), + // TypeRef(TypeRef::TagReference(), lifetime, (fcn_class == AST::Function::CLASS_MUTMETHOD), ) + //) ); + + // Prime tok for next step + GET_TOK(tok, lex); + } + else + { + // Unbound method + } + } + else if( tok.type() == TOK_RWORD_MUT ) + { + if( LOOK_AHEAD(lex) == TOK_RWORD_SELF ) + { + GET_TOK(tok, lex); + if( allow_self == false ) + throw ParseError::Generic(lex, "Self binding not expected"); + TypeRef ty = TypeRef( lex.getPosition(), "Self", 0xFFFF ); + if( GET_TOK(tok, lex) == TOK_COLON ) { + // Typed mut self + ty = Parse_Type(lex); + } + else { + PUTBACK(tok, lex); + } + args.push_back( ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "self"), mv$(ty)) ); + GET_TOK(tok, lex); + } + } + else if( tok.type() == TOK_RWORD_SELF ) + { + // By-value method + if( allow_self == false ) + throw ParseError::Generic(lex, "Self binding not expected"); + TypeRef ty = TypeRef( lex.getPosition(), "Self", 0xFFFF ); + if( GET_TOK(tok, lex) == TOK_COLON ) { + // Typed mut self + ty = Parse_Type(lex); + } + else { + PUTBACK(tok, lex); + } + args.push_back( ::std::make_pair( AST::Pattern(AST::Pattern::TagBind(), "self"), mv$(ty)) ); + GET_TOK(tok, lex); + } + else + { + // Unbound method + } + + bool is_variadic = false; + if( tok.type() != TOK_PAREN_CLOSE ) + { + // Comma after self + if( args.size() ) + { + CHECK_TOK(tok, TOK_COMMA); + } + else { + PUTBACK(tok, lex); + } + + // Argument list + do { + if( LOOK_AHEAD(lex) == TOK_PAREN_CLOSE ) { + GET_TOK(tok, lex); + break; + } + if( LOOK_AHEAD(lex) == TOK_TRIPLE_DOT ) { + GET_TOK(tok, lex); + is_variadic = true; + GET_TOK(tok, lex); + break; + } + args.push_back( Parse_Function_Arg(lex, !can_be_prototype) ); + } while( GET_TOK(tok, lex) == TOK_COMMA ); + CHECK_TOK(tok, TOK_PAREN_CLOSE); + } + else { + // Eat 'tok', negative comparison + } + + TypeRef ret_type = TypeRef(TypeRef::TagUnit(), Span(tok.get_pos())); + if( GET_TOK(tok, lex) == TOK_THINARROW ) + { + // Return type + ret_type = Parse_Type(lex); + } + else + { + PUTBACK(tok, lex); + } + + if( GET_TOK(tok, lex) == TOK_RWORD_WHERE ) + { + Parse_WhereClause(lex, params); + } + else { + PUTBACK(tok, lex); + } + + return AST::Function(lex.end_span(ps), mv$(params), mv$(abi), is_unsafe, is_const, is_variadic, mv$(ret_type), mv$(args)); +} + +AST::Function Parse_FunctionDefWithCode(TokenStream& lex, ::std::string abi, bool allow_self, bool is_unsafe, bool is_const) +{ + Token tok; + auto ret = Parse_FunctionDef(lex, abi, allow_self, false, is_unsafe, is_const); + GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); + PUTBACK(tok, lex); + ret.set_code( Parse_ExprBlock(lex) ); + return ret; +} + +AST::TypeAlias Parse_TypeAlias(TokenStream& lex) +{ + TRACE_FUNCTION; + + Token tok; + + // Params + AST::GenericParams params; + if( GET_TOK(tok, lex) == TOK_LT ) + { + params = Parse_GenericParams(lex); + GET_CHECK_TOK(tok, lex, TOK_GT); + GET_TOK(tok, lex); + } + + if( tok.type() == TOK_RWORD_WHERE ) + { + Parse_WhereClause(lex, params); + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_EQUAL); + + // Type + TypeRef type = Parse_Type(lex); + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + + return AST::TypeAlias( ::std::move(params), ::std::move(type) ); +} + +AST::Struct Parse_Struct(TokenStream& lex, const AST::MetaItems& meta_items) +{ + TRACE_FUNCTION; + + Token tok; + + tok = lex.getToken(); + AST::GenericParams params; + if( tok.type() == TOK_LT ) + { + params = Parse_GenericParams(lex); + GET_CHECK_TOK(tok, lex, TOK_GT); + if(GET_TOK(tok, lex) == TOK_RWORD_WHERE) + { + Parse_WhereClause(lex, params); + tok = lex.getToken(); + } + } + if(tok.type() == TOK_PAREN_OPEN) + { + // Tuple structs + ::std::vector refs; + while(GET_TOK(tok, lex) != TOK_PAREN_CLOSE) + { + AST::MetaItems item_attrs; + while( tok.type() == TOK_ATTR_OPEN ) + { + item_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + GET_TOK(tok, lex); + } + SET_ATTRS(lex, item_attrs); + + bool is_pub = false; + if(tok.type() == TOK_RWORD_PUB) + is_pub = true; + else + PUTBACK(tok, lex); + + refs.push_back( AST::TupleItem( mv$(item_attrs), is_pub, Parse_Type(lex) ) ); + if( GET_TOK(tok, lex) != TOK_COMMA ) + break; + } + CHECK_TOK(tok, TOK_PAREN_CLOSE); + + if(LOOK_AHEAD(lex) == TOK_RWORD_WHERE) + { + GET_TOK(tok, lex); + Parse_WhereClause(lex, params); + } + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + //if( refs.size() == 0 ) + // WARNING( , W000, "Use 'struct Name;' instead of 'struct Name();' ... ning-nong"); + return AST::Struct(mv$(params), mv$(refs)); + } + else if(tok.type() == TOK_SEMICOLON) + { + // Unit-like struct + return AST::Struct(mv$(params), ::std::vector()); + } + else if(tok.type() == TOK_BRACE_OPEN) + { + ::std::vector items; + while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) + { + AST::MetaItems item_attrs; + while( tok.type() == TOK_ATTR_OPEN ) + { + item_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + GET_TOK(tok, lex); + } + SET_ATTRS(lex, item_attrs); + + bool is_pub = false; + if(tok.type() == TOK_RWORD_PUB) { + is_pub = true; + GET_TOK(tok, lex); + } + + CHECK_TOK(tok, TOK_IDENT); + auto name = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_COLON); + TypeRef type = Parse_Type(lex); + + items.push_back( AST::StructItem( mv$(item_attrs), is_pub, mv$(name), mv$(type) ) ); + if(GET_TOK(tok, lex) == TOK_BRACE_CLOSE) + break; + CHECK_TOK(tok, TOK_COMMA); + } + //if( items.size() == 0 ) + // WARNING( , W000, "Use 'struct Name;' instead of 'struct Nam { };' ... ning-nong"); + return AST::Struct(::std::move(params), ::std::move(items)); + } + else + { + throw ParseError::Unexpected(lex, tok); + } +} + +AST::Trait Parse_TraitDef(TokenStream& lex, const AST::MetaItems& meta_items) +{ + TRACE_FUNCTION; + + Token tok; + + AST::GenericParams params; + if( GET_TOK(tok, lex) == TOK_LT ) + { + params = Parse_GenericParams(lex); + GET_CHECK_TOK(tok, lex, TOK_GT); + tok = lex.getToken(); + } + + // Trait bounds "trait Trait : 'lifetime + OtherTrait + OtherTrait2" + ::std::vector > supertraits; + if(tok.type() == TOK_COLON) + { + do { + if( GET_TOK(tok, lex) == TOK_LIFETIME ) { + // TODO: Need a better way of indiciating 'static than just an invalid path + supertraits.push_back( make_spanned( Span(tok.get_pos()), AST::Path() ) ); + } + else { + PUTBACK(tok, lex); + supertraits.push_back( GET_SPANNED(::AST::Path, lex, Parse_Path(lex, PATH_GENERIC_TYPE)) ); + } + } while( GET_TOK(tok, lex) == TOK_PLUS ); + } + + // TODO: Support "for Sized?" + if(tok.type() == TOK_RWORD_WHERE) + { + //if( params.ty_params().size() == 0 ) + // throw ParseError::Generic("Where clause with no generic params"); + Parse_WhereClause(lex, params); + tok = lex.getToken(); + } + + + AST::Trait trait( mv$(params), mv$(supertraits) ); + + CHECK_TOK(tok, TOK_BRACE_OPEN); + while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) + { + + AST::MetaItems item_attrs; + while( tok.type() == TOK_ATTR_OPEN ) + { + item_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + GET_TOK(tok, lex); + } + SET_ATTRS(lex, item_attrs); + + auto ps = lex.start_span(); + if( tok.type() == TOK_MACRO ) { + auto inv = Parse_MacroInvocation( ps, mv$(tok.str()), lex ); + // - Silently consume ';' after the macro + if( GET_TOK(tok, lex) != TOK_SEMICOLON ) + PUTBACK(tok, lex); + + trait.items().push_back( AST::Named("", AST::Item(mv$(inv)), false) ); + continue ; + } + + bool is_specialisable = false; + if( tok.type() == TOK_IDENT && tok.str() == "default" ) { + is_specialisable = true; + GET_TOK(tok, lex); + } + // TODO: Mark specialisation + (void)is_specialisable; + + bool fn_is_const = false; + bool fn_is_unsafe = false; + ::std::string abi = ABI_RUST; + switch(tok.type()) + { + case TOK_RWORD_STATIC: { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + auto name = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_COLON); + auto ty = Parse_Type(lex); + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + + ::AST::Expr val; + if(GET_TOK(tok, lex) == TOK_EQUAL) { + val = Parse_Expr(lex); + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_SEMICOLON); + + // TODO: Attributes on associated statics + trait.add_static( mv$(name), ::AST::Static(AST::Static::STATIC, mv$(ty), val)/*, mv$(item_attrs)*/ ); + break; } + case TOK_RWORD_CONST: { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + auto name = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_COLON); + auto ty = Parse_Type(lex); + + ::AST::Expr val; + if(GET_TOK(tok, lex) == TOK_EQUAL) { + val = Parse_Expr(lex); + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_SEMICOLON); + + // TODO: Attributes on associated constants + trait.add_static( mv$(name), ::AST::Static(AST::Static::CONST, mv$(ty), val)/*, mv$(item_attrs)*/ ); + break; } + // Associated type + case TOK_RWORD_TYPE: { + auto atype_params = ::AST::GenericParams { }; + GET_CHECK_TOK(tok, lex, TOK_IDENT); + auto name = mv$(tok.str()); + if( GET_TOK(tok, lex) == TOK_COLON ) + { + // Bounded associated type + Parse_TypeBound(lex, atype_params, TypeRef(lex.getPosition(), "Self", 0xFFFF)); + GET_TOK(tok, lex); + } + if( tok.type() == TOK_RWORD_WHERE ) { + throw ParseError::Todo(lex, "Where clause on associated type"); + } + + TypeRef default_type = TypeRef( lex.getPosition() ); + if( tok.type() == TOK_EQUAL ) { + default_type = Parse_Type(lex); + GET_TOK(tok, lex); + } + + CHECK_TOK(tok, TOK_SEMICOLON); + trait.add_type( ::std::move(name), ::std::move(default_type) ); + trait.items().back().data.as_Type().params() = mv$(atype_params); + break; } + + // Functions (possibly unsafe) + // TODO: Const? + case TOK_RWORD_UNSAFE: + fn_is_unsafe = true; + if( GET_TOK(tok, lex) == TOK_RWORD_EXTERN ) + case TOK_RWORD_EXTERN: + { + abi = "C"; + if( GET_TOK(tok, lex) == TOK_STRING ) + abi = tok.str(); + else + PUTBACK(tok, lex); + + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_RWORD_FN); + case TOK_RWORD_FN: { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + ::std::string name = mv$(tok.str()); + // Self allowed, prototype-form allowed (optional names and no code) + auto fcn = Parse_FunctionDef(lex, abi, true, true, fn_is_unsafe, fn_is_const); + if( GET_TOK(tok, lex) == TOK_BRACE_OPEN ) + { + PUTBACK(tok, lex); + fcn.set_code( Parse_ExprBlock(lex) ); + } + else if( tok.type() == TOK_SEMICOLON ) + { + // Accept it + } + else + { + throw ParseError::Unexpected(lex, tok); + } + // TODO: Store `item_attrs` + trait.add_function( ::std::move(name), /*mv$(item_attrs),*/ ::std::move(fcn) ); + break; } + default: + throw ParseError::Unexpected(lex, tok); + } + } + + return trait; +} + +AST::Enum Parse_EnumDef(TokenStream& lex, const AST::MetaItems& meta_items) +{ + TRACE_FUNCTION; + + Token tok; + + tok = lex.getToken(); + // Type params supporting "where" + AST::GenericParams params; + if( tok.type() == TOK_LT ) + { + params = Parse_GenericParams(lex); + GET_CHECK_TOK(tok, lex, TOK_GT); + if(GET_TOK(tok, lex) == TOK_RWORD_WHERE) + { + Parse_WhereClause(lex, params); + tok = lex.getToken(); + } + } + + // Body + CHECK_TOK(tok, TOK_BRACE_OPEN); + ::std::vector variants; + while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) + { + auto sp = lex.start_span(); + + AST::MetaItems item_attrs; + while( tok.type() == TOK_ATTR_OPEN ) + { + item_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + GET_TOK(tok, lex); + } + SET_ATTRS(lex, item_attrs); + + CHECK_TOK(tok, TOK_IDENT); + ::std::string name = mv$(tok.str()); + if( GET_TOK(tok, lex) == TOK_PAREN_OPEN ) + { + ::std::vector types; + // Get type list + do + { + if(LOOK_AHEAD(lex) == TOK_PAREN_CLOSE) + { + GET_TOK(tok, lex); + break; + } + + AST::MetaItems field_attrs; + while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) + { + GET_TOK(tok, lex); + field_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + + types.push_back( Parse_Type(lex) ); + } while( GET_TOK(tok, lex) == TOK_COMMA ); + CHECK_TOK(tok, TOK_PAREN_CLOSE); + GET_TOK(tok, lex); + variants.push_back( AST::EnumVariant(mv$(item_attrs), mv$(name), mv$(types)) ); + } + else if( tok.type() == TOK_BRACE_OPEN ) + { + ::std::vector<::AST::StructItem> fields; + do + { + if(LOOK_AHEAD(lex) == TOK_BRACE_CLOSE) + { + GET_TOK(tok, lex); + break; + } + + AST::MetaItems field_attrs; + while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) + { + GET_TOK(tok, lex); + field_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + + GET_CHECK_TOK(tok, lex, TOK_IDENT); + auto name = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_COLON); + auto ty = Parse_Type(lex); + fields.push_back( ::AST::StructItem(mv$(field_attrs), true, mv$(name), mv$(ty)) ); + } while( GET_TOK(tok, lex) == TOK_COMMA ); + CHECK_TOK(tok, TOK_BRACE_CLOSE); + GET_TOK(tok, lex); + + variants.push_back( AST::EnumVariant(mv$(item_attrs), mv$(name), mv$(fields)) ); + } + else if( tok.type() == TOK_EQUAL ) + { + auto node = Parse_Expr(lex); + variants.push_back( AST::EnumVariant(mv$(item_attrs), mv$(name), mv$(node)) ); + GET_TOK(tok, lex); + } + else + { + variants.push_back( AST::EnumVariant(mv$(item_attrs), mv$(name), ::AST::Expr()) ); + } + + if( tok.type() != TOK_COMMA ) + break; + } + CHECK_TOK(tok, TOK_BRACE_CLOSE); + + + return AST::Enum( mv$(params), mv$(variants) ); +} + +::AST::Union Parse_Union(TokenStream& lex, AST::MetaItems& meta_items) +{ + Token tok; + + TRACE_FUNCTION; + + AST::GenericParams params; + if( GET_TOK(tok, lex) == TOK_LT ) + { + params = Parse_GenericParams(lex); + GET_CHECK_TOK(tok, lex, TOK_GT); + if(GET_TOK(tok, lex) == TOK_RWORD_WHERE) + { + Parse_WhereClause(lex, params); + tok = lex.getToken(); + } + } + + ::std::vector< ::AST::StructItem> variants; + + CHECK_TOK(tok, TOK_BRACE_OPEN); + do { + if( LOOK_AHEAD(lex) == TOK_BRACE_CLOSE ) { + GET_TOK(tok, lex); + break ; + } + + AST::MetaItems item_attrs; + while( LOOK_AHEAD(lex) == TOK_ATTR_OPEN ) + { + GET_TOK(tok, lex); + item_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + SET_ATTRS(lex, item_attrs); + + bool is_pub = false; + if( LOOK_AHEAD(lex) == TOK_RWORD_PUB ) { + is_pub = true; + GET_TOK(tok, lex); + } + + GET_CHECK_TOK(tok, lex, TOK_IDENT); + auto name = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_COLON); + + auto ty = Parse_Type(lex); + + variants.push_back( ::AST::StructItem( mv$(item_attrs), is_pub, mv$(name), mv$(ty) ) ); + + } while( GET_TOK(tok, lex) == TOK_COMMA ); + CHECK_TOK(tok, TOK_BRACE_CLOSE); + + return ::AST::Union( mv$(params), mv$(variants) ); +} + +/// Parse a meta-item declaration (either #![ or #[) +AST::MetaItem Parse_MetaItem(TokenStream& lex) +{ + TRACE_FUNCTION; + Token tok; + GET_TOK(tok, lex); + + if( tok.type() == TOK_INTERPOLATED_META ) { + return mv$(tok.frag_meta()); + } + + CHECK_TOK(tok, TOK_IDENT); + ::std::string name = mv$(tok.str()); + switch(GET_TOK(tok, lex)) + { + case TOK_EQUAL: + GET_CHECK_TOK(tok, lex, TOK_STRING); + return AST::MetaItem(name, tok.str()); + case TOK_PAREN_OPEN: { + ::std::vector items; + do { + if(LOOK_AHEAD(lex) == TOK_PAREN_CLOSE) { + GET_TOK(tok, lex); + break; + } + items.push_back(Parse_MetaItem(lex)); + } while(GET_TOK(tok, lex) == TOK_COMMA); + CHECK_TOK(tok, TOK_PAREN_CLOSE); + return AST::MetaItem(name, mv$(items)); } + default: + PUTBACK(tok, lex); + return AST::MetaItem(name); + } +} + +::AST::Item Parse_Impl(TokenStream& lex, AST::MetaItems attrs, bool is_unsafe=false) +{ + TRACE_FUNCTION; + Token tok; + auto ps = lex.start_span(); + + AST::GenericParams params; + // 1. (optional) type parameters + if( GET_TOK(tok, lex) == TOK_LT ) + { + params = Parse_GenericParams(lex); + GET_CHECK_TOK(tok, lex, TOK_GT); + } + else { + PUTBACK(tok, lex); + } + // 2. Either a trait name (with type params), or the type to impl + + Spanned trait_path; + + // - Handle negative impls specially, which must be a trait + // "impl !Trait for Type {}" + if( GET_TOK(tok, lex) == TOK_EXCLAM ) + { + trait_path = GET_SPANNED(::AST::Path, lex, Parse_Path(lex, PATH_GENERIC_TYPE)); + GET_CHECK_TOK(tok, lex, TOK_RWORD_FOR); + auto impl_type = Parse_Type(lex, true); + + if( GET_TOK(tok, lex) == TOK_RWORD_WHERE ) + { + Parse_WhereClause(lex, params); + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_BRACE_OPEN); + // negative impls can't have any content + GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); + + return ::AST::Item::make_NegImpl( AST::ImplDef(lex.end_span(ps), mv$(attrs), mv$(params), mv$(trait_path), mv$(impl_type) ) ); + } + + // - Don't care which at this stage + PUTBACK(tok, lex); + + auto impl_type = Parse_Type(lex, true); + + if( GET_TOK(tok, lex) == TOK_RWORD_FOR ) + { + // Trickery! All traits parse as valid types, so this works. + if( !impl_type.is_path() ) + throw ParseError::Generic(lex, "Trait was not a path"); + trait_path = Spanned< AST::Path> { + impl_type.span(), + mv$(impl_type.path()) + }; + // Implementing a trait for another type, get the target type + if( GET_TOK(tok, lex) == TOK_DOUBLE_DOT ) + { + // Default impl + impl_type = TypeRef(TypeRef::TagInvalid(), lex.getPosition()); + } + else + { + PUTBACK(tok, lex); + impl_type = Parse_Type(lex, true); + } + } + else { + PUTBACK(tok, lex); + } + + // Where clause + if( GET_TOK(tok, lex) == TOK_RWORD_WHERE ) + { + Parse_WhereClause(lex, params); + } + else { + PUTBACK(tok, lex); + } + GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); + + while( LOOK_AHEAD(lex) == TOK_CATTR_OPEN ) + { + GET_TOK(tok, lex); + attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + + AST::Impl impl( AST::ImplDef( lex.end_span(ps), mv$(attrs), mv$(params), mv$(trait_path), mv$(impl_type) ) ); + + // A sequence of method implementations + while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) + { + auto ps = lex.start_span(); + if( tok.type() == TOK_MACRO ) + { + impl.add_macro_invocation( Parse_MacroInvocation( ps, mv$(tok.str()), lex ) ); + // - Silently consume ';' after the macro + if( GET_TOK(tok, lex) != TOK_SEMICOLON ) + PUTBACK(tok, lex); + } + else + { + PUTBACK(tok, lex); + Parse_Impl_Item(lex, impl); + } + } + + return ::AST::Item::make_Impl( mv$(impl) ); +} + +void Parse_Impl_Item(TokenStream& lex, AST::Impl& impl) +{ + TRACE_FUNCTION; + Token tok; + + GET_TOK(tok, lex); + + AST::MetaItems item_attrs; + while( tok.type() == TOK_ATTR_OPEN ) + { + item_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + GET_TOK(tok, lex); + } + SET_ATTRS(lex, item_attrs); + + auto ps = lex.start_span(); + + bool is_public = false; + if(tok.type() == TOK_RWORD_PUB) { + is_public = true; + GET_TOK(tok, lex); + } + + bool is_specialisable = false; + if( tok.type() == TOK_IDENT && tok.str() == "default" ) { + is_specialisable = true; + GET_TOK(tok, lex); + } + + ::std::string abi = ABI_RUST; + bool fn_is_unsafe = false; + bool fn_is_const = false; + switch(tok.type()) + { + case TOK_RWORD_TYPE: { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + auto name = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_EQUAL); + impl.add_type(is_public, is_specialisable, name, Parse_Type(lex)); + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + break; } + case TOK_RWORD_UNSAFE: + fn_is_unsafe = true; + GET_TOK(tok, lex); + if( tok.type() == TOK_RWORD_CONST ) + case TOK_RWORD_CONST: + { + GET_TOK(tok, lex); + if( tok.type() != TOK_RWORD_FN && tok.type() != TOK_RWORD_UNSAFE && !fn_is_unsafe ) + { + CHECK_TOK(tok, TOK_IDENT); + auto name = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_COLON); + auto ty = Parse_Type(lex); + GET_CHECK_TOK(tok, lex, TOK_EQUAL); + auto val = Parse_Expr(lex); + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + + auto i = ::AST::Static(AST::Static::CONST, mv$(ty), mv$(val)); + // TODO: Attributes on associated constants + impl.add_static( is_public, is_specialisable, mv$(name), mv$(i) /*, mv$(item_attrs)*/ ); + break ; + } + else if( tok.type() == TOK_RWORD_UNSAFE ) + { + fn_is_unsafe = true; + GET_CHECK_TOK(tok, lex, TOK_RWORD_FN); + } + fn_is_const = true; + } + if( tok.type() == TOK_RWORD_EXTERN ) + // FALL + case TOK_RWORD_EXTERN: + { + abi = "C"; + if( GET_TOK(tok, lex) == TOK_STRING ) + abi = tok.str(); + else + PUTBACK(tok, lex); + + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_RWORD_FN); + // FALL + case TOK_RWORD_FN: { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + // TODO: Hygine on function names? - Not in impl blocks? + ::std::string name = mv$(tok.str()); + DEBUG("Function " << name); + // - Self allowed, can't be prototype-form + auto fcn = Parse_FunctionDefWithCode(lex, abi, true, fn_is_unsafe, fn_is_const); + impl.add_function(is_public, is_specialisable, mv$(name), mv$(fcn)); + break; } + + default: + throw ParseError::Unexpected(lex, tok); + } + + impl.items().back().data->span = lex.end_span(ps); + impl.items().back().data->attrs = mv$(item_attrs); // Empty for functions +} + +AST::ExternBlock Parse_ExternBlock(TokenStream& lex, ::std::string abi, ::AST::MetaItems& block_attrs) +{ + TRACE_FUNCTION; + Token tok; + + while( GET_TOK(tok, lex) == TOK_CATTR_OPEN ) + { + block_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + PUTBACK(tok, lex); + + AST::ExternBlock rv { abi }; + + while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) + { + AST::MetaItems meta_items; + while( tok.type() == TOK_ATTR_OPEN ) + { + meta_items.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + GET_TOK(tok, lex); + } + SET_ATTRS(lex, meta_items); + + auto ps = lex.start_span(); + + bool is_public = false; + if( tok.type() == TOK_RWORD_PUB ) { + is_public = true; + GET_TOK(tok, lex); + } + switch(tok.type()) + { + case TOK_RWORD_FN: { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + auto name = mv$(tok.str()); + // parse function as prototype + // - no self, is prototype, is unsafe and not const + auto i = ::AST::Item( Parse_FunctionDef(lex, abi, false, true, true,false) ); + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + + i.attrs = mv$(meta_items); + i.span = lex.end_span(ps); + + rv.add_item( AST::Named { mv$(name), mv$(i), is_public } ); + break; } + case TOK_RWORD_STATIC: { + bool is_mut = false; + if( GET_TOK(tok, lex) == TOK_RWORD_MUT ) + is_mut = true; + else + PUTBACK(tok, lex); + GET_CHECK_TOK(tok, lex, TOK_IDENT); + auto name = mv$(tok.str()); + GET_CHECK_TOK(tok, lex, TOK_COLON); + auto type = Parse_Type(lex); + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + + auto i = ::AST::Item(::AST::Static( (is_mut ? ::AST::Static::MUT : ::AST::Static::STATIC), mv$(type), ::AST::Expr() )); + i.attrs = mv$(meta_items); + i.span = lex.end_span(ps); + rv.add_item( AST::Named { mv$(name), mv$(i), is_public } ); + break; } + default: + throw ParseError::Unexpected(lex, tok, {TOK_RWORD_FN, TOK_RWORD_STATIC}); + } + } + + return rv; +} + +void Parse_Use_Wildcard(Span sp, AST::Path base_path, ::std::function fcn) +{ + fcn( AST::UseStmt(mv$(sp), mv$(base_path)), "" ); // HACK! Empty path indicates wilcard import +} +void Parse_Use_Set(TokenStream& lex, const ProtoSpan& ps, const AST::Path& base_path, ::std::function fcn) +{ + TRACE_FUNCTION; + + Token tok; + do { + AST::Path path; + ::std::string name; + if( GET_TOK(tok, lex) == TOK_RWORD_SELF ) { + path = ::AST::Path(base_path); + name = base_path[base_path.size()-1].name(); + } + else if( tok.type() == TOK_BRACE_CLOSE ) { + break ; + } + else { + CHECK_TOK(tok, TOK_IDENT); + path = base_path + AST::PathNode(tok.str(), {}); + name = mv$(tok.str()); + } + + if( GET_TOK(tok, lex) == TOK_RWORD_AS ) { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + name = mv$(tok.str()); + } + else { + PUTBACK(tok, lex); + } + fcn(AST::UseStmt(lex.end_span(ps), mv$(path)), mv$(name)); + } while( GET_TOK(tok, lex) == TOK_COMMA ); + PUTBACK(tok, lex); +} + +void Parse_Use(TokenStream& lex, ::std::function fcn) +{ + TRACE_FUNCTION; + + Token tok; + AST::Path path = AST::Path("", {}); + ::std::vector nodes; + ProtoSpan span_start = lex.start_span(); + + switch( GET_TOK(tok, lex) ) + { + case TOK_RWORD_SELF: + path = AST::Path( AST::Path::TagSelf(), {} ); // relative path + break; + case TOK_RWORD_SUPER: { + unsigned int count = 1; + while( LOOK_AHEAD(lex) == TOK_DOUBLE_COLON && lex.lookahead(1) == TOK_RWORD_SUPER ) { + GET_CHECK_TOK(tok, lex, TOK_DOUBLE_COLON); + GET_CHECK_TOK(tok, lex, TOK_RWORD_SUPER); + count += 1; + } + path = AST::Path( AST::Path::TagSuper(), count, {} ); + break; } + case TOK_IDENT: + path.append( AST::PathNode(mv$(tok.str()), {}) ); + break; + // Leading :: is allowed and ignored for the $crate feature + case TOK_DOUBLE_COLON: + // Absolute path + // HACK! mrustc emits $crate as `::"crate-name"` + if( LOOK_AHEAD(lex) == TOK_STRING ) + { + GET_CHECK_TOK(tok, lex, TOK_STRING); + path = ::AST::Path(tok.str(), {}); + GET_CHECK_TOK(tok, lex, TOK_DOUBLE_COLON); + } + else { + PUTBACK(tok, lex); + } + break; + case TOK_BRACE_OPEN: + Parse_Use_Set(lex, span_start, path, fcn); + GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); + return; + default: + throw ParseError::Unexpected(lex, tok); + } + while( GET_TOK(tok, lex) == TOK_DOUBLE_COLON ) + { + if( GET_TOK(tok, lex) == TOK_IDENT ) + { + path.append( AST::PathNode( mv$(tok.str()), {}) ); + } + else + { + //path.set_span( lex.end_span(span_start) ); + switch( tok.type() ) + { + case TOK_BRACE_OPEN: + Parse_Use_Set(lex, span_start, mv$(path), fcn); + GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); + break ; + case TOK_STAR: + Parse_Use_Wildcard( lex.end_span(span_start), mv$(path), fcn ); + break ; + default: + throw ParseError::Unexpected(lex, tok); + } + // early return - This branch is either the end of the use statement, or a syntax error + return ; + } + } + //path.set_span( lex.end_span(span_start) ); + + ::std::string name; + // This should only be allowed if the last token was an ident + // - Above checks ensure this + if( tok.type() == TOK_RWORD_AS ) + { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + name = mv$(tok.str()); + } + else + { + PUTBACK(tok, lex); + assert(path.nodes().size() > 0); + name = path.nodes().back().name(); + } + + fcn( AST::UseStmt(lex.end_span(span_start), mv$(path)), name); +} + + +::AST::MacroInvocation Parse_MacroInvocation(ProtoSpan span_start, ::std::string name, TokenStream& lex) +{ + Token tok; + ::std::string ident; + if( GET_TOK(tok, lex) == TOK_IDENT ) { + ident = mv$(tok.str()); + } + else { + PUTBACK(tok, lex); + } + DEBUG("name=" << name << ", ident=" << ident); + TokenTree tt = Parse_TT(lex, true); + return ::AST::MacroInvocation( lex.end_span(span_start), mv$(name), mv$(ident), mv$(tt)); +} + +::AST::Named<::AST::Item> Parse_Mod_Item_S(TokenStream& lex, const AST::Module::FileInfo& mod_fileinfo, const ::AST::Path& mod_path, AST::MetaItems meta_items) +{ + TRACE_FUNCTION_F("mod_path="<" fn ...` + // `extern "" { ...` + case TOK_STRING: { + ::std::string abi = tok.str(); + switch(GET_TOK(tok, lex)) + { + // `extern "" fn ...` + case TOK_RWORD_FN: { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, abi, false, false,false) ); + break; } + // `extern "ABI" {` + case TOK_BRACE_OPEN: + item_name = ""; + item_data = ::AST::Item( Parse_ExternBlock(lex, mv$(abi), meta_items) ); + break; + default: + throw ParseError::Unexpected(lex, tok, {TOK_RWORD_FN, TOK_BRACE_OPEN}); + } + break; } + // `extern fn ...` + case TOK_RWORD_FN: + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, "C", false, false,false) ); + break; + + // NOTE: `extern { ...` is handled in caller + case TOK_BRACE_OPEN: + item_name = ""; + item_data = ::AST::Item( Parse_ExternBlock(lex, "C", meta_items) ); + break; + + // `extern crate "crate-name" as crate_name;` + // `extern crate crate_name;` + // `extern crate crate_name as other_name;` + case TOK_RWORD_CRATE: + switch( GET_TOK(tok, lex) ) + { + // `extern crate "crate-name" as crate_name;` + // NOTE: rustc doesn't allow this, keep in mrustc for for reparse support + case TOK_STRING: + item_data = ::AST::Item::make_Crate({ tok.str() }); + GET_CHECK_TOK(tok, lex, TOK_RWORD_AS); + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + break; + // `extern crate crate_name;` + // `extern crate crate_name as other_name;` + case TOK_IDENT: + item_name = mv$(tok.str()); + if(GET_TOK(tok, lex) == TOK_RWORD_AS) { + item_data = ::AST::Item::make_Crate({ mv$(item_name) }); + + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + } + else { + PUTBACK(tok, lex); + item_data = ::AST::Item::make_Crate({ item_name }); + } + break; + default: + throw ParseError::Unexpected(lex, tok, {TOK_STRING, TOK_IDENT}); + } + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + break; + default: + throw ParseError::Unexpected(lex, tok, {TOK_STRING, TOK_RWORD_FN, TOK_BRACE_OPEN, TOK_RWORD_CRATE}); + } + break; + + // `const NAME` + // `const [unsafe] fn` + case TOK_RWORD_CONST: + switch( GET_TOK(tok, lex) ) + { + case TOK_IDENT: { + item_name = mv$(tok.str()); + + GET_CHECK_TOK(tok, lex, TOK_COLON); + TypeRef type = Parse_Type(lex); + GET_CHECK_TOK(tok, lex, TOK_EQUAL); + AST::Expr val = Parse_Expr(lex); + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + item_data = ::AST::Item( ::AST::Static(AST::Static::CONST, mv$(type), mv$(val)) ); + break; } + case TOK_RWORD_UNSAFE: + GET_CHECK_TOK(tok, lex, TOK_RWORD_FN); + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, ABI_RUST, false, true,true/*unsafe,const*/) ); + break; + case TOK_RWORD_FN: + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + // - self not allowed, not prototype + item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, ABI_RUST, false, false,true/*unsafe,const*/) ); + break; + default: + throw ParseError::Unexpected(lex, tok, {TOK_IDENT, TOK_RWORD_FN}); + } + break; + // `static NAME` + // `static mut NAME` + case TOK_RWORD_STATIC: { + bool is_mut = false; + if(GET_TOK(tok, lex) == TOK_RWORD_MUT) { + is_mut = true; + GET_TOK(tok, lex); + } + CHECK_TOK(tok, TOK_IDENT); + item_name = mv$(tok.str()); + + GET_CHECK_TOK(tok, lex, TOK_COLON); + TypeRef type = Parse_Type(lex); + + GET_CHECK_TOK(tok, lex, TOK_EQUAL); + + AST::Expr val = Parse_Expr(lex); + + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + item_data = ::AST::Item( ::AST::Static( (is_mut ? AST::Static::MUT : AST::Static::STATIC), mv$(type), mv$(val)) ); + break; } + + // `unsafe fn` + // `unsafe trait` + // `unsafe impl` + case TOK_RWORD_UNSAFE: + meta_items.push_back( AST::MetaItem("#UNSAFE") ); + switch(GET_TOK(tok, lex)) + { + // `unsafe extern fn` + case TOK_RWORD_EXTERN: { + ::std::string abi = "C"; + if(GET_TOK(tok, lex) == TOK_STRING) { + abi = mv$(tok.str()); + } + else { + PUTBACK(tok, lex); + } + GET_CHECK_TOK(tok, lex, TOK_RWORD_FN); + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, abi, false, true,false/*unsafe,const*/) ); + break; } + // `unsafe fn` + case TOK_RWORD_FN: + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + // - self not allowed, not prototype + item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, ABI_RUST, false, true,false/*unsafe,const*/) ); + break; + // `unsafe trait` + case TOK_RWORD_TRAIT: + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + // TODO: Mark as unsafe + meta_items.push_back( AST::MetaItem("#UNSAFE") ); + item_data = ::AST::Item( Parse_TraitDef(lex, meta_items) ); + break; + // `unsafe impl` + case TOK_RWORD_IMPL: + return ::AST::Named< ::AST::Item> { "", Parse_Impl(lex, mv$(meta_items), true), false }; + default: + throw ParseError::Unexpected(lex, tok, {TOK_RWORD_FN, TOK_RWORD_TRAIT, TOK_RWORD_IMPL}); + } + break; + // `fn` + case TOK_RWORD_FN: + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + // - self not allowed, not prototype + item_data = ::AST::Item( Parse_FunctionDefWithCode(lex, ABI_RUST, false, false,false/*unsafe,const*/) ); + break; + // `type` + case TOK_RWORD_TYPE: + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + item_data = ::AST::Item( Parse_TypeAlias(lex) ); + break; + // `struct` + case TOK_RWORD_STRUCT: + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + item_data = ::AST::Item( Parse_Struct(lex, meta_items) ); + break; + // `enum` + case TOK_RWORD_ENUM: + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + item_data = ::AST::Item( Parse_EnumDef(lex, meta_items) ); + break; + // Contextual keywords + case TOK_IDENT: + if( tok.str() == "union" ) { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + item_data = ::AST::Item( Parse_Union(lex, meta_items) ); + } + else { + throw ParseError::Unexpected(lex, tok); + } + break; + // `impl` + case TOK_RWORD_IMPL: + return ::AST::Named< ::AST::Item> { "", Parse_Impl(lex, mv$(meta_items)), false }; + // `trait` + case TOK_RWORD_TRAIT: + GET_CHECK_TOK(tok, lex, TOK_IDENT); + item_name = mv$(tok.str()); + item_data = ::AST::Item( Parse_TraitDef(lex, meta_items) ); + break; + + case TOK_RWORD_MOD: { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + auto name = mv$(tok.str()); + DEBUG("Sub module '" << name << "'"); + AST::Module submod( mod_path + name ); + + // Rules for external files (/ path handling): + // - IF using stdin (path='-') - Disallow and propagate '-' as path + // - IF a #[path] attribute was passed, allow + // - IF in crate root or mod.rs, allow (input flag) + // - else, disallow and set flag + ::std::string path_attr = (meta_items.has("path") ? meta_items.get("path")->string() : ""); + + //submod.m_file_info = get_submod_file(lex.end_span(ps), mod_fileinfo, name, path_attr, LOOK_AHEAD(lex) == TOK_SEMICOLON); + + ::std::string sub_path; + bool sub_file_controls_dir = true; + if( mod_fileinfo.path == "-" ) { + if( path_attr.size() ) { + ERROR(lex.getPosition(), E0000, "Cannot load module from file when reading stdin"); + } + sub_path = "-"; + } + else if( path_attr.size() > 0 ) + { + sub_path = dirname(mod_fileinfo.path) + path_attr; + } + else if( mod_fileinfo.controls_dir ) + { + sub_path = dirname(mod_fileinfo.path) + name; + } + else + { + sub_path = mod_fileinfo.path; + sub_file_controls_dir = false; + } + DEBUG("Mod '" << name << "', sub_path = " << sub_path); + + submod.m_file_info.path = sub_path; + submod.m_file_info.controls_dir = sub_file_controls_dir; + + // Check #[cfg] and don't load if it fails + struct H { + static bool check_item_cfg(const ::AST::MetaItems& attrs) + { + for(const auto& at : attrs.m_items) { + if( at.name() == "cfg" && !check_cfg(attrs.m_span, at) ) { + return false; + } + } + return true; + } + }; + + switch( GET_TOK(tok, lex) ) + { + case TOK_BRACE_OPEN: + submod.m_file_info.path = sub_path + "/"; + Parse_ModRoot(lex, submod, meta_items); + GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); + break; + case TOK_SEMICOLON: + if( sub_path == "-" ) { + ERROR(lex.getPosition(), E0000, "Cannot load module from file when reading stdin"); + } + else if( path_attr.size() == 0 && ! mod_fileinfo.controls_dir ) + { + ERROR(lex.getPosition(), E0000, "Can't load from files outside of mod.rs or crate root"); + } + else if( !H::check_item_cfg(meta_items) ) { + // Ignore - emit Item::None + item_name = mv$(name); + item_data = ::AST::Item( ); + break ; + } + else + { + ::std::string newpath_dir = sub_path + "/"; + ::std::string newpath_file = path_attr.size() > 0 ? sub_path : sub_path + ".rs"; + DEBUG("newpath_dir = '" << newpath_dir << "', newpath_file = '" << newpath_file << "'"); + ::std::ifstream ifs_dir (newpath_dir + "mod.rs"); + ::std::ifstream ifs_file(newpath_file); + if( ifs_dir.is_open() && ifs_file.is_open() ) + { + // Collision + ERROR(lex.getPosition(), E0000, "Both modname.rs and modname/mod.rs exist"); + } + else if( ifs_dir.is_open() ) + { + // Load from dir + submod.m_file_info.path = newpath_dir + "mod.rs"; + } + else if( ifs_file.is_open() ) + { + submod.m_file_info.path = newpath_file; + } + else + { + // Can't find file + ERROR(lex.getPosition(), E0000, "Can't find file for '" << name << "' in '" << mod_fileinfo.path << "'"); + } + DEBUG("- path = " << submod.m_file_info.path); + Lexer sub_lex(submod.m_file_info.path); + Parse_ModRoot(sub_lex, submod, meta_items); + GET_CHECK_TOK(tok, sub_lex, TOK_EOF); + } + break; + default: + throw ParseError::Generic("Expected { or ; after module name"); + } + item_name = mv$(name); + item_data = ::AST::Item( mv$(submod) ); + break; } + + default: + throw ParseError::Unexpected(lex, tok); + } + + item_data.attrs = mv$(meta_items); + item_data.span = lex.end_span(ps); + + return ::AST::Named< ::AST::Item> { mv$(item_name), mv$(item_data), is_public }; +} + +void Parse_Mod_Item(TokenStream& lex, AST::Module& mod, AST::MetaItems meta_items) +{ + SET_MODULE(lex, mod); + lex.parse_state().parent_attrs = &meta_items; + + //TRACE_FUNCTION; + Token tok; + + // `use ...` + if( LOOK_AHEAD(lex) == TOK_RWORD_USE || (lex.lookahead(0) == TOK_RWORD_PUB && lex.lookahead(1) == TOK_RWORD_USE) ) + { + bool is_public = false; + if( GET_TOK(tok, lex) == TOK_RWORD_PUB ) { + is_public = true; + GET_TOK(tok, lex); + } + + Parse_Use(lex, [&mod,is_public,&meta_items](AST::UseStmt p, std::string s) { + DEBUG(mod.path() << " - use " << p << " as '" << s << "'"); + mod.add_alias(is_public, mv$(p), s, meta_items.clone()); + }); + GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); + } + else + { + mod.add_item( Parse_Mod_Item_S(lex, mod.m_file_info, mod.path(), mv$(meta_items)) ); + } +} + +void Parse_ModRoot_Items(TokenStream& lex, AST::Module& mod) +{ + Token tok; + + for(;;) + { + // Check 1 - End of module (either via a closing brace, or EOF) + switch(GET_TOK(tok, lex)) + { + case TOK_BRACE_CLOSE: + case TOK_EOF: + PUTBACK(tok, lex); + return; + default: + PUTBACK(tok, lex); + break; + } + + // Attributes on the following item + AST::MetaItems meta_items; + while( GET_TOK(tok, lex) == TOK_ATTR_OPEN ) + { + meta_items.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + PUTBACK(tok, lex); + DEBUG("meta_items = " << meta_items); + + Parse_Mod_Item(lex, mod, mv$(meta_items)); + } +} + +void Parse_ModRoot(TokenStream& lex, AST::Module& mod, AST::MetaItems& mod_attrs) +{ + TRACE_FUNCTION; + + Token tok; + + // Attributes on module/crate (will continue loop) + while( GET_TOK(tok, lex) == TOK_CATTR_OPEN ) + { + AST::MetaItem item = Parse_MetaItem(lex); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + + mod_attrs.push_back( mv$(item) ); + } + PUTBACK(tok, lex); + + Parse_ModRoot_Items(lex, mod); +} + +AST::Crate Parse_Crate(::std::string mainfile) +{ + Token tok; + + Lexer lex(mainfile); + + size_t p = mainfile.find_last_of('/'); + ::std::string mainpath = (p != ::std::string::npos ? ::std::string(mainfile.begin(), mainfile.begin()+p+1) : "./"); + + AST::Crate crate; + + crate.root_module().m_file_info.path = mainpath; + crate.root_module().m_file_info.controls_dir = true; + + Parse_ModRoot(lex, crate.root_module(), crate.m_attrs); + + return crate; +} diff --git a/src/parse/token.cpp b/src/parse/token.cpp index 95adf594..13af2043 100644 --- a/src/parse/token.cpp +++ b/src/parse/token.cpp @@ -38,7 +38,7 @@ Token::~Token() default: break; } - + } Token::Token(): @@ -87,7 +87,7 @@ Token::Token(const InterpolatedFragment& frag) m_type = TOK_INTERPOLATED_STMT; if(0) case InterpolatedFragment::BLOCK: m_type = TOK_INTERPOLATED_BLOCK; - + m_data = reinterpret_cast(frag.m_ptr)->clone().release(); break; case InterpolatedFragment::META: @@ -125,7 +125,7 @@ Token::Token(TagTakeIP, InterpolatedFragment frag) m_type = TOK_INTERPOLATED_STMT; if(0) case InterpolatedFragment::BLOCK: m_type = TOK_INTERPOLATED_BLOCK; - + m_data = reinterpret_cast(frag.m_ptr); frag.m_ptr = nullptr; break; @@ -160,7 +160,7 @@ Token Token::clone() const { Token rv(m_type); rv.m_pos = m_pos; - + assert( m_data.tag() != Data::TAGDEAD ); TU_MATCH(Data, (m_data), (e), (None, @@ -254,7 +254,7 @@ enum eTokenType Token::typefromstr(const ::std::string& s) struct EscapedString { const ::std::string& s; EscapedString(const ::std::string& s): s(s) {} - + friend ::std::ostream& operator<<(::std::ostream& os, const EscapedString& x) { for(auto b : x.s) { switch(b) @@ -284,7 +284,7 @@ struct EscapedString { { switch(m_type) { - case TOK_NULL: return "/*null*/"; + case TOK_NULL: return "/*null*/"; case TOK_EOF: return "/*eof*/"; case TOK_NEWLINE: return "\n"; diff --git a/src/parse/token.hpp b/src/parse/token.hpp index 3e7f7c24..0fcf0ed7 100644 --- a/src/parse/token.hpp +++ b/src/parse/token.hpp @@ -26,7 +26,7 @@ public: RcString filename; unsigned int line; unsigned int ofs; - + Position(): filename(""), line(0), @@ -71,11 +71,11 @@ class Token: }), (Fragment, void*) ); - + enum eTokenType m_type; Data m_data; Position m_pos; - + public: virtual ~Token(); Token(); @@ -95,7 +95,7 @@ public: } Token(const Token& t); Token clone() const; - + Token(enum eTokenType type); Token(enum eTokenType type, ::std::string str); Token(uint64_t val, enum eCoreType datatype); @@ -110,14 +110,14 @@ public: enum eCoreType datatype() const { TU_MATCH_DEF(Data, (m_data), (e), (assert(!"Getting datatype of invalid token type");), (Integer, return e.m_datatype;), (Float, return e.m_datatype;)) } uint64_t intval() const { return m_data.as_Integer().m_intval; } double floatval() const { return m_data.as_Float().m_floatval; } - + TypeRef& frag_type() { assert(m_type == TOK_INTERPOLATED_TYPE); return *reinterpret_cast( m_data.as_Fragment() ); } AST::Path& frag_path() { assert(m_type == TOK_INTERPOLATED_PATH); return *reinterpret_cast( m_data.as_Fragment() ); } AST::Pattern& frag_pattern() { assert(m_type == TOK_INTERPOLATED_PATTERN); return *reinterpret_cast( m_data.as_Fragment() ); } AST::MetaItem& frag_meta() { assert(m_type == TOK_INTERPOLATED_META); return *reinterpret_cast( m_data.as_Fragment() ); } ::std::unique_ptr take_frag_node(); ::AST::Named take_frag_item(); - + bool operator==(const Token& r) const { if(type() != r.type()) return false; @@ -133,13 +133,13 @@ public: bool operator!=(const Token& r) const { return !(*this == r); } ::std::string to_str() const; - + void set_pos(Position pos) { m_pos = pos; } const Position& get_pos() const { return m_pos; } - + static const char* typestr(enum eTokenType type); static eTokenType typefromstr(const ::std::string& s); - + SERIALISABLE_PROTOTYPES(); friend ::std::ostream& operator<<(::std::ostream& os, const Token& tok); diff --git a/src/parse/tokenstream.cpp b/src/parse/tokenstream.cpp index 0182e548..9a961a04 100644 --- a/src/parse/tokenstream.cpp +++ b/src/parse/tokenstream.cpp @@ -73,17 +73,17 @@ void TokenStream::putback(Token tok) eTokenType TokenStream::lookahead(unsigned int i) { const unsigned int MAX_LOOKAHEAD = 3; - + if( m_cache_valid ) { if( i == 0 ) return m_cache.type(); i --; } - + if( i >= MAX_LOOKAHEAD ) throw ParseError::BugCheck("Excessive lookahead"); - + while( i >= m_lookahead.size() ) { DEBUG("lookahead - read #" << m_lookahead.size()); @@ -91,7 +91,7 @@ eTokenType TokenStream::lookahead(unsigned int i) auto hygiene = this->realGetHygiene(); m_lookahead.push_back( ::std::make_pair(mv$(tok), mv$(hygiene)) ); } - + DEBUG("lookahead(" << i << ") = " << m_lookahead[i]); return m_lookahead[i].first.type(); } diff --git a/src/parse/tokenstream.hpp b/src/parse/tokenstream.hpp index ae737d0d..85fc62e2 100644 --- a/src/parse/tokenstream.hpp +++ b/src/parse/tokenstream.hpp @@ -26,15 +26,15 @@ struct ParseState bool disallow_struct_literal = false; // A debugging hook that disables expansion of macros bool no_expand_macros = false; - + ::AST::Module* module = nullptr; ::AST::MetaItems* parent_attrs = nullptr; - + ::AST::Module& get_current_mod() { assert(this->module); return *this->module; } - + friend ::std::ostream& operator<<(::std::ostream& os, const ParseState& ps) { os << "ParseState {"; if(ps.disallow_struct_literal) os << " disallow_struct_literal"; @@ -47,7 +47,7 @@ struct ParseState class TokenStream { friend class TTLexer; // needs access to internals to know what was consumed - + bool m_cache_valid; Token m_cache; Ident::Hygiene m_hygiene; @@ -59,17 +59,17 @@ public: Token getToken(); void putback(Token tok); eTokenType lookahead(unsigned int count); - + virtual Position getPosition() const = 0; Ident::Hygiene getHygiene() const; - + ParseState& parse_state() { return m_parse_state; } - + ProtoSpan start_span() const; Span end_span(ProtoSpan ps) const; - + Ident get_ident(Token tok) const; - + protected: virtual Token realGetToken() = 0; virtual Ident::Hygiene realGetHygiene() const = 0; diff --git a/src/parse/tokentree.hpp b/src/parse/tokentree.hpp index 8472ec7f..26e23876 100644 --- a/src/parse/tokentree.hpp +++ b/src/parse/tokentree.hpp @@ -1,61 +1,61 @@ -/* - * MRustC - Rust Compiler - * - By John Hodge (Mutabah/thePowersGang) - * - * parse/tokentree.hpp - * - Token Trees (groups of tokens - */ -#ifndef TOKENTREE_HPP_INCLUDED -#define TOKENTREE_HPP_INCLUDED - -#include "token.hpp" -#include -#include - -class TokenTree -{ - Ident::Hygiene m_hygiene; - Token m_tok; - ::std::vector m_subtrees; -public: - virtual ~TokenTree() {} - TokenTree() {} - TokenTree(TokenTree&&) = default; - TokenTree& operator=(TokenTree&&) = default; - TokenTree(enum eTokenType ty): - m_tok( Token(ty) ) - { - } - TokenTree(Token tok): - m_tok( ::std::move(tok) ) - { - } - TokenTree(Ident::Hygiene hygiene, Token tok): - m_hygiene( ::std::move(hygiene) ), - m_tok( ::std::move(tok) ) - { - } - TokenTree(Ident::Hygiene hygiene, ::std::vector subtrees): - m_hygiene( ::std::move(hygiene) ), - m_subtrees( ::std::move(subtrees) ) - { - } - - TokenTree clone() const; - - bool is_token() const { - return m_tok.type() != TOK_NULL; - } - unsigned int size() const { - return m_subtrees.size(); - } - const TokenTree& operator[](unsigned int idx) const { return m_subtrees[idx]; } - TokenTree& operator[](unsigned int idx) { return m_subtrees[idx]; } - const Token& tok() const { return m_tok; } - Token& tok() { return m_tok; } - const Ident::Hygiene& hygiene() const { return m_hygiene; } - - friend ::std::ostream& operator<<(::std::ostream& os, const TokenTree& tt); -}; - -#endif // TOKENTREE_HPP_INCLUDED +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * parse/tokentree.hpp + * - Token Trees (groups of tokens + */ +#ifndef TOKENTREE_HPP_INCLUDED +#define TOKENTREE_HPP_INCLUDED + +#include "token.hpp" +#include +#include + +class TokenTree +{ + Ident::Hygiene m_hygiene; + Token m_tok; + ::std::vector m_subtrees; +public: + virtual ~TokenTree() {} + TokenTree() {} + TokenTree(TokenTree&&) = default; + TokenTree& operator=(TokenTree&&) = default; + TokenTree(enum eTokenType ty): + m_tok( Token(ty) ) + { + } + TokenTree(Token tok): + m_tok( ::std::move(tok) ) + { + } + TokenTree(Ident::Hygiene hygiene, Token tok): + m_hygiene( ::std::move(hygiene) ), + m_tok( ::std::move(tok) ) + { + } + TokenTree(Ident::Hygiene hygiene, ::std::vector subtrees): + m_hygiene( ::std::move(hygiene) ), + m_subtrees( ::std::move(subtrees) ) + { + } + + TokenTree clone() const; + + bool is_token() const { + return m_tok.type() != TOK_NULL; + } + unsigned int size() const { + return m_subtrees.size(); + } + const TokenTree& operator[](unsigned int idx) const { return m_subtrees[idx]; } + TokenTree& operator[](unsigned int idx) { return m_subtrees[idx]; } + const Token& tok() const { return m_tok; } + Token& tok() { return m_tok; } + const Ident::Hygiene& hygiene() const { return m_hygiene; } + + friend ::std::ostream& operator<<(::std::ostream& os, const TokenTree& tt); +}; + +#endif // TOKENTREE_HPP_INCLUDED diff --git a/src/parse/ttstream.hpp b/src/parse/ttstream.hpp index 575178ac..946cca5f 100644 --- a/src/parse/ttstream.hpp +++ b/src/parse/ttstream.hpp @@ -21,7 +21,7 @@ public: ~TTStream(); TTStream& operator=(const TTStream& x) { m_stack = x.m_stack; return *this; } - + Position getPosition() const override; protected: @@ -44,7 +44,7 @@ public: TTStreamO& operator=(const TTStreamO& x) { m_stack = x.m_stack; return *this; } TTStreamO& operator=(TTStreamO&& x) = default; - + Position getPosition() const override; protected: diff --git a/src/parse/types.cpp b/src/parse/types.cpp index 4cd9d755..cb664f79 100644 --- a/src/parse/types.cpp +++ b/src/parse/types.cpp @@ -32,7 +32,7 @@ TypeRef Parse_Type_Int(TokenStream& lex, bool allow_trait_list) auto ps = lex.start_span(); Token tok; - + switch( GET_TOK(tok, lex) ) { case TOK_INTERPOLATED_TYPE: @@ -45,7 +45,7 @@ TypeRef Parse_Type_Int(TokenStream& lex, bool allow_trait_list) // '_' = Wildcard (type inferrence variable) case TOK_UNDERSCORE: return TypeRef(Span(tok.get_pos())); - + // 'unsafe' - An unsafe function type case TOK_RWORD_UNSAFE: // 'extern' - A function type with an ABI @@ -54,10 +54,10 @@ TypeRef Parse_Type_Int(TokenStream& lex, bool allow_trait_list) case TOK_RWORD_FN: PUTBACK(tok, lex); return Parse_Type_Fn(lex); - + case TOK_RWORD_IMPL: return Parse_Type_ErasedType(lex, allow_trait_list); - + // '<' - An associated type cast case TOK_LT: case TOK_DOUBLE_LT: { @@ -65,7 +65,7 @@ TypeRef Parse_Type_Int(TokenStream& lex, bool allow_trait_list) auto path = Parse_Path(lex, PATH_GENERIC_TYPE); return TypeRef(TypeRef::TagPath(), lex.end_span(ps), mv$(path)); } - // + // case TOK_RWORD_FOR: { auto hrls = Parse_HRB(lex); switch(LOOK_AHEAD(lex)) @@ -156,14 +156,14 @@ TypeRef Parse_Type_Int(TokenStream& lex, bool allow_trait_list) throw ParseError::Unexpected(lex, tok/*, "; or ]"*/); } } - + // '(' - Tuple (or lifetime bounded trait) case TOK_PAREN_OPEN: { DEBUG("Tuple"); if( GET_TOK(tok, lex) == TOK_PAREN_CLOSE ) return TypeRef(TypeRef::TagTuple(), lex.end_span(ps), {}); PUTBACK(tok, lex); - + TypeRef inner = Parse_Type(lex, true); if( LOOK_AHEAD(lex) == TOK_PAREN_CLOSE ) { @@ -198,12 +198,12 @@ TypeRef Parse_Type_Fn(TokenStream& lex, ::std::vector<::std::string> hrls) // TODO: HRLs TRACE_FUNCTION; Token tok; - + ::std::string abi = ""; bool is_unsafe = false; - + GET_TOK(tok, lex); - + if( tok.type() == TOK_RWORD_UNSAFE ) { is_unsafe = true; @@ -231,7 +231,7 @@ TypeRef Parse_Type_Fn(TokenStream& lex, ::std::vector<::std::string> hrls) if( LOOK_AHEAD(lex) == TOK_TRIPLE_DOT ) { GET_TOK(tok, lex); is_variadic = true; - break; + break; } // Handle `ident: ` if( lex.lookahead(0) == TOK_IDENT && lex.lookahead(1) == TOK_COLON ) { @@ -245,7 +245,7 @@ TypeRef Parse_Type_Fn(TokenStream& lex, ::std::vector<::std::string> hrls) } } GET_CHECK_TOK(tok, lex, TOK_PAREN_CLOSE); - + TypeRef ret_type = TypeRef(TypeRef::TagUnit(), Span(tok.get_pos())); if( GET_TOK(tok, lex) == TOK_THINARROW ) { @@ -254,7 +254,7 @@ TypeRef Parse_Type_Fn(TokenStream& lex, ::std::vector<::std::string> hrls) else { PUTBACK(tok, lex); } - + return TypeRef(TypeRef::TagFunction(), lex.end_span(ps), is_unsafe, mv$(abi), mv$(args), is_variadic, mv$(ret_type)); } @@ -263,7 +263,7 @@ TypeRef Parse_Type_Path(TokenStream& lex, ::std::vector<::std::string> hrls, boo Token tok; auto ps = lex.start_span(); - + if( ! allow_trait_list ) { return TypeRef(TypeRef::TagPath(), lex.end_span(ps), Parse_Path(lex, PATH_GENERIC_TYPE)); @@ -307,7 +307,7 @@ TypeRef Parse_Type_ErasedType(TokenStream& lex, bool allow_trait_list) traits.push_back( Parse_Path(lex, PATH_GENERIC_TYPE) ); } while( GET_TOK(tok, lex) == TOK_PLUS ); PUTBACK(tok, lex); - + if( lifetimes.size() ) DEBUG("TODO: Lifetime bounds on erased types"); return TypeRef(lex.end_span(ps), TypeData::make_ErasedType({ {}, mv$(traits) })); -- cgit v1.2.3