LCOV - code coverage report
Current view: top level - src/plugins/yanlr - listener.cpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 55 59 93.2 %
Date: 2019-09-12 12:28:41 Functions: 12 13 92.3 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief A listener reacting to matches for the grammar rules defined in `YAML.g4`
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : // -- Imports ------------------------------------------------------------------
      10             : 
      11             : #include <kdbmacros.h>
      12             : 
      13             : #include "listener.hpp"
      14             : 
      15             : using std::string;
      16             : 
      17             : // -- Functions ----------------------------------------------------------------
      18             : 
      19             : namespace
      20             : {
      21             : 
      22             : /**
      23             :  * @brief This function converts a given number to an array base name.
      24             :  *
      25             :  * @param index This number specifies the index of the array entry.
      26             :  *
      27             :  * @return A string representing the given indices as Elektra array name.
      28             :  */
      29          25 : string indexToArrayBaseName (uintmax_t const index)
      30             : {
      31             :         using std::to_string;
      32             : 
      33          25 :         size_t digits = 1;
      34             : 
      35          25 :         for (uintmax_t value = index; value > 9; digits++)
      36             :         {
      37           0 :                 value /= 10;
      38             :         }
      39             : 
      40         175 :         return "#" + string (digits - 1, '_') + to_string (index);
      41             : }
      42             : 
      43             : /**
      44             :  * @brief This function converts a YAML scalar to a string.
      45             :  *
      46             :  * @param text This string contains a YAML scalar (including quote
      47             :  *             characters).
      48             :  *
      49             :  * @return A string without leading and trailing quote characters
      50             :  */
      51         155 : string scalarToText (string const & text)
      52             : {
      53         155 :         if (text.length () == 0)
      54             :         {
      55             :                 return text;
      56             :         }
      57         408 :         if (*(text.begin ()) == '"' || *(text.begin ()) == '\'')
      58             :         {
      59          57 :                 return text.substr (1, text.length () - 2);
      60             :         }
      61             :         return text;
      62             : }
      63             : 
      64             : } // namespace
      65             : 
      66             : // -- Class --------------------------------------------------------------------
      67             : 
      68             : namespace yanlr
      69             : {
      70             : 
      71             : using kdb::Key;
      72             : using kdb::KeySet;
      73             : 
      74             : using ElementContext = yanlr::YAML::ElementContext;
      75             : using EmptyContext = yanlr::YAML::EmptyContext;
      76             : using PairContext = YAML::PairContext;
      77             : using ValueContext = YAML::ValueContext;
      78             : using SequenceContext = yanlr::YAML::SequenceContext;
      79             : 
      80             : /**
      81             :  * @brief This constructor creates a new empty key storage using the given
      82             :  *        parent key.
      83             :  *
      84             :  * @param parent This key specifies the parent of all keys stored in the
      85             :  *               object.
      86             :  */
      87         248 : KeyListener::KeyListener (Key parent) : keys{}
      88             : {
      89         124 :         parents.push (parent.dup ());
      90          31 : }
      91             : 
      92             : /**
      93             :  * @brief This function returns the data read by the parser.
      94             :  *
      95             :  * @return The key set representing the data from the textual input
      96             :  */
      97          28 : KeySet KeyListener::keySet ()
      98             : {
      99          56 :         return keys;
     100             : }
     101             : 
     102             : /**
     103             :  * @brief This function will be called when the listener enters an empty file (that might contain comments).
     104             :  *
     105             :  * @param context The context specifies data matched by the rule.
     106             :  */
     107           0 : void KeyListener::enterEmpty (EmptyContext * context ELEKTRA_UNUSED)
     108             : {
     109             :         // We add a parent key that stores nothing representing an empty file.
     110           0 :         keys.append (Key{ parents.top ().getName (), KEY_BINARY, KEY_END });
     111           0 : }
     112             : 
     113             : /**
     114             :  * @brief This function will be called after the parser exits a value.
     115             :  *
     116             :  * @param context The context specifies data matched by the rule.
     117             :  */
     118          79 : void KeyListener::exitValue (ValueContext * context)
     119             : {
     120         316 :         Key key = parents.top ();
     121         158 :         string value = context->getText ();
     122         156 :         if (value == "true" || value == "false")
     123             :         {
     124           2 :                 key.set<bool> (value == "true");
     125             :         }
     126             :         else
     127             :         {
     128         231 :                 key.setString (scalarToText (value));
     129             :         }
     130         158 :         keys.append (key);
     131          79 : }
     132             : 
     133             : /**
     134             :  * @brief This function will be called after the parser enters a key-value pair.
     135             :  *
     136             :  * @param context The context specifies data matched by the rule.
     137             :  */
     138          78 : void KeyListener::enterPair (PairContext * context)
     139             : {
     140             :         // Entering a mapping such as `part: …` means that we need to add `part` to
     141             :         // the key name
     142         312 :         Key child{ parents.top ().getName (), KEY_END };
     143         234 :         child.addBaseName (scalarToText (context->key ()->getText ()));
     144         156 :         parents.push (child);
     145          78 :         if (!context->child ())
     146             :         {
     147             :                 // Add key with empty value
     148             :                 // The parser does not visit `exitValue` in that case
     149           3 :                 child.setBinary (NULL, 0);
     150           3 :                 keys.append (child);
     151             :         }
     152          78 : }
     153             : 
     154             : /**
     155             :  * @brief This function will be called after the parser exits a key-value pair.
     156             :  *
     157             :  * @param context The context specifies data matched by the rule.
     158             :  */
     159          78 : void KeyListener::exitPair (PairContext * context ELEKTRA_UNUSED)
     160             : {
     161             :         // Returning from a mapping such as `part: …` means that we need need to
     162             :         // remove the key for `part` from the stack.
     163         156 :         parents.pop ();
     164          78 : }
     165             : 
     166             : /**
     167             :  * @brief This function will be called after the parser enters a sequence.
     168             :  *
     169             :  * @param context The context specifies data matched by the rule.
     170             :  */
     171          10 : void KeyListener::enterSequence (SequenceContext * context ELEKTRA_UNUSED)
     172             : {
     173          20 :         indices.push (0);
     174          50 :         parents.top ().setMeta ("array", ""); // We start with an empty array
     175          10 : }
     176             : 
     177             : /**
     178             :  * @brief This function will be called after the parser exits a sequence.
     179             :  *
     180             :  * @param context The context specifies data matched by the rule.
     181             :  */
     182          10 : void KeyListener::exitSequence (SequenceContext * context ELEKTRA_UNUSED)
     183             : {
     184             :         // We add the parent key of all array elements after we leave the sequence
     185          30 :         keys.append (parents.top ());
     186          20 :         indices.pop ();
     187          10 : }
     188             : 
     189             : /**
     190             :  * @brief This function will be called after the parser recognizes an element
     191             :  *        of a sequence.
     192             :  *
     193             :  * @param context The context specifies data matched by the rule.
     194             :  */
     195          25 : void KeyListener::enterElement (ElementContext * context ELEKTRA_UNUSED)
     196             : {
     197             : 
     198         100 :         Key key{ parents.top ().getName (), KEY_END };
     199          75 :         key.addBaseName (indexToArrayBaseName (indices.top ()));
     200             : 
     201          50 :         uintmax_t index = indices.top ();
     202          50 :         indices.pop ();
     203          25 :         if (index < UINTMAX_MAX)
     204             :         {
     205          25 :                 index++;
     206             :         }
     207          50 :         indices.push (index);
     208             : 
     209         150 :         parents.top ().setMeta ("array", key.getBaseName ());
     210          50 :         parents.push (key);
     211          25 : }
     212             : 
     213             : /**
     214             :  * @brief This function will be called after the parser read an element of a
     215             :  *        sequence.
     216             :  *
     217             :  * @param context The context specifies data matched by the rule.
     218             :  */
     219          25 : void KeyListener::exitElement (ElementContext * context ELEKTRA_UNUSED)
     220             : {
     221          50 :         parents.pop (); // Remove the key for the current array entry
     222          25 : }
     223         140 : }

Generated by: LCOV version 1.13