/* Copyright (c) 2013 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Leonardo de Moura */ #pragma once #include #include #include #include #include #include #include #include "util/buffer.h" #include "util/pair.h" #include "util/nat.h" #include "util/string_ref.h" #include "util/list_ref.h" namespace lean { constexpr char const * lean_name_separator = "."; #ifdef _MSC_VER constexpr char16_t id_begin_escape = L'\xab'; constexpr char16_t id_end_escape = L'\xbb'; #else constexpr char16_t id_begin_escape = u'«'; constexpr char16_t id_end_escape = u'»'; #endif bool is_id_first(unsigned char const * begin, unsigned char const * end); inline bool is_id_first(char const * begin, char const * end) { return is_id_first(reinterpret_cast(begin), reinterpret_cast(end)); } bool is_id_rest(unsigned char const * begin, unsigned char const * end); inline bool is_id_rest(char const * begin, char const * end) { return is_id_rest(reinterpret_cast(begin), reinterpret_cast(end)); } enum class name_kind { ANONYMOUS, STRING, NUMERAL }; /** \brief Hierarchical names. */ class name : public object_ref { public: /* Low level primitives */ static bool eq_core(b_obj_arg n1, b_obj_arg n2); static bool eq(b_obj_arg n1, b_obj_arg n2) { if (n1 == n2) return true; if (is_scalar(n1) != is_scalar(n2) || name::hash(n1) != name::hash(n2)) return false; return eq_core(n1, n2); } static name_kind kind(object * o) { return static_cast(obj_tag(o)); } static bool is_anonymous(object * o) { return is_scalar(o); } static object * get_prefix(object * o) { return cnstr_get(o, 0); } static string_ref const & get_string(object * o) { return static_cast(cnstr_get_ref(o, 1)); } static nat const & get_numeral(object * o) { return static_cast(cnstr_get_ref(o, 1)); } static int cmp_core(object * o1, object * o2); size_t size_core(bool unicode) const; private: friend name read_name(deserializer & d); explicit name(object_ref && r):object_ref(r) {} public: name():object_ref(box(static_cast(name_kind::ANONYMOUS))) {} explicit name(obj_arg o):object_ref(o) {} name(b_obj_arg r, bool b):object_ref(r, b) {} name(name const & prefix, char const * name); name(name const & prefix, unsigned k); name(name const & prefix, nat const & n); name(name const & prefix, string_ref const & s); name(char const * n):name(name(), n) {} name(std::string const & s):name(name(), string_ref(s)) {} name(string_ref const & s):name(name(), s) {} name(name const & other):object_ref(other) {} name(name && other):object_ref(other) {} /** \brief Create a hierarchical name using the given strings. Example: name{"foo", "bla", "tst"} creates the hierarchical name foo::bla::tst. */ name(std::initializer_list const & l); static name const & anonymous(); /** \brief Create a unique internal name that is not meant to exposed to the user. Different modules require a unique name. The unique name is created using a numeric prefix. A module that needs to create several unique names should the following idiom: name unique_prefix = name::mk_internal_unique_name(); name unique_name_1(unique_prefix, 1); ... name unique_name_k(unique_prefix, k); */ static name mk_internal_unique_name(); name & operator=(name const & other) { object_ref::operator=(other); return *this; } name & operator=(name && other) { object_ref::operator=(other); return *this; } static usize hash(b_obj_arg n); unsigned hash() const { return hash(raw()); } /** \brief Return true iff \c n1 is a prefix of \c n2. */ friend bool is_prefix_of(name const & n1, name const & n2); friend bool operator==(name const & a, name const & b) { return name::eq(a.raw(), b.raw()); } friend bool operator!=(name const & a, name const & b) { return !(a == b); } friend bool operator==(name const & a, char const * b); friend bool operator!=(name const & a, char const * b) { return !(a == b); } /** \brief Total order on hierarchical names. */ friend int cmp(name const & a, name const & b) { return cmp_core(a.raw(), b.raw()); } friend bool operator<(name const & a, name const & b) { return cmp(a, b) < 0; } friend bool operator>(name const & a, name const & b) { return cmp(a, b) > 0; } friend bool operator<=(name const & a, name const & b) { return cmp(a, b) <= 0; } friend bool operator>=(name const & a, name const & b) { return cmp(a, b) >= 0; } name_kind kind() const { return kind(raw()); } bool is_anonymous() const { return kind() == name_kind::ANONYMOUS; } bool is_string() const { return kind() == name_kind::STRING; } bool is_numeral() const { return kind() == name_kind::NUMERAL; } explicit operator bool() const { return kind() != name_kind::ANONYMOUS; } nat const & get_numeral() const { lean_assert(is_numeral()); return get_numeral(raw()); } string_ref const & get_string() const { lean_assert(is_string()); return get_string(raw()); } name const & get_prefix() const { if (is_anonymous()) return *this; else return static_cast(cnstr_get_ref(*this, 0)); } bool is_atomic() const { return is_anonymous() || kind(get_prefix(raw())) == name_kind::ANONYMOUS; } /** \brief Given a name of the form a_1.a_2. ... .a_k, return a_1 if k >= 1, or the empty name otherwise. */ name get_root() const; /** \brief Convert this hierarchical name into a string. */ std::string to_string(char const * sep = lean_name_separator) const; std::string escape(char const * sep = lean_name_separator) const; /** \brief Size of the this name (in characters). */ size_t size() const; /** \brief Size of the this name in unicode. */ size_t utf8_size() const; /** \brief Return true iff the name contains only safe ASCII chars */ bool is_safe_ascii() const; friend std::ostream & operator<<(std::ostream & out, name const & n); /** \brief Concatenate the two given names. */ friend name operator+(name const & n1, name const & n2); /** \brief Given a name of the form a_1.a_2. ... .a_k, If a_k is a string, return a_1.a_2. ... .a_k', where a_k' is the string p concatenated with a_k. If a_k is a numeral, return a_1.a_2. ... .p.a_k */ name append_before(char const * p) const; /** \brief Given a name of the form a_1.a_2. ... .a_k, If a_k is a string, return a_1.a_2. ... .a_k', where a_k' is the string a_k concatenated with s. If a_k is a numeral, return a_1.a_2. ... .a_k.s */ name append_after(char const * s) const; /** \brief Given a name of the form a_1.a_2. ... .a_k, If a_k is a string, return a_1.a_2. ... .a_k', where a_k' is the string a_k concatenated with _i. Otherwise add _i as the last component. */ name append_after(unsigned i) const; /** \brief Given a name of the form a_1.a_2. ... .a_k, If a_k is a string, return the name itself. Otherwise add the empty string as the last component. */ name get_subscript_base() const; /** \brief Given a name of the form a_1.a_2. ... .a_k, determine whether it was produced by append_after(unsigned). */ optional> is_subscripted() const; /** \brief If prefix is a prefix of this name, then return a new name where the prefix is replaced with new_prefix. Otherwise, return this name. */ name replace_prefix(name const & prefix, name const & new_prefix) const; friend void swap(name & a, name & b) { object_ref::swap(a, b); } /** \brief Quicker version of \c cmp that uses the hashcode. Remark: we should not use it when we want to order names using lexicographical order. */ friend int quick_cmp(name const & a, name const & b) { if (a.raw() == b.raw()) return 0; unsigned h1 = a.hash(); unsigned h2 = b.hash(); if (h1 != h2) { return h1 < h2 ? -1 : 1; } else if (a == b) { return 0; } else { return cmp(a, b); } } void serialize(serializer & s) const { s.write_object(raw()); } }; name string_to_name(std::string const & str); struct name_hash_fn { unsigned operator()(name const & n) const { return n.hash(); } }; struct name_eq_fn { bool operator()(name const & n1, name const & n2) const { return n1 == n2; } }; struct name_cmp { typedef name type; int operator()(name const & n1, name const & n2) const { return cmp(n1, n2); } }; struct name_quick_cmp { typedef name type; int operator()(name const & n1, name const & n2) const { return quick_cmp(n1, n2); } }; /** \brief Return true if \c p is part of \c n */ bool is_part_of(std::string const & p, name n); /** \brief Return true iff the two given names are independent. That \c a is not a prefix of \c b, nor \c b is a prefix of \c a \remark forall a b c d, independent(a, b) => independent(join(a, c), join(b, d)) */ inline bool independent(name const & a, name const & b) { return !is_prefix_of(a, b) && !is_prefix_of(b, a); } typedef pair name_pair; struct name_pair_quick_cmp { int operator()(name_pair const & p1, name_pair const & p2) const { int r = quick_cmp(p1.first, p2.first); if (r != 0) return r; return quick_cmp(p1.second, p2.second); } }; typedef std::function name_predicate; // NOLINT inline serializer & operator<<(serializer & s, name const & n) { n.serialize(s); return s; } inline name read_name(deserializer & d) { return name(d.read_object(), true); } inline deserializer & operator>>(deserializer & d, name & n) { n = read_name(d); return d; } /** \brief Return true if it is a lean internal name, i.e., the name starts with a `_` */ bool is_internal_name(name const & n); typedef list_ref names; inline serializer & operator<<(serializer & s, names const & ns) { ns.serialize(s); return s; } inline names read_names(deserializer & d) { return read_list_ref(d); } void initialize_name(); void finalize_name(); }