Skip to content

YAMLParser.cpp

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "YAMLParser.hpp"

YAMLParser::YAMLParser() {
  yaml_parser_initialize(&parser);
}

YAMLParser::~YAMLParser() {
  yaml_parser_delete(&parser);
}

YAMLNode YAMLParser::parse(const std::filesystem::path& filename) {
  YAMLNode root;
  FILE* file = fopen(filename.string().c_str(), "r");
  if (!file) {
    throw std::runtime_error("could not read file " + filename.string());
  }

  yaml_parser_set_input_file(&parser, file);
  bool done = false;
  try {
    while (!done) {
      if (!yaml_parser_parse(&parser, &event)) {
        throw std::runtime_error("YAML syntax error in file " +
            filename.string());
      }
      if (event.type == YAML_SEQUENCE_START_EVENT) {
        parseSequence(filename, root);
      } else if (event.type == YAML_MAPPING_START_EVENT) {
        parseMapping(filename, root);
      } else if (event.type == YAML_DOCUMENT_END_EVENT ||
          event.type == YAML_STREAM_END_EVENT) {
        done = true;
        yaml_event_delete(&event);
      } else {
        yaml_event_delete(&event);
      }
    }
  } catch (const std::runtime_error& e) {
    /* close file before propagating exception */
    fclose(file);
    throw e;
  }
  fclose(file);

  return root;
}

void YAMLParser::parseMapping(const std::filesystem::path& filename,
    YAMLNode& node) {
  yaml_event_delete(&event);
  node.setMapping();
  int done = 0;
  while (!done) {
    /* read one name/value pair on each iteration */
    if (!yaml_parser_parse(&parser, &event)) {
      throw std::runtime_error("YAML syntax error in file " +
          filename.string());
    }
    if (event.type == YAML_SCALAR_EVENT) {
      /* key */
      auto data = (char*)event.data.scalar.value;
      auto length = event.data.scalar.length;
      auto key = std::string{data, length};
      yaml_event_delete(&event);

      /* value */
      if (!yaml_parser_parse(&parser, &event)) {
        throw std::runtime_error("YAML syntax error in file " +
            filename.string());
      }
      if (event.type == YAML_SCALAR_EVENT) {
        parseValue(filename, node.insert(key));
      } else if (event.type == YAML_SEQUENCE_START_EVENT) {
        parseSequence(filename, node.insert(key));
      } else if (event.type == YAML_MAPPING_START_EVENT) {
        parseMapping(filename, node.insert(key));
      } else {
        yaml_event_delete(&event);
      }
    } else {
      done = event.type == YAML_MAPPING_END_EVENT;
      yaml_event_delete(&event);
    }
  }
}

void YAMLParser::parseSequence(const std::filesystem::path& filename,
    YAMLNode& node) {
  yaml_event_delete(&event);
  node.setSequence();
  int done = 0;
  while (!done) {
    if (!yaml_parser_parse(&parser, &event)) {
      throw std::runtime_error("YAML syntax error in file " +
          filename.string());
    }
    if (event.type == YAML_SCALAR_EVENT) {
      parseValue(filename, node.push());
    } else if (event.type == YAML_SEQUENCE_START_EVENT) {
      parseSequence(filename, node.push());
    } else if (event.type == YAML_MAPPING_START_EVENT) {
      parseMapping(filename, node.push());
    } else {
      done = event.type == YAML_SEQUENCE_END_EVENT;
      yaml_event_delete(&event);
    }
  }
}

void YAMLParser::parseValue(const std::filesystem::path& filename,
    YAMLNode& node) {
  auto data = (char*)event.data.scalar.value;
  auto length = event.data.scalar.length;
  node.set(std::string(data, length));
  yaml_event_delete(&event);
}