diff options
author | John Hodge <tpg@ucc.asn.au> | 2017-11-15 21:19:22 +0800 |
---|---|---|
committer | John Hodge <tpg@ucc.asn.au> | 2017-11-15 21:19:22 +0800 |
commit | 97839be171f00c9f6ea3d9921ca89db918027800 (patch) | |
tree | c60922d8147128d34b8514e1fd30b73b91a750cf /src | |
parent | d585aa08551aa78b692bab8326509baef02a7b3d (diff) | |
download | mrust-97839be171f00c9f6ea3d9921ca89db918027800.tar.gz |
proc_macro - Working invocations (some work needed on AST->TT)
Diffstat (limited to 'src')
-rw-r--r-- | src/expand/derive.cpp | 10 | ||||
-rw-r--r-- | src/expand/proc_macro.cpp | 625 | ||||
-rw-r--r-- | src/expand/proc_macro.hpp | 15 | ||||
-rw-r--r-- | src/parse/lex.cpp | 34 | ||||
-rw-r--r-- | src/parse/lex.hpp | 3 | ||||
-rw-r--r-- | src/parse/token.cpp | 2 |
6 files changed, 686 insertions, 3 deletions
diff --git a/src/expand/derive.cpp b/src/expand/derive.cpp index 25cc6742..3b25831c 100644 --- a/src/expand/derive.cpp +++ b/src/expand/derive.cpp @@ -2227,8 +2227,14 @@ static void derive_item(const Span& sp, const AST::Crate& crate, AST::Module& mo else { // proc_macro - Invoke the handler. auto lex = ProcMacro_Invoke(sp, crate, mac_path.first, path.nodes().back().name(), item); - Parse_ModRoot_Items(*lex, mod); - found = true; + if( lex ) + { + Parse_ModRoot_Items(*lex, mod); + found = true; + } + else { + ERROR(sp, E0000, "proc_macro derive failed"); + } break; } } diff --git a/src/expand/proc_macro.cpp b/src/expand/proc_macro.cpp new file mode 100644 index 00000000..ea8625de --- /dev/null +++ b/src/expand/proc_macro.cpp @@ -0,0 +1,625 @@ +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * expand/proc_macro.cpp + * - Support for the `#[proc_macro_derive]` attribute + */ +#include <synext.hpp> +#include "../common.hpp" +#include <ast/ast.hpp> +#include <ast/expr.hpp> +#include <ast/crate.hpp> +#include <main_bindings.hpp> +#include <hir/hir.hpp> // ABI_RUST +#include "proc_macro.hpp" +#include <parse/lex.hpp> +#ifdef WIN32 +#else +# include <unistd.h> // read/write/pipe +# include <spawn.h> +# include <sys/wait.h> +#endif + +#define NEWNODE(_ty, ...) ::AST::ExprNodeP(new ::AST::ExprNode##_ty(__VA_ARGS__)) + +class Decorator_ProcMacroDerive: + public ExpandDecorator +{ +public: + AttrStage stage() const override { return AttrStage::Post; } + void handle(const Span& sp, const AST::MetaItem& attr, ::AST::Crate& crate, const AST::Path& path, AST::Module& mod, AST::Item& i) const override + { + if( i.is_None() ) + return; + + if( !i.is_Function() ) + TODO(sp, "Error for proc_macro_derive on non-Function"); + //auto& fcn = i.as_Function(); + auto trait_name = attr.items().at(0).name(); + ::std::vector<::std::string> attributes; + for(size_t i = 1; i < attr.items().size(); i ++) + { + if( attr.items()[i].name() == "attributes") { + for(const auto& si : attr.items()[i].items()) { + attributes.push_back( si.name() ); + } + } + } + + // TODO: Store attributes for later use. + crate.m_proc_macros.push_back(::std::make_pair( FMT("derive#" << trait_name), path )); + } +}; + +STATIC_DECORATOR("proc_macro_derive", Decorator_ProcMacroDerive) + + + +void Expand_ProcMacro(::AST::Crate& crate) +{ + // Create the following module: + // ``` + // mod `proc_macro#` { + // extern crate proc_macro; + // fn main() { + // self::proc_macro::main(&::`proc_macro#`::MACROS); + // } + // static TESTS: [proc_macro::MacroDesc; _] = [ + // proc_macro::MacroDesc { name: "deriving_Foo", handler: ::path::to::foo } + // ]; + // } + // ``` + + // ---- main function ---- + auto main_fn = ::AST::Function { Span(), {}, ABI_RUST, false, false, false, TypeRef(TypeRef::TagUnit(), Span()), {} }; + { + auto call_node = NEWNODE(_CallPath, + ::AST::Path("proc_macro", { ::AST::PathNode("main") }), + ::make_vec1( + NEWNODE(_UniOp, ::AST::ExprNode_UniOp::REF, + NEWNODE(_NamedValue, ::AST::Path("", { ::AST::PathNode("proc_macro#"), ::AST::PathNode("MACROS") })) + ) + ) + ); + main_fn.set_code( mv$(call_node) ); + } + + + // ---- test list ---- + ::std::vector< ::AST::ExprNodeP> test_nodes; + + for(const auto& desc : crate.m_proc_macros) + { + ::AST::ExprNode_StructLiteral::t_values desc_vals; + // `name: "foo",` + desc_vals.push_back({ {}, "name", NEWNODE(_String, desc.first) }); + // `handler`: ::foo + desc_vals.push_back({ {}, "handler", NEWNODE(_NamedValue, AST::Path(desc.second)) }); + + test_nodes.push_back( NEWNODE(_StructLiteral, ::AST::Path("proc_macro", { ::AST::PathNode("MacroDesc")}), nullptr, mv$(desc_vals) ) ); + } + auto* tests_array = new ::AST::ExprNode_Array(mv$(test_nodes)); + + size_t test_count = tests_array->m_values.size(); + auto tests_list = ::AST::Static { ::AST::Static::Class::STATIC, + TypeRef(TypeRef::TagSizedArray(), Span(), + TypeRef(Span(), ::AST::Path("proc_macro", { ::AST::PathNode("MacroDesc") })), + ::std::shared_ptr<::AST::ExprNode>( new ::AST::ExprNode_Integer(test_count, CORETYPE_UINT) ) + ), + ::AST::Expr( mv$(tests_array) ) + }; + + // ---- module ---- + auto newmod = ::AST::Module { ::AST::Path("", { ::AST::PathNode("proc_macro#") }) }; + // - TODO: These need to be loaded too. + // > They don't actually need to exist here, just be loaded (and use absolute paths) + newmod.add_ext_crate(false, "proc_macro", "proc_macro", {}); + + newmod.add_item(false, "main", mv$(main_fn), {}); + newmod.add_item(false, "MACROS", mv$(tests_list), {}); + + crate.m_root_module.add_item(false, "proc_macro#", mv$(newmod), {}); + crate.m_lang_items["mrustc-main"] = ::AST::Path("", { AST::PathNode("proc_macro#"), AST::PathNode("main") }); +} + +enum class TokenClass +{ + Symbol = 0, + Ident = 1, + Lifetime = 2, + String = 3, + ByteString = 4, // String + CharLit = 5, // v128 + UnsignedInt = 6, + SignedInt = 7, + Float = 8, + Fragment = 9, +}; +enum class FragType +{ + Ident = 0, + Tt = 1, + + Path = 2, + Type = 3, + + Expr = 4, + Statement = 5, + Block = 6, + Pattern = 7, +}; +struct ProcMacroInv: + public TokenStream +{ + Span m_parent_span; + +#ifdef WIN32 + HANDLE child_handle; + HANDLE child_stdin; + HANDLE child_stdout; +#else + // POSIX + pid_t child_pid; // Questionably needed + int child_stdin; + int child_stdout; + // NOTE: stderr stays as our stderr +#endif + bool m_eof_hit = false; + +public: + ProcMacroInv(const Span& sp, const char* executable, const char* macro_name); + ProcMacroInv(const ProcMacroInv&) = delete; + ProcMacroInv(ProcMacroInv&&); + ProcMacroInv& operator=(const ProcMacroInv&) = delete; + ProcMacroInv& operator=(ProcMacroInv&&); + virtual ~ProcMacroInv(); + + bool check_good(); + void send_done() { + send_symbol(""); + DEBUG("Input tokens sent"); + } + void send_symbol(const char* val) { + this->send_u8(static_cast<uint8_t>(TokenClass::Symbol)); + this->send_bytes(val, ::std::strlen(val)); + } + void send_ident(const char* val) { + this->send_u8(static_cast<uint8_t>(TokenClass::Ident)); + this->send_bytes(val, ::std::strlen(val)); + } + void send_lifetime(const char* val) { + this->send_u8(static_cast<uint8_t>(TokenClass::Lifetime)); + this->send_bytes(val, ::std::strlen(val)); + } + void send_string(const ::std::string& s) { + this->send_u8(static_cast<uint8_t>(TokenClass::String)); + this->send_bytes(s.data(), s.size()); + } + void send_bytestring(const ::std::string& s); + void send_char(uint32_t ch); + void send_int(eCoreType ct, int64_t v); + void send_float(eCoreType ct, double v); + //void send_fragment(); + + virtual Position getPosition() const override; + virtual Token realGetToken() override; + virtual Ident::Hygiene realGetHygiene() const override; +private: + Token realGetToken_(); + void send_u8(uint8_t v); + void send_bytes(const void* val, size_t size); + void send_v128u(uint64_t val); + + uint8_t recv_u8(); + ::std::string recv_bytes(); + uint64_t recv_v128u(); +}; + +ProcMacroInv ProcMacro_Invoke_int(const Span& sp, const ::AST::Crate& crate, const ::std::vector<::std::string>& mac_path) +{ + // 1. Locate macro in HIR list + const auto& crate_name = mac_path.front(); + const auto& ext_crate = crate.m_extern_crates.at(crate_name); + // TODO: Ensure that this macro is in the listed crate. + + // 2. Get executable and macro name + ::std::string proc_macro_exe_name = (ext_crate.m_filename + "-plugin"); + ::std::string mac_name = mac_path.at(1); + for(size_t i = 2; i < mac_path.size(); i ++) + { + mac_name += "::"; + mac_name += mac_path[i]; + } + + // 3. Create ProcMacroInv + return ProcMacroInv(sp, proc_macro_exe_name.c_str(), mac_name.c_str()); +} + + +namespace { + struct Visitor/*: + public AST::NodeVisitor*/ + { + ProcMacroInv& m_pmi; + Visitor(ProcMacroInv& pmi): + m_pmi(pmi) + { + } + + void visit_type(const ::TypeRef& ty) + { + // TODO: Correct handling of visit_type + m_pmi.send_symbol("("); + m_pmi.send_symbol(")"); + } + + void visit_params(const AST::GenericParams& params) + { + if( params.ty_params().size() > 0 || params.lft_params().size() > 0 ) + { + bool is_first = true; + m_pmi.send_symbol("<"); + // Lifetimes + for( const auto& p : params.lft_params() ) + { + if( !is_first ) + m_pmi.send_symbol(","); + m_pmi.send_lifetime(p.c_str()); + is_first = false; + } + // Types + for( const auto& p : params.ty_params() ) + { + if( !is_first ) + m_pmi.send_symbol(","); + m_pmi.send_ident(p.name().c_str()); + if( !p.get_default().is_wildcard() ) + { + m_pmi.send_symbol("="); + this->visit_type(p.get_default()); + } + is_first = false; + } + m_pmi.send_symbol(">"); + } + } + void visit_bounds(const AST::GenericParams& params) + { + } + void visit_nodes(const ::AST::Expr& e) + { + //e.visit_nodes(*this); + } + void visit_struct(const ::std::string& name, bool is_pub, const ::AST::Struct& str) + { + if( is_pub ) { + m_pmi.send_ident("pub"); + } + + m_pmi.send_ident("struct"); + m_pmi.send_ident(name.c_str()); + this->visit_params(str.params()); + TU_MATCH(AST::StructData, (str.m_data), (se), + (Unit, + this->visit_bounds(str.params()); + m_pmi.send_symbol(";"); + ), + (Tuple, + m_pmi.send_symbol("("); + for( const auto& si : se.ents ) + { + if( si.m_is_public ) + m_pmi.send_ident("pub"); + this->visit_type(si.m_type); + m_pmi.send_symbol(","); + } + m_pmi.send_symbol(")"); + this->visit_bounds(str.params()); + m_pmi.send_symbol(";"); + ), + (Struct, + this->visit_bounds(str.params()); + m_pmi.send_symbol("{"); + + for( const auto& si : se.ents ) + { + if( si.m_is_public ) + m_pmi.send_ident("pub"); + m_pmi.send_ident(si.m_name.c_str()); + m_pmi.send_symbol(":"); + this->visit_type(si.m_type); + m_pmi.send_symbol(","); + } + m_pmi.send_symbol("}"); + ) + ) + } + void visit_enum(const ::std::string& name, bool is_pub, const ::AST::Enum& str) + { + } + void visit_union(const ::std::string& name, bool is_pub, const ::AST::Union& str) + { + } + }; +} +::std::unique_ptr<TokenStream> ProcMacro_Invoke(const Span& sp, const ::AST::Crate& crate, const ::std::vector<::std::string>& mac_path, const ::std::string& item_name, const ::AST::Struct& i) +{ + // 1. Create ProcMacroInv instance + auto pmi = ProcMacro_Invoke_int(sp, crate, mac_path); + if( !pmi.check_good() ) + return ::std::unique_ptr<TokenStream>(); + // 2. Feed item as a token stream. + Visitor(pmi).visit_struct(item_name, false, i); + pmi.send_done(); + // 3. Return boxed invocation instance + return box$(pmi); +} +::std::unique_ptr<TokenStream> ProcMacro_Invoke(const Span& sp, const ::AST::Crate& crate, const ::std::vector<::std::string>& mac_path, const ::std::string& item_name, const ::AST::Enum& i) +{ + // 1. Create ProcMacroInv instance + auto pmi = ProcMacro_Invoke_int(sp, crate, mac_path); + if( !pmi.check_good() ) + return ::std::unique_ptr<TokenStream>(); + // 2. Feed item as a token stream. + Visitor(pmi).visit_enum(item_name, false, i); + pmi.send_done(); + // 3. Return boxed invocation instance + return box$(pmi); +} +::std::unique_ptr<TokenStream> ProcMacro_Invoke(const Span& sp, const ::AST::Crate& crate, const ::std::vector<::std::string>& mac_path, const ::std::string& item_name, const ::AST::Union& i) +{ + // 1. Create ProcMacroInv instance + auto pmi = ProcMacro_Invoke_int(sp, crate, mac_path); + if( !pmi.check_good() ) + return ::std::unique_ptr<TokenStream>(); + // 2. Feed item as a token stream. + Visitor(pmi).visit_union(item_name, false, i); + pmi.send_done(); + // 3. Return boxed invocation instance + return box$(pmi); +} + +ProcMacroInv::ProcMacroInv(const Span& sp, const char* executable, const char* macro_name): + m_parent_span(sp) +{ +#ifdef WIN32 +#else + int stdin_pipes[2]; + pipe(stdin_pipes); + this->child_stdin = stdin_pipes[1]; // Write end + int stdout_pipes[2]; + pipe(stdout_pipes); + this->child_stdout = stdout_pipes[0]; // Read end + + posix_spawn_file_actions_t file_actions; + posix_spawn_file_actions_init(&file_actions); + posix_spawn_file_actions_adddup2(&file_actions, stdin_pipes[0], 0); + posix_spawn_file_actions_adddup2(&file_actions, stdout_pipes[1], 1); + posix_spawn_file_actions_addclose(&file_actions, stdin_pipes[0]); + posix_spawn_file_actions_addclose(&file_actions, stdin_pipes[1]); + posix_spawn_file_actions_addclose(&file_actions, stdout_pipes[0]); + posix_spawn_file_actions_addclose(&file_actions, stdout_pipes[1]); + + char* argv[3] = { const_cast<char*>(executable), const_cast<char*>(macro_name), nullptr }; + char* envp[] = { nullptr }; + int rv = posix_spawn(&this->child_pid, executable, &file_actions, nullptr, argv, envp); + if( rv != 0 ) + { + BUG(sp, "Error in posix_spawn - " << rv); + } + + posix_spawn_file_actions_destroy(&file_actions); + // Close the ends we don't care about. + close(stdin_pipes[0]); + close(stdout_pipes[1]); + +#endif +} +ProcMacroInv::ProcMacroInv(ProcMacroInv&& x): + m_parent_span(x.m_parent_span), +#ifdef WIN32 +#else + child_pid(x.child_pid), + child_stdin(x.child_stdin), + child_stdout(x.child_stdout) +#endif +{ +#ifdef WIN32 +#else + x.child_pid = 0; +#endif + DEBUG(""); +} +ProcMacroInv& ProcMacroInv::operator=(ProcMacroInv&& x) +{ + m_parent_span = x.m_parent_span; +#ifdef WIN32 +#else + child_pid = x.child_pid; + child_stdin = x.child_stdin; + child_stdout = x.child_stdout; + + x.child_pid = 0; +#endif + DEBUG(""); + return *this; +} +ProcMacroInv::~ProcMacroInv() +{ +#ifdef WIN32 +#else + if( this->child_pid != 0 ) + { + DEBUG("Waiting for child " << this->child_pid << " to terminate"); + int status; + waitpid(this->child_pid, &status, 0); + close(this->child_stdout); + close(this->child_stdin); + } +#endif +} +bool ProcMacroInv::check_good() +{ + char v; + int rv = read(this->child_stdout, &v, 1); + if( rv == 0 ) + { + DEBUG("Unexpected EOF from child"); + return false; + } + if( rv < 0 ) + { + DEBUG("Error reading from child, rv=" << rv << " " << strerror(errno)); + return false; + } + DEBUG("Child started, value = " << (int)v); + if( v != 0 ) + return false; + return true; +} +void ProcMacroInv::send_u8(uint8_t v) +{ +#ifdef WIN32 +#else + write(this->child_stdin, &v, 1); +#endif +} +void ProcMacroInv::send_bytes(const void* val, size_t size) +{ + this->send_v128u( static_cast<uint64_t>(size) ); +#ifdef WIN32 +#else + write(this->child_stdin, val, size); +#endif +} +void ProcMacroInv::send_v128u(uint64_t val) +{ + while( val >= 128 ) { + this->send_u8( static_cast<uint8_t>(val & 0x7F) | 0x80 ); + val >>= 7; + } + this->send_u8( static_cast<uint8_t>(val & 0x7F) ); +} +uint8_t ProcMacroInv::recv_u8() +{ + uint8_t v; + if( read(this->child_stdout, &v, 1) != 1 ) + BUG(this->m_parent_span, "Unexpected EOF while reading from child process"); + return v; +} +::std::string ProcMacroInv::recv_bytes() +{ + auto len = this->recv_v128u(); + ASSERT_BUG(this->m_parent_span, len < SIZE_MAX, "Oversized string from child process"); + ::std::string val; + val.resize(len); + size_t ofs = 0, rem = len; + while( rem > 0 ) + { + auto n = read(this->child_stdout, &val[ofs], rem); + if( n == 0 ) { + BUG(this->m_parent_span, "Unexpected EOF while reading from child process"); + } + if( n < 0 ) { + BUG(this->m_parent_span, "Error while reading from child process"); + } + assert(static_cast<size_t>(n) <= rem); + ofs += n; + rem -= n; + } + + return val; +} +uint64_t ProcMacroInv::recv_v128u() +{ + uint64_t v = 0; + unsigned ofs = 0; + for(;;) + { + auto b = recv_u8(); + v |= static_cast<uint64_t>(b) << ofs; + if( (b & 0x80) == 0 ) + break; + ofs += 7; + } + return v; +} + +Position ProcMacroInv::getPosition() const { + return Position(); +} +Token ProcMacroInv::realGetToken() { + auto rv = this->realGetToken_(); + DEBUG(rv); + return rv; +} +Token ProcMacroInv::realGetToken_() { + if( m_eof_hit ) + return Token(TOK_EOF); + uint8_t v = this->recv_u8(); + + switch( static_cast<TokenClass>(v) ) + { + case TokenClass::Symbol: { + auto val = this->recv_bytes(); + if( val == "" ) { + m_eof_hit = true; + return Token(TOK_EOF); + } + auto t = Lex_FindOperator(val); + ASSERT_BUG(this->m_parent_span, t != TOK_NULL, "Unknown symbol from child process - " << val); + return t; + } + case TokenClass::Ident: { + auto val = this->recv_bytes(); + auto t = Lex_FindReservedWord(val); + if( t != TOK_NULL ) + return t; + return Token(TOK_IDENT, mv$(val)); + } + case TokenClass::Lifetime: { + auto val = this->recv_bytes(); + return Token(TOK_LIFETIME, mv$(val)); + } + case TokenClass::String: { + auto val = this->recv_bytes(); + return Token(TOK_STRING, mv$(val)); + } + case TokenClass::ByteString: { + auto val = this->recv_bytes(); + return Token(TOK_BYTESTRING, mv$(val)); + } + case TokenClass::CharLit: { + auto val = this->recv_v128u(); + return Token(static_cast<uint64_t>(val), CORETYPE_CHAR); + } + case TokenClass::UnsignedInt: { + ::eCoreType ty; + switch(this->recv_u8()) + { + case 0: ty = CORETYPE_ANY; break; + case 1: ty = CORETYPE_UINT; break; + case 8: ty = CORETYPE_U8; break; + case 16: ty = CORETYPE_U16; break; + case 32: ty = CORETYPE_U32; break; + case 64: ty = CORETYPE_U64; break; + case 128: ty = CORETYPE_U128; break; + default: BUG(this->m_parent_span, "Invalid integer size from child process"); + } + auto val = this->recv_v128u(); + return Token(static_cast<uint64_t>(val), ty); + } + case TokenClass::SignedInt: + case TokenClass::Float: + case TokenClass::Fragment: + TODO(this->m_parent_span, "Handle ints/floats/fragments from child process"); + } + BUG(this->m_parent_span, "Invalid token class from child process"); + + throw ""; +} +Ident::Hygiene ProcMacroInv::realGetHygiene() const { + return Ident::Hygiene(); +} + diff --git a/src/expand/proc_macro.hpp b/src/expand/proc_macro.hpp new file mode 100644 index 00000000..8c5b71c7 --- /dev/null +++ b/src/expand/proc_macro.hpp @@ -0,0 +1,15 @@ +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * expand/proc_macro.hpp + * - Support for the `#[proc_macro_derive]` attribute + */ +#pragma once +#include <parse/tokenstream.hpp> + +extern ::std::unique_ptr<TokenStream> ProcMacro_Invoke(const Span& sp, const ::AST::Crate& crate, const ::std::vector<::std::string>& mac_path, const ::std::string& name, const ::AST::Struct& i); +extern ::std::unique_ptr<TokenStream> ProcMacro_Invoke(const Span& sp, const ::AST::Crate& crate, const ::std::vector<::std::string>& mac_path, const ::std::string& name, const ::AST::Enum& i); +extern ::std::unique_ptr<TokenStream> ProcMacro_Invoke(const Span& sp, const ::AST::Crate& crate, const ::std::vector<::std::string>& mac_path, const ::std::string& name, const ::AST::Union& i); +//extern ::std::unique_ptr<TokenStream> ProcMacro_Invoke(const Span& sp, const ::AST::Crate& crate, const ::std::vector<::std::string>& mac_path, const TokenStream& tt); + diff --git a/src/parse/lex.cpp b/src/parse/lex.cpp index 5e57b418..17d7bf27 100644 --- a/src/parse/lex.cpp +++ b/src/parse/lex.cpp @@ -1066,3 +1066,37 @@ bool Codepoint::isxdigit() const { } return os; } + +Token Lex_FindOperator(const ::std::string& s) +{ + if( s == "_" ) + return TOK_UNDERSCORE; + for(size_t i = 0; i < LEN(TOKENMAP); i++) + { + const auto& e = TOKENMAP[i]; + if( s < e.chars ) + break; + if( s == e.chars ) + { + if( e.type < 0 ) + break ; + return static_cast<eTokenType>(e.type); + } + } + return TOK_NULL; +} +Token Lex_FindReservedWord(const ::std::string& s) +{ + for(size_t i = 0; i < LEN(RWORDS); i++) + { + const auto& e = RWORDS[i]; + if( s < e.chars ) + break; + if( s == e.chars ) + { + assert(e.type > 0); + return static_cast<eTokenType>(e.type); + } + } + return TOK_NULL; +} diff --git a/src/parse/lex.hpp b/src/parse/lex.hpp index 67aa155d..55af4c56 100644 --- a/src/parse/lex.hpp +++ b/src/parse/lex.hpp @@ -27,6 +27,9 @@ struct Codepoint { extern ::std::string& operator+=(::std::string& s, const Codepoint& cp); extern ::std::ostream& operator<<(::std::ostream& s, const Codepoint& cp); +extern Token Lex_FindOperator(const ::std::string& s); +extern Token Lex_FindReservedWord(const ::std::string& s); + typedef Codepoint uchar; class Lexer: diff --git a/src/parse/token.cpp b/src/parse/token.cpp index d626659c..768a96bc 100644 --- a/src/parse/token.cpp +++ b/src/parse/token.cpp @@ -331,7 +331,7 @@ struct EscapedString { case TOK_COMMA: return ","; case TOK_SEMICOLON: return ";"; case TOK_COLON: return ":"; - case TOK_DOUBLE_COLON: return ":"; + case TOK_DOUBLE_COLON: return "::"; case TOK_STAR: return "*"; case TOK_AMP: return "&"; case TOK_PIPE: return "|"; |