ModuleDefBuilder.cc
1 /*
2  * MoMEMta: a modular implementation of the Matrix Element Method
3  * Copyright (C) 2016 Universite catholique de Louvain (UCL), Belgium
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <momemta/ModuleDefBuilder.h>
20 
21 #include <momemta/Logging.h>
22 
23 #include <ModuleDefUtils.h>
24 #include <strings/Scanner.h>
25 
26 namespace momemta {
27 namespace registration {
28 
29 namespace {
30 
31 class invalid_name: public std::runtime_error {
32  using std::runtime_error::runtime_error;
33 };
34 
35 class invalid_type: public std::runtime_error {
36  using std::runtime_error::runtime_error;
37 };
38 
39 class missing_attribute: public std::runtime_error {
40  using std::runtime_error::runtime_error;
41 };
42 
52 void finalizeInputOrOutput(StringPiece spec, bool is_output, ModuleRegistrationData& data) {
53  using namespace strings;
54 
55  ArgDef def;
56  StringPiece out;
57 
58  // An optional input starts with a '?'
59 
60  def.optional = spec.Consume("?");
61 
62  def.many = spec.Consume("*");
63 
64  if (! is_output) {
65  // This can be a nested input declaration
66  // Format is `<attr>/[<attr>/]*<name>`
67  while (true) {
68  if (! Scanner(spec)
69  .RestartCapture()
70  .ScanUntil('/')
71  .StopCapture()
72  .OneLiteral("/")
73  .GetResult(&spec, &out)) {
74  // No (more) match, exiting the loop
75  break;
76  }
77 
78  auto nested_attribute = out.ToString();
79 
80  // Ensure attribute exists
81  auto attr = momemta::findAttr(nested_attribute, data.module_def);
82  if (! attr) {
83  std::string error = "Input definition for module " + data.module_def.name + " references a non-existing "
84  "attribute: " + nested_attribute;
85  LOG(fatal) << error;
86  throw missing_attribute(error);
87  } else {
88  def.nested_attributes.push_back(*attr);
89  }
90  }
91  }
92 
93  // Parse <name>
94  if (!Scanner(spec)
95  .One(Scanner::LETTER)
96  .Any(Scanner::LETTER_DIGIT_UNDERSCORE)
97  .StopCapture()
98  .AnySpace()
99  .GetResult(&spec, &out)) {
100  throw invalid_name(std::string("Invalid ") +
101  ((is_output) ? "output" : "input") +
102  " name format (<name>:) for module " + data.module_def.name);
103  }
104 
105  def.name = out.ToString();
106 
107  if (!is_output) {
108  // Parse default value
109  bool has_default = spec.Consume("=");
110  if (has_default) {
111  Scanner(spec)
112  .Any(Scanner::LETTER_DIGIT_UNDERSCORE_COLON)
113  .StopCapture()
114  .AnySpace()
115  .GetResult(&spec, &out);
116 
117  def.default_value = out.ToString();
118  def.optional = true;
119  }
120  }
121 
122  if (is_output) {
123  data.module_def.outputs.emplace_back(def);
124  } else {
125  data.module_def.inputs.emplace_back(def);
126  }
127 }
128 
129 void finalizeAttr(StringPiece spec, ModuleRegistrationData& data) {
130  using namespace strings;
131 
132  AttrDef def;
133 
134  // A global attribute starts with a '^'.
135  def.global = spec.Consume("^");
136 
137  // An optional attribute starts with '?'
138  def.optional = spec.Consume("?");
139 
140  StringPiece out;
141 
142  // Parse `<name>:`
143  if (! Scanner(spec)
144  .One(Scanner::LETTER)
145  .Any(Scanner::LETTER_DIGIT_UNDERSCORE)
146  .StopCapture()
147  .AnySpace()
148  .OneLiteral(":")
149  .AnySpace()
150  .GetResult(&spec, &out)) {
151  throw invalid_name("Invalid attribute name format (<name>:) for module " + data.module_def.name);
152  }
153 
154  def.name = out.ToString();
155 
156  // Read "<type>" or "list(<type>)".
157  bool is_list = Scanner(spec)
158  .OneLiteral("list")
159  .AnySpace()
160  .OneLiteral("(")
161  .AnySpace()
162  .GetResult(&spec);
163 
164  // Consume type
165  if (!Scanner(spec)
166  .Any(Scanner::LOWERLETTER)
167  .StopCapture()
168  .AnySpace()
169  .GetResult(&spec, &out)) {
170  throw invalid_type("Invalid type format for attribute " + def.name + " for module " + data.module_def.name);
171  }
172 
173  // TODO: Validate type against a list of acceptable types
174 
175  if (is_list) {
176  def.type = "list(" + out.ToString() + ")";
177  } else {
178  def.type = out.ToString();
179  }
180 
181  if (is_list) {
182  spec.Consume(")");
183  }
184 
185  // Check if there's a default value, and parse it
186  if (spec.Consume("=")) {
187  Scanner(spec)
188  .Any(Scanner::LETTER_DIGIT_DASH_DOT_SLASH_UNDERSCORE)
189  .StopCapture()
190  .AnySpace()
191  .GetResult(&spec, &out);
192  def.default_value = out.ToString();
193  def.optional = true;
194  }
195 
196  data.module_def.attributes.emplace_back(def);
197 }
198 
199 }
200 
201 ModuleDefBuilder::ModuleDefBuilder(const std::string& name) {
202  reg_data.module_def.name = name;
203 
204  if (name.length() > 1 && name[0] == '_')
205  reg_data.module_def.internal = true;
206 }
207 
208 ModuleDefBuilder& ModuleDefBuilder::Input(const std::string& spec) {
209  inputs.emplace_back(spec);
210  return *this;
211 }
212 
214  // Prefix spec with '?' to indicate an optional input
215  inputs.emplace_back("?" + spec);
216  return *this;
217 }
218 
219 ModuleDefBuilder& ModuleDefBuilder::Inputs(const std::string& spec) {
220  // Prefix spec with '*' to indicate a list of inputs
221  inputs.emplace_back("*" + spec);
222  return *this;
223 }
224 
226  // Prefix spec with '?*' to indicate optional inputs
227  inputs.emplace_back("?*" + spec);
228  return *this;
229 }
230 
231 ModuleDefBuilder& ModuleDefBuilder::Output(const std::string& spec) {
232  outputs.emplace_back(spec);
233  return *this;
234 }
235 
236 ModuleDefBuilder& ModuleDefBuilder::Attr(const std::string& spec) {
237  attrs.emplace_back(spec);
238  return *this;
239 }
240 
242  // Prefix spec with '^' to indicate a global attribute
243  attrs.emplace_back("^" + spec);
244  return *this;
245 }
246 
248  // Prefix spec with '?' to indicate an optional attribute
249  attrs.emplace_back("?" + spec);
250  return *this;
251 }
252 
254  reg_data.module_def.sticky = true;
255  return *this;
256 }
257 
258 std::string ModuleDefBuilder::name() const {
259  return reg_data.module_def.name;
260 }
261 
262 ModuleRegistrationData ModuleDefBuilder::Build() const {
263 
264  auto data = reg_data;
265 
266  for (const auto& spec: attrs) {
267  finalizeAttr(spec, data);
268  }
269 
270  for (const auto& spec: inputs) {
271  finalizeInputOrOutput(spec, false, data);
272  }
273 
274  for (const auto& spec: outputs) {
275  finalizeInputOrOutput(spec, true, data);
276  }
277 
278  return data;
279 }
280 
281 }
282 }
ModuleDefBuilder & Sticky()
Flag this module as sticky.
ModuleDefBuilder & GlobalAttr(const std::string &spec)
Adds a global attribute to the module definition (and returns *this).
ModuleDefBuilder & OptionalInputs(const std::string &spec)
Adds optional inputs to the module definition (and returns *this).
ModuleDefBuilder & Inputs(const std::string &spec)
Adds inputs to the module definition (and returns *this).
Definition: Graph.h:21
ModuleDefBuilder & OptionalInput(const std::string &spec)
Adds an optional input to the module definition (and returns *this).
ModuleDefBuilder & Input(const std::string &spec)
Adds an input to the module definition (and returns *this).
ModuleDefBuilder & Attr(const std::string &spec)
Adds an attribute to the module definition (and returns *this).
ModuleDefBuilder & OptionalAttr(const std::string &spec)
Adds an optional attribute to the module definition (and returns *this).