1 /*
2  * Copyright 2019 by its authors. See AUTHORS.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #ifndef EOLIAN_MONO_NAME_HELPERS_HH
17 #define EOLIAN_MONO_NAME_HELPERS_HH
18 
19 #include <algorithm>
20 #include <cctype>
21 #include <iterator>
22 #include <sstream>
23 #include <string>
24 #include <vector>
25 #include <map>
26 #include "utils.hh"
27 
28 #include "grammar/integral.hpp"
29 #include "grammar/generator.hpp"
30 #include "grammar/klass_def.hpp"
31 #include "grammar/list.hpp"
32 #include "grammar/string.hpp"
33 #include "grammar/integral.hpp"
34 
35 using efl::eolian::grammar::as_generator;
36 using efl::eolian::grammar::string;
37 using efl::eolian::grammar::lit;
38 using efl::eolian::grammar::operator*;
39 
40 namespace eolian_mono {
41 
42 /* Utility functions for naming things. Compared to the utils.hh, this header has higher level
43  * functions, dealing with the knowledge of how to convert the items to the C# style we are using, for
44  * example, while being too short to be implemented as full-fledged generators.
45  */
46 namespace name_helpers {
47 
48 namespace attributes = efl::eolian::grammar::attributes;
49 
50 namespace detail {
is_equal(std::string const & lhs,std::string const & rhs)51 inline bool is_equal(std::string const& lhs, std::string const& rhs)
52 {
53   return lhs == rhs;
54 }
55 }
56 
57 // Forward declarations
58 template<typename  T>
59 inline std::string klass_concrete_or_interface_name(T const& klass);
60 
identity(std::string const & str)61 inline std::string identity(std::string const& str)
62 {
63   return str;
64 }
65 
escape_keyword(std::string const & name)66 inline std::string escape_keyword(std::string const& name)
67 {
68   using detail::is_equal;
69   if(is_equal(name, "delete")
70      || is_equal(name, "do")
71      || is_equal(name, "lock")
72      || is_equal(name, "event")
73      || is_equal(name, "in")
74      || is_equal(name, "out")
75      || is_equal(name, "object")
76      || is_equal(name, "interface")
77      || is_equal(name, "string")
78      || is_equal(name, "internal")
79      || is_equal(name, "fixed")
80      || is_equal(name, "var")
81      || is_equal(name, "base"))
82     return "kw_" + name;
83 
84   if (is_equal(name, "Finalize"))
85     return name + "Add"; // Eo's Finalize is actually the end of efl_add.
86   return name;
87 }
88 
89 typedef std::function<std::string(std::string const&)> string_transform_func;
90 
join_namespaces(std::vector<std::string> const & namespaces,char separator,string_transform_func func=identity)91 inline std::string join_namespaces(std::vector<std::string> const& namespaces, char separator,
92                                    string_transform_func func=identity)
93 {
94    std::stringstream s;
95    for (auto&& n : namespaces)
96      s << func(n) << separator;
97 
98    return s.str();
99 }
100 
101 static const std::vector<std::string> verbs =
102   {
103     "add",
104     "get",
105     "is",
106     "del",
107     "thaw",
108     "freeze",
109     "save",
110     "wait",
111     "eject",
112     "raise",
113     "lower",
114     "load",
115     "dup",
116     "reset",
117     "unload",
118     "close",
119     "set",
120     "interpolate",
121     "has",
122     "grab",
123     "check",
124     "find",
125     "ungrab",
126     "unset",
127     "clear",
128     "pop",
129     "new",
130     "peek",
131     "push",
132     "update",
133     "show",
134     "move",
135     "hide",
136     "calculate",
137     "resize",
138     "attach",
139     "pack",
140     "unpack",
141     "emit",
142     "call",
143     "append",
144     "apply",
145     "bind",
146     "cancel",
147     "copy",
148     "create",
149     "cut",
150     "delete",
151     "deselect",
152     "detach",
153     "do",
154     "gen",
155     "insert",
156     "iterate",
157     "join",
158     "leave",
159     "limit",
160     "paste",
161     "parse",
162     "prepend",
163     "process",
164     "query",
165     "refresh",
166     "remove",
167     "register",
168     "reject",
169     "release",
170     "reply",
171     "send",
172     "select",
173     "serialize",
174     "steal",
175     "sync",
176     "toggle",
177     "unbind",
178     "unregister",
179     "unselect"
180   };
181 
182 const std::vector<std::string> not_verbs =
183   {
184     "below",
185     "above",
186     "name",
187     "unfreezable",
188     "value",
189     "r",
190     "g",
191     "b",
192     "a",
193     "finalize",
194     "destructor",
195     "to",
196     "circle",
197     "rect",
198     "path",
199     "commands",
200     "type",
201     "colorspace"
202     "op",
203     "type",
204     "properties",
205     "status",
206     "status",
207     "relative",
208     "ptr",
209     "pair",
210     "pos",
211     "end"
212   };
213 
reorder_verb(std::vector<std::string> & names)214 void reorder_verb(std::vector<std::string> &names)
215 {
216   if (names.size() <= 1)
217     return;
218 
219   std::string verb = names.back();
220 
221   if (std::find(verbs.begin(), verbs.end(), verb) != verbs.end())
222     {
223        names.pop_back();
224        names.insert(names.begin(), verb);
225     }
226 }
227 
managed_namespace(std::string const & ns)228 inline std::string managed_namespace(std::string const& ns)
229 {
230   return escape_keyword(utils::remove_all(ns, '_'));
231 }
232 
managed_name(std::string const & name,char separator='_')233 inline std::string managed_name(std::string const& name, char separator='_')
234 {
235   auto tokens = utils::split(name, separator);
236   return utils::to_pascal_case(tokens);
237 }
238 
managed_method_name(attributes::function_def const & f)239 inline std::string managed_method_name(attributes::function_def const& f)
240 {
241   std::vector<std::string> names = utils::split(f.name, '_');
242 
243   name_helpers::reorder_verb(names);
244 
245   std::string candidate = escape_keyword(utils::to_pascal_case(names));
246 
247   // Some eolian methods have the same name as their parent class
248   if (candidate == klass_concrete_or_interface_name(f.klass))
249       candidate = "Do" + candidate;
250 
251   // Avoid clashing with System.Object.GetType
252   if (candidate == "GetType" || candidate == "SetType")
253     {
254        candidate.insert(3, managed_name(f.klass.eolian_name));
255     }
256 
257   return candidate;
258 }
259 
full_managed_name(std::string const & name)260 inline std::string full_managed_name(std::string const& name)
261 {
262   std::stringstream ss;
263 
264   auto words = utils::split(name, '.');
265   std::transform(words.begin(), words.end(), words.begin(), [](std::string const& word) {
266      return managed_name(word);
267   });
268 
269   auto b = std::begin(words), e = std::end(words);
270 
271   if (b != e)
272     {
273       std::copy(b, std::prev(e), std::ostream_iterator<std::string>(ss, "."));
274       b = std::prev(e);
275     }
276 
277   // Avoid trailing separator
278   if (b != e)
279     ss << *b;
280 
281   return ss.str();
282 }
283 
alias_full_eolian_name(attributes::alias_def const & alias)284 inline std::string alias_full_eolian_name(attributes::alias_def const& alias)
285 {
286 
287    std::string eolian_name = utils::remove_all(alias.eolian_name, '_');
288    return join_namespaces(alias.namespaces, '.') + eolian_name;
289 }
290 
managed_async_method_name(attributes::function_def const & f)291 inline std::string managed_async_method_name(attributes::function_def const& f)
292 {
293   return managed_method_name(f) + "Async";
294 }
function_ptr_full_eolian_name(attributes::function_def const & func)295 inline std::string function_ptr_full_eolian_name(attributes::function_def const& func)
296 {
297    return join_namespaces(func.namespaces, '.') + func.name;
298 }
299 
type_full_eolian_name(attributes::regular_type_def const & type)300 inline std::string type_full_eolian_name(attributes::regular_type_def const& type)
301 {
302    return join_namespaces(type.namespaces, '.') + type.base_type;
303 }
304 
type_full_managed_name(attributes::regular_type_def const & type)305 inline std::string type_full_managed_name(attributes::regular_type_def const& type)
306 {
307    return join_namespaces(type.namespaces, '.', managed_namespace) + utils::remove_all(type.base_type, '_');
308 }
309 
struct_full_eolian_name(attributes::struct_def const & struct_)310 inline std::string struct_full_eolian_name(attributes::struct_def const& struct_)
311 {
312    return join_namespaces(struct_.namespaces, '.') + struct_.cxx_name;
313 }
314 
315 template<typename T>
typedecl_managed_name(T const & item)316 inline std::string typedecl_managed_name(T const& item)
317 {
318    return utils::remove_all(item.cxx_name, '_');
319 }
320 
typedecl_managed_name(attributes::function_def const & func)321 inline std::string typedecl_managed_name(attributes::function_def const& func)
322 {
323    return utils::remove_all(func.name, '_');
324 }
325 
326 
enum_field_managed_name(std::string name)327 inline std::string enum_field_managed_name(std::string name)
328 {
329    std::vector<std::string> names = utils::split(name, '_');
330    return utils::to_pascal_case(names);
331 }
332 
to_field_name(std::string const & in)333 inline std::string to_field_name(std::string const& in)
334 {
335    std::vector<std::string> names = utils::split(in, '_');
336    return utils::to_camel_case(names);
337 }
338 
339 
340 
341 template<typename T>
property_managed_name(T const & klass,std::string const & name)342 inline std::string property_managed_name(T const& klass, std::string const& name)
343 {
344   auto names = utils::split(name, '_');
345   // No need to escape keyword here as it will be capitalized and already
346   // namespaced inside the owner class.
347   auto managed_name = utils::to_pascal_case(names);
348   auto managed_klass_name = klass_concrete_or_interface_name(klass);
349 
350   if (managed_name == "Type")
351     managed_name = managed_klass_name + managed_name;
352 
353   return managed_name;
354 }
355 
property_managed_name(attributes::property_def const & property)356 inline std::string property_managed_name(attributes::property_def const& property)
357 {
358   return property_managed_name(property.klass, property.name);
359 }
360 
managed_part_name(attributes::part_def const & part)361 inline std::string managed_part_name(attributes::part_def const& part)
362 {
363   std::vector<std::string> names = utils::split(part.name, '_');
364   return utils::to_pascal_case(names) + "Part";
365 }
366 
367 // Class name translation (interface/concrete/inherit/etc)
368 struct klass_interface_name_generator
369 {
370 
371   template <typename T>
operator ()eolian_mono::name_helpers::klass_interface_name_generator372   std::string operator()(T const& klass) const
373   {
374      return ((klass.type == attributes::class_type::mixin
375               || klass.type == attributes::class_type::interface_) ? "I" : "")
376        + utils::remove_all(klass.eolian_name, '_');
377   }
378 
379   template <typename OutputIterator, typename Attr, typename Context>
generateeolian_mono::name_helpers::klass_interface_name_generator380   bool generate(OutputIterator sink, Attr const& attribute, Context const& context) const
381   {
382     return as_generator((*this).operator()<Attr>(attribute)).generate(sink, attributes::unused, context);
383   }
384 } const klass_interface_name;
385 
386 struct klass_full_interface_name_generator
387 {
388   template <typename T>
operator ()eolian_mono::name_helpers::klass_full_interface_name_generator389   std::string operator()(T const& klass) const
390   {
391     return join_namespaces(klass.namespaces, '.', managed_namespace) + klass_interface_name(klass);
392   }
393 
394   template <typename OutputIterator, typename Attr, typename Context>
generateeolian_mono::name_helpers::klass_full_interface_name_generator395   bool generate(OutputIterator sink, Attr const& attribute, Context const& context) const
396   {
397     return as_generator((*this).operator()<Attr>(attribute)).generate(sink, attributes::unused, context);
398   }
399 } const klass_full_interface_name;
400 
401 template<typename T>
klass_concrete_name(T const & klass)402 inline std::string klass_concrete_name(T const& klass)
403 {
404    return utils::remove_all(klass.eolian_name, '_');
405 }
406 
407 template<typename  T>
klass_concrete_or_interface_name(T const & klass)408 inline std::string klass_concrete_or_interface_name(T const& klass)
409 {
410     switch(klass.type)
411     {
412     case attributes::class_type::abstract_:
413     case attributes::class_type::regular:
414       return klass_concrete_name(klass);
415     default:
416       return klass_interface_name(klass);
417     }
418 }
419 
420 struct klass_full_concrete_name_generator
421 {
422   template <typename T>
operator ()eolian_mono::name_helpers::klass_full_concrete_name_generator423   std::string operator()(T const& klass) const
424   {
425     return join_namespaces(klass.namespaces, '.', managed_namespace) + klass_concrete_name(klass);
426   }
427 
428   template <typename OutputIterator, typename Attr, typename Context>
generateeolian_mono::name_helpers::klass_full_concrete_name_generator429   bool generate(OutputIterator sink, Attr const& attribute, Context const& context) const
430   {
431     return as_generator((*this).operator()<Attr>(attribute)).generate(sink, attributes::unused, context);
432   }
433 } const klass_full_concrete_name;
434 
435 struct klass_full_concrete_or_interface_name_generator
436 {
437   template <typename T>
operator ()eolian_mono::name_helpers::klass_full_concrete_or_interface_name_generator438   std::string operator()(T const& klass) const
439   {
440     switch(klass.type)
441     {
442     case attributes::class_type::abstract_:
443     case attributes::class_type::regular:
444       return klass_full_concrete_name(klass);
445     default:
446       return klass_full_interface_name(klass);
447     }
448   }
449 
450   template <typename OutputIterator, typename Context>
generateeolian_mono::name_helpers::klass_full_concrete_or_interface_name_generator451   bool generate(OutputIterator, attributes::unused_type, Context const&) const
452   {
453     return true;
454   }
455 
456   template <typename OutputIterator, typename Attr, typename Context>
generateeolian_mono::name_helpers::klass_full_concrete_or_interface_name_generator457   bool generate(OutputIterator sink, Attr const& attribute, Context const& context) const
458   {
459     return as_generator((*this).operator()<Attr>(attribute)).generate(sink, attributes::unused, context);
460   }
461 } const klass_full_concrete_or_interface_name;
462 
463 template<typename T>
klass_inherit_name(T const & klass)464 inline std::string klass_inherit_name(T const& klass)
465 {
466   return klass_concrete_name(klass);
467 }
468 
469 template<typename T>
klass_native_inherit_name(T const & klass)470 inline std::string klass_native_inherit_name(T const& klass)
471 {
472   return ((klass.type == attributes::class_type::mixin
473            || klass.type == attributes::class_type::interface_) ? klass_interface_name(klass) : "") + "NativeMethods";
474 }
475 
476 template<typename T>
klass_full_native_inherit_name(T const & klass)477 inline std::string klass_full_native_inherit_name(T const& klass)
478 {
479   if(klass.type == attributes::class_type::mixin
480      || klass.type == attributes::class_type::interface_)
481     return join_namespaces(klass.namespaces, '.', managed_namespace) + klass_native_inherit_name(klass);
482 
483   return klass_full_concrete_name(klass) + "." + klass_native_inherit_name(klass);
484 }
485 
486 template<typename T>
klass_get_name(T const & clsname)487 inline std::string klass_get_name(T const& clsname)
488 {
489   return clsname.klass_get_name;
490 }
491 
492 template<typename T>
klass_get_full_name(T const & clsname)493 inline std::string klass_get_full_name(T const& clsname)
494 {
495   if(clsname.type == attributes::class_type::mixin
496      || clsname.type == attributes::class_type::interface_)
497     return klass_get_name(clsname);
498 
499   return klass_full_concrete_name(clsname) + "." + klass_get_name(clsname);
500 }
501 
502 // Events
managed_event_name(std::string const & name)503 inline std::string managed_event_name(std::string const& name)
504 {
505    return utils::to_pascal_case(utils::split(name, "_,"), "") + "Event";
506 }
507 
managed_event_args_short_name(attributes::event_def const & evt)508 inline std::string managed_event_args_short_name(attributes::event_def const& evt)
509 {
510    return utils::remove_all(evt.klass.eolian_name, '_') + name_helpers::managed_event_name(evt.name) + "Args";
511 }
512 
managed_event_args_name(attributes::event_def evt)513 inline std::string managed_event_args_name(attributes::event_def evt)
514 {
515    return join_namespaces(evt.klass.namespaces, '.', managed_namespace) +
516           managed_event_args_short_name(evt);
517 }
518 
translate_inherited_event_name(const attributes::event_def & evt,const attributes::klass_def & klass)519 inline std::string translate_inherited_event_name(const attributes::event_def &evt, const attributes::klass_def &klass)
520 {
521    return join_namespaces(klass.namespaces, '_') + klass_interface_name(klass) + "_" + managed_event_name(evt.name);
522 }
523 
524 // Open/close namespaces
525 template<typename OutputIterator, typename Context>
open_namespaces(OutputIterator sink,std::vector<std::string> namespaces,Context const & context)526 bool open_namespaces(OutputIterator sink, std::vector<std::string> namespaces, Context const& context)
527 {
528   std::transform(namespaces.begin(), namespaces.end(), namespaces.begin(), managed_namespace);
529 
530   std::string joined_namespace = join_namespaces(namespaces, '.');
531   if (joined_namespace.empty()) return true;
532   joined_namespace.pop_back();
533 
534   return as_generator("namespace " << string << " {\n").generate(sink, joined_namespace, context);
535 }
536 
537 template<typename OutputIterator, typename Context>
close_namespaces(OutputIterator sink,std::vector<std::string> const & namespaces,Context const & context)538 bool close_namespaces(OutputIterator sink, std::vector<std::string> const& namespaces, Context const& context)
539 {
540      if (namespaces.empty()) return true;
541      return as_generator("}\n\n").generate(sink, attributes::unused, context);
542 }
543 
constructor_managed_name(std::string full_name)544 std::string constructor_managed_name(std::string full_name)
545 {
546     auto tokens = utils::split(full_name, '.');
547 
548     return managed_name(tokens.at(tokens.size()-1));
549 }
550 
translate_value_type(std::string const & name)551 std::string translate_value_type(std::string const& name)
552 {
553   static std::map<std::string, std::string> table = {
554     {"sbyte", "SByte"},
555     {"byte","Byte"},
556     {"short","Int16"},
557     {"ushort","UInt16"},
558     {"int", "Int32"},
559     {"uint","UInt32"},
560     {"long","Int64"},
561     {"ulong","UInt64"},
562     {"char","Char"},
563     {"float","Single"},
564     {"double","Double"},
565     {"bool","Boolean"},
566     {"decimal","Decimal"},
567   };
568 
569   auto found = table.find(name);
570 
571   if (found != table.end())
572     return found->second;
573 
574   return name;
575 }
576 
577 
578 // Field names //
579 struct struct_field_name_generator
580 {
581   template <typename OutputIterator, typename Context>
generateeolian_mono::name_helpers::struct_field_name_generator582   bool generate(OutputIterator sink, attributes::struct_field_def const& field, Context const& context) const
583   {
584     return as_generator(string).generate(sink, name_helpers::to_field_name(field.name), context);
585   }
586 } const struct_field_name {};
587 
588 // Property names //
589 struct struct_property_name_generator
590 {
591   template <typename OutputIterator, typename Context>
generateeolian_mono::name_helpers::struct_property_name_generator592   bool generate(OutputIterator sink, attributes::struct_field_def const& field, Context const& context) const
593   {
594     return as_generator(string).generate(sink, name_helpers::managed_name(field.name), context);
595   }
596 } const struct_property_name {};
597 
598 } // namespace name_helpers
599 
600 } // namespace eolian_mono
601 
602 
603 namespace efl { namespace eolian { namespace grammar {
604 
605 template <>
606 struct is_eager_generator<eolian_mono::name_helpers::klass_interface_name_generator> : std::true_type {};
607 template <>
608 struct is_generator<eolian_mono::name_helpers::klass_interface_name_generator> : std::true_type {};
609 
610 template <>
611 struct is_eager_generator<eolian_mono::name_helpers::klass_full_interface_name_generator> : std::true_type {};
612 template <>
613 struct is_generator<eolian_mono::name_helpers::klass_full_interface_name_generator> : std::true_type {};
614 
615 template <>
616 struct is_eager_generator<eolian_mono::name_helpers::klass_full_concrete_or_interface_name_generator> : std::true_type {};
617 template <>
618 struct is_generator<eolian_mono::name_helpers::klass_full_concrete_or_interface_name_generator> : std::true_type {};
619 
620 template <>
621 struct is_eager_generator<eolian_mono::name_helpers::klass_full_concrete_name_generator> : std::true_type {};
622 template <>
623 struct is_generator<eolian_mono::name_helpers::klass_full_concrete_name_generator> : std::true_type {};
624 
625 template <>
626 struct is_eager_generator<eolian_mono::name_helpers::struct_field_name_generator> : std::true_type {};
627 template <>
628 struct is_generator< ::eolian_mono::name_helpers::struct_field_name_generator> : std::true_type {};
629 
630 template <>
631 struct is_eager_generator<eolian_mono::name_helpers::struct_property_name_generator> : std::true_type {};
632 template <>
633 struct is_generator< ::eolian_mono::name_helpers::struct_property_name_generator> : std::true_type {};
634 
635 namespace type_traits {
636 template <>
637 struct attributes_needed<struct ::eolian_mono::name_helpers::klass_full_concrete_or_interface_name_generator> : std::integral_constant<int, 1> {};
638 
639 template <>
640 struct attributes_needed< ::eolian_mono::name_helpers::struct_field_name_generator> : std::integral_constant<int, 1> {};
641 template <>
642 struct attributes_needed< ::eolian_mono::name_helpers::struct_property_name_generator> : std::integral_constant<int, 1> {};
643 
644 }
645 
646 } } }
647 
648 #endif
649