Fleet  0.0.9
Inference in the LOT
Combinators.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <signal.h>
4 
5 #include "Errors.h"
6 #include "Grammar.h"
7 #include "Node.h"
8 #include "Builtins.h"
9 
10 #include "SExpression.h"
11 
12 extern volatile sig_atomic_t CTRL_C;
13 
14 // Funny ordering is requird here because Builtins need these, Grammar needs builtins
15 namespace Combinators {
16 
17  // we need a void class to pass to Grammar, but void won't work!
18  // So here we make a class we can never instantiate
19  struct cl_void {
20  cl_void() { assert(false); }
21  };
22 
23  struct CL {
24  // must be defined (but not used)
25  bool operator<(const CL& other) const { throw NotImplementedError(); }
26  }; // just a dummy type here for the grammar
27 
28 
29  // Combinatory logic operations
30  template<typename Grammar_t>
32  CL_I(Op::CL_I, BUILTIN_LAMBDA {assert(false);});
33 
34  template<typename Grammar_t>
36  CL_S(Op::CL_S, BUILTIN_LAMBDA {assert(false);});
37 
38  template<typename Grammar_t>
40  CL_K(Op::CL_K, BUILTIN_LAMBDA {assert(false);});
41 
42  template<typename Grammar_t>
44  CL_Apply(Op::CL_Apply, BUILTIN_LAMBDA {assert(false);});
45 
53  class SKGrammar : public Grammar<cl_void,CL, CL, cl_void> {
55  using Super::Super;
56 
57  public:
58  SKGrammar() : Super() {
59  // These are given NoOps because they are't interpreted in the normal evaluator
60  add("I", CL_I<SKGrammar>);
61  add("S", CL_S<SKGrammar>);
62  add("K", CL_K<SKGrammar>);
63  add("(%s %s)", CL_Apply<SKGrammar>, 2.0);
64  }
65 
66  } skgrammar;
67 
75  class ReductionException: public std::exception {} reduction_exception;
76 
82  void reduce(SExpression::SExpNode& n, int& remaining_calls) {
83  // returns true if we change anything
84 
85  bool modified; // did we change anything?
86  do { // do this without recursion since its faster and we don't have the depth limit
87  modified = false;
88 
89  // first ensure that my children have all been reduced
90  for(size_t c=0;c<n.nchildren();c++){
91  reduce(n.child(c),remaining_calls);
92  }
93 
94  // std::string original = string();
95  // print("REDUCE", n.string());
96 
97  if(remaining_calls-- == 0)
99 
100  // try to evaluate n according to the rules
101  const auto NC = n.nchildren();
102  assert(NC <= 2); // we don't handle this -- 3+ children should have been caught in the constructor
103 
104  if(NC == 2 and not n.label.has_value()) { // we are an application node
105  if(n.child(0).get_label() == "I"){ // (I x) = x
106  assert(n.child(0).nchildren() == 0);
107  auto x = std::move(n.child(1));
108  n = x;
109  modified = true;
110  // print("HERE I", string());
111  }
112  else if(n.child(0).nchildren() == 2 and
113  n.child(0).child(0).get_label() == "K") { // ((K x) y) = x
114  assert(n.child(0).child(0).nchildren() == 0);
115  auto x = std::move(n.child(0).child(1));
116  n = x;
117  // print("HERE K", string());
118  modified = true;
119  }
120  else if(n.child(0).nchildren() == 2 and
121  n.child(0).child(0).nchildren() == 2 and
122  n.child(0).child(0).child(0).get_label() == "S") { // (((S x) y) z) = ((x z) (y z))
123  assert(n.child(0).child(0).child(0).nchildren() == 0);
124 
125  // just make the notation handier here
126  auto x = std::move(n.child(0).child(0).child(1));
127  auto y = std::move(n.child(0).child(1));
128  auto z = std::move(n.child(1));
129  SExpression::SExpNode zz = z; // copy
130 
131  SExpression::SExpNode q; // build our rule
132  q.set_child(0, std::move(x));
133  q.set_child(1, std::move(zz)); //copy
134 
136  r.set_child(0, std::move(y));
137  r.set_child(1, std::move(z));
138 
139  n.label.reset(); // clear the label value since I'm an apply now
140  n.set_child(0,std::move(q));
141  n.set_child(1,std::move(r));
142  modified = true;
143  // print("HERE S", string());
144  }
145  }
146  else if( (not n.label.has_value()) and n.nchildren() == 1) {
147  auto x = std::move(n.child(0));
148  n = x; // ((x)) = x
149  modified = true;
150  // print("HERE APP", string());
151  }
152 
153  // print("GOT", this, label, original, string());
154 
155 
156  } while(modified); // end while
157  }
158 
160  int remaining_calls = 256;
161  reduce(n,remaining_calls);
162  }
163 
165  Rule* rapp = Combinators::skgrammar.get_rule(Combinators::skgrammar.nt<CL>(), "(%s %s)");
166 
167  if(n.rule == rapp) {
169  for(size_t i=0;i<n.nchildren();i++){
170  out.set_child(i, NodeToSExpNode(n.child(i)));
171  }
172  return out;
173  }
174  else {
175  assert(n.nchildren() == 0); // must be a terminal
176  return SExpression::SExpNode(n.format()); // must match ISK
177  }
178 
179  }
180 
182  if(n.nchildren() == 0) {
183  assert(n.label.has_value());
185  return Node(rl);
186  }
187  else if(n.nchildren() == 1) {
188  return SExpNodeToNode(n.child(0));
189  }
190  else if(n.nchildren() == 2) {
191  Rule* rapp = Combinators::skgrammar.get_rule(Combinators::skgrammar.nt<CL>(), "(%s %s)");
192  assert(rapp != nullptr);
193  Node out(rapp);
194  for(size_t i=0;i<n.nchildren();i++){
195  out.set_child(i, SExpNodeToNode(n.child(i)));
196  }
197  return out;
198  }
199  else {
200  assert(false);
201  }
202  }
203 
204  template<typename L>
205  void substitute(SExpression::SExpNode& n, const L& m) {
206  //print(n.string(), "LABEL=",n.get_label(), m.factors.contains(n.get_label()));
207 
208  if(n.label.has_value() and m.factors.contains(n.label.value())) {
209  auto v = m.at(n.label.value()).get_value(); // copy
210  n = NodeToSExpNode(v);
211  }
212 
213  for(auto& c : n.get_children()) {
214  substitute(c, m);
215  }
216  }
217 
218 }
219 
Definition: Combinators.h:75
The Primitive type just stores a function pointer and an Op command.
This is the return type of parsing S-expressions. It contains an optional label and typically we be c...
Definition: Grammar.h:44
Combinators::SKGrammar skgrammar
SExpression::SExpNode NodeToSExpNode(const Node &n)
Definition: Combinators.h:164
decltype(children) & get_children()
Definition: BaseNode.h:168
Definition: Node.h:22
Primitive< Combinators::CL > CL_I(Op::CL_I, BUILTIN_LAMBDA {assert(false);})
const std::string & format() const
Definition: Node.h:74
void set_child(const size_t i, this_t &n)
Definition: BaseNode.h:283
Definition: Rule.h:21
Definition: Combinators.h:15
Node SExpNodeToNode(const SExpression::SExpNode &n)
Definition: Combinators.h:181
Definition: Primitive.h:13
void reduce(SExpression::SExpNode &n, int &remaining_calls)
Combinator reduction, assuming that n is a SExpNode.
Definition: Combinators.h:82
bool operator<(const CL &other) const
Definition: Combinators.h:25
Definition: SExpression.h:20
void substitute(SExpression::SExpNode &n, const L &m)
Definition: Combinators.h:205
Definition: Combinators.h:53
Primitive< Combinators::CL > CL_S(Op::CL_S, BUILTIN_LAMBDA {assert(false);})
volatile sig_atomic_t CTRL_C
Definition: Combinators.h:23
cl_void()
Definition: Combinators.h:20
A Node is the primary internal representation for a program – it recursively stores a rule and the a...
Definition: Combinators.h:19
this_t & child(const size_t i)
Definition: BaseNode.h:175
const Rule * rule
Definition: Node.h:32
std::optional< std::string > label
Definition: SExpression.h:21
void set_child(const size_t i, Node &n)
Definition: Node.h:88
Definition: Errors.h:7
#define BUILTIN_LAMBDA
Definition: Builtins.h:9
Primitive< Combinators::CL > CL_K(Op::CL_K, BUILTIN_LAMBDA {assert(false);})
Primitive< Combinators::CL, Combinators::CL, Combinators::CL > CL_Apply(Op::CL_Apply, BUILTIN_LAMBDA {assert(false);})
SKGrammar()
Definition: Combinators.h:58
virtual Rule * get_rule(const nonterminal_t nt, size_t k) const
Definition: Grammar.h:489
Combinators::ReductionException reduction_exception
size_t nchildren() const
Definition: BaseNode.h:208
A grammar stores all of the rules associated with any kind of nonterminal and permits us to sample as...