xo-expression xo-reader: type unifier + misc improvements

This commit is contained in:
Roland Conybeare 2025-07-26 17:28:41 -04:00
commit 75b74918b7
31 changed files with 1005 additions and 76 deletions

View file

@ -31,7 +31,7 @@ namespace xo {
exprtype extype() const { return extype_; }
TypeDescr valuetype() const { return valuetype_; }
/** write human-readable representation to stream **/
/** write human-readable representation to stream @p os **/
virtual void display(std::ostream & os) const = 0;
/** human-readable string representation **/
virtual std::string display_string() const;

View file

@ -40,6 +40,7 @@ namespace xo {
**/
static rp<Lambda> make_from_env(const std::string & name,
const rp<LocalEnv> & env,
TypeDescr explicit_return_td,
const rp<Expression> & body);
/** downcast from Expression **/
@ -105,6 +106,7 @@ namespace xo {
* and body expression @p body
**/
static TypeDescr assemble_lambda_td(const std::vector<rp<Variable>> & argv,
TypeDescr explicit_return_td,
const rp<Expression> & body);
/** create string description for function signature,

View file

@ -0,0 +1,89 @@
/** @file TypeBlueprint.hpp **/
#include "xo/refcnt/Refcounted.hpp"
#include "type_ref.hpp"
#include <map>
#include <set>
namespace xo {
namespace scm {
class TypeBlueprint;
/** map from a type variable, to contraints on the resolution of that variable **/
using type_substitution_map = std::map<type_var, rp<TypeBlueprint>>;
/** @class TypeBlueprint
* @brief record constraints on a type variable.
*
* Within type unification, a TypeBlueprint represents
* current state of knowledge as to the resolution of a particular type.
*
* Structurally homologous to @ref xo::reflect::TypeDescr,
* but TypeDescr is intended to represent fully-defined types.
* Conversely TypeBlueprint instances will be abandoned once
* a corresponding TypeDescr exists.
**/
class TypeBlueprint : public xo::ref::Refcount {
public:
using TypeDescr = xo::reflect::TypeDescr;
public:
TypeBlueprint() = default;
/** contruct blueprint for type_ref @p ref **/
static rp<TypeBlueprint> make(const type_ref& ref);
/** contruct blueprint for type variable @p name.
* equivalent to @c make(type_ref(name, nullptr))
**/
static rp<TypeBlueprint> typevar(const type_var& name);
/** compare two blueprints for equality.
* blueprints are equal iff (we know that) they refer to the same concrete type.
**/
static bool equals(bp<TypeBlueprint> lhs, bp<TypeBlueprint> rhs);
const type_ref& ref() const { return ref_; }
const type_var& id() const { return ref_.id(); }
TypeDescr td() const { return ref_.td(); }
bool is_concrete() const { return ref_.is_concrete(); }
bool is_variable() const;
/** upsert into @p *p_typevarset all unresolved type variables **/
void upsert_typevars(std::set<type_var> * p_typevar_set) const;
/** apply substitutions in @p sub_map to this type **/
bp<TypeBlueprint> substitute(const type_substitution_map& sub_map);
/** replace with resolved type description.
* Promise:
* 1. ref().td() == @p td
* 2. this->is_concrete() == true
* 3. this->is_variable() == false
**/
void resolve_to(TypeDescr td);
/** write human-readable representation to stream @p os **/
void display(std::ostream & os) const;
private:
/** construct blueprint for @p ref **/
explicit TypeBlueprint(const type_ref & ref);
private:
/** name of the type being constrained here **/
type_ref ref_;
// additional descriptive info..
};
inline std::ostream &
operator<<(std::ostream & os, const TypeBlueprint & x) {
x.display(os);
return os;
}
} /*namespace scm*/
} /*namespace xo*/
/** end TypeBlueprint.hpp **/

View file

@ -2,18 +2,48 @@
#pragma once
#include "xo/flatstring/flatstring.hpp"
#include "xo/reflect/TypeDescr.hpp"
namespace xo {
namespace scm {
using type_var = xo::flatstring<11>;
using prefix_type = xo::flatstring<8>;
using type_var = xo::flatstring<16>;
/** @class type_ref
* @brief name and eventual resolution for type associated with an expression.
*
*
* Type inference / unification operates on @ref xo::scm::TypeTemplate instances, see also.
**/
struct type_ref {
class type_ref {
public:
using TypeDescr = xo::reflect::TypeDescr;
public:
type_ref() = default;
type_ref(const type_var& id, TypeDescr td);
/** generate a unique type-variable name, that begins with @p prefix **/
static type_var generate_unique(prefix_type prefix);
const type_var& id() const { return id_; }
TypeDescr td() const { return td_; }
/** true iff type at this location has been resolved **/
bool is_concrete() const;
void resolve_to(TypeDescr td);
private:
/** unique (likely generated) name for type at this location **/
type_var id_;
/** description for concrete type, once resolved.
* may be null when type_ref created.
* expected to be immutable once established.
* note that TypeDescr itself may be incomplete,
* but not for inference purposes
**/
TypeDescr td_;
};
} /*namespace scm*/
} /*namespace xo*/

View file

@ -0,0 +1,60 @@
/** @file type_unifier.hpp **/
#include "type_ref.hpp"
#include "TypeBlueprint.hpp"
#include <map>
namespace xo {
namespace scm {
struct unify_result {
/** true iff unification success **/
bool success_;
/** blueprint (possibly concrete) for unified type **/
rp<TypeBlueprint> unified_;
/** if @ref success_ is false -> non-null source function
* in which contradiction detected
**/
const char * error_src_function_ = nullptr;
/** if @ref success_ is false -> human-readable error description **/
std::string error_description_;
};
std::ostream & operator<< (std::ostream & os, const unify_result & x);
/** @class type_unifer
* @brief type unification algorithm
**/
class type_unifier {
public:
type_unifier() = default;
/** error message where unification would require both
* 1. equals(s1,t2)
* 2. t2 contains s1
**/
static unify_result occurs_error(const char * src_function,
bp<TypeBlueprint> t1,
bp<TypeBlueprint> t2,
bp<TypeBlueprint> s1,
bp<TypeBlueprint> s2);
/** given fact that @p lhs and @p rhs must refer to
* the same type:
* 1. unify their type blueprints to get new blueprint U(lhs.rhs)
* 2. update @ref constraint_map_ so that typevar ids for
* @p lhs and @p rhs refer to U(lhs,rhs)
* 3. also update @ref constraint_map_ for any secondary unifications
* that are discovered
* 4. return error if unification is contradiction encountered.
**/
unify_result unify(bp<TypeBlueprint> lhs, bp<TypeBlueprint> rhs);
private:
type_substitution_map constraint_map_;
};
} /*namespace scm*/
} /*namespace xo*/
/** end type_unifier.hpp **/