xo-expression xo-reader: parser improvements, prep type inf/unify

This commit is contained in:
Roland Conybeare 2025-07-28 09:12:41 -04:00
commit 9172fcdc27
9 changed files with 147 additions and 31 deletions

View file

@ -6,7 +6,8 @@
#pragma once
#include "xo/refcnt/Refcounted.hpp"
#include "xo/reflect/TypeDescr.hpp"
#include "xo/expression/typeinf/type_ref.hpp"
//#include "xo/reflect/TypeDescr.hpp"
#include "exprtype.hpp"
namespace xo {
@ -20,16 +21,25 @@ namespace xo {
**/
class GeneralizedExpression : public ref::Refcount {
public:
using type_ref = xo::scm::type_ref;
using prefix_type = xo::scm::prefix_type;
using TypeDescr = xo::reflect::TypeDescr;
using ppstate = xo::print::ppstate;
using ppindentinfo = xo::print::ppindentinfo;
public:
GeneralizedExpression(exprtype extype, TypeDescr valuetype)
: extype_{extype}, valuetype_{valuetype}{}
/** if @p valuetype is null, generate unique type variable
* using prefix derived from @p extype.
**/
GeneralizedExpression(exprtype extype, TypeDescr valuetype);
/** if @p valuetype is null, generate unique type variable
* name, beginning with @p prefix
**/
GeneralizedExpression(exprtype extype, prefix_type prefix, TypeDescr valuetype);
exprtype extype() const { return extype_; }
TypeDescr valuetype() const { return valuetype_; }
const type_ref & valuetype_ref() const { return valuetype_ref_; }
TypeDescr valuetype() const { return valuetype_ref_.td(); }
/** write human-readable representation to stream @p os **/
virtual void display(std::ostream & os) const = 0;
@ -39,7 +49,7 @@ namespace xo {
virtual std::uint32_t pretty_print(const ppindentinfo & ppii) const = 0;
/** useful when scaffolding expressions in a parser **/
void assign_valuetype(TypeDescr x) { valuetype_ = x; }
void assign_valuetype(TypeDescr x) { valuetype_ref_.resolve_to(x); }
private:
/** expression type (constant | apply | ..) for this expression **/
@ -47,7 +57,7 @@ namespace xo {
/** type information (when available) for values produced by this
* expression.
**/
TypeDescr valuetype_ = nullptr;
type_ref valuetype_ref_;
};
inline std::ostream &

View file

@ -22,6 +22,17 @@ namespace xo {
**/
class Lambda : public FunctionInterface {
public:
/**
* @p name. Name for this lambda -- must be unique
* @p lambda_type. Function signature
* @p local_env. Environment with formals as content
* @p body. Expression for lambda function body
**/
static rp<Lambda> make(const std::string & name,
TypeDescr lambda_type,
const rp<LocalEnv> & local_env,
const rp<Expression> & body);
/**
* @p name Name for this lambda -- must be unique
* @p argv Formal parameters, in left-to-right order
@ -43,6 +54,21 @@ namespace xo {
TypeDescr explicit_return_td,
const rp<Expression> & body);
/** create type description for lambda with arguments @p argv
* and return type @p return_td
**/
static TypeDescr assemble_lambda_td(const std::vector<rp<Variable>> & argv,
TypeDescr return_td);
/** create type description for lambda with arguments @p argv
* and body expression @p body.
* @p explicit_return_td will be used if non-null.
* otherwise use @p body valuetype
**/
static TypeDescr assemble_lambda_td(const std::vector<rp<Variable>> & argv,
TypeDescr explicit_return_td,
const rp<Expression> & body);
/** downcast from Expression **/
static bp<Lambda> from(bp<Expression> x) {
return bp<Lambda>::from(x);
@ -102,13 +128,6 @@ namespace xo {
virtual std::uint32_t pretty_print(const ppindentinfo & ppii) const override;
protected:
/** create type description for lambda with arguments @p argv
* 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,
* consistent with c++ expectation
**/

View file

@ -13,7 +13,8 @@ namespace xo {
/** @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.
* Type inference / unification operates on
* @ref xo::scm::TypeBlueprint instances, see also.
**/
class type_ref {
public:
@ -23,6 +24,12 @@ namespace xo {
type_ref() = default;
type_ref(const type_var& id, TypeDescr td);
/** if type not determined (@p td is nullptr),
* -> generate and store type variable name.
* otherwise type already resolvedn
**/
static type_ref dwim(prefix_type prefix, TypeDescr td);
/** generate a unique type-variable name, that begins with @p prefix **/
static type_var generate_unique(prefix_type prefix);

View file

@ -49,6 +49,9 @@ namespace xo {
**/
unify_result unify(bp<TypeBlueprint> lhs, bp<TypeBlueprint> rhs);
/** lookup type variable by @p name, to get resolution **/
rp<TypeBlueprint> lookup(const type_var & name) const;
private:
type_substitution_map constraint_map_;
};

View file

@ -64,6 +64,7 @@ namespace xo {
DefineExpr::pretty_print(const ppindentinfo & ppii) const
{
return ppii.pps()->pretty_struct(ppii, "Define",
//refrtag("type", this->valuetype()), // need pretty
refrtag("name", lhs_var_->name()),
refrtag("rhs", rhs_));
}

View file

@ -6,6 +6,43 @@
namespace xo {
namespace ast {
namespace {
using xo::scm::prefix_type;
prefix_type exprtype2prefix(exprtype x)
{
switch (x) {
case exprtype::invalid: assert(false); break;
case exprtype::constant: return prefix_type::from_chars("k");
case exprtype::primitive: return prefix_type::from_chars("pm");
case exprtype::define: return prefix_type::from_chars("def");
case exprtype::assign: return prefix_type::from_chars("=");
case exprtype::apply: return prefix_type::from_chars("@");
case exprtype::lambda: return prefix_type::from_chars("lm");
case exprtype::variable: return prefix_type::from_chars("var");
case exprtype::ifexpr: return prefix_type::from_chars("if");
case exprtype::sequence: return prefix_type::from_chars("seq");
case exprtype::convert: return prefix_type::from_chars("cvt");
case exprtype::n_expr: assert(false); break;
}
return prefix_type::from_chars("?expr");
}
}
GeneralizedExpression::GeneralizedExpression(exprtype extype,
TypeDescr valuetype)
: extype_{extype},
valuetype_ref_{type_ref::dwim(exprtype2prefix(extype), valuetype)}
{}
GeneralizedExpression::GeneralizedExpression(exprtype extype,
prefix_type prefix,
TypeDescr valuetype)
: extype_{extype},
valuetype_ref_{type_ref::dwim(prefix, valuetype)}
{}
std::string
GeneralizedExpression::display_string() const {
return tostr(*this);

View file

@ -20,16 +20,9 @@ namespace xo {
namespace ast {
TypeDescr
Lambda::assemble_lambda_td(const std::vector<rp<Variable>> & argv,
TypeDescr explicit_return_td,
const rp<Expression> & body)
TypeDescr return_td)
{
if (!body)
return nullptr;
/** assemble function type.
*
* NOTE: need this to be unique!
**/
assert(return_td != nullptr);
std::vector<TypeDescr> arg_td_v;
{
@ -40,6 +33,25 @@ namespace xo {
}
}
auto function_info
= FunctionTdxInfo(return_td,
arg_td_v,
false /*!is_noexcept*/);
TypeDescr lambda_td
= TypeDescrBase::require_by_fn_info(function_info);
return lambda_td;
}
TypeDescr
Lambda::assemble_lambda_td(const std::vector<rp<Variable>> & argv,
TypeDescr explicit_return_td,
const rp<Expression> & body)
{
if (!body)
return nullptr;
if (explicit_return_td && body->valuetype() && (explicit_return_td != body->valuetype())) {
throw std::runtime_error(tostr("explicit lambda return type T1 conflicts with lambda body T2",
xtag("T1", explicit_return_td),
@ -48,15 +60,9 @@ namespace xo {
// TODO: unify(explicit_return_td, body->valuetype())
auto function_info
= FunctionTdxInfo(explicit_return_td ? explicit_return_td : body->valuetype(),
arg_td_v,
false /*!is_noexcept*/);
TypeDescr return_td = explicit_return_td ? explicit_return_td : body->valuetype();
TypeDescr lambda_td
= TypeDescrBase::require_by_fn_info(function_info);
return lambda_td;
return assemble_lambda_td(argv, return_td);
}
std::string
@ -78,6 +84,15 @@ namespace xo {
return ss.str();
}
rp<Lambda>
Lambda::make(const std::string & name,
TypeDescr lambda_td,
const rp<LocalEnv> & env,
const rp<Expression> & body)
{
return new Lambda(name, lambda_td, env, body);
}
rp<Lambda>
Lambda::make_from_env(const std::string & name,
const rp<LocalEnv> & env,

View file

@ -11,6 +11,18 @@ namespace xo {
bool type_ref::is_concrete() const { return td_ != nullptr; }
type_ref
type_ref::dwim(prefix_type prefix, TypeDescr td)
{
if (td) {
/** type resolved, type variable not needed **/
return type_ref(type_var(), td);
} else {
/** type not resolved, assign a unique type variable **/
return type_ref(generate_unique(prefix), td);
}
}
auto
type_ref::generate_unique(xo::scm::prefix_type prefix) -> xo::scm::type_var
{

View file

@ -212,6 +212,18 @@ namespace xo {
};
}
rp<TypeBlueprint>
type_unifier::lookup(const type_var & name) const
{
auto ix = constraint_map_.find(name);
if (ix != constraint_map_.end()) {
return ix->second;
} else {
return nullptr;
}
}
} /*namespace scm*/
} /*namespace xo*/