MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
axm.h
Go to the documentation of this file.
1#pragma once
2
3#include <fe/assert.h>
4
5#include "mim/plugin.h"
6
7namespace mim {
8
9class Axm : public Def, public Setters<Axm> {
10private:
11 Axm(NormalizeFn, u8 curry, u8 trip, const Def* type, plugin_t, tag_t, sub_t);
12
13public:
14 using Setters<Axm>::set;
15
16 /// @name Normalization
17 /// @anchor normalization
18 /// For a curried App of an Axm, you only want to trigger normalization at specific spots.
19 /// For this reason, MimIR maintains a Def::curry_ counter that each App decrements.
20 /// The Axm::normalizer() will be triggered when Axm::curry() becomes `0`.
21 /// These are also the spots that you can mim::test / mim::force / mim::IsA.
22 /// After that, the counter will be set to Axm::trip().
23 /// E.g., let's say an Axm has this type:
24 /// ```
25 /// A -> B -> C -> D -> E
26 /// ^ |
27 /// | |
28 /// +---------+
29 /// ```
30 /// Using an initial value as `5` for Axm::curry and `3` as Axm::trip has the effect that here
31 /// ```
32 /// x a b c1 d1 e1 c2 d2 e2 c3 d3 e3
33 /// ```
34 /// the Axm::normalizer will be triggered after App'ing `e1`, `e2`, and `e3`.
35 ///@{
36 NormalizeFn normalizer() const { return normalizer_; }
37 u8 curry() const { return curry_; }
38 u8 trip() const { return trip_; }
39 ///@}
40
41 /// @name Normalization - Helpers
42 ///@{
43
44 /// Yields currying counter of @p def.
45 /// @returns `{nullptr, 0, 0}` if no Axm is present.
46 static std::tuple<const Axm*, u8, u8> get(const Def* def);
47
48 /// Like Axm::get, but advances the counter as one more App is about to be built on top of @p callee.
49 /// @returns `{nullptr, 0, 0}` if no Axm is present.
50 static std::tuple<const Axm*, u8, u8> next(const Def* callee);
51
52 static std::pair<u8, u8> infer_curry_and_trip(const Def* type);
53 ///@}
54
55 /// @name Annex Name
56 /// @anchor anatomy
57 /// @see annex_name "Annex Name"
58 ///@{
60 tag_t tag() const { return Annex::flags2tag(flags()); }
61 sub_t sub() const { return Annex::flags2sub(flags()); }
62 flags_t base() const { return Annex::flags2base(flags()); }
63 ///@}
64
65 /// @name IsA
66 ///@{
67 /// Type of IsA::def_.
68 template<class T>
69 struct IsANode {
70 using type = App;
71 };
72
73 template<class Id, class D>
74 class IsA {
75 static_assert(Annex::num<Id>() != size_t(-1), "invalid number of sub tags");
76 static_assert(Annex::base<Id>() != flags_t(-1), "invalid axm base");
77
78 public:
79 IsA() = default;
80 IsA(const Axm* axm, const D* def)
81 : axm_(axm)
82 , def_(def) {}
83
84 /// @name Getters
85 ///@{
86 const Axm* axm() const { return axm_; }
87 const D* operator->() const { return def_; }
88 operator const D*() const { return def_; }
89 explicit operator bool() const { return axm_ != nullptr; }
90 ///@}
91
92 /// @name Axm Name
93 /// @see annex_name "Annex Name"
94 ///@{
95 auto plugin() const { return axm()->plugin(); } ///< @see Axm::plugin.
96 auto tag() const { return axm()->tag(); } ///< @see Axm::tag.
97 auto sub() const { return axm()->sub(); } ///< @see Axm::sub.
98 auto base() const { return axm()->base(); } ///< @see Axm::base.
99 auto id() const { return Id(axm()->flags()); } ///< Axm::flags cast to @p Id.
100 ///@}
101
102 private:
103 const Axm* axm_ = nullptr;
104 const D* def_ = nullptr;
105 };
106 ///@}
107
108 /// @name isa/as
109 ///@{
110 /// @see @ref cast_axm
111 template<class Id, u8 Curry = 0, bool DynCast = true>
112 static auto isa(const Def* def) {
113 using D = std::conditional_t<Curry == 0, typename Axm::IsANode<Id>::type, Def>;
114 auto [axm, curry, _] = Axm::get(def);
115 bool cond = axm && curry == Curry && axm->base() == Annex::base<Id>();
116
117 if constexpr (DynCast) return cond ? IsA<Id, D>(axm, def->as<D>()) : IsA<Id, D>();
118 assert(cond && "assumed to be correct axm");
119 return IsA<Id, D>(axm, def->as<D>());
120 }
121
122 template<class Id, u8 Curry = 0, bool DynCast = true>
123 static auto isa(Id id, const Def* def) {
124 using D = std::conditional_t<Curry == 0, typename Axm::IsANode<Id>::type, Def>;
125 auto [axm, curry, _] = Axm::get(def);
126 bool cond = axm && curry == Curry && axm->flags() == (flags_t)id;
127
128 if constexpr (DynCast) return cond ? IsA<Id, D>(axm, def->as<D>()) : IsA<Id, D>();
129 assert(cond && "assumed to be correct axm");
130 return IsA<Id, D>(axm, def->as<D>());
131 }
132
133 // clang-format off
134 template<class Id, u8 Curry=0> static auto as( const Def* def) { return isa<Id, Curry, false>( def); }
135 template<class Id, u8 Curry=0> static auto as(Id id, const Def* def) { return isa<Id, Curry, false>(id, def); }
136 // clang-format on
137
138 /// Like Axm::as but - instead of merely asserting in `Debug` builds - throws a formatted mim::error when @p def
139 /// is not the expected axm.
140 /// @p fmt / @p args describe what was expected; a plain string works, as does a format string plus arguments.
141 template<class Id, u8 Curry = 0, class... Args>
142 static auto expect(const Def* def, std::format_string<Args...> fmt, Args&&... args) {
143 if (auto res = isa<Id, Curry>(def)) return res;
144 fe::throwf("expected {}, but got `{}`", std::format(fmt, std::forward<Args>(args)...), def);
145 }
146 ///@}
147
148 static constexpr u8 Trip_End = u8(-1);
149 static constexpr auto Node = mim::Node::Axm;
150 static constexpr size_t Num_Ops = 0;
151
152private:
153 friend class World;
154};
155
156// clang-format off
157template<class Id> concept annex_with_subs = Annex::num<Id>() != 0;
158template<class Id> concept annex_without_subs = Annex::num<Id>() == 0;
159// clang-format on
160
161/// @name is_commutative/is_associative
162///@{
163template<class Id>
164constexpr bool is_commutative(Id) {
165 return false;
166}
167/// @warning By default we assume that any commutative operation is also associative.
168/// Please provide a proper specialization if this is not the case.
169template<class Id>
170constexpr bool is_associative(Id id) {
171 return is_commutative(id);
172}
173///@}
174
175} // namespace mim
176
177#ifndef DOXYGEN
178template<class Id, class D>
179struct std::formatter<mim::Axm::IsA<Id, D>> : fe::ostream_formatter {};
180#endif
auto base() const
Definition axm.h:98
auto sub() const
Definition axm.h:97
auto id() const
Axm::flags cast to Id.
Definition axm.h:99
const D * operator->() const
Definition axm.h:87
const Axm * axm() const
Definition axm.h:86
IsA(const Axm *axm, const D *def)
Definition axm.h:80
auto tag() const
Definition axm.h:96
auto plugin() const
Definition axm.h:95
IsA()=default
static auto as(Id id, const Def *def)
Definition axm.h:135
tag_t tag() const
Definition axm.h:60
u8 trip() const
Definition axm.h:38
static std::tuple< const Axm *, u8, u8 > next(const Def *callee)
Like Axm::get, but advances the counter as one more App is about to be built on top of callee.
Definition axm.cpp:44
static auto isa(const Def *def)
Definition axm.h:112
static auto isa(Id id, const Def *def)
Definition axm.h:123
friend class World
Definition axm.h:153
flags_t base() const
Definition axm.h:62
static constexpr size_t Num_Ops
Definition axm.h:150
NormalizeFn normalizer() const
Definition axm.h:36
sub_t sub() const
Definition axm.h:61
static std::pair< u8, u8 > infer_curry_and_trip(const Def *type)
Definition axm.cpp:14
static auto as(const Def *def)
Definition axm.h:134
plugin_t plugin() const
Definition axm.h:59
static constexpr u8 Trip_End
Definition axm.h:148
static constexpr auto Node
Definition axm.h:149
static auto expect(const Def *def, std::format_string< Args... > fmt, Args &&... args)
Like Axm::as but - instead of merely asserting in Debug builds - throws a formatted mim::error when d...
Definition axm.h:142
u8 curry() const
Definition axm.h:37
static std::tuple< const Axm *, u8, u8 > get(const Def *def)
Yields currying counter of def.
Definition axm.cpp:38
Base class for all Defs.
Definition def.h:273
Def * set(size_t i, const Def *)
Successively set from left to right.
Definition def.cpp:196
u8 trip_
Definition def.h:772
constexpr flags_t flags() const noexcept
Definition def.h:293
u8 curry_
Definition def.h:771
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1111
CRTP-based mixin to declare setters for Def::loc & Def::name using a covariant return type.
Definition def.h:209
Definition ast.h:16
u8 sub_t
Definition types.h:42
u64 flags_t
Definition types.h:39
constexpr bool is_commutative(Id)
Definition axm.h:164
const Def *(*)(const Def *, const Def *, const Def *) NormalizeFn
Definition def.h:115
u64 plugin_t
Definition types.h:40
constexpr bool is_associative(Id id)
Definition axm.h:170
u8 tag_t
Definition types.h:41
uint8_t u8
Definition types.h:27
@ Axm
Definition def.h:122
static constexpr tag_t flags2tag(flags_t f)
Yields the tag part of the name as integer.
Definition plugin.h:231
static constexpr sub_t flags2sub(flags_t f)
Yields the sub part of the name as integer.
Definition plugin.h:234
static constexpr plugin_t flags2plugin(flags_t f)
Definition plugin.h:228
static consteval size_t num()
Definition plugin.h:249
static consteval flags_t base()
Definition plugin.h:250
static constexpr flags_t flags2base(flags_t f)
Includes Axm::plugin() and Axm::tag() but not Axm::sub.
Definition plugin.h:237