/* */ #include "preproc.hpp" #include "../ast/ast.hpp" #include "parseerror.hpp" #include "common.hpp" #include unsigned int TraceLog::depth = 0; ::std::vector Parse_Path_GenericList(TokenStream& lex) { TRACE_FUNCTION; ::std::vector types; Token tok; do { types.push_back( Parse_Type(lex) ); } while( GET_TOK(tok, lex) == TOK_COMMA ); // HACK: Split >> into > if(tok.type() == TOK_DOUBLE_GT) { lex.putback(Token(TOK_GT)); } else { CHECK_TOK(tok, TOK_GT); } return types; } AST::Path Parse_PathFrom(TokenStream& lex, AST::Path path, eParsePathGenericMode generic_mode) { TRACE_FUNCTION; Token tok; tok = lex.getToken(); while(true) { ::std::vector params; CHECK_TOK(tok, TOK_IDENT); ::std::string component = tok.str(); tok = lex.getToken(); if(generic_mode == PATH_GENERIC_TYPE && tok.type() == TOK_LT) { // Type-mode generics "::path::to::Type" params = Parse_Path_GenericList(lex); tok = lex.getToken(); } if( tok.type() != TOK_DOUBLE_COLON ) { path.append( AST::PathNode(component, params) ); break; } tok = lex.getToken(); if( generic_mode == PATH_GENERIC_EXPR && tok.type() == TOK_LT ) { // Expr-mode generics "::path::to::function::(arg1, arg2)" params = Parse_Path_GenericList(lex); tok = lex.getToken(); if( tok.type() != TOK_DOUBLE_COLON ) { path.append( AST::PathNode(component, params) ); break; } } path.append( AST::PathNode(component, params) ); } lex.putback(tok); return path; } AST::Path Parse_Path(TokenStream& lex, bool is_abs, eParsePathGenericMode generic_mode) { if( is_abs ) return Parse_PathFrom(lex, AST::Path(AST::Path::TagAbsolute()), generic_mode); else return Parse_PathFrom(lex, AST::Path(), generic_mode); } static const struct { const char* name; enum eCoreType type; } CORETYPES[] = { {"char", CORETYPE_CHAR}, {"f32", CORETYPE_F32}, {"f64", CORETYPE_F64}, {"i16", CORETYPE_I16}, {"i32", CORETYPE_I32}, {"i64", CORETYPE_I64}, {"i8", CORETYPE_I8}, {"int", CORETYPE_INT}, {"u16", CORETYPE_U16}, {"u32", CORETYPE_U32}, {"u64", CORETYPE_U64}, {"u8", CORETYPE_U8}, {"uint", CORETYPE_UINT}, }; TypeRef Parse_Type(TokenStream& lex) { TRACE_FUNCTION; Token tok = lex.getToken(); switch(tok.type()) { case TOK_IDENT: // Either a path (with generics) if( tok.str() == "_" ) return TypeRef(); for(unsigned int i = 0; i < sizeof(CORETYPES)/sizeof(CORETYPES[0]); i ++) { if( tok.str() < CORETYPES[i].name ) break; if( tok.str() == CORETYPES[i].name ) return TypeRef(TypeRef::TagPrimitive(), CORETYPES[i].type); } // or a primitive lex.putback(tok); return TypeRef(TypeRef::TagPath(), Parse_Path(lex, false, PATH_GENERIC_TYPE)); // relative path case TOK_DOUBLE_COLON: // Path with generics return TypeRef(TypeRef::TagPath(), Parse_Path(lex, true, PATH_GENERIC_TYPE)); case TOK_AMP: // Reference tok = lex.getToken(); if( tok.type() == TOK_RWORD_MUT ) { // Mutable reference return TypeRef(TypeRef::TagReference(), true, Parse_Type(lex)); } else { lex.putback(tok); // Immutable reference return TypeRef(TypeRef::TagReference(), false, Parse_Type(lex)); } throw ParseError::BugCheck("Reached end of Parse_Type:AMP"); case TOK_STAR: // Pointer switch( GET_TOK(tok, lex) ) { case TOK_RWORD_MUT: // Mutable pointer return TypeRef(TypeRef::TagPointer(), true, Parse_Type(lex)); case TOK_RWORD_CONST: // Immutable pointer return TypeRef(TypeRef::TagPointer(), false, Parse_Type(lex)); default: throw ParseError::Unexpected(tok, Token(TOK_RWORD_CONST)); } throw ParseError::BugCheck("Reached end of Parse_Type:STAR"); case TOK_SQUARE_OPEN: { // Array TypeRef inner = Parse_Type(lex); tok = lex.getToken(); if( tok.type() == TOK_COMMA ) { // Sized array GET_CHECK_TOK(tok, lex, TOK_DOUBLE_DOT); AST::Expr array_size = Parse_Expr(lex, true); GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); return TypeRef(TypeRef::TagSizedArray(), inner, array_size); } else { GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); return TypeRef(TypeRef::TagUnsizedArray(), inner); } throw ParseError::BugCheck("Reached end of Parse_Type:SQUARE"); } case TOK_PAREN_OPEN: { ::std::vector types; if( (tok = lex.getToken()).type() == TOK_PAREN_CLOSE) return TypeRef(TypeRef::TagTuple(), types); do { TypeRef type = Parse_Type(lex); types.push_back(type); } while( (tok = lex.getToken()).type() == TOK_COMMA ); GET_CHECK_TOK(tok, lex, TOK_PAREN_CLOSE); return TypeRef(TypeRef::TagTuple(), types); } case TOK_EXCLAM: throw ParseError::Todo("noreturn type"); default: throw ParseError::Unexpected(tok); } throw ParseError::BugCheck("Reached end of Parse_Type"); } AST::TypeParams Parse_TypeParams(TokenStream& lex) { TRACE_FUNCTION; AST::TypeParams ret; Token tok; do { bool is_lifetime = false; tok = lex.getToken(); switch(tok.type()) { case TOK_IDENT: break; case TOK_LIFETIME: is_lifetime = true; break; default: // Oopsie! throw ParseError::Unexpected(tok); } AST::TypeParam param( is_lifetime, tok.str() ); tok = lex.getToken(); if( tok.type() == TOK_COLON ) { // TODO: Conditions if( is_lifetime ) { throw ParseError::Todo("lifetime param conditions"); } do { tok = lex.getToken(); if(tok.type() == TOK_LIFETIME) param.addLifetimeBound(tok.str()); else { lex.putback(tok); param.addTypeBound(Parse_Type(lex)); } tok = lex.getToken(); } while(tok.type() == TOK_PLUS); } ret.push_back(param); } while( tok.type() == TOK_COMMA ); lex.putback(tok); return ret; } void Parse_TypeConds(TokenStream& lex, AST::TypeParams& params) { TRACE_FUNCTION; throw ParseError::Todo("type param conditions (where)"); } /// Parse a function definition (after the 'fn') AST::Function Parse_FunctionDef(TokenStream& lex) { TRACE_FUNCTION; Token tok; // Name GET_CHECK_TOK(tok, lex, TOK_IDENT); ::std::string name = tok.str(); // Parameters AST::TypeParams params; if( GET_TOK(tok, lex) == TOK_LT ) { params = Parse_TypeParams(lex); GET_CHECK_TOK(tok, lex, TOK_GT); //if(GET_TOK(tok, lex) == TOK_RWORD_WHERE) //{ // Parse_TypeConds(lex, params); // tok = lex.getToken(); //} } else { lex.putback(tok); } AST::Function::Class fcn_class = AST::Function::CLASS_UNBOUND; GET_CHECK_TOK(tok, lex, TOK_PAREN_OPEN); GET_TOK(tok, lex); if( tok.type() == TOK_AMP ) { // By-reference method if( GET_TOK(tok, lex) == TOK_LIFETIME ) { throw ParseError::Todo("Lifetimes on self in methods"); } if( tok.type() == TOK_RWORD_MUT ) { GET_CHECK_TOK(tok, lex, TOK_RWORD_SELF); fcn_class = AST::Function::CLASS_MUTMETHOD; } else { CHECK_TOK(tok, TOK_RWORD_SELF); fcn_class = AST::Function::CLASS_REFMETHOD; } GET_TOK(tok, lex); } else if( tok.type() == TOK_RWORD_SELF ) { // By-value method fcn_class = AST::Function::CLASS_VALMETHOD; GET_TOK(tok, lex); throw ParseError::Todo("By-value methods"); } else { // Unbound method } ::std::vector args; if( tok.type() != TOK_PAREN_CLOSE ) { lex.putback(tok); // Argument list do { GET_CHECK_TOK(tok, lex, TOK_IDENT); ::std::string name = tok.str(); GET_CHECK_TOK(tok, lex, TOK_COLON); TypeRef type = Parse_Type(lex); args.push_back( ::std::make_pair(name, type) ); tok = lex.getToken(); } while( tok.type() == TOK_COMMA ); CHECK_TOK(tok, TOK_PAREN_CLOSE); } else { // Eat 'tok', negative comparison } TypeRef ret_type; if( GET_TOK(tok, lex) == TOK_THINARROW ) { // Return type ret_type = Parse_Type(lex); } else { lex.putback(tok); } AST::Expr code = Parse_ExprBlock(lex); return AST::Function(name, params, fcn_class, ret_type, args, code); } void Parse_Struct(AST::Module& mod, TokenStream& lex, const bool is_public, const ::std::vector meta_items) { Token tok; GET_CHECK_TOK(tok, lex, TOK_IDENT); ::std::string name = tok.str(); tok = lex.getToken(); AST::TypeParams params; if( tok.type() == TOK_LT ) { params = Parse_TypeParams(lex); GET_CHECK_TOK(tok, lex, TOK_GT); tok = lex.getToken(); if(tok.type() == TOK_RWORD_WHERE) { Parse_TypeConds(lex, params); tok = lex.getToken(); } } if(tok.type() == TOK_PAREN_OPEN) { TypeRef inner = Parse_Type(lex); tok = lex.getToken(); if(tok.type() != TOK_PAREN_CLOSE) { ::std::vector refs; refs.push_back(inner); while( (tok = lex.getToken()).type() == TOK_COMMA ) { refs.push_back( Parse_Type(lex) ); } if( tok.type() != TOK_PAREN_CLOSE ) throw ParseError::Unexpected(tok, Token(TOK_PAREN_CLOSE)); inner = TypeRef(TypeRef::TagTuple(), refs); } throw ParseError::Todo("tuple struct"); } else if(tok.type() == TOK_SEMICOLON) { throw ParseError::Todo("unit-like struct"); } else if(tok.type() == TOK_BRACE_OPEN) { ::std::vector items; while( (tok = lex.getToken()).type() != TOK_BRACE_CLOSE ) { CHECK_TOK(tok, TOK_IDENT); ::std::string name = tok.str(); GET_CHECK_TOK(tok, lex, TOK_COLON); TypeRef type = Parse_Type(lex); items.push_back( ::std::make_pair(name, type) ); tok = lex.getToken(); if(tok.type() == TOK_BRACE_CLOSE) break; if(tok.type() != TOK_COMMA) throw ParseError::Unexpected(tok, Token(TOK_COMMA)); } mod.add_struct(is_public, name, params, items); } else { throw ParseError::Unexpected(tok); } } /// Parse a meta-item declaration (either #![ or #[) AST::MetaItem Parse_MetaItem(TokenStream& lex) { Token tok; GET_CHECK_TOK(tok, lex, TOK_IDENT); ::std::string name = tok.str(); switch(GET_TOK(tok, lex)) { case TOK_EQUAL: throw ParseError::Todo("Meta item key-value"); case TOK_PAREN_OPEN: { ::std::vector items; do { items.push_back(Parse_MetaItem(lex)); } while(GET_TOK(tok, lex) == TOK_COMMA); CHECK_TOK(tok, TOK_PAREN_CLOSE); return AST::MetaItem(name, items); } default: lex.putback(tok); return AST::MetaItem(name); } } AST::Impl Parse_Impl(TokenStream& lex) { Token tok; AST::TypeParams params; // 1. (optional) type parameters if( GET_TOK(tok, lex) == TOK_LT ) { params = Parse_TypeParams(lex); GET_CHECK_TOK(tok, lex, TOK_GT); } else { lex.putback(tok); } // 2. Either a trait name (with type params), or the type to impl // - Don't care which at this stage TypeRef trait_type; TypeRef impl_type = Parse_Type(lex); if( GET_TOK(tok, lex) == TOK_RWORD_FOR ) { // Implementing a trait for another type, get the target type trait_type = impl_type; impl_type = Parse_Type(lex); } else { lex.putback(tok); } // Where clause if( GET_TOK(tok, lex) == TOK_RWORD_WHERE ) { Parse_TypeConds(lex, params); } else { lex.putback(tok); } GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); AST::Impl impl(impl_type, trait_type); // A sequence of method implementations while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) { bool is_public = false; if(tok.type() == TOK_RWORD_PUB) { is_public = true; GET_TOK(tok, lex); } switch(tok.type()) { case TOK_RWORD_FN: impl.add_function(is_public, Parse_FunctionDef(lex)); break; default: throw ParseError::Unexpected(tok); } } return impl; } AST::Module Parse_ModRoot(const ::std::string& path, Preproc& lex) { Token tok; AST::Module mod; // 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); throw ParseError::Todo("Parent attrs"); //mod_attrs.push_back( item ); } lex.putback(tok); // TODO: Handle known parent attribs if operating on crate root for(;;) { // Check 1 - End of module (either via a closing brace, or EOF) switch(GET_TOK(tok, lex)) { case TOK_BRACE_CLOSE: if( path.size() > 0 ) throw ParseError::Unexpected(tok); return mod; case TOK_EOF: if( path.size() == 0 ) throw ParseError::Unexpected(tok); return mod; default: lex.putback(tok); break; } // Attributes on the following item ::std::vector 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); } lex.putback(tok); // Module visibility bool is_public = false; if( GET_TOK(tok, lex) == TOK_RWORD_PUB ) { is_public = true; } else { lex.putback(tok); } // The actual item! switch( GET_TOK(tok, lex) ) { case TOK_RWORD_USE: // TODO: Do manual path parsing here, as use has its own special set of quirks mod.add_alias( is_public, Parse_Path(lex, true, PATH_GENERIC_NONE) ); GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); break; case TOK_RWORD_CONST: { GET_CHECK_TOK(tok, lex, TOK_IDENT); ::std::string name = 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, true); GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); mod.add_constant(is_public, name, type, val); break; } case TOK_RWORD_STATIC: { tok = lex.getToken(); bool is_mut = false; if(tok.type() == TOK_RWORD_MUT) { is_mut = true; tok = lex.getToken(); } CHECK_TOK(tok, TOK_IDENT); ::std::string name = 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, true); GET_CHECK_TOK(tok, lex, TOK_SEMICOLON); mod.add_global(is_public, is_mut, name, type, val); break; } case TOK_RWORD_FN: mod.add_function(is_public, Parse_FunctionDef(lex)); break; case TOK_RWORD_STRUCT: Parse_Struct(mod, lex, is_public, meta_items); break; case TOK_RWORD_ENUM: throw ParseError::Todo("modroot enum"); case TOK_RWORD_IMPL: mod.add_impl(Parse_Impl(lex)); break; case TOK_RWORD_TRAIT: throw ParseError::Todo("modroot trait"); case TOK_RWORD_MOD: throw ParseError::Todo("sub-modules"); default: throw ParseError::Unexpected(tok); } } } AST::Crate Parse_Crate(::std::string mainfile) { Preproc lex(mainfile); return AST::Crate( Parse_ModRoot(mainfile, lex) ); }