2818 lines
88 KiB
C++
2818 lines
88 KiB
C++
/*
|
|
* Copyright 2020 Cerebras Systems. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY CEREBRAS SYSTEMS ''AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CEREBRAS SYSTEMS OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* The views and conclusions contained in the software and documentation
|
|
* are those of the authors and should not be interpreted as
|
|
* representing official policies, either expressed or implied, of
|
|
* Cerebras Systems.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
|
|
#include "template_cpp.h"
|
|
#include "isl_config.h"
|
|
|
|
/* The textual representation of this tuple kind.
|
|
*
|
|
* By default, the textual representation is just the name.
|
|
*/
|
|
std::string TupleKind::to_string() const
|
|
{
|
|
return name;
|
|
}
|
|
|
|
/* Return the parameters of this tuple kind.
|
|
*
|
|
* By default, there are no parameters.
|
|
*/
|
|
std::vector<std::string> TupleKind::params() const
|
|
{
|
|
return { };
|
|
}
|
|
|
|
/* Apply the substitution "subs" to this tuple kind and return the result.
|
|
* "self" is a shared pointer to this.
|
|
*
|
|
* If the name of this tuple kind appears in the substitution,
|
|
* then return the corresponding tuple kind pointer.
|
|
* Otherwise, return "self".
|
|
*/
|
|
TupleKindPtr TupleKind::apply(const Substitution &subs,
|
|
const TupleKindPtr &self) const
|
|
{
|
|
if (subs.count(name) != 0)
|
|
return subs.at(name);
|
|
return self;
|
|
}
|
|
|
|
/* Apply the substitution "subs" to "tuple" and return the result.
|
|
*/
|
|
static TupleKindPtr apply(const TupleKindPtr tuple, const Substitution &subs)
|
|
{
|
|
return tuple->apply(subs, tuple);
|
|
}
|
|
|
|
/* Return the left child of this tuple kind.
|
|
*
|
|
* Since this is not a pair, there is no left child.
|
|
*/
|
|
TupleKindPtr TupleKind::left() const
|
|
{
|
|
return TupleKindPtr();
|
|
}
|
|
|
|
/* Return the right child of this tuple kind.
|
|
*
|
|
* Since this is not a pair, there is no right child.
|
|
*/
|
|
TupleKindPtr TupleKind::right() const
|
|
{
|
|
return TupleKindPtr();
|
|
}
|
|
|
|
/* Helper class used to construct a pointer to a tuple kind
|
|
* that refers to a non-template type.
|
|
*/
|
|
struct Fixed {
|
|
};
|
|
|
|
/* Construct a pointer to a tuple kind that refers to a non-template type.
|
|
*
|
|
* Use an empty string as name. Since this is a non-template type,
|
|
* the kind name will never appear in the generated code.
|
|
*/
|
|
TupleKindPtr::TupleKindPtr(Fixed) : Base(std::make_shared<TupleKind>(""))
|
|
{
|
|
}
|
|
|
|
/* Tuple pointers for non-template types.
|
|
*/
|
|
static TupleKindPtr Ctx{Fixed()};
|
|
static TupleKindPtr Integer{Fixed()};
|
|
static TupleKindPtr Str{Fixed()};
|
|
static TupleKindPtr Res{Fixed()};
|
|
|
|
/* Special tuple pointers.
|
|
* Anonymous appears in the generated code but cannot be unified
|
|
* with anything else since it is a predefined template argument.
|
|
* Leaf can only be unified with something that is not a pair and
|
|
* does not appear in the generated code.
|
|
*/
|
|
static TupleKindPtr Anonymous("Anonymous");
|
|
static TupleKindPtr Leaf("Leaf");
|
|
|
|
/* Placeholder tuple pointers that refer to (part of) the domain or range.
|
|
*/
|
|
static TupleKindPtr Domain("Domain");
|
|
static TupleKindPtr Domain2("Domain2");
|
|
static TupleKindPtr Domain3("Domain3");
|
|
static TupleKindPtr Range("Range");
|
|
static TupleKindPtr Range2("Range2");
|
|
static TupleKindPtr Range3("Range3");
|
|
|
|
/* A representation of a proper tuple kind that is used as a template
|
|
* parameter or a template argument.
|
|
*/
|
|
struct ProperTupleKind : public TupleKind {
|
|
ProperTupleKind(const std::string &name) : TupleKind(name) {}
|
|
|
|
virtual std::vector<std::string> params() const override;
|
|
};
|
|
|
|
/* Return the parameters of this tuple kind.
|
|
*
|
|
* Return the name of this tuple kind, unless it is the special Anonymous
|
|
* predefined template argument.
|
|
*/
|
|
std::vector<std::string> ProperTupleKind::params() const
|
|
{
|
|
if (Anonymous.get() == this)
|
|
return { };
|
|
return { name };
|
|
}
|
|
|
|
/* Construct a pointer to a tuple kind that refers
|
|
* to a proper tuple kind with the given name.
|
|
*/
|
|
TupleKindPtr::TupleKindPtr(const std::string &name) :
|
|
Base(std::make_shared<ProperTupleKind>(name))
|
|
{
|
|
}
|
|
|
|
/* A tuple kind that represents an anonymous pair of nested tuple kinds.
|
|
*/
|
|
struct Pair : public TupleKind {
|
|
Pair(const TupleKindPtr &tuple1, const TupleKindPtr &tuple2) :
|
|
TupleKind(""), tuple1(tuple1), tuple2(tuple2) {}
|
|
|
|
virtual std::string to_string() const override;
|
|
virtual std::vector<std::string> params() const override;
|
|
virtual TupleKindPtr apply(const Substitution &match,
|
|
const TupleKindPtr &self) const override;
|
|
virtual TupleKindPtr left() const override;
|
|
virtual TupleKindPtr right() const override;
|
|
|
|
const TupleKindPtr tuple1;
|
|
const TupleKindPtr tuple2;
|
|
};
|
|
|
|
/* The textual representation of this tuple kind.
|
|
*
|
|
* The textual representation of a pair is of the form "pair<tuple1, tuple2>".
|
|
*/
|
|
std::string Pair::to_string() const
|
|
{
|
|
return std::string("pair<") + tuple1->to_string() + ", " +
|
|
tuple2->to_string() + ">";
|
|
}
|
|
|
|
/* Add the elements of "vec2" that do not already appear in "vec1"
|
|
* at the end of "vec1".
|
|
*
|
|
* The two vectors are assumed not to have any repeated elements.
|
|
* The updated vector will then also not have repeated elements.
|
|
*/
|
|
static void combine(std::vector<std::string> &vec1,
|
|
const std::vector<std::string> &vec2)
|
|
{
|
|
for (const auto &s : vec2)
|
|
if (std::find(vec1.begin(), vec1.end(), s) == vec1.end())
|
|
vec1.emplace_back(s);
|
|
}
|
|
|
|
/* Return the parameters of this tuple kind.
|
|
*
|
|
* Combine the parameters of the two nested tuple kinds.
|
|
*/
|
|
std::vector<std::string> Pair::params() const
|
|
{
|
|
auto names1 = tuple1->params();
|
|
auto names2 = tuple2->params();
|
|
|
|
combine(names1, names2);
|
|
|
|
return names1;
|
|
}
|
|
|
|
/* Apply the substitution "subs" to this tuple kind and return the result.
|
|
* "self" is a shared pointer to this.
|
|
*
|
|
* Construct a new tuple kind consisting of the result of applying
|
|
* the substitution to the two nested tuple kinds.
|
|
*/
|
|
TupleKindPtr Pair::apply(const Substitution &subs, const TupleKindPtr &self)
|
|
const
|
|
{
|
|
return TupleKindPtr(::apply(tuple1, subs), ::apply(tuple2, subs));
|
|
}
|
|
|
|
/* Return the left child of this tuple kind.
|
|
*/
|
|
TupleKindPtr Pair::left() const
|
|
{
|
|
return tuple1;
|
|
}
|
|
|
|
/* Return the right child of this tuple kind.
|
|
*/
|
|
TupleKindPtr Pair::right() const
|
|
{
|
|
return tuple2;
|
|
}
|
|
|
|
/* Construct a pointer to a tuple kind that refers
|
|
* to the given pair of nested tuple kinds.
|
|
*/
|
|
TupleKindPtr::TupleKindPtr(const TupleKindPtr &left, const TupleKindPtr &right)
|
|
: Base(std::make_shared<Pair>(left, right))
|
|
{
|
|
}
|
|
|
|
/* Is this a kind of object representing an anonymous function?
|
|
*/
|
|
bool Kind::is_anon() const
|
|
{
|
|
return size() != 0 && back() == Anonymous;
|
|
}
|
|
|
|
/* Is this a kind of object with a single tuple?
|
|
*/
|
|
bool Kind::is_set() const
|
|
{
|
|
return size() == 1;
|
|
}
|
|
|
|
/* Is this a kind of object with a single, anonymous tuple?
|
|
*/
|
|
bool Kind::is_anon_set() const
|
|
{
|
|
return is_set() && is_anon();
|
|
}
|
|
|
|
/* Return the parameters of this kind.
|
|
*
|
|
* Collect the parameters of the tuple kinds in the sequence.
|
|
*/
|
|
std::vector<std::string> Kind::params() const
|
|
{
|
|
std::vector<std::string> params;
|
|
|
|
for (const auto &tuple : *this)
|
|
combine(params, tuple->params());
|
|
|
|
return params;
|
|
}
|
|
|
|
/* Apply the substitution "subs" to this kind and return the result.
|
|
*
|
|
* Apply the substitution to each of the tuple kinds in the sequence.
|
|
*/
|
|
Kind Kind::apply(const Substitution &subs) const
|
|
{
|
|
Kind applied;
|
|
|
|
for (const auto &tuple : *this)
|
|
applied.emplace_back(::apply(tuple, subs));
|
|
|
|
return applied;
|
|
}
|
|
|
|
/* A signature of a method in terms of kinds,
|
|
* consisting of a return kind and a sequence of argument kinds.
|
|
*/
|
|
struct Signature {
|
|
Kind ret;
|
|
std::vector<Kind> args;
|
|
|
|
std::vector<std::string> params() const;
|
|
Signature apply(const Substitution &match) const;
|
|
};
|
|
|
|
/* Return the parameters of this signature.
|
|
*
|
|
* Collect the parameters of the argument kinds and the return kind.
|
|
*/
|
|
std::vector<std::string> Signature::params() const
|
|
{
|
|
std::vector<std::string> params;
|
|
|
|
for (const auto &arg : args)
|
|
combine(params, arg.params());
|
|
combine(params, ret.params());
|
|
|
|
return params;
|
|
}
|
|
|
|
/* Apply the substitution "subs" to this kind and return the result.
|
|
*
|
|
* Apply the substitution to the argument kinds and the return kind.
|
|
*/
|
|
Signature Signature::apply(const Substitution &subs) const
|
|
{
|
|
std::vector<Kind> applied_args;
|
|
|
|
for (const auto &arg : args)
|
|
applied_args.emplace_back(arg.apply(subs));
|
|
|
|
return { ret.apply(subs), applied_args };
|
|
}
|
|
|
|
/* Return a renaming substitution that renames the elements of "params"
|
|
* using names starting with "prefix".
|
|
*/
|
|
static Substitution param_renamer(const std::vector<std::string> ¶ms,
|
|
const std::string &prefix)
|
|
{
|
|
Substitution renamer;
|
|
int n = 0;
|
|
|
|
for (const auto &name : params) {
|
|
auto suffix = std::to_string(++n);
|
|
auto arg_name = prefix + suffix;
|
|
auto arg = TupleKindPtr(arg_name);
|
|
|
|
if (name == Leaf->name)
|
|
generator::die("Leaf cannot be renamed");
|
|
|
|
renamer.emplace(name, arg);
|
|
}
|
|
|
|
return renamer;
|
|
}
|
|
|
|
/* Does the vector "v" contain the element "el"?
|
|
*/
|
|
static bool contains(const std::vector<std::string> &v, const std::string &el)
|
|
{
|
|
return find(v.begin(), v.end(), el) != v.end();
|
|
}
|
|
|
|
|
|
/* Return the shared elements of "v1" and "v2", preserving the order
|
|
* of those elements in "v1".
|
|
*/
|
|
static std::vector<std::string> intersect(const std::vector<std::string> &v1,
|
|
const std::vector<std::string> &v2)
|
|
{
|
|
std::vector<std::string> intersection;
|
|
|
|
for (const auto &el : v1)
|
|
if (contains(v2, el))
|
|
intersection.push_back(el);
|
|
|
|
return intersection;
|
|
}
|
|
|
|
/* Return a renaming substitution that renames
|
|
* any parameters that appears in both "sig" and "kind".
|
|
*/
|
|
static Substitution shared_param_renamer(const Signature &sig, const Kind &kind)
|
|
{
|
|
return param_renamer(intersect(sig.params(), kind.params()), "Arg");
|
|
}
|
|
|
|
/* Signatures for unary operations.
|
|
* Functions have at least one tuple.
|
|
*/
|
|
static Signature un_params = { { }, { { } } };
|
|
static Signature un_set = { { Domain }, { { Domain } } };
|
|
static Signature un_map = { { Domain, Range }, { { Domain, Range } } };
|
|
static std::vector<Signature> un_op = { un_params, un_set, un_map };
|
|
static std::vector<Signature> fn_un_op = { un_set, un_map };
|
|
|
|
/* Signatures for binary operations, with the second argument
|
|
* possibly referring to part of the first argument.
|
|
* Functions have at least one tuple.
|
|
*/
|
|
static Signature bin_params = { { }, { { }, { } } };
|
|
static Signature bin_set = { { Domain }, { { Domain }, { Domain } } };
|
|
static Signature bin_map =
|
|
{ { Domain, Range }, { { Domain, Range }, { Domain, Range } } };
|
|
static std::vector<Signature> bin_op = { bin_params, bin_set, bin_map };
|
|
static std::vector<Signature> fn_bin_op = { bin_set, bin_map };
|
|
static Signature bin_set_params = { { Domain }, { { Domain }, { } } };
|
|
static Signature bin_map_params =
|
|
{ { Domain, Range }, { { Domain, Range }, { } } };
|
|
static Signature bin_map_domain =
|
|
{ { Domain, Range }, { { Domain, Range }, { Domain } } };
|
|
static Signature bin_map_range =
|
|
{ { Domain, Range }, { { Domain, Range }, { Range } } };
|
|
|
|
/* Signatures for binary operations, where the second argument
|
|
* is an identifier (with an anonymous tuple).
|
|
*/
|
|
static Signature bin_params_anon = { { }, { { }, { Anonymous } } };
|
|
static Signature bin_set_anon = { { Domain }, { { Domain }, { Anonymous } } };
|
|
static Signature bin_map_anon =
|
|
{ { Domain, Range }, { { Domain, Range }, { Anonymous } } };
|
|
static std::vector<Signature> bin_op_anon =
|
|
{ bin_params_anon, bin_set_anon, bin_map_anon };
|
|
|
|
/* Signatures for ternary operations, where the last two arguments are integers.
|
|
*/
|
|
static Signature ter_params_int_int =
|
|
{ { }, { { }, { Integer }, { Integer } } };
|
|
static Signature ter_set_int_int =
|
|
{ { Domain }, { { Domain }, { Integer }, { Integer } } };
|
|
static Signature ter_map_int_int =
|
|
{ { Domain, Range }, { { Domain, Range }, { Integer }, { Integer } } };
|
|
static std::vector<Signature> ter_int_int =
|
|
{ ter_params_int_int, ter_set_int_int, ter_map_int_int };
|
|
|
|
/* Signatures for ternary operations.
|
|
* Functions have at least one tuple.
|
|
*/
|
|
static Signature ter_set =
|
|
{ { Domain }, { { Domain }, { Domain }, { Domain } } };
|
|
static Signature ter_map =
|
|
{ { Domain, Range },
|
|
{ { Domain, Range }, { Domain, Range }, { Domain, Range } } };
|
|
static std::vector<Signature> fn_ter_op = { ter_set, ter_map };
|
|
|
|
/* Signatures for naming a leaf tuple using an identifier (with an anonymous
|
|
* tuple).
|
|
*/
|
|
static Signature update_set = { { Domain2 }, { { Leaf }, { Anonymous } } };
|
|
static Signature update_domain =
|
|
{ { Domain2, Range }, { { Leaf, Range }, { Anonymous } } };
|
|
static Signature update_range =
|
|
{ { Domain, Range2 }, { { Domain, Leaf }, { Anonymous } } };
|
|
|
|
/* Signatures for the functions "min" and "max", which can be either
|
|
* unary or binary operations.
|
|
*/
|
|
static std::vector<Signature> min_max = { un_set, bin_set, un_map, bin_map };
|
|
|
|
/* Signatures for adding an unnamed tuple to an object with zero or one tuple.
|
|
*/
|
|
static Signature to_set = { { Domain }, { { }, { Integer } } };
|
|
static Signature add_range = { { Domain, Range }, { { Domain }, { Integer } } };
|
|
/* Signatures for adding a named tuple to an object with zero or one tuple.
|
|
*/
|
|
static Signature to_set_named =
|
|
{ { Domain }, { { }, { Anonymous }, { Integer } } };
|
|
static Signature add_range_named =
|
|
{ { Domain, Range }, { { Domain }, { Anonymous }, { Integer } } };
|
|
|
|
/* Signatures for methods applying a map to a set, a function or
|
|
* part of a map.
|
|
*/
|
|
static Signature set_forward = { { Range }, { { Domain }, { Domain, Range } } };
|
|
static Signature domain_forward =
|
|
{ { Domain2, Range }, { { Domain, Range }, { Domain, Domain2 } } };
|
|
static Signature range_forward =
|
|
{ { Domain, Range2 }, { { Domain, Range }, { Range, Range2 } } };
|
|
|
|
/* Signatures for methods plugging in a function into a set, a function or
|
|
* part of a map.
|
|
*/
|
|
static Signature set_backward =
|
|
{ { Domain2 }, { { Domain }, { Domain2, Domain } } };
|
|
static Signature domain_backward =
|
|
{ { Domain2, Range }, { { Domain, Range }, { Domain2, Domain } } };
|
|
static Signature range_backward =
|
|
{ { Domain, Range2 }, { { Domain, Range }, { Range2, Range } } };
|
|
static Signature domain_wrapped_domain_backward =
|
|
{ { { Domain3, Domain2 }, Range },
|
|
{ { { Domain, Domain2 }, Range }, { Domain3, Domain } } };
|
|
|
|
/* Signatures for methods binding a set, a function,
|
|
* or (part of) a map to parameters or an object of the same kind.
|
|
*/
|
|
static Signature bind_set = { { }, { { Domain }, { Domain } } };
|
|
static Signature bind_domain = { { Range }, { { Domain, Range }, { Domain } } };
|
|
static Signature bind_range = { { Domain }, { { Domain, Range }, { Range } } };
|
|
static Signature bind_domain_wrapped_domain =
|
|
{ { Range2, Range }, { { { Domain2, Range2 }, Range }, { Domain2 } } };
|
|
|
|
/* Signatures for functions that take a callback accepting
|
|
* objects of the same kind (but a different type).
|
|
*
|
|
* The return and argument kinds of the callback appear
|
|
* at the position of the callback.
|
|
*/
|
|
static Signature each_params = { { Res }, { { }, { Res }, { } } };
|
|
static Signature each_set = { { Res }, { { Domain }, { Res }, { Domain } } };
|
|
static Signature each_map =
|
|
{ { Res }, { { Domain, Range }, { Res }, { Domain, Range } } };
|
|
static std::vector<Signature> each = { each_params, each_set, each_map };
|
|
|
|
/* Signature for creating a map from a range,
|
|
* where the domain is given by an extra argument.
|
|
*/
|
|
static Signature map_from_range_and_domain =
|
|
{ { Domain, Range }, { { Range }, { Domain } } };
|
|
|
|
/* Signature for creating a map from a domain,
|
|
* where the range is given by an extra argument.
|
|
*/
|
|
static Signature map_from_domain_and_range =
|
|
{ { Domain, Range }, { { Domain }, { Range } } };
|
|
|
|
/* Signatures for creating an anonymous set from a parameter set
|
|
* or a map from a domain, where the range is anonymous.
|
|
*/
|
|
static Signature anonymous_set_from_params = { { Anonymous }, { { } } };
|
|
static Signature anonymous_map_from_domain =
|
|
{ { Domain, Anonymous }, { { Domain } } };
|
|
static std::vector<Signature> anonymous_from_domain =
|
|
{ anonymous_set_from_params, anonymous_map_from_domain };
|
|
|
|
/* Signature for creating a set from a parameter set,
|
|
* where the domain is given by an extra argument.
|
|
*/
|
|
static Signature set_from_params = { { Domain }, { { }, { Domain } } };
|
|
|
|
/* Signatures for creating an anonymous function from a domain,
|
|
* where the second argument is an identifier (with an anonymous tuple).
|
|
*/
|
|
static Signature anonymous_set_from_params_bin_anon =
|
|
{ { Anonymous }, { { }, { Anonymous } } };
|
|
static Signature anonymous_map_from_domain_bin_anon =
|
|
{ { Domain, Anonymous }, { { Domain }, { Anonymous } } };
|
|
static std::vector<Signature> anonymous_from_domain_bin_anon = {
|
|
anonymous_set_from_params_bin_anon,
|
|
anonymous_map_from_domain_bin_anon
|
|
};
|
|
|
|
/* Signature for creating a map from a domain,
|
|
* where the range tuple is equal to the domain tuple.
|
|
*/
|
|
static Signature set_to_map = { { Domain, Domain }, { { Domain } } };
|
|
|
|
/* Signatures for obtaining the range or the domain of a map.
|
|
* In case of a transformation, the domain and range are the same.
|
|
*/
|
|
static Signature domain = { { Domain }, { { Domain, Range } } };
|
|
static Signature range = { { Range }, { { Domain, Range } } };
|
|
static Signature transformation_domain = { { Domain }, { { Domain, Domain } } };
|
|
|
|
/* Signatures for obtaining the parameter domain of a set or map.
|
|
*/
|
|
static Signature set_params = { { }, { { Domain } } };
|
|
static Signature map_params = { { }, { { Domain, Range } } };
|
|
|
|
/* Signatures for obtaining the domain of a function.
|
|
*/
|
|
static std::vector<Signature> fn_domain = { domain, set_params };
|
|
|
|
/* Signatures for interchanging (wrapped) domain and range.
|
|
*/
|
|
static Signature map_reverse = { { Range, Domain }, { { Domain, Range } } };
|
|
static Signature map_range_reverse =
|
|
{ { Domain, { Range2, Range } }, { { Domain, { Range, Range2 } } } };
|
|
|
|
/* Signatures for constructing products.
|
|
*/
|
|
static Signature set_product =
|
|
{ { { Domain, Range } }, { { Domain }, { Range } } };
|
|
static Signature map_product =
|
|
{ { { Domain, Domain2 }, { Range, Range2 } },
|
|
{ { Domain, Range }, { Domain2, Range2 } } };
|
|
static Signature domain_product =
|
|
{ { { Domain, Domain2 }, Range },
|
|
{ { Domain, Range }, { Domain2, Range } } };
|
|
static Signature range_product =
|
|
{ { Domain, { Range, Range2 } },
|
|
{ { Domain, Range }, { Domain, Range2 } } };
|
|
|
|
/* Signatures for obtaining factors from a product.
|
|
*/
|
|
static Signature domain_factor_domain =
|
|
{ { Domain, Range }, { { { Domain, Domain2 }, Range } } };
|
|
static Signature domain_factor_range =
|
|
{ { Domain2, Range }, { { { Domain, Domain2 }, Range } } };
|
|
static Signature range_factor_domain =
|
|
{ { Domain, Range }, { { Domain, { Range, Range2 } } } };
|
|
static Signature range_factor_range =
|
|
{ { Domain, Range2 }, { { Domain, { Range, Range2 } } } };
|
|
|
|
/* Signatures for (un)currying.
|
|
*/
|
|
static Signature curry =
|
|
{ { Domain, { Range, Range2 } },
|
|
{ { { Domain, Range }, Range2 } } };
|
|
static Signature uncurry =
|
|
{ { { Domain, Range }, Range2 },
|
|
{ { Domain, { Range, Range2 } } } };
|
|
|
|
/* Signatures for (un)wrapping.
|
|
*/
|
|
static Signature wrap = { { { Domain, Range } }, { { Domain, Range } } };
|
|
static Signature unwrap = { { Domain, Range }, { { { Domain, Range } } } };
|
|
|
|
/* Signatures for constructing objects that map to the domain or range
|
|
* of a map.
|
|
*/
|
|
static Signature domain_map =
|
|
{ { { Domain, Range }, Domain }, { { Domain, Range } } };
|
|
static Signature range_map =
|
|
{ { { Domain, Range }, Range }, { { Domain, Range } } };
|
|
|
|
/* Signature for applying a comparison between the domain and the range
|
|
* of a map.
|
|
*/
|
|
static Signature map_cmp =
|
|
{ { Domain, Domain }, { { Domain, Domain }, { Domain, Range } } };
|
|
|
|
/* Signature for creating a set corresponding to the domains
|
|
* of two functions.
|
|
*/
|
|
static Signature set_join =
|
|
{ { Domain }, { { Domain, Range }, { Domain, Range } } };
|
|
|
|
/* Signatures for flattening the domain or range of a map,
|
|
* replacing it with either an anonymous tuple or a tuple with a given name.
|
|
*/
|
|
static Signature anonymize_nested_domain =
|
|
{ { Anonymous, Range2 }, { { { Domain, Range }, Range2 } } };
|
|
static Signature anonymize_nested_range =
|
|
{ { Domain, Anonymous }, { { Domain, { Range, Range2 } } } };
|
|
static Signature replace_nested_domain =
|
|
{ { Domain2, Range2 },
|
|
{ { { Domain, Range }, Range2 }, { Anonymous} } };
|
|
static Signature replace_nested_range =
|
|
{ { Domain, Range3 }, { { Domain, { Range, Range2 } }, { Anonymous} } };
|
|
static std::vector<Signature> flatten_domain =
|
|
{ anonymize_nested_domain, replace_nested_domain };
|
|
static std::vector<Signature> flatten_range =
|
|
{ anonymize_nested_range, replace_nested_range };
|
|
|
|
/* Signatures for "set_at" methods.
|
|
*/
|
|
static Signature set_at_set =
|
|
{ { Domain }, { { Domain }, { Integer }, { Anonymous } } };
|
|
static Signature set_at_map =
|
|
{ { Domain, Range },
|
|
{ { Domain, Range }, { Integer }, { Domain, Anonymous } } };
|
|
static std::vector<Signature> set_at = { set_at_set, set_at_map };
|
|
|
|
/* Signatures for "list" methods, extracting a list
|
|
* from a multi-expression.
|
|
*/
|
|
static Signature to_list_set = { { Anonymous }, { { Domain } } };
|
|
static Signature to_list_map = { { Domain, Anonymous }, { { Domain, Range } } };
|
|
|
|
/* Signatures for functions constructing an object from only an isl::ctx.
|
|
*/
|
|
static Signature ctx_params = { { }, { { Ctx } } };
|
|
static Signature ctx_set = { { Domain }, { { Ctx } } };
|
|
static Signature ctx_map = { { Domain, Range }, { { Ctx } } };
|
|
|
|
/* Helper structure for sorting the keys of static_methods and
|
|
* special_member_methods such that the larger keys appear first.
|
|
* In particular, a key should appear before any key that appears
|
|
* as a substring in the key.
|
|
* Note that this sorting is currently only important
|
|
* for special_member_methods.
|
|
*/
|
|
struct larger_infix {
|
|
bool operator()(const std::string &x, const std::string &y) const {
|
|
if (x.length() > y. length())
|
|
return true;
|
|
return x < y;
|
|
}
|
|
};
|
|
|
|
/* A map from part of a type name to a sequence of signatures.
|
|
*/
|
|
typedef std::map<std::string, std::vector<Signature>, larger_infix> infix_map;
|
|
|
|
/* A map from a method name to a map from part of a type name
|
|
* to a sequence of signatures.
|
|
*/
|
|
typedef std::map<std::string, infix_map> infix_map_map;
|
|
|
|
/* Signatures for static methods.
|
|
*
|
|
* The "unit" static method is only available in a 0-tuple space.
|
|
*
|
|
* The "empty" static method creates union objects with the relevant
|
|
* number of tuples.
|
|
*
|
|
* The "universe" static methods create objects from the corresponding spaces.
|
|
*/
|
|
static const infix_map_map static_methods {
|
|
{ "unit",
|
|
{ { "space", { ctx_params } } }
|
|
},
|
|
{ "empty",
|
|
{
|
|
{ "union_set", { ctx_params, ctx_set } },
|
|
{ "union_map", { ctx_map } },
|
|
{ "union_pw_multi_aff", { ctx_set, ctx_map } },
|
|
}
|
|
},
|
|
{ "universe",
|
|
{
|
|
{ "set", { un_params, un_set } },
|
|
{ "map", { un_map } },
|
|
}
|
|
},
|
|
};
|
|
|
|
/* Signatures for unary operations that either take something in a set space
|
|
* and return something in the same space or take something in a map space
|
|
* and return something in the range of that space.
|
|
*/
|
|
static std::vector<Signature> range_op = { un_set, range };
|
|
|
|
/* Signatures for binary operations where the second argument
|
|
* is a (multi-)value.
|
|
*/
|
|
static std::vector<Signature> bin_val = { bin_set, bin_map_range };
|
|
|
|
/* The (default) signatures for methods with a given name.
|
|
* Some of these are overridden by special_member_methods.
|
|
*/
|
|
static const std::unordered_map<std::string, std::vector<Signature>>
|
|
member_methods {
|
|
{ "add", bin_op },
|
|
{ "add_constant", bin_val },
|
|
{ "add_named_tuple", { to_set_named, add_range_named } },
|
|
{ "add_param", bin_op_anon },
|
|
{ "add_unnamed_tuple", { to_set, add_range } },
|
|
{ "apply", { set_forward, range_forward } },
|
|
{ "apply_domain", { domain_forward } },
|
|
{ "apply_range", { range_forward } },
|
|
{ "as", un_op },
|
|
{ "as_map", { un_map } },
|
|
{ "as_union_map", { un_map } },
|
|
{ "as_set", { un_set } },
|
|
{ "bind", { bind_set, bind_range } },
|
|
{ "bind_domain", { bind_domain } },
|
|
{ "bind_range", { bind_range } },
|
|
{ "bind_domain_wrapped_domain",
|
|
{ bind_domain_wrapped_domain } },
|
|
{ "ceil", fn_un_op },
|
|
{ "coalesce", un_op },
|
|
{ "cond", fn_ter_op },
|
|
{ "constant_multi_val", range_op },
|
|
{ "curry", { curry } },
|
|
{ "deltas", { transformation_domain } },
|
|
{ "detect_equalities", un_op },
|
|
{ "domain", fn_domain },
|
|
{ "domain_factor_domain",
|
|
{ domain_factor_domain } },
|
|
{ "domain_factor_range",
|
|
{ domain_factor_range } },
|
|
{ "domain_map", { domain_map } },
|
|
{ "domain_product", { domain_product } },
|
|
{ "drop", ter_int_int },
|
|
{ "eq_at", { map_cmp } },
|
|
{ "every", each },
|
|
{ "extract", bin_op },
|
|
{ "flatten_domain", flatten_domain },
|
|
{ "flatten_range", flatten_range },
|
|
{ "floor", fn_un_op },
|
|
{ "foreach", each },
|
|
{ "ge_set", { set_join } },
|
|
{ "gt_set", { set_join } },
|
|
{ "gist", bin_op },
|
|
{ "gist_domain", { bin_map_domain } },
|
|
{ "identity", { un_map, set_to_map } },
|
|
{ "identity_on_domain", { set_to_map } },
|
|
{ "indicator_function", anonymous_from_domain },
|
|
{ "insert_domain", { map_from_range_and_domain } },
|
|
{ "intersect", bin_op },
|
|
{ "intersect_params", { bin_set_params, bin_map_params } },
|
|
{ "intersect_domain", { bin_map_domain } },
|
|
{ "intersect_range", { bin_map_range } },
|
|
{ "le_set", { set_join } },
|
|
{ "lt_set", { set_join } },
|
|
{ "lex_le_at", { map_cmp } },
|
|
{ "lex_lt_at", { map_cmp } },
|
|
{ "lex_ge_at", { map_cmp } },
|
|
{ "lex_gt_at", { map_cmp } },
|
|
{ "lexmin", fn_un_op },
|
|
{ "lexmax", fn_un_op },
|
|
{ "list", { to_list_set, to_list_map } },
|
|
{ "lower_bound", fn_bin_op },
|
|
{ "map_from_set", { set_to_map } },
|
|
{ "max", min_max },
|
|
{ "max_multi_val", range_op },
|
|
{ "min", min_max },
|
|
{ "min_multi_val", range_op },
|
|
{ "mod", bin_val },
|
|
{ "on_domain", { map_from_domain_and_range } },
|
|
{ "neg", fn_un_op },
|
|
{ "offset", fn_un_op },
|
|
{ "param_on_domain", anonymous_from_domain_bin_anon },
|
|
{ "params", { set_params, map_params } },
|
|
{ "plain_multi_val_if_fixed",
|
|
{ un_set } },
|
|
{ "preimage", { set_backward } },
|
|
{ "preimage_domain", { domain_backward } },
|
|
{ "preimage_domain_wrapped_domain",
|
|
{ domain_wrapped_domain_backward } },
|
|
{ "preimage_range", { range_backward } },
|
|
{ "product", { set_product, map_product } },
|
|
{ "project_out_param", bin_op_anon },
|
|
{ "project_out_all_params",
|
|
un_op },
|
|
{ "pullback", { domain_backward, bind_domain } },
|
|
{ "range", { range } },
|
|
{ "range_factor_domain",
|
|
{ range_factor_domain } },
|
|
{ "range_factor_range", { range_factor_range } },
|
|
{ "range_lattice_tile", { un_map } },
|
|
{ "range_map", { range_map } },
|
|
{ "range_product", { range_product } },
|
|
{ "range_reverse", { map_range_reverse } },
|
|
{ "range_simple_fixed_box_hull",
|
|
{ un_map } },
|
|
{ "reverse", { map_reverse } },
|
|
{ "scale", bin_val },
|
|
{ "scale_down", bin_val },
|
|
{ "set_at", set_at },
|
|
{ "set_domain_tuple", { update_domain } },
|
|
{ "set_range_tuple", { update_set, update_range } },
|
|
{ "simple_fixed_box_hull",
|
|
{ un_set } },
|
|
{ "sub", fn_bin_op },
|
|
{ "subtract", bin_op },
|
|
{ "subtract_domain", { bin_map_domain } },
|
|
{ "subtract_range", { bin_map_range } },
|
|
{ "translation", { set_to_map } },
|
|
{ "to", un_op },
|
|
{ "unbind_params", { set_from_params } },
|
|
{ "unbind_params_insert_domain",
|
|
{ map_from_range_and_domain } },
|
|
{ "uncurry", { uncurry } },
|
|
{ "union_add", fn_bin_op },
|
|
{ "unite", bin_op },
|
|
{ "universe", un_op },
|
|
{ "unwrap", { unwrap } },
|
|
{ "upper_bound", fn_bin_op },
|
|
{ "wrap", { wrap } },
|
|
{ "zero", fn_un_op },
|
|
{ "zero_on_domain", { anonymous_map_from_domain } },
|
|
};
|
|
|
|
/* Signatures for methods of types containing a given substring
|
|
* that override the default signatures, where larger substrings
|
|
* appear first.
|
|
*
|
|
* In particular, "gist" is usually a regular binary operation,
|
|
* but for any type derived from "aff", the argument refers
|
|
* to the domain of the function.
|
|
*
|
|
* The "size" method can usually simply be inherited from
|
|
* the corresponding plain C++ type, but for a "fixed_box",
|
|
* the size lives in the space of the box or its range.
|
|
*
|
|
* The "space" method is usually a regular unary operation
|
|
* that returns the single space of the elements in the object,
|
|
* with the same number of tuples.
|
|
* However, a "union" object may contain elements from many spaces and
|
|
* therefore its space only refers to the symbolic constants and
|
|
* has zero tuples, except if it is also a "multi_union" object,
|
|
* in which case it has a fixed range space and the space of the object
|
|
* has a single tuple.
|
|
* Note that since "space' is also the name of a template class,
|
|
* the default space method is handled by print_type_named_member_method.
|
|
*/
|
|
static const infix_map_map special_member_methods {
|
|
{ "gist",
|
|
{ { "aff", { bin_set_params, bin_map_domain } } }
|
|
},
|
|
{ "size",
|
|
{ { "fixed_box", range_op } },
|
|
},
|
|
{ "space",
|
|
{
|
|
{ "multi_union", range_op },
|
|
{ "union", { un_params, set_params, map_params } },
|
|
}
|
|
},
|
|
};
|
|
|
|
/* Generic kinds for objects with zero, one or two tuples,
|
|
* the last of which may be anonymous.
|
|
*/
|
|
static Kind params{};
|
|
static Kind set_type{ Domain };
|
|
static Kind set_anon{ Anonymous };
|
|
static Kind map_type{ Domain, Range };
|
|
static Kind map_anon{ Domain, Anonymous };
|
|
|
|
/* The initial sequence of specialization kinds for base types.
|
|
* The specialization kinds for other types are derived
|
|
* from the corresponding base types.
|
|
*
|
|
* In particular, this sequence specifies how many tuples
|
|
* a given type can have and whether it is anonymous.
|
|
*
|
|
* "space" can have any number of tuples.
|
|
* "set" and "point" can have zero or one tuple.
|
|
* "map" can only have two tuples.
|
|
* "aff" can have one or two tuples, the last of which is anonymous.
|
|
* "fixed_box" can represent a (proper) set) or a map.
|
|
* "val" and "id" are treated as anonymous sets so that
|
|
* they can form the basis of "multi_val" and "multi_id".
|
|
*/
|
|
static const std::unordered_map<std::string, std::vector<Kind>> base_kinds {
|
|
{ "space", { params, set_type, map_type } },
|
|
{ "set", { params, set_type } },
|
|
{ "point", { params, set_type } },
|
|
{ "map", { map_type } },
|
|
{ "aff", { set_anon, map_anon } },
|
|
{ "fixed_box", { set_type, map_type } },
|
|
{ "val", { set_anon } },
|
|
{ "id", { set_anon } },
|
|
};
|
|
|
|
/* Prefixes introduced by type constructors.
|
|
*/
|
|
static const std::unordered_set<std::string> type_prefixes {
|
|
"basic",
|
|
"multi",
|
|
"pw",
|
|
"union",
|
|
};
|
|
|
|
/* If "type" has a "_list" suffix, then return "type" with this suffix removed.
|
|
* Otherwise, simply return "type".
|
|
*/
|
|
static std::string drop_list(const std::string &type)
|
|
{
|
|
size_t pos = type.rfind('_');
|
|
|
|
if (pos == std::string::npos)
|
|
return type;
|
|
if (type.substr(pos + 1) == "list")
|
|
return type.substr(0, pos);
|
|
return type;
|
|
}
|
|
|
|
/* Given the name of a plain C++ type, return the base type
|
|
* from which it was derived using type constructors.
|
|
*
|
|
* In particular, drop any "list" suffix and
|
|
* drop any prefixes from type_prefixes, stopping
|
|
* as soon as a base type is found for which kinds have been registered
|
|
* in base_kinds.
|
|
*/
|
|
static std::string base_type(const std::string &type)
|
|
{
|
|
auto base = type;
|
|
size_t pos;
|
|
|
|
base = drop_list(base);
|
|
while (base_kinds.count(base) == 0 &&
|
|
(pos = base.find('_')) != std::string::npos &&
|
|
type_prefixes.count(base.substr(0, pos)) != 0) {
|
|
base = base.substr(pos + 1);
|
|
}
|
|
|
|
return base;
|
|
}
|
|
|
|
/* A mapping from anonymous kinds to named kinds.
|
|
*/
|
|
static std::map<Kind, Kind> anon_to_named {
|
|
{ set_anon, set_type },
|
|
{ map_anon, map_type },
|
|
};
|
|
|
|
/* Given a sequence of anonymous kinds, replace them
|
|
* by the corresponding named kinds.
|
|
*/
|
|
static std::vector<Kind> add_name(const std::vector<Kind> &tuples)
|
|
{
|
|
std::vector<Kind> named;
|
|
|
|
for (const auto &tuple : tuples)
|
|
named.emplace_back(anon_to_named.at(tuple));
|
|
|
|
return named;
|
|
}
|
|
|
|
/* Add a template class called "name", of which the methods are described
|
|
* by "clazz" and where the corresponding base type has kinds "base_kinds".
|
|
*
|
|
* If this template class is a multi-expression, then it was derived
|
|
* from an anonymous function type. Replace the final Anonymous
|
|
* tuple kind by a placeholder in this case.
|
|
*/
|
|
void template_cpp_generator::add_template_class(const isl_class &clazz,
|
|
const std::string &name, const std::vector<Kind> &base_kinds)
|
|
{
|
|
auto isl_namespace = cpp_type_printer().isl_namespace();
|
|
auto super = isl_namespace + name;
|
|
auto class_tuples = base_kinds;
|
|
|
|
if (name.find("multi_") != std::string::npos)
|
|
class_tuples = add_name(class_tuples);
|
|
template_classes.emplace(name,
|
|
template_class{name, super, clazz, class_tuples});
|
|
}
|
|
|
|
/* Construct a templated C++ bindings generator from
|
|
* the exported types and functions and the set of all declared functions.
|
|
*
|
|
* On top of the initialization of the shared parts
|
|
* of C++ bindings generators, add a template class
|
|
* for each plain C++ class for which template kinds
|
|
* have been defined.
|
|
* In particular, determine the base type from which the plain C++ class
|
|
* was derived using type constructors and check if any template kinds
|
|
* have been registered for this base type.
|
|
*/
|
|
template_cpp_generator::template_cpp_generator(clang::SourceManager &SM,
|
|
std::set<clang::RecordDecl *> &exported_types,
|
|
std::set<clang::FunctionDecl *> exported_functions,
|
|
std::set<clang::FunctionDecl *> functions) :
|
|
cpp_generator(SM, exported_types, exported_functions,
|
|
functions)
|
|
{
|
|
for (const auto &kvp : classes) {
|
|
const auto &clazz = kvp.second;
|
|
std::string name = type2cpp(clazz);
|
|
std::string base = base_type(name);
|
|
|
|
if (base_kinds.count(base) == 0)
|
|
continue;
|
|
add_template_class(clazz, name, base_kinds.at(base));
|
|
}
|
|
}
|
|
|
|
/* Call "fn" on each template class.
|
|
*/
|
|
void template_cpp_generator::foreach_template_class(
|
|
const std::function<void(const template_class &)> &fn) const
|
|
{
|
|
for (const auto &kvp : template_classes)
|
|
fn(kvp.second);
|
|
}
|
|
|
|
/* Print forward declarations for all template classes to "os".
|
|
*
|
|
* For template classes that represent an anonymous function
|
|
* that can also have a domain tuple, provide an <name>_on alias
|
|
* that adds the fixed Anonymous tuple kind.
|
|
*/
|
|
void template_cpp_generator::print_forward_declarations(std::ostream &os)
|
|
{
|
|
foreach_template_class([&os] (const template_class &template_class) {
|
|
auto name = template_class.class_name;
|
|
|
|
os << "\n";
|
|
os << "template <typename...>\n";
|
|
os << "struct " << name << ";\n";
|
|
|
|
if (!template_class.is_anon())
|
|
return;
|
|
if (template_class.is_anon_set())
|
|
return;
|
|
|
|
os << "\n";
|
|
os << "template <typename...Ts>\n";
|
|
os << "using " << name << "_on = "
|
|
<< name << "<Ts..., Anonymous>;\n";
|
|
});
|
|
}
|
|
|
|
/* Print friend declarations for all template classes to "os".
|
|
*/
|
|
void template_cpp_generator::print_friends(std::ostream &os)
|
|
{
|
|
foreach_template_class([&os] (const template_class &template_class) {
|
|
os << " template <typename...>\n";
|
|
os << " friend struct " << template_class.class_name << ";\n";
|
|
});
|
|
}
|
|
|
|
/* Print a template parameter or argument.
|
|
* In case of a std::string, it's a template parameter
|
|
* that needs to be declared.
|
|
*/
|
|
static void print_template_arg(std::ostream &os, const std::string &arg)
|
|
{
|
|
os << "typename " << arg;
|
|
}
|
|
|
|
/* Print a template parameter or argument.
|
|
* In case of a TupleKindPtr, it's a template argument.
|
|
*/
|
|
static void print_template_arg(std::ostream &os, const TupleKindPtr &kind)
|
|
{
|
|
os << kind->to_string();
|
|
}
|
|
|
|
/* Print a sequence of template parameters (std::string) or
|
|
* arguments (TupleKindPtr) "args", without the enclosing angle brackets.
|
|
*/
|
|
template <typename List>
|
|
static void print_pure_template_args(std::ostream &os, const List &args)
|
|
{
|
|
for (size_t i = 0; i < args.size(); ++i) {
|
|
if (i != 0)
|
|
os << ", ";
|
|
print_template_arg(os, args[i]);
|
|
}
|
|
}
|
|
|
|
/* Print a sequence of template parameters (std::string) or
|
|
* arguments (TupleKindPtr) "args".
|
|
*/
|
|
template <typename List>
|
|
static void print_template_args(std::ostream &os, const List &args)
|
|
{
|
|
os << "<";
|
|
print_pure_template_args(os, args);
|
|
os << ">";
|
|
}
|
|
|
|
/* Print a declaration of the template parameters "params".
|
|
*/
|
|
static void print_template(std::ostream &os,
|
|
const std::vector<std::string> ¶ms)
|
|
{
|
|
os << "template ";
|
|
print_template_args(os, params);
|
|
os << "\n";
|
|
}
|
|
|
|
/* Print a declaration of the template parameters "params",
|
|
* if there are any.
|
|
*/
|
|
static void print_non_empty_template(std::ostream &os,
|
|
const std::vector<std::string> ¶ms)
|
|
{
|
|
if (params.size() > 0)
|
|
print_template(os, params);
|
|
}
|
|
|
|
/* Print a bare template type, i.e., without namespace,
|
|
* consisting of the type "type" and the kind "kind" to "os".
|
|
*
|
|
* In particular, print "type" followed by the template arguments
|
|
* as specified by "kind".
|
|
*/
|
|
static void print_bare_template_type(std::ostream &os, const std::string &type,
|
|
const Kind &kind)
|
|
{
|
|
os << type;
|
|
print_template_args(os, kind);
|
|
}
|
|
|
|
/* A specific instance of "template_class", with tuple kinds given by "kind".
|
|
*/
|
|
struct specialization {
|
|
struct template_class &template_class;
|
|
Kind kind;
|
|
|
|
const std::string &base_name() const;
|
|
const std::string &class_name() const;
|
|
};
|
|
|
|
/* The name of the plain C++ interface class
|
|
* from which this template class (instance) derives.
|
|
*/
|
|
const std::string &specialization::base_name() const
|
|
{
|
|
return template_class.super_name;
|
|
}
|
|
|
|
/* The name of the template class.
|
|
*/
|
|
const std::string &specialization::class_name() const
|
|
{
|
|
return template_class.class_name;
|
|
}
|
|
|
|
/* Helper class for printing the specializations of template classes
|
|
* that is used to print both the class declarations and the class definitions.
|
|
*
|
|
* "os" is the stream onto which the classes should be printed.
|
|
* "generator" is the templated C++ interface generator printing the classes.
|
|
*/
|
|
struct specialization_printer {
|
|
specialization_printer(std::ostream &os,
|
|
template_cpp_generator &generator) :
|
|
os(os), generator(generator) {}
|
|
|
|
virtual void print_class(const specialization &instance) const = 0;
|
|
void print_classes() const;
|
|
|
|
std::ostream &os;
|
|
template_cpp_generator &generator;
|
|
};
|
|
|
|
/* Print all specializations of all template classes.
|
|
*
|
|
* Each class has a predefined set of initial specializations,
|
|
* but while such a specialization is being printed,
|
|
* the need for other specializations may arise and
|
|
* these are added at the end of the list of specializations.
|
|
* That is, class_tuples.size() may change during the execution
|
|
* of the loop.
|
|
*
|
|
* For each specialization of a template class, call
|
|
* the print_class virtual method.
|
|
*/
|
|
void specialization_printer::print_classes() const
|
|
{
|
|
for (auto &kvp : generator.template_classes) {
|
|
auto &template_class = kvp.second;
|
|
const auto &class_tuples = template_class.class_tuples;
|
|
|
|
for (size_t i = 0; i < class_tuples.size(); ++i)
|
|
print_class({ template_class, class_tuples[i] });
|
|
}
|
|
}
|
|
|
|
/* A helper class for printing method declarations and definitions
|
|
* of a template class specialization.
|
|
*
|
|
* "instance" is the template class specialization for which methods
|
|
* are printed.
|
|
* "generator" is the templated C++ interface generator printing the classes.
|
|
*/
|
|
struct template_cpp_generator::class_printer :
|
|
public cpp_generator::class_printer {
|
|
class_printer(const specialization &instance,
|
|
const specialization_printer &instance_printer,
|
|
bool is_declaration);
|
|
|
|
void print_return_type(const Method &method, const Kind &kind)
|
|
const;
|
|
void print_method_template_arguments(const Signature &sig);
|
|
void print_method_header(const Method &method, const Signature &sig);
|
|
bool print_special_method(const Method &method,
|
|
const infix_map_map &special_methods);
|
|
void print_static_method(const Method &method);
|
|
void print_constructor(const Method &method);
|
|
bool is_return_kind(const Method &method, const Kind &return_kind);
|
|
void add_specialization(const Kind &kind);
|
|
bool print_matching_method(const Method &method, const Signature &sig,
|
|
const Kind &match_arg);
|
|
bool print_matching_method(const Method &method, const Signature &sig);
|
|
void print_matching_method(const Method &method,
|
|
const std::vector<Signature> &signatures);
|
|
void print_at_method(const Method &method);
|
|
bool print_special_member_method(const Method &method);
|
|
bool print_type_named_member_method(const Method &method);
|
|
bool print_member_method_with_name(const Method &method,
|
|
const std::string &name);
|
|
void print_member_method(const Method &method);
|
|
void print_any_method(const Method &method);
|
|
virtual void print_method(const Method &method) override;
|
|
virtual void print_method(const ConversionMethod &method) override;
|
|
virtual void print_method_sig(const Method &method,
|
|
const Signature &sig, bool deleted) = 0;
|
|
virtual bool want_descendent_overloads(const function_set &methods)
|
|
override;
|
|
void print_all_methods();
|
|
|
|
const specialization &instance;
|
|
template_cpp_generator &generator;
|
|
};
|
|
|
|
/* Construct a class_printer from the template class specialization
|
|
* for which methods are printed and
|
|
* the printer of the template class.
|
|
*
|
|
* The template class printer is only used to obtain the output stream and
|
|
* the templated C++ interface generator printing the classes.
|
|
*/
|
|
template_cpp_generator::class_printer::class_printer(
|
|
const specialization &instance,
|
|
const specialization_printer &instance_printer,
|
|
bool is_declaration) :
|
|
cpp_generator::class_printer(instance_printer.os,
|
|
instance.template_class.clazz, instance_printer.generator,
|
|
is_declaration),
|
|
instance(instance), generator(instance_printer.generator)
|
|
{
|
|
}
|
|
|
|
/* An abstract template type printer, where the way of obtaining
|
|
* the argument kind is specified by the subclasses.
|
|
*/
|
|
struct template_cpp_type_printer : public cpp_type_printer {
|
|
template_cpp_type_printer() {}
|
|
|
|
std::string base(const std::string &type, const Kind &kind) const;
|
|
virtual Kind kind(int arg) const = 0;
|
|
virtual std::string qualified(int arg, const std::string &cpp_type)
|
|
const override;
|
|
};
|
|
|
|
/* Print a template type consisting of the type "type" and the kind "kind",
|
|
* including the "typed::" namespace specifier.
|
|
*/
|
|
std::string template_cpp_type_printer::base(const std::string &type,
|
|
const Kind &kind) const
|
|
{
|
|
std::ostringstream ss;
|
|
|
|
ss << "typed::";
|
|
print_bare_template_type(ss, type, kind);
|
|
return ss.str();
|
|
}
|
|
|
|
/* Return the qualified form of the given C++ isl type name appearing
|
|
* in argument position "arg" (-1 for return type).
|
|
*
|
|
* isl::ctx is not templated, so if "cpp_type" is "ctx",
|
|
* then print a non-templated version.
|
|
* Otherwise, look up the kind of the argument and print
|
|
* the corresponding template type.
|
|
*/
|
|
std::string template_cpp_type_printer::qualified(int arg,
|
|
const std::string &cpp_type) const
|
|
{
|
|
if (cpp_type == "ctx")
|
|
return cpp_type_printer::qualified(arg, cpp_type);
|
|
|
|
return base(cpp_type, kind(arg));
|
|
}
|
|
|
|
/* A template type printer for printing types with a fixed kind.
|
|
*
|
|
* "fixed_kind" is the fixed kind.
|
|
*/
|
|
struct template_cpp_kind_type_printer : public template_cpp_type_printer {
|
|
template_cpp_kind_type_printer(const Kind &kind) :
|
|
template_cpp_type_printer(), fixed_kind(kind) {}
|
|
|
|
virtual Kind kind(int arg) const override;
|
|
|
|
const Kind &fixed_kind;
|
|
};
|
|
|
|
/* Return the kind of the argument at position "arg",
|
|
* where position -1 refers to the return type.
|
|
*
|
|
* Always use the fixed kind.
|
|
*/
|
|
Kind template_cpp_kind_type_printer::kind(int arg) const
|
|
{
|
|
return fixed_kind;
|
|
}
|
|
|
|
/* A template type printer for printing a method with a given signature.
|
|
*
|
|
* "sig" is the signature of the method being printed.
|
|
*/
|
|
struct template_cpp_arg_type_printer : public template_cpp_type_printer {
|
|
template_cpp_arg_type_printer(const Signature &sig) :
|
|
template_cpp_type_printer(), sig(sig) {}
|
|
|
|
virtual Kind kind(int arg) const override;
|
|
|
|
const Signature &sig;
|
|
};
|
|
|
|
/* Return the kind of the argument at position "arg",
|
|
* where position -1 refers to the return type.
|
|
*
|
|
* Look up the kind in the signature.
|
|
*/
|
|
Kind template_cpp_arg_type_printer::kind(int arg) const
|
|
{
|
|
int n_args = sig.args.size();
|
|
|
|
if (arg < 0)
|
|
return sig.ret;
|
|
if (arg >= n_args)
|
|
generator::die("argument out of bounds");
|
|
return sig.args[arg];
|
|
}
|
|
|
|
/* A template type printer for printing a method with a given signature
|
|
* as part of a template class specialization of a given kind.
|
|
*
|
|
* "class_kind" is the template class specialization kind.
|
|
*/
|
|
struct template_method_type_printer : public template_cpp_arg_type_printer {
|
|
template_method_type_printer(const Signature &sig,
|
|
const Kind &class_kind) :
|
|
template_cpp_arg_type_printer(sig),
|
|
class_kind(class_kind) {}
|
|
|
|
virtual std::string class_type(const std::string &cpp_name)
|
|
const override;
|
|
|
|
const Kind &class_kind;
|
|
};
|
|
|
|
/* Print the class type "cpp_name".
|
|
*
|
|
* Print the templated version using the template class specialization kind.
|
|
*/
|
|
std::string template_method_type_printer::class_type(
|
|
const std::string &cpp_name) const
|
|
{
|
|
return base(cpp_name, class_kind);
|
|
}
|
|
|
|
/* Print the templated return type of "method" of the kind "return_kind".
|
|
*
|
|
* Construct a type printer with "return_kind" as fixed kind and
|
|
* use it to print the return type.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_return_type(
|
|
const Method &method, const Kind &return_kind) const
|
|
{
|
|
template_cpp_kind_type_printer printer(return_kind);
|
|
|
|
os << printer.return_type(method);
|
|
}
|
|
|
|
/* Remove the initial "n" elements from "v".
|
|
*/
|
|
template <typename T>
|
|
static void drop_initial(std::vector<T> &v, size_t n)
|
|
{
|
|
v.erase(v.begin(), v.begin() + n);
|
|
}
|
|
|
|
/* If a method with signature "sig" requires additional template parameters
|
|
* compared to those of the class, then print a declaration for them.
|
|
* If this->declarations is set, then this will be part of a method declaration,
|
|
* requiring extra indentation.
|
|
*
|
|
* Construct the sequence of all required template parameters
|
|
* with those of the template class appearing first.
|
|
* If this sequence has any parameters not induced by the template class itself,
|
|
* then print a declaration for these extra parameters.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_method_template_arguments(
|
|
const Signature &sig)
|
|
{
|
|
std::vector<std::string> class_params, method_params;
|
|
|
|
class_params = instance.kind.params();
|
|
method_params = class_params;
|
|
combine(method_params, sig.params());
|
|
|
|
if (class_params.size() == method_params.size())
|
|
return;
|
|
|
|
drop_initial(method_params, class_params.size());
|
|
|
|
if (declarations)
|
|
os << " ";
|
|
print_template(os, method_params);
|
|
}
|
|
|
|
/* Print the header for "method" with signature "sig".
|
|
*
|
|
* First print any additional template parameters that may be required and
|
|
* then print a regular method header, using a template type printer.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_method_header(
|
|
const Method &method, const Signature &sig)
|
|
{
|
|
template_method_type_printer type_printer(sig, instance.kind);
|
|
|
|
print_method_template_arguments(sig);
|
|
cpp_generator::class_printer::print_method_header(method,
|
|
type_printer);
|
|
}
|
|
|
|
/* Given a group of methods with the same name,
|
|
* should extra methods be added that take as arguments
|
|
* those types that can be converted to the original argument type
|
|
* through a unary constructor?
|
|
*
|
|
* Since type deduction does not consider implicit conversions,
|
|
* these extra methods should always be printed.
|
|
*/
|
|
bool template_cpp_generator::class_printer::want_descendent_overloads(
|
|
const function_set &methods)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/* Print all constructors and methods that forward
|
|
* to the corresponding methods in the plain C++ interface class.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_all_methods()
|
|
{
|
|
print_constructors();
|
|
print_methods();
|
|
}
|
|
|
|
/* A helper class for printing method declarations
|
|
* of a template class specialization.
|
|
*/
|
|
struct template_cpp_generator::method_decl_printer :
|
|
public template_cpp_generator::class_printer {
|
|
method_decl_printer(const specialization &instance,
|
|
const struct specialization_printer &instance_printer) :
|
|
class_printer(instance, instance_printer, true) {}
|
|
|
|
virtual void print_method_sig(const Method &method,
|
|
const Signature &sig, bool deleted) override;
|
|
virtual void print_get_method(FunctionDecl *fd) override;
|
|
};
|
|
|
|
/* Print a declaration of the method "method" with signature "sig".
|
|
* Mark is "delete" if "deleted" is set.
|
|
*/
|
|
void template_cpp_generator::method_decl_printer::print_method_sig(
|
|
const Method &method, const Signature &sig, bool deleted)
|
|
{
|
|
print_method_header(method, sig);
|
|
if (deleted)
|
|
os << " = delete";
|
|
os << ";\n";
|
|
}
|
|
|
|
/* Return the total number of arguments in the signature for "method",
|
|
* taking into account a possible callback argument.
|
|
*
|
|
* In particular, if the method has a callback argument,
|
|
* then the return kind of the callback appears at the position
|
|
* of the callback and the kinds of the arguments (except
|
|
* the user pointer argument) appear in the following positions.
|
|
*/
|
|
static int total_params(const Method &method)
|
|
{
|
|
int n = method.num_params();
|
|
|
|
if (method.callback) {
|
|
auto callback_type = method.callback->getType();
|
|
auto callback = generator::extract_prototype(callback_type);
|
|
|
|
n += callback->getNumArgs() - 1;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/* Return a signature for "method" that matches "instance".
|
|
*/
|
|
static Signature instance_sig(const Method &method,
|
|
const specialization &instance)
|
|
{
|
|
std::vector<Kind> args(total_params(method));
|
|
|
|
args[0] = instance.kind;
|
|
return { instance.kind, args };
|
|
}
|
|
|
|
/* Print a declaration for the "get" method "fd",
|
|
* using a name that includes the "get_" prefix.
|
|
*
|
|
* These methods are only included in the plain interface.
|
|
* Explicitly delete them from the templated interface.
|
|
*/
|
|
void template_cpp_generator::method_decl_printer::print_get_method(
|
|
FunctionDecl *fd)
|
|
{
|
|
Method method(clazz, fd, clazz.base_method_name(fd));
|
|
|
|
print_method_sig(method, instance_sig(method, instance), true);
|
|
}
|
|
|
|
/* A helper class for printing method definitions
|
|
* of a template class specialization.
|
|
*/
|
|
struct template_cpp_generator::method_impl_printer :
|
|
public template_cpp_generator::class_printer {
|
|
method_impl_printer(const specialization &instance,
|
|
const struct specialization_printer &instance_printer) :
|
|
class_printer(instance, instance_printer, false) {}
|
|
|
|
void print_callback_method_body(const Method &method,
|
|
const Signature &sig);
|
|
void print_method_body(const Method &method, const Signature &sig);
|
|
void print_constructor_body(const Method &method, const Signature &sig);
|
|
virtual void print_method_sig(const Method &method,
|
|
const Signature &sig, bool deleted) override;
|
|
virtual void print_get_method(FunctionDecl *fd) override;
|
|
};
|
|
|
|
/* Print a definition of the constructor "method" with signature "sig".
|
|
*
|
|
* Simply pass all arguments to the constructor of the corresponding
|
|
* plain type.
|
|
*/
|
|
void template_cpp_generator::method_impl_printer::print_constructor_body(
|
|
const Method &method, const Signature &sig)
|
|
{
|
|
const auto &base_name = instance.base_name();
|
|
|
|
os << " : " << base_name;
|
|
method.print_cpp_arg_list(os, [&] (int i) {
|
|
os << method.fd->getParamDecl(i)->getName().str();
|
|
});
|
|
os << "\n";
|
|
|
|
os << "{\n";
|
|
os << "}\n";
|
|
}
|
|
|
|
/* Print the arguments of the callback function "callback" to "os",
|
|
* calling "print_arg" with the type and the name of the arguments,
|
|
* where the type is obtained from "type_printer" with argument positions
|
|
* shifted by "shift".
|
|
*/
|
|
static void print_callback_args(std::ostream &os,
|
|
const FunctionProtoType *callback, const cpp_type_printer &type_printer,
|
|
int shift,
|
|
const std::function<void(const std::string &type,
|
|
const std::string &name)> &print_arg)
|
|
{
|
|
auto n_arg = callback->getNumArgs() - 1;
|
|
|
|
Method::print_arg_list(os, 0, n_arg, [&] (int i) {
|
|
auto type = callback->getArgType(i);
|
|
auto name = "arg" + std::to_string(i);
|
|
auto cpptype = type_printer.param(shift + i, type);
|
|
|
|
print_arg(cpptype, name);
|
|
});
|
|
}
|
|
|
|
/* Print a lambda for passing to the plain method corresponding to "method"
|
|
* with signature "sig".
|
|
*
|
|
* The method is assumed to have only the callback as argument,
|
|
* which means the arguments of the callback are shifted by 2
|
|
* with respect to the arguments of the signature
|
|
* (one for the position of the callback argument plus
|
|
* one for the return kind of the callback).
|
|
*
|
|
* The lambda takes arguments with plain isl types and
|
|
* calls the callback of "method" with templated arguments.
|
|
*/
|
|
static void print_callback_lambda(std::ostream &os, const Method &method,
|
|
const Signature &sig)
|
|
{
|
|
auto callback_type = method.callback->getType();
|
|
auto callback_name = method.callback->getName().str();
|
|
auto callback = generator::extract_prototype(callback_type);
|
|
|
|
if (method.num_params() != 2)
|
|
generator::die("callback is assumed to be single argument");
|
|
|
|
os << " auto lambda = [&] ";
|
|
print_callback_args(os, callback, cpp_type_printer(), 2,
|
|
[&] (const std::string &type, const std::string &name) {
|
|
os << type << " " << name;
|
|
});
|
|
os << " {\n";
|
|
|
|
os << " return " << callback_name;
|
|
print_callback_args(os, callback, template_cpp_arg_type_printer(sig), 2,
|
|
[&] (const std::string &type, const std::string &name) {
|
|
os << type << "(" << name << ")";
|
|
});
|
|
os << ";\n";
|
|
|
|
os << " };\n";
|
|
}
|
|
|
|
/* Print a definition of the member method "method", which is known
|
|
* to have a callback argument, with signature "sig".
|
|
*
|
|
* First print a lambda for passing to the corresponding plain method and
|
|
* calling the callback of "method" with templated arguments.
|
|
* Then call the plain method, replacing the original callback
|
|
* by the lambda.
|
|
*
|
|
* The return value is assumed to be isl_bool or isl_stat
|
|
* so that no conversion to a template type is required.
|
|
*/
|
|
void template_cpp_generator::method_impl_printer::print_callback_method_body(
|
|
const Method &method, const Signature &sig)
|
|
{
|
|
const auto &base_name = instance.base_name();
|
|
auto return_type = method.fd->getReturnType();
|
|
|
|
if (!is_isl_bool(return_type) && !is_isl_stat(return_type))
|
|
die("only isl_bool and isl_stat return types are supported");
|
|
|
|
os << "{\n";
|
|
|
|
print_callback_lambda(os, method, sig);
|
|
|
|
os << " return ";
|
|
os << base_name << "::" << method.name;
|
|
method.print_cpp_arg_list(os, [&] (int i) {
|
|
auto param = method.fd->getParamDecl(i);
|
|
|
|
if (param == method.callback)
|
|
os << "lambda";
|
|
else
|
|
os << param->getName().str();
|
|
});
|
|
os << ";\n";
|
|
|
|
os << "}\n";
|
|
}
|
|
|
|
/* Print a definition of the member or static method "method"
|
|
* with signature "sig".
|
|
*
|
|
* The body calls the corresponding method of the base class
|
|
* in the plain interface and
|
|
* then casts the result to the templated result type.
|
|
*/
|
|
void template_cpp_generator::method_impl_printer::print_method_body(
|
|
const Method &method, const Signature &sig)
|
|
{
|
|
const auto &base_name = instance.base_name();
|
|
|
|
os << "{\n";
|
|
os << " auto res = ";
|
|
os << base_name << "::" << method.name;
|
|
method.print_cpp_arg_list(os, [&] (int i) {
|
|
os << method.fd->getParamDecl(i)->getName().str();
|
|
});
|
|
os << ";\n";
|
|
|
|
os << " return ";
|
|
print_return_type(method, sig.ret);
|
|
os << "(res);\n";
|
|
os << "}\n";
|
|
}
|
|
|
|
/* Print a definition of the method "method" with signature "sig",
|
|
* if "deleted" is not set.
|
|
*
|
|
* If "deleted" is set, then the corresponding declaration
|
|
* is marked "delete" and no definition needs to be printed.
|
|
*
|
|
* Otherwise print the method header, preceded by the template parameters,
|
|
* if needed.
|
|
* The body depends on whether the method is a constructor or
|
|
* takes a callback.
|
|
*/
|
|
void template_cpp_generator::method_impl_printer::print_method_sig(
|
|
const Method &method, const Signature &sig, bool deleted)
|
|
{
|
|
if (deleted)
|
|
return;
|
|
|
|
os << "\n";
|
|
print_non_empty_template(os, instance.kind.params());
|
|
print_method_header(method, sig);
|
|
os << "\n";
|
|
if (method.kind == Method::Kind::constructor)
|
|
print_constructor_body(method, sig);
|
|
else if (method.callback)
|
|
print_callback_method_body(method, sig);
|
|
else
|
|
print_method_body(method, sig);
|
|
}
|
|
|
|
/* Print a definition for the "get" method "fd" in class "clazz",
|
|
* using a name that includes the "get_" prefix, to "os".
|
|
*
|
|
* The declarations of these methods are explicitly delete'd
|
|
* so no definition needs to be printed.
|
|
*/
|
|
void template_cpp_generator::method_impl_printer::print_get_method(
|
|
FunctionDecl *fd)
|
|
{
|
|
}
|
|
|
|
/* Print a declaration or definition of the static method "method",
|
|
* if it has a signature specified by static_methods.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_static_method(
|
|
const Method &method)
|
|
{
|
|
print_special_method(method, static_methods);
|
|
}
|
|
|
|
/* Signatures for constructors of multi-expressions
|
|
* from a space and a list.
|
|
*/
|
|
static Signature from_list_set = { { Domain }, { { Domain }, { Anonymous } } };
|
|
static Signature from_list_map =
|
|
{ { Domain, Range }, { { Domain, Range }, { Domain, Anonymous } } };
|
|
|
|
/* Signatures for constructors from a string.
|
|
*/
|
|
static Signature params_from_str = { { }, { { Ctx }, { Str } } };
|
|
static Signature set_from_str = { { Domain }, { { Ctx }, { Str } } };
|
|
static Signature map_from_str = { { Domain, Range }, { { Ctx }, { Str } } };
|
|
static std::vector<Signature> from_str =
|
|
{ params_from_str, set_from_str, map_from_str };
|
|
|
|
/* Signature for a constructor from an integer.
|
|
*/
|
|
static Signature int_from_si = { { Anonymous }, { { Ctx }, { Integer } } };
|
|
|
|
/* Signatures for constructors of lists from the initial number
|
|
* of elements.
|
|
*/
|
|
static Signature alloc_params = { { }, { { Ctx }, { Integer } } };
|
|
static Signature alloc_set = { { Domain }, { { Ctx }, { Integer } } };
|
|
static Signature alloc_map = { { Domain, Range }, { { Ctx }, { Integer } } };
|
|
|
|
/* Signatures for constructors and methods named after some other class.
|
|
*
|
|
* Two forms of constructors are handled
|
|
* - conversion from another object
|
|
* - construction of a multi-expression from a space and a list
|
|
*
|
|
* Methods named after some other class also come in two forms
|
|
* - extraction of information such as the space or a list
|
|
* - construction of a multi-expression from a space and a list
|
|
*
|
|
* In both cases, the first form is a unary operation and
|
|
* the second has an extra argument with a kind that is equal
|
|
* to that of the first argument, except that the final tuple is anonymous.
|
|
*/
|
|
static std::vector<Signature> constructor_sig = {
|
|
un_params,
|
|
un_set,
|
|
un_map,
|
|
from_list_set,
|
|
from_list_map,
|
|
};
|
|
|
|
/* Signatures for constructors derived from methods
|
|
* with the given names that override the default signatures.
|
|
*/
|
|
static const std::unordered_map<std::string, std::vector<Signature>>
|
|
special_constructors {
|
|
{ "alloc", { alloc_params, alloc_set, alloc_map } },
|
|
{ "int_from_si", { int_from_si } },
|
|
{ "read_from_str", from_str },
|
|
};
|
|
|
|
/* Print a declaration or definition of the constructor "method".
|
|
*/
|
|
void template_cpp_generator::class_printer::print_constructor(
|
|
const Method &method)
|
|
{
|
|
if (special_constructors.count(method.name) != 0) {
|
|
const auto &sigs = special_constructors.at(method.name);
|
|
return print_matching_method(method, sigs);
|
|
}
|
|
print_matching_method(method, constructor_sig);
|
|
}
|
|
|
|
/* Does this template class represent an anonymous function?
|
|
*
|
|
* If any specialization represents an anonymous function,
|
|
* then every specialization does, so simply check
|
|
* the first specialization.
|
|
*/
|
|
bool template_class::is_anon() const
|
|
{
|
|
return class_tuples[0].is_anon();
|
|
}
|
|
|
|
/* Does this template class represent an anonymous value?
|
|
*
|
|
* That is, is there only a single specialization that moreover
|
|
* has a single, anonymous tuple?
|
|
*/
|
|
bool template_class::is_anon_set() const
|
|
{
|
|
return class_tuples.size() == 1 && class_tuples[0].is_anon_set();
|
|
}
|
|
|
|
/* Update the substitution "sub" to map "general" to "specific"
|
|
* if "specific" is a special case of "general" consistent with "sub",
|
|
* given that "general" is not a pair and can be assigned "specific".
|
|
* Return true if successful.
|
|
* Otherwise, return false.
|
|
*
|
|
* Check whether "general" is already assigned something in "sub".
|
|
* If so, it must be assigned "specific".
|
|
* Otherwise, there is a conflict.
|
|
*/
|
|
static bool update_sub_base(Substitution &sub, const TupleKindPtr &general,
|
|
const TupleKindPtr &specific)
|
|
{
|
|
auto name = general->name;
|
|
|
|
if (sub.count(name) != 0 && sub.at(name) != specific)
|
|
return false;
|
|
sub.emplace(name, specific);
|
|
return true;
|
|
}
|
|
|
|
/* Update the substitution "sub" to map "general" to "specific"
|
|
* if "specific" is a special case of "general" consistent with "sub".
|
|
* Return true if successful.
|
|
* Otherwise, return false.
|
|
*
|
|
* If "general" is a pair and "specific" is not,
|
|
* then "specific" cannot be a special case.
|
|
* If both are pairs, then update the substitution based
|
|
* on both sides.
|
|
* If "general" is Anonymous, then "specific" must be Anonymous as well.
|
|
* If "general" is Leaf, then "specific" cannot be a pair.
|
|
*
|
|
* Otherwise, assign "specific" to "general", if possible.
|
|
*/
|
|
static bool update_sub(Substitution &sub, const TupleKindPtr &general,
|
|
const TupleKindPtr &specific)
|
|
{
|
|
if (general->left() && !specific->left())
|
|
return false;
|
|
if (general->left())
|
|
return update_sub(sub, general->left(), specific->left()) &&
|
|
update_sub(sub, general->right(), specific->right());
|
|
if (general == Anonymous && specific != Anonymous)
|
|
return false;
|
|
if (general == Leaf && specific->left())
|
|
return false;
|
|
|
|
return update_sub_base(sub, general, specific);
|
|
}
|
|
|
|
/* Check if "specific" is a special case of "general" and,
|
|
* if so, return true along with a substitution
|
|
* that maps "general" to "specific".
|
|
* Otherwise return false.
|
|
*
|
|
* This can only happen if the number of tuple kinds is the same.
|
|
* If so, start with an empty substitution and update it
|
|
* for each pair of tuple kinds, checking that each update succeeds.
|
|
*/
|
|
static std::pair<bool, Substitution> specializer(const Kind &general,
|
|
const Kind &specific)
|
|
{
|
|
Substitution specializer;
|
|
|
|
if (general.size() != specific.size())
|
|
return { false, Substitution() };
|
|
|
|
for (size_t i = 0; i < general.size(); ++i) {
|
|
auto general_tuple = general[i];
|
|
|
|
if (!update_sub(specializer, general[i], specific[i]))
|
|
return { false, Substitution() };
|
|
}
|
|
|
|
return { true, specializer };
|
|
}
|
|
|
|
/* Is "kind1" equivalent to "kind2"?
|
|
* That is, is each a special case of the other?
|
|
*/
|
|
static bool equivalent(const Kind &kind1, const Kind &kind2)
|
|
{
|
|
return specializer(kind1, kind2).first &&
|
|
specializer(kind2, kind1).first;
|
|
}
|
|
|
|
/* Add the specialization "kind" to the sequence of specializations,
|
|
* provided there is no equivalent specialization already in there.
|
|
*/
|
|
void template_class::add_specialization(const Kind &kind)
|
|
{
|
|
for (const auto &special : class_tuples)
|
|
if (equivalent(special, kind))
|
|
return;
|
|
class_tuples.emplace_back(kind);
|
|
}
|
|
|
|
/* A type printer that prints the plain interface type,
|
|
* without namespace.
|
|
*/
|
|
struct plain_cpp_type_printer : public cpp_type_printer {
|
|
plain_cpp_type_printer() {}
|
|
|
|
virtual std::string qualified(int arg, const std::string &cpp_type)
|
|
const override;
|
|
};
|
|
|
|
/* Return the qualified form of the given C++ isl type name appearing
|
|
* in argument position "arg" (-1 for return type).
|
|
*
|
|
* For printing the plain type without namespace, no modifications
|
|
* are required.
|
|
*/
|
|
std::string plain_cpp_type_printer::qualified(int arg,
|
|
const std::string &cpp_type) const
|
|
{
|
|
return cpp_type;
|
|
}
|
|
|
|
/* Return a string representation of the plain type "type".
|
|
*
|
|
* For the plain printer, the argument position is irrelevant,
|
|
* so simply pass in -1.
|
|
*/
|
|
static std::string plain_type(QualType type)
|
|
{
|
|
return plain_cpp_type_printer().param(-1, type);
|
|
}
|
|
|
|
/* Return a string representation of the plain return type of "method".
|
|
*/
|
|
static std::string plain_return_type(const Method &method)
|
|
{
|
|
return plain_type(method.fd->getReturnType());
|
|
}
|
|
|
|
/* Return that part of the signature "sig" that should match
|
|
* the template class specialization for the given method.
|
|
*
|
|
* In particular, if the method is a regular member method,
|
|
* then the instance should match the first argument.
|
|
* Otherwise, it should match the return kind.
|
|
*/
|
|
static const Kind &matching_kind(const Method &method, const Signature &sig)
|
|
{
|
|
if (method.kind == Method::Kind::member_method)
|
|
return sig.args[0];
|
|
else
|
|
return sig.ret;
|
|
}
|
|
|
|
/* Is it possible for "template_class" to have the given kind?
|
|
*
|
|
* If the template class represents an anonymous function,
|
|
* then so must the given kind.
|
|
* There should also be specialization with the same number of tuple kinds.
|
|
*/
|
|
static bool has_kind(const template_class &template_class, const Kind &kind)
|
|
{
|
|
if (template_class.is_anon() && !kind.is_anon())
|
|
return false;
|
|
for (const auto &class_tuple : template_class.class_tuples)
|
|
if (class_tuple.size() == kind.size())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/* Is "return_kind" a possible kind for the return type of "method"?
|
|
*
|
|
* If the return type is not a template class,
|
|
* then "return_kind" should not have any template parameters.
|
|
* Otherwise, "return_kind" should be a valid kind for the template class.
|
|
*/
|
|
bool template_cpp_generator::class_printer::is_return_kind(
|
|
const Method &method, const Kind &return_kind)
|
|
{
|
|
const auto &template_classes = generator.template_classes;
|
|
auto return_type = plain_return_type(method);
|
|
|
|
if (template_classes.count(return_type) == 0)
|
|
return return_kind.params().size() == 0;
|
|
return has_kind(template_classes.at(return_type), return_kind);
|
|
}
|
|
|
|
/* Is "kind" a placeholder that can be assigned something else
|
|
* in a substitution?
|
|
*
|
|
* Anonymous can only be mapped to itself. This is taken care of
|
|
* by assign().
|
|
* Leaf can only be assigned a placeholder, but there is no need
|
|
* to handle this specifically since Leaf can still be assigned
|
|
* to the placeholder.
|
|
*/
|
|
static bool assignable(const TupleKindPtr &kind)
|
|
{
|
|
return kind != Anonymous && kind != Leaf;
|
|
}
|
|
|
|
/* Return a substitution that maps "kind1" to "kind2", if possible.
|
|
* Otherwise return an empty substitution.
|
|
*
|
|
* Check if "kind1" can be assigned anything or
|
|
* if "kind1" and "kind2" are identical.
|
|
* The latter case handles mapping Anonymous to itself.
|
|
*/
|
|
static Substitution assign(const TupleKindPtr &kind1, const TupleKindPtr &kind2)
|
|
{
|
|
Substitution res;
|
|
|
|
if (assignable(kind1) || kind1 == kind2)
|
|
res.emplace(kind1->name, kind2);
|
|
return res;
|
|
}
|
|
|
|
/* Return a substitution that first applies "first" and then "second".
|
|
*
|
|
* The result consists of "second" and of "second" applied to "first".
|
|
*/
|
|
static Substitution compose(const Substitution &first,
|
|
const Substitution &second)
|
|
{
|
|
Substitution res = second;
|
|
|
|
for (const auto &kvp : first)
|
|
res.emplace(kvp.first, apply(kvp.second, second));
|
|
|
|
return res;
|
|
}
|
|
|
|
static Substitution compute_unifier(const TupleKindPtr &kind1,
|
|
const TupleKindPtr &kind2);
|
|
|
|
/* Try and extend "unifier" with a unifier for "kind1" and "kind2".
|
|
* Return the resulting unifier if successful.
|
|
* Otherwise, return an empty substitution.
|
|
*
|
|
* First apply "unifier" to "kind1" and "kind2".
|
|
* Then compute a unifier for the resulting tuple kinds and
|
|
* combine it with "unifier".
|
|
*/
|
|
static Substitution combine_unifiers(const TupleKindPtr &kind1,
|
|
const TupleKindPtr &kind2, const Substitution &unifier)
|
|
{
|
|
auto k1 = apply(kind1, unifier);
|
|
auto k2 = apply(kind2, unifier);
|
|
auto u = compute_unifier(k1, k2);
|
|
if (u.size() == 0)
|
|
return Substitution();
|
|
return compose(unifier, u);
|
|
}
|
|
|
|
/* Try and compute a unifier of "kind1" and "kind2",
|
|
* i.e., a substitution that produces the same result when
|
|
* applied to both "kind1" and "kind2",
|
|
* for the case where both "kind1" and "kind2" are pairs.
|
|
* Return this unifier if it was found.
|
|
* Return an empty substitution if no unifier can be found.
|
|
*
|
|
* First compute a unifier for the left parts of the pairs and,
|
|
* if successful, combine it with a unifier for the right parts.
|
|
*/
|
|
static Substitution compute_pair_unifier(const TupleKindPtr &kind1,
|
|
const TupleKindPtr &kind2)
|
|
{
|
|
auto unifier_left = compute_unifier(kind1->left(), kind2->left());
|
|
if (unifier_left.size() == 0)
|
|
return Substitution();
|
|
return combine_unifiers(kind1->right(), kind2->right(), unifier_left);
|
|
}
|
|
|
|
/* Try and compute a unifier of "kind1" and "kind2",
|
|
* i.e., a substitution that produces the same result when
|
|
* applied to both "kind1" and "kind2".
|
|
* Return this unifier if it was found.
|
|
* Return an empty substitution if no unifier can be found.
|
|
*
|
|
* If one of the tuple kinds is a pair then assign it
|
|
* to the other tuple kind, if possible.
|
|
* If neither is a pair, then try and assign one to the other.
|
|
* Otherwise, let compute_pair_unifier compute a unifier.
|
|
*
|
|
* Note that an assignment is added to the unifier even
|
|
* if "kind1" and "kind2" are identical.
|
|
* This ensures that a successful substitution is never empty.
|
|
*/
|
|
static Substitution compute_unifier(const TupleKindPtr &kind1,
|
|
const TupleKindPtr &kind2)
|
|
{
|
|
if (kind1->left() && !kind2->left())
|
|
return assign(kind2, kind1);
|
|
if (!kind1->left() && kind2->left())
|
|
return assign(kind1, kind2);
|
|
if (!kind1->left() && !kind2->left()) {
|
|
if (assignable(kind1))
|
|
return assign(kind1, kind2);
|
|
else
|
|
return assign(kind2, kind1);
|
|
}
|
|
|
|
return compute_pair_unifier(kind1, kind2);
|
|
}
|
|
|
|
/* Try and compute a unifier of "kind1" and "kind2",
|
|
* i.e., a substitution that produces the same result when
|
|
* applied to both "kind1" and "kind2".
|
|
* Return this unifier if it was found.
|
|
* Return an empty substitution if no unifier can be found.
|
|
*
|
|
* Start with an empty substitution and compute a unifier for
|
|
* each pair of tuple kinds, combining the results.
|
|
* If no combined unifier can be found or
|
|
* if the numbers of tuple kinds are different, then return
|
|
* an empty substitution.
|
|
* This assumes that the number of tuples is greater than zero,
|
|
* as otherwise an empty substitution would be returned as well.
|
|
*/
|
|
static Substitution compute_unifier(const Kind &kind1, const Kind &kind2)
|
|
{
|
|
Substitution unifier;
|
|
|
|
if (kind1.size() != kind2.size())
|
|
return Substitution();
|
|
|
|
for (size_t i = 0; i < kind1.size(); ++i)
|
|
unifier = combine_unifiers(kind1[i], kind2[i], unifier);
|
|
|
|
return unifier;
|
|
}
|
|
|
|
/* Try and construct a Kind that is a specialization of both "general" and
|
|
* "specific", where "specific" is known _not_ to be a specialization
|
|
* of "general" and not to contain any Leaf.
|
|
*
|
|
* First check whether "general" is a specialization of "specific".
|
|
* If so, simply return "general".
|
|
* Otherwise, rename the placeholders in the two kinds apart and
|
|
* try and compute a unifier.
|
|
* If this succeeds, then return the result of applying the unifier.
|
|
*/
|
|
static std::pair<bool, Kind> unify(const Kind &general, const Kind &specific)
|
|
{
|
|
if (specializer(specific, general).first) {
|
|
return { true, general };
|
|
} else {
|
|
auto rename = param_renamer(specific.params(), "T");
|
|
auto renamed = specific.apply(rename);
|
|
auto unifier = compute_unifier(general, renamed);
|
|
|
|
if (unifier.size() == 0)
|
|
return { false, { } };
|
|
|
|
return { true, general.apply(unifier) };
|
|
}
|
|
}
|
|
|
|
/* Try and add a template class specialization corresponding to "kind".
|
|
* The new specialization needs to be a specialization of both
|
|
* the current specialization and "kind".
|
|
*
|
|
* The current template class specialization is known not to be a special case
|
|
* of "kind".
|
|
*
|
|
* Try and unify the two kinds and, if this succeeds, add the result
|
|
* to this list of template class specializations.
|
|
*/
|
|
void template_cpp_generator::class_printer::add_specialization(
|
|
const Kind &kind)
|
|
{
|
|
auto maybe_unified = unify(kind, instance.kind);
|
|
|
|
if (!maybe_unified.first)
|
|
return;
|
|
instance.template_class.add_specialization(maybe_unified.second);
|
|
}
|
|
|
|
/* Print a declaration or definition of the method "method"
|
|
* if the template class specialization matches "match_arg".
|
|
* Return true if so.
|
|
* "sig" is the complete signature, of which "match_arg" refers
|
|
* to the first argument or the return type.
|
|
*
|
|
* Since "sig" may have parameters with the same names as
|
|
* those in instance.kind, rename them apart first.
|
|
*
|
|
* If the template class specialization is a special case of
|
|
* (the renamed) "match_arg"
|
|
* then apply the specializer to the complete (renamed) signature,
|
|
* check that the return kind is allowed and, if so,
|
|
* print the declaration or definition using the specialized signature.
|
|
*
|
|
* If the template class specialization is not a special case of "match_arg"
|
|
* then add a further specialization to the list of specializations
|
|
* of the template class.
|
|
*/
|
|
bool template_cpp_generator::class_printer::print_matching_method(
|
|
const Method &method, const Signature &sig, const Kind &match_arg)
|
|
{
|
|
auto rename = shared_param_renamer(sig, instance.kind);
|
|
auto renamed_arg = match_arg.apply(rename);
|
|
auto maybe_specializer = specializer(renamed_arg, instance.kind);
|
|
if (maybe_specializer.first) {
|
|
const auto &specializer = maybe_specializer.second;
|
|
auto specialized_sig = sig.apply(rename).apply(specializer);
|
|
if (!is_return_kind(method, specialized_sig.ret))
|
|
return false;
|
|
|
|
print_method_sig(method, specialized_sig, false);
|
|
} else {
|
|
add_specialization(match_arg);
|
|
}
|
|
return maybe_specializer.first;
|
|
}
|
|
|
|
/* Is the first argument of "method" of type "isl_ctx *"?
|
|
*/
|
|
static bool first_arg_is_ctx(const Method &method)
|
|
{
|
|
return generator::first_arg_is_isl_ctx(method.fd);
|
|
}
|
|
|
|
/* Is the first signature argument set to { Ctx }?
|
|
*/
|
|
static bool first_kind_is_ctx(const Signature &sig)
|
|
{
|
|
return sig.args[0].size() > 0 && sig.args[0][0] == Ctx;
|
|
}
|
|
|
|
/* Print a declaration or definition of the member method "method"
|
|
* if it matches the signature "sig".
|
|
* Return true if so.
|
|
*
|
|
* First determine the part of the signature that needs to match
|
|
* the template class specialization and
|
|
* check that it has the same number of template arguments.
|
|
* Also check that the number of arguments of the signature
|
|
* matches that of the method.
|
|
* If there is at least one argument, then check that the first method argument
|
|
* is an isl_ctx if and only if the first signature argument is Ctx.
|
|
*
|
|
* If these tests succeed, proceed with the actual matching.
|
|
*/
|
|
bool template_cpp_generator::class_printer::print_matching_method(
|
|
const Method &method, const Signature &sig)
|
|
{
|
|
auto match_arg = matching_kind(method, sig);
|
|
int n_args = sig.args.size();
|
|
|
|
if (match_arg.size() != instance.kind.size())
|
|
return false;
|
|
if (n_args != total_params(method))
|
|
return false;
|
|
if (n_args > 0 && first_arg_is_ctx(method) != first_kind_is_ctx(sig))
|
|
return false;
|
|
|
|
return print_matching_method(method, sig, match_arg);
|
|
}
|
|
|
|
/* Print a declaration or definition of the member method "method"
|
|
* for each matching signature in "signatures".
|
|
*
|
|
* If there is no matching signature in "signatures",
|
|
* then explicitly delete the method (using a signature based on
|
|
* the specialization) so that it is not inherited from the base class.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_matching_method(
|
|
const Method &method, const std::vector<Signature> &signatures)
|
|
{
|
|
auto any = false;
|
|
|
|
for (const auto &sig : signatures)
|
|
if (print_matching_method(method, sig))
|
|
any = true;
|
|
|
|
if (!any)
|
|
print_method_sig(method, instance_sig(method, instance), true);
|
|
}
|
|
|
|
/* Signatures for "at" methods applied to a multi-expression,
|
|
* which make the final tuple anonymous.
|
|
*/
|
|
static Signature select_set = { { Anonymous }, { { Domain }, { Integer } } };
|
|
static Signature select_map =
|
|
{ { Domain, Anonymous }, { { Domain, Range }, { Integer } } };
|
|
static std::vector<Signature> at_select = { select_set, select_map };
|
|
|
|
/* Signatures for other "at" methods applied to a list,
|
|
* which do not modify the tuple kind.
|
|
*/
|
|
static Signature bin_set_int = { { Domain }, { { Domain }, { Integer } } };
|
|
static Signature bin_map_int =
|
|
{ { Domain, Range }, { { Domain, Range }, { Integer } } };
|
|
static std::vector<Signature> at_keep = { bin_set_int, bin_map_int };
|
|
|
|
/* Print a declaration or definition of the "at" member method "method".
|
|
*
|
|
* There are two types of methods called "at".
|
|
* One type extracts an element from a multi-expression and
|
|
* the other extracts an element from a list.
|
|
*
|
|
* In the first case, the return type is an anonymous function
|
|
* while the object type is not. In this case, the return kind
|
|
* should have a final Anonymous tuple.
|
|
* Otherwise, the return kind should be the same as the object kind.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_at_method(
|
|
const Method &method)
|
|
{
|
|
auto anon = instance.template_class.is_anon();
|
|
auto return_type = plain_return_type(method);
|
|
auto return_class = generator.template_classes.at(return_type);
|
|
|
|
if (!anon && return_class.is_anon())
|
|
return print_matching_method(method, at_select);
|
|
else
|
|
return print_matching_method(method, at_keep);
|
|
}
|
|
|
|
/* Does the string "s" contain "sub" as a substring?
|
|
*/
|
|
static bool contains(const std::string &s, const std::string &sub)
|
|
{
|
|
return s.find(sub) != std::string::npos;
|
|
}
|
|
|
|
/* Print a declaration or definition of the member method "method",
|
|
* if it has a special signature in "special_methods".
|
|
* Return true if this is the case.
|
|
*
|
|
* Check if any special signatures are specified for this method and
|
|
* if the class name matches any of those with special signatures.
|
|
* If so, pick the one with the best match, i.e., the first match
|
|
* since the largest keys appear first.
|
|
*/
|
|
bool template_cpp_generator::class_printer::print_special_method(
|
|
const Method &method, const infix_map_map &special_methods)
|
|
{
|
|
if (special_methods.count(method.name) == 0)
|
|
return false;
|
|
|
|
for (const auto &kvp : special_methods.at(method.name)) {
|
|
if (!contains(instance.template_class.class_name, kvp.first))
|
|
continue;
|
|
print_matching_method(method, kvp.second);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Print a declaration or definition of the member method "method",
|
|
* if it has a special signature specified by special_member_methods.
|
|
* Return true if this is the case.
|
|
*/
|
|
bool template_cpp_generator::class_printer::print_special_member_method(
|
|
const Method &method)
|
|
{
|
|
return print_special_method(method, special_member_methods);
|
|
}
|
|
|
|
/* Print a declaration or definition of the member method "method",
|
|
* if it is named after a template class. Return true if this is the case.
|
|
*/
|
|
bool template_cpp_generator::class_printer::print_type_named_member_method(
|
|
const Method &method)
|
|
{
|
|
if (generator.template_classes.count(method.name) == 0)
|
|
return false;
|
|
|
|
print_matching_method(method, constructor_sig);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Print a declaration or definition of the member method "method"
|
|
* using a signature associated to method name "name", if there is any.
|
|
* Return true if this is the case.
|
|
*/
|
|
bool template_cpp_generator::class_printer::print_member_method_with_name(
|
|
const Method &method, const std::string &name)
|
|
{
|
|
if (member_methods.count(name) == 0)
|
|
return false;
|
|
|
|
print_matching_method(method, member_methods.at(name));
|
|
return true;
|
|
}
|
|
|
|
/* If "sub" appears inside "str", then remove the first occurrence and
|
|
* return the result. Otherwise, simply return "str".
|
|
*/
|
|
static std::string drop_occurrence(const std::string &str,
|
|
const std::string &sub)
|
|
{
|
|
auto res = str;
|
|
auto pos = str.find(sub);
|
|
|
|
if (pos != std::string::npos)
|
|
res.erase(pos, sub.length());
|
|
|
|
return res;
|
|
}
|
|
|
|
/* If "sub" appears in "str" next to an underscore, then remove the combination.
|
|
* Otherwise, simply return "str".
|
|
*/
|
|
static std::string drop_underscore_occurrence(const std::string &str,
|
|
const std::string &sub)
|
|
{
|
|
auto res = drop_occurrence(str, sub + "_");
|
|
if (res != str)
|
|
return res;
|
|
return drop_occurrence(res, std::string("_") + sub);
|
|
}
|
|
|
|
/* Return the name of "method", with the name of the return type,
|
|
* along with an underscore, removed, if this combination appears in the name.
|
|
* Otherwise, simply return the name.
|
|
*/
|
|
const std::string name_without_return(const Method &method)
|
|
{
|
|
auto return_infix = plain_return_type(method);
|
|
return drop_underscore_occurrence(method.name, return_infix);
|
|
}
|
|
|
|
/* If this method has a callback, then remove the type
|
|
* of the first argument of the callback from the name of the method.
|
|
* Otherwise, simply return the name of the method.
|
|
*/
|
|
const std::string callback_name(const Method &method)
|
|
{
|
|
if (!method.callback)
|
|
return method.name;
|
|
|
|
auto type = method.callback->getType();
|
|
auto callback = cpp_generator::extract_prototype(type);
|
|
auto arg_type = plain_type(callback->getArgType(0));
|
|
return generator::drop_suffix(method.name, "_" + arg_type);
|
|
}
|
|
|
|
/* Print a declaration or definition of the member method "method".
|
|
*
|
|
* If the method is called "at", then it requires special treatment.
|
|
* Otherwise, check if the signature is overridden for this class or
|
|
* if the method is named after some other type.
|
|
* Otherwise look for an appropriate signature using different variations
|
|
* of the method name. First try the method name itself,
|
|
* then the method name with the return type removed and
|
|
* finally the method name with the callback argument type removed.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_member_method(
|
|
const Method &method)
|
|
{
|
|
if (method.name == "at")
|
|
return print_at_method(method);
|
|
if (print_special_member_method(method))
|
|
return;
|
|
if (print_type_named_member_method(method))
|
|
return;
|
|
if (print_member_method_with_name(method, method.name))
|
|
return;
|
|
if (print_member_method_with_name(method, name_without_return(method)))
|
|
return;
|
|
if (print_member_method_with_name(method, callback_name(method)))
|
|
return;
|
|
}
|
|
|
|
/* Print a declaration or definition of "method" based on its type.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_any_method(
|
|
const Method &method)
|
|
{
|
|
switch (method.kind) {
|
|
case Method::Kind::static_method:
|
|
print_static_method(method);
|
|
break;
|
|
case Method::Kind::constructor:
|
|
print_constructor(method);
|
|
break;
|
|
case Method::Kind::member_method:
|
|
print_member_method(method);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Print a declaration or definition of "method".
|
|
*
|
|
* Mark the method as not requiring copies of the arguments.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_method(const Method &method)
|
|
{
|
|
print_any_method(NoCopyMethod(method));
|
|
}
|
|
|
|
/* Print a declaration or definition of "method".
|
|
*
|
|
* Note that a ConversionMethod is already marked
|
|
* as not requiring copies of the arguments.
|
|
*/
|
|
void template_cpp_generator::class_printer::print_method(
|
|
const ConversionMethod &method)
|
|
{
|
|
print_any_method(method);
|
|
}
|
|
|
|
/* Helper class for printing the declarations for
|
|
* template class specializations.
|
|
*/
|
|
struct template_cpp_generator::class_decl_printer :
|
|
public specialization_printer
|
|
{
|
|
class_decl_printer(std::ostream &os,
|
|
template_cpp_generator &generator) :
|
|
specialization_printer(os, generator) {}
|
|
|
|
void print_arg_subclass_constructor(const specialization &instance,
|
|
const std::vector<std::string> ¶ms) const;
|
|
void print_super_constructor(const specialization &instance) const;
|
|
virtual void print_class(const specialization &instance) const override;
|
|
};
|
|
|
|
/* Print the declaration and definition of a constructor
|
|
* for the template class specialization "instance" taking
|
|
* an instance with more specialized template arguments,
|
|
* where "params" holds the template parameters of "instance".
|
|
* It is assumed that there is at least one template parameter as otherwise
|
|
* there are no template arguments to be specialized and
|
|
* no constructor needs to be printed.
|
|
*
|
|
* In particular, the constructor takes an object of the same instance where
|
|
* for each template parameter, the corresponding template argument
|
|
* of the input object is a subclass of the template argument
|
|
* of the constructed object.
|
|
*
|
|
* Pick fresh names for all template parameters and
|
|
* add a constructor with these fresh names as extra template parameters and
|
|
* a constraint requiring that each of them is a subclass
|
|
* of the corresponding class template parameter.
|
|
* The plain C++ interface object of the constructed object is initialized with
|
|
* the plain C++ interface object of the constructor argument.
|
|
*/
|
|
void template_cpp_generator::class_decl_printer::print_arg_subclass_constructor(
|
|
const specialization &instance,
|
|
const std::vector<std::string> ¶ms) const
|
|
{
|
|
const auto &class_name = instance.class_name();
|
|
auto rename = param_renamer(params, "Arg");
|
|
auto derived = instance.kind.apply(rename);
|
|
|
|
os << " template ";
|
|
os << "<";
|
|
print_pure_template_args(os, derived.params());
|
|
os << ",\n";
|
|
os << " typename std::enable_if<\n";
|
|
for (size_t i = 0; i < params.size(); ++i) {
|
|
if (i != 0)
|
|
os << " &&\n";
|
|
os << " std::is_base_of<"
|
|
<< params[i] << ", "
|
|
<< rename.at(params[i])->params()[0] << ">{}";
|
|
}
|
|
os << ",\n";
|
|
os << " bool>::type = true>";
|
|
os << "\n";
|
|
os << " " << class_name << "(const ";
|
|
print_bare_template_type(os, class_name, derived);
|
|
os << " &obj) : " << instance.base_name() << "(obj) {}\n";
|
|
}
|
|
|
|
/* Print the declaration and definition of a constructor
|
|
* for the template class specialization "instance" taking
|
|
* an instance of the base class.
|
|
*
|
|
* If the instance kind is that of an anonymous set
|
|
* (i.e., it has a single tuple that is set to Anonymous),
|
|
* then allow the constructor to be called externally.
|
|
* This is mostly useful for being able to use isl::val and
|
|
* isl::typed::val<Anonymous> interchangeably and similarly for isl::id.
|
|
*
|
|
* If the instance is of any other kind, then make this constructor private
|
|
* to avoid objects of the plain interface being converted automatically.
|
|
* Also make sure that it does not apply to any type derived
|
|
* from the base class. In particular, this makes sure it does
|
|
* not apply to any other specializations of this template class as
|
|
* otherwise any conflict in specializations would simply point
|
|
* to the private constructor.
|
|
*
|
|
* A factory method is added to be able to perform the conversion explicitly,
|
|
* with an explicit specification of the template arguments.
|
|
*/
|
|
void template_cpp_generator::class_decl_printer::print_super_constructor(
|
|
const specialization &instance) const
|
|
{
|
|
bool hide = !instance.kind.is_anon_set();
|
|
const auto &base_name = instance.base_name();
|
|
const auto &arg_name = hide ? "base" : base_name;
|
|
|
|
if (hide) {
|
|
os << " private:\n";
|
|
os << " template <typename base,\n";
|
|
os << " typename std::enable_if<\n";
|
|
os << " std::is_same<base, " << base_name
|
|
<< ">{}, bool>::type = true>\n";
|
|
}
|
|
os << " " << instance.class_name()
|
|
<< "(const " << arg_name << " &obj) : "
|
|
<< base_name << "(obj) {}\n";
|
|
if (hide)
|
|
os << " public:\n";
|
|
os << " static " << instance.class_name() << " from"
|
|
<< "(const " << base_name << " &obj) {\n";
|
|
os << " return " << instance.class_name() << "(obj);\n";
|
|
os << " }\n";
|
|
}
|
|
|
|
/* Print a "declaration" for the given template class specialization.
|
|
* In particular, print the class definition and the method declarations.
|
|
*
|
|
* The template parameters are the distinct variable names
|
|
* in the instance kind.
|
|
*
|
|
* Each instance of the template class derives from the corresponding
|
|
* plain C++ interface class.
|
|
*
|
|
* All (other) template classes are made friends of this template class
|
|
* to allow them to call the private constructor taking an object
|
|
* of the plain interface.
|
|
*
|
|
* Besides the constructors and methods that forward
|
|
* to the corresponding methods in the plain C++ interface class,
|
|
* some extra constructors are defined.
|
|
* The default zero-argument constructor is useful for declaring
|
|
* a variable that only gets assigned a value at a later stage.
|
|
* The constructor taking an instance with more specialized
|
|
* template arguments is useful for lifting the class hierarchy
|
|
* of the template arguments to the template class.
|
|
* The constructor taking an instance of the base class
|
|
* is useful for (explicitly) constructing a template type
|
|
* from a plain type.
|
|
*/
|
|
void template_cpp_generator::class_decl_printer::print_class(
|
|
const specialization &instance) const
|
|
{
|
|
const auto &class_name = instance.class_name();
|
|
auto params = instance.kind.params();
|
|
|
|
os << "\n";
|
|
|
|
print_template(os, params);
|
|
|
|
os << "struct ";
|
|
print_bare_template_type(os, class_name, instance.kind);
|
|
os << " : public " << instance.base_name() << " {\n";
|
|
|
|
generator.print_friends(os);
|
|
os << "\n";
|
|
|
|
os << " " << class_name << "() = default;\n";
|
|
if (params.size() != 0)
|
|
print_arg_subclass_constructor(instance, params);
|
|
print_super_constructor(instance);
|
|
method_decl_printer(instance, *this).print_all_methods();
|
|
|
|
os << "};\n";
|
|
}
|
|
|
|
/* Helper class for printing the definitions of template class specializations.
|
|
*/
|
|
struct template_cpp_generator::class_impl_printer :
|
|
public specialization_printer
|
|
{
|
|
class_impl_printer(std::ostream &os,
|
|
template_cpp_generator &generator) :
|
|
specialization_printer(os, generator) {}
|
|
|
|
virtual void print_class(const specialization &instance) const override;
|
|
};
|
|
|
|
/* Print a definition for the given template class specialization.
|
|
*
|
|
* In particular, print definitions
|
|
* for the constructors and methods that forward
|
|
* to the corresponding methods in the plain C++ interface class.
|
|
* The extra constructors declared in the class definition
|
|
* are defined inline.
|
|
*/
|
|
void template_cpp_generator::class_impl_printer::print_class(
|
|
const specialization &instance) const
|
|
{
|
|
method_impl_printer(instance, *this).print_all_methods();
|
|
}
|
|
|
|
/* Generate a templated cpp interface
|
|
* based on the extracted types and functions.
|
|
*
|
|
* First print forward declarations for all template classes,
|
|
* then the declarations of the classes, and at the end all
|
|
* method implementations.
|
|
*/
|
|
void template_cpp_generator::generate()
|
|
{
|
|
ostream &os = std::cout;
|
|
|
|
os << "\n";
|
|
|
|
print_forward_declarations(os);
|
|
class_decl_printer(os, *this).print_classes();
|
|
class_impl_printer(os, *this).print_classes();
|
|
}
|