Fleet  0.0.9
Inference in the LOT
Node.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <functional>
4 #include <stack>
5 
6 #include "IO.h"
7 #include "Miscellaneous.h"
8 #include "Rule.h"
9 #include "Program.h"
10 #include "BaseNode.h"
11 #include "Builtins.h"
12 
22 class Node : public BaseNode<Node> {
23  friend class BaseNode<Node>;
24 
25 public:
26 
27  // These are used in parseable to delimit nodes nonterminals and multiple nodes in a tree
28  const static char NTDelimiter = ':'; // delimit nt:format
29  const static char RuleDelimiter = ';'; // delimit a sequence of nt:format;nt:format; etc
30 
31 public:
32  const Rule* rule; // which rule did I use?
33  double lp;
34  bool can_resample;
35 
36  Node(const Rule* r=nullptr, double _lp=0.0, bool cr=true) :
37  BaseNode(r==nullptr?0:r->N), rule(r==nullptr ? NullRule : r), lp(_lp), can_resample(cr) {
38  // NOTE: We don't allow parent to be set here bcause that maeks pi hard to set. We shuold only be placed
39  // in trees with set_child
40  if(r != nullptr)
41  children.resize(r->N);
42  }
43 
44  /* We must define our own copy and move since parent can't just be simply copied */
45  Node(const Node& n) :
46  BaseNode(n), rule(n.rule), lp(n.lp), can_resample(n.can_resample) {
47  }
48  Node(Node&& n) :
49  BaseNode(n), rule(n.rule), lp(n.lp), can_resample(n.can_resample) {
50  }
51 
52  virtual ~Node() {}
53 
59  void assign(Node& n) {
60  children = n.children;
61  this->rule = n.rule;
62  this->lp = n.lp;
63  this->can_resample = n.can_resample;
64  fix_child_info(); // update the children so they point to the right parent
65  }
66  void assign(Node&& n) {
67  children = std::move(n.children);
68  this->rule = n.rule;
69  this->lp = n.lp;
70  this->can_resample = n.can_resample;
72  }
73 
74  const std::string& format() const {
75  return this->rule->format;
76  }
77 
78  nonterminal_t type(const size_t i) const {
85  return rule->type(i);
86  }
87 
88  void set_child(const size_t i, Node& n) {
94  assert(i < rule->N);
95  assert(n.rule == NullRule or n.nt() == rule->type(i)); // no type checking when the rule is null
97  }
98  void set_child(const size_t i, Node&& n) {
104  assert(i < rule->N);
105  if(n.rule != NullRule and n.nt() != rule->type(i)) {
106  print("### Error in set_child: ", n.string(), " does not match required type", rule->type(i), n.nt(), string());
107  assert(false);
108  }
109  assert(n.rule == NullRule or n.nt() == rule->type(i)); // no type checking when the rule is null
110  BaseNode<Node>::set_child(i,std::move(n));
111  }
112 
113 
114  void operator=(const Node& n) {
115  // Here in the assignment operator we don't set the parent to n.parent, otherwise the parent pointers get broken
116  // (nor pi). This leaves them in their place in a tree (so e.g. we can set a node of a tree and it still works)
117 
119 
120  this->rule = n.rule;
121  this->lp = n.lp;
122  this->can_resample = n.can_resample;
123  }
124 
125  void operator=(Node&& n) {
126 
128 
129  this->rule = n.rule;
130  this->lp = n.lp;
131  this->can_resample = n.can_resample;
132  }
133 
134  auto operator<=>(const Node& other) const {
135  // We will sort based on the rules, except we recurse when they are unequal.
136  // This therefore sorts by the uppermost-leftmost rule that doesn't match. We are less than
137  // if we have fewer children
138  if(*rule != *other.rule) {
139  return (*rule) <=> (*other.rule);
140  }
141  else {
142 
143  if(this->children.size() != other.children.size()) {
144  return this->children.size() <=> other.children.size();
145  }
146 
147  for(size_t i=0;i<other.children.size();i++) {
148  if(this->child(i) != other.child(i))
149  return this->child(i) <=> other.child(i);
150  }
151 
152  return std::strong_ordering::equal;
153  }
154  }
155 
156  nonterminal_t nt() const {
162  return this->rule->nt;
163  }
164 
165  bool is_null() const {
171  return this->rule == NullRule;
172  }
173 
174  virtual size_t count_nonnull() const {
180  size_t n = 1*(not this->is_null()); // me
181  for(auto& c : children) {
182  n += c.count_nonnull();
183  }
184  return n;
185  }
186 
187 
188  virtual bool is_complete() const {
194  if(this->is_null()) return false;
195 
196  // does this have any subnodes below that are null?
197  for(auto& c: this->children) {
198  if(c.is_null()) return false;
199  if(not c.is_complete()) return false;
200  }
201  return this->children.size() == rule->N; // must have all my kids
202  }
203 
204 
205  virtual std::string string(bool usedot=true) const override {
212  // Be careful to process the arguments in the right orders
213 
214  if(this->rule->N == 0) {
215  assert(this->children.size() == 0 && "*** Should have zero children -- did you accidentally add children to a null node (that doesn't have a format)?");
216  return this->rule->format;
217  }
218  else {
219 
220  std::vector<std::string> childStrings(this->children.size());
221 
222  for(size_t i=0;i<this->rule->N;i++) {
223  childStrings[i] = this->children[i].string();
224  }
225 
226  // now substitute the children into the format
227  std::string s = this->rule->format;
228  if(usedot and not this->can_resample) s = "\u2022"+s; // just to help out in some cases, we'll add this to nodes that we can't resample
229 
230  for(size_t i=0;i<this->rule->N;i++) {
231 
232  // Here we put the i'th child by first trying position i, otherwise
233  // we search for %S
234  // NOTE: This change might be a bit slower since it matches each %i, but hopefully that doesn't matter...
235  std::string numstr = "%"+str(i+1); // This indexing is 1-based for some reason
236  auto numpos = s.find(numstr);
237  if(numpos != std::string::npos){
238  s.replace(numpos, numstr.length(), childStrings[i]);
239  }
240  else { // match via %s or %!s
241  auto pos = s.find(Rule::ChildStr); // position of the next real match (typically matched)
242  auto silpos = s.find(Rule::SilentChildStr); // position of the next silent match
243 
244  if(pos == std::string::npos and silpos == std::string::npos) {
245  CERR "# Error on " TAB this->rule->format ENDL;
246  assert(false && "Node format must contain one ChildStr (typically='%s') for each argument"); // must contain the ChildStr for all children all children
247  }
248 
249  if(pos < silpos or silpos == std::string::npos) {
250  // if the next thing to substitute is a real string
251  assert(pos != std::string::npos);
252  s.replace(pos, Rule::ChildStr.length(), childStrings[i] );
253  }
254  else {
255  // else we we do a "silent" replacement, matching silpos
256  assert(silpos != std::string::npos);
257  s.replace(silpos, Rule::SilentChildStr.length(), "");
258  }
259 
260 
261  }
262  }
263  return s;
264  }
265  }
266 
267 
268  virtual std::string parseable() const {
274  // get a string like one we could parse
275  std::string out = str(this->nt()) + NTDelimiter + this->rule->format;
276  for(auto& c: this->children) {
277  out += RuleDelimiter + c.parseable();
278  }
279  return out;
280  }
281 
282 
283  /********************************************************
284  * Operaitons for running programs
285  ********************************************************/
286 
287  template<typename VirtualMachineState_t, typename Grammar_t>
288  inline int linearize(Program<VirtualMachineState_t> &program) const {
304  // NOTE: If you change the order here ever, you have to change how string() works so that
305  // string order matches evaluation order
306 
307  // and just a little checking here
308  assert(rule != NullRule && "*** Cannot linearize if there is a null rule");
309  for(size_t i=0;i<this->rule->N;i++) {
310  assert(not this->children[i].is_null() && "Cannot linearize a Node with null children");
311  assert(this->children[i].rule->nt == rule->type(i) && "Somehow the child has incorrect types -- this is bad news for you."); // make sure my kids types are what they should be
312  }
313 
314  // If we are an if, then we must do some fancy short-circuiting
315  if( rule->is_a(Op::If) ) {
316  [[unlikely]]
317  assert(rule->N == 3 && "BuiltinOp::op_IF require three arguments"); // must have 3 parts
318 
319  int ysize = children[2].linearize<VirtualMachineState_t,Grammar_t>(program);
320 
321  // encode the jump
322  program.push(Builtins::Jmp<Grammar_t>.makeInstruction(ysize));
323 
324  int xsize = children[1].linearize<VirtualMachineState_t,Grammar_t>(program)+1; // must be +1 in order to skip over the JMP too
325 
326  // encode the if
327  program.push(rule->makeInstruction(xsize));
328 
329  // evaluate the bool first so its on the stack when we get to if
330  int boolsize = children[0].linearize<VirtualMachineState_t,Grammar_t>(program);
331 
332  return ysize + xsize + boolsize + 1; // +1 for if
333  }
334  else if( rule->is_a(Op::And) or rule->is_a(Op::Or)) {
335  [[unlikely]]
336 
337  // short circuit forms of and(x,y) and or(x,y)
338  assert(rule->N == 2 and children.size() == 2 && "BuiltinOp::op_AND and BuiltinOp::op_OR require two arguments");
339 
340  // second arg pushed on first, on the bottom
341  int ysize = children[1].linearize<VirtualMachineState_t,Grammar_t>(program);
342 
343  if(rule->is_a(Op::And)) {
344  program.push(rule->makeInstruction(ysize));
345  }
346  else if(rule->is_a(Op::Or)) {
347  program.push(rule->makeInstruction(ysize));
348  }
349  else assert(false);
350 
351  return children[0].linearize<VirtualMachineState_t,Grammar_t>(program)+ysize+1;
352  }
353  else {
354  [[likely]]
355 
356  /* Else we just process a normal child.
357  * Here we push the children in increasing order. Then, when we pop rightmost first (as Primitive does), it
358  * assigns the correct index. */
359  program.push(this->rule->makeInstruction());
360 
361  int mysize = 1; // one for my own instruction
362  for(int i=this->rule->N-1;i>=0;i--) { // here we linearize right to left so that when we call right to left, it matches string order
363  mysize += this->children[i].linearize<VirtualMachineState_t,Grammar_t>(program);
364  }
365  return mysize;
366  }
367  }
368 
369 
370  virtual bool operator==(const Node& n) const override {
377  if(not (*rule == *n.rule))
378  return false;
379 
380  if(this->children.size() != n.children.size())
381  return false;
382 
383  for(size_t i=0;i<this->children.size();i++){
384  if(not (this->children[i] == n.children[i]))
385  return false;
386  }
387  return true;
388  }
389 
390  virtual size_t hash(size_t depth=0) const {
397  size_t ret = rule->get_hash(); // tunrs out, this is actually important to prevent hash collisions when rule_id and i are small
398  for(size_t i=0;i<this->children.size();i++) {
399  hash_combine(ret, depth, this->children[i].hash(depth+1), i); // modifies output
400  }
401  return ret;
402  }
403 
408  virtual void fullprint(size_t tab=0) {
409 
410  std::string tb;
411  for(size_t i=0;i<tab;i++) tb += "\t";
412 
413  print(tb, "this="+str(this), "parent="+str(parent), pi, children.size() == rule->N, rule->format);
414  for(size_t i=0;i<children.size();i++) {
415  children[i].fullprint(tab+1);
416  if(children[i].parent != this) print("*** Warning child above does not have correct parent pointer.");
417  if(children[i].pi != i) print("*** Warning child above does not have correct parent index.");
418 
419  }
420  }
421 
422 
423 };
The Primitive type just stores a function pointer and an Op command.
void operator=(const Node &n)
Definition: Node.h:114
double lp
Definition: Node.h:33
virtual ~Node()
Definition: Node.h:52
void push(const Instruction &val)
Push val onto the stack.
Definition: Stack.h:49
size_t pi
Definition: BaseNode.h:27
void set_child(const size_t i, Node &&n)
Definition: Node.h:98
Definition: Node.h:22
void assign(Node &&n)
Definition: Node.h:66
std::vector< Node > children
Definition: BaseNode.h:23
void operator=(Node &&n)
Definition: Node.h:125
This is a general tree class, which we are adding because there are currently at least 3 different tr...
const std::string & format() const
Definition: Node.h:74
virtual std::string string(bool usedot=true) const override
Definition: Node.h:205
void fix_child_info()
Definition: BaseNode.h:238
#define TAB
Definition: IO.h:19
bool is_a(Op o) const
Definition: Rule.h:74
nonterminal_t type(size_t i) const
Definition: Rule.h:152
virtual std::string parseable() const
Definition: Node.h:268
void set_child(const size_t i, this_t &n)
Definition: BaseNode.h:283
Definition: Rule.h:21
nonterminal_t type(const size_t i) const
Definition: Node.h:78
static const std::string ChildStr
Definition: Rule.h:24
size_t get_hash() const
Definition: Rule.h:139
const Rule * NullRule
Definition: Rule.h:186
std::string str(BindingTree *t)
Definition: BindingTree.h:195
virtual bool is_complete() const
Definition: Node.h:188
static const char RuleDelimiter
Definition: Node.h:29
size_t N
Definition: Rule.h:29
void print(FIRST f, ARGS... args)
Lock output_lock and print to std:cout.
Definition: IO.h:53
bool is_null() const
Definition: Node.h:165
Definition: BaseNode.h:20
static const char NTDelimiter
Definition: Node.h:28
std::string format
Definition: Rule.h:28
void assign(Node &n)
Assign will set everything to n BUT it will not copy the parent pointer etc since we&#39;re assuming this...
Definition: Node.h:59
#define CERR
Definition: IO.h:23
nonterminal_t nt() const
Definition: Node.h:156
Node & child(const size_t i)
Definition: BaseNode.h:175
const Rule * rule
Definition: Node.h:32
unsigned short nonterminal_t
Definition: Nonterminal.h:4
A Rule stores one possible expansion in the grammar, specifying a nonterminal type, an instruction that gets executed, a forma string, a number of children, and an array of types of each child. Here we "emulate" a type system using t_nonterminal to store an integer for the types. *.
void set_child(const size_t i, Node &n)
Definition: Node.h:88
Node * parent
Definition: BaseNode.h:26
virtual void fullprint(size_t tab=0)
A function to print out everything, for debugging purposes.
Definition: Node.h:408
int linearize(Program< VirtualMachineState_t > &program) const
Definition: Node.h:288
#define ENDL
Definition: IO.h:21
virtual size_t count_nonnull() const
Definition: Node.h:174
virtual bool operator==(const Node &n) const override
Definition: Node.h:370
static const std::string SilentChildStr
Definition: Rule.h:25
void operator=(const this_t &t)
Definition: BaseNode.h:51
bool can_resample
Definition: Node.h:34
nonterminal_t nt
Definition: Rule.h:27
size_t depth() const
Definition: BaseNode.h:230
Node(const Rule *r=nullptr, double _lp=0.0, bool cr=true)
Definition: Node.h:36
Definition: Program.h:6
Instruction makeInstruction(int a) const
Definition: Rule.h:92
A program here stores just a stack of instructions which can be executed by the VirtualMachineState_t...
Node(const Node &n)
Definition: Node.h:45
Node(Node &&n)
Definition: Node.h:48
virtual size_t hash(size_t depth=0) const
Definition: Node.h:390