Program Listing for File connectionparser.cpp

Return to documentation for file (falcon/connectionparser.cpp)

// ---------------------------------------------------------------------
// This file is part of falcon-core.
//
// Copyright (C) 2015, 2016, 2017 Neuro-Electronics Research Flanders
//
// Falcon-server is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Falcon-server is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with falcon-core. If not, see <http://www.gnu.org/licenses/>.
// ---------------------------------------------------------------------

#include <iostream>
#include <list>
#include <regex>
#include <string>
#include <utility>

#include "connectionparser.hpp"
#include "utilities/string.hpp"

ConnectionRule parseConnectionRule(std::string rulestring) {
  // rule
  // <specifier>:<name><id>.<specifier>:<name><id>.<specifier>:<name><id>
  // specifier is one of f (processor), p (port) or s (slot)
  // name is any character in [a-zA-Z_]
  // id is either: number or list of ranges (e.g. [1, 4-8, 10])

  static const int type_specifier = 1;
  static const int name_group = 2;
  static const int range_group = 3;
  static const int first_range_id = 1;
  static const int end_range_id = 2;

  ConnectionRule rule;
  SingleConnectionRule single_rules[2];

  std::string expr("^(?:(f|p|s)\\:)?([a-zA-Z]*(?:[ -_][a-zA-Z]+)*)[ "
                   "]*((?:\\d+)|(?:\\([\\d,\\-]+\\)))?$");
  std::regex re(expr);
  std::smatch match;

  int startid;
  int endid;
  int current_rule_part = 0;
  int current_connection_part;

  // split on "="
  auto rule_parts = split(rulestring, '=');

  if (rule_parts.size() != 2 || rule_parts[0].length() == 0 ||
      rule_parts[1].length() == 0) {
    throw std::runtime_error(
        "Error parsing connection rule. Use the following pattern: "
        "[upstream] = [downstream].");
  }

  for (auto &rule_part : rule_parts) {
    rule_part =
        std::regex_replace(rule_part, std::regex("^ +| +$"), std::string(""));
    // split on "."
    auto connection_parts = split(rule_part, '.');

    if (connection_parts.size() > 3) {
      throw std::runtime_error("Error parsing connection rule. "
                               "Port/slot address can have at most 3 parts "
                               "(given: " +
                               rule_part + ").");
    }

    current_connection_part = 0;

    std::list<NodePart> available_specifiers{PROCESSOR, PORT, SLOT};
    NodePart specifier;

    for (auto &connection_part : connection_parts) {
      // match regular expression
      if (!std::regex_match(connection_part, match, re)) {
        throw std::runtime_error("Error parsing connection rule. "
                                 "Cannot parse part of address: " +
                                 connection_part + ".");
      }
      // parse part specifier

      if (!match[type_specifier].matched) {

        // get next available specifier
        specifier = available_specifiers.front();

        available_specifiers.pop_front();

      } else {
        // check if specifier is available
        std::string type_spec = match[type_specifier].str();

        if (type_spec == "f") {
          specifier = PROCESSOR;
        } else if (type_spec == "p") {
          specifier = PORT;
        } else {
          specifier = SLOT;
        }

        auto it = std::find(available_specifiers.begin(),
                            available_specifiers.end(), specifier);
        if (it == available_specifiers.end()) {
          throw std::runtime_error(
              "Error parsing connection rule. Duplicate address specifier.");
        }

        available_specifiers.remove(specifier);
      }

      // parse part name

      if (!match[name_group].matched && specifier != SLOT) {
        throw std::runtime_error("Error parsing connection rule. "
                                 "Invalid processor or port name: " +
                                 connection_part + ".");
      }

      std::string name = match[name_group].str();

      // parse part identifiers
      std::vector<int> identifiers;
      if (!match[range_group].matched) {
        // match all or default
        if (specifier == SLOT) {
          identifiers.push_back(-1);
        } else {
          identifiers.push_back(MATCH_NONE);
        }
      } else {
        std::string range = match[range_group].str();

        if (range[0] == '(') {
          // match ID range vector
          // remove brackets and spaces
          range.erase(std::remove_if(range.begin(), range.end(),
                                     [](char x) {
                                       return (x == '(' || x == ')' ||
                                               std::isspace(x));
                                     }),
                                     range.end());

          // split on comma
          auto id_range = split(range, ',');

          std::regex re_range("(\\d+)(?:\\-(\\d+))?");
          std::smatch match_range;

          // match start and end id of ranges
          for (const auto &q : id_range) {
            if (std::regex_match(q, match_range, re_range)) {
              startid = stoi(match_range[first_range_id].str());
              if (match_range[end_range_id].matched) {
                endid = stoi(match_range[end_range_id].str());
              } else {
                endid = startid;
              }
              for (auto kk = startid; kk <= endid; kk++) {
                identifiers.push_back(kk);
              }
            } else {
              throw std::runtime_error("Error parsing connection rule. "
                                       "Cannot parse range: " +
                                       q + ".");
            }
          }
        } else {
          // try to convert to int
          try {
            identifiers.push_back(stoi(range));
          } catch (std::invalid_argument &e) {
            throw std::runtime_error("Error parsing connection rule. "
                                     "Cannot parse range: " +
                                     range + ".");
          }
        }
      }

      if(specifier == PORT){
          name = std::regex_replace(name, std::regex("[ _]"), "-");

      }
      // construct ConnectionPart and add to SingleConnectionRule
      single_rules[current_rule_part][current_connection_part] =
          std::make_tuple(specifier, name, identifiers);

      current_connection_part++;
    }

    // TODO: complete missing ConnectionParts of SingleConnectionRule
    // go through available specifiers
    for (auto &k : available_specifiers) {
      if (k == PROCESSOR) {
        throw std::runtime_error("Error parsing connection rule. "
                                 "No processor specified");
      } else if (k == PORT) {
        single_rules[current_rule_part][current_connection_part] =
            std::make_tuple(PORT, std::string(""),
                            std::vector<int>(1, MATCH_NONE));
      } else if (k == SLOT) {
        single_rules[current_rule_part][current_connection_part] =
            std::make_tuple(SLOT, std::string(""), std::vector<int>(1, -1));
      }
      current_connection_part++;
    }
    current_rule_part++;
  }

  // construct ConnectionRule from both SingleConnectionRules
  rule = std::make_pair(single_rules[0], single_rules[1]);

  return rule;
}

std::vector<SlotAddress> expandSingleConnectionRule(SingleConnectionRule rule) {
  std::array<int, 3> index;
  std::array<std::string, 3> names;
  int idx;
  std::array<int, 3> tmp;
  std::string processor;
  std::string port;
  int slot = -1;

  std::vector<SlotAddress> cpoints;

  for (int i = 0; i < 3; i++) {
    idx = std::get<0>(rule[i]);
    index[i] = idx;
    names[i] = std::get<1>(rule[i]);

  }

  for (auto a : std::get<2>(rule[0])) {
    for (auto b : std::get<2>(rule[1])) {
      for (auto c : std::get<2>(rule[2])) {
        tmp[0] = a;
        tmp[1] = b;
        tmp[2] = c;


        for (int d = 0; d < 3; d++) {

          if (index[d] == 0) {   // processor
            if (tmp[d] == MATCH_NONE) {
              processor = names[d];
            } else {
              processor = names[d] + std::to_string(tmp[d]);
            }
          } else if (index[d] == 1) {   // port
            if (tmp[d] == MATCH_NONE) {
              port = names[d];
            } else {
              port = names[d] + std::to_string(tmp[d]);
            }
          } else {   // slot
            slot = tmp[d];
          }
        }

        cpoints.push_back(SlotAddress(processor, port, slot));
      }
    }
  }
  return cpoints;
}

void expandConnectionRule(ConnectionRule rule, StreamConnections &connections) {
  // for output SingleConnectionRule
  auto out = rule.first;
  auto out_points = expandSingleConnectionRule(out);

  // for input SingleConnectionRule
  auto in = rule.second;
  auto in_points = expandSingleConnectionRule(in);

  if (out_points.size() != 1 && out_points.size() != in_points.size()) {
    throw std::runtime_error("Invalid connection rule: number of outputs and "
                             "inputs does not match.");
  }

  if (out_points.size() == 1) {
    for (int i = 0; i < (int)in_points.size(); i++) {
      connections.push_back(std::make_pair(out_points[0], in_points[i]));
    }
  } else {
    for (int i = 0; i < (int)out_points.size(); i++) {
      connections.push_back(std::make_pair(out_points[i], in_points[i]));
    }
  }
}

void printConnectionPart(const ConnectionPart &part) {
  std::cout << std::get<0>(part);
  std::cout << std::get<1>(part);

  auto v = std::get<2>(part);

  if (v.size() > 0 && v[0] >= 0) {
    std::cout << "[";
    for (auto &it : v) {
      std::cout << it << ", ";
    }
    std::cout << "]";
  }
}

void printSingleConnectionRule(const SingleConnectionRule &rule) {
  for (int i = 0; i < 3; i++) {
    printConnectionPart(rule[i]);
    if (i < 2) {
      std::cout << ".";
    }
  }
}

void printConnectionRule(const ConnectionRule &rule) {
  printSingleConnectionRule(rule.first);
  std::cout << " = ";
  printSingleConnectionRule(rule.second);
  std::cout << std::endl;
}

void printConnectionList(const StreamConnections &connections) {
  for (auto &it : connections) {
    std::cout << it.first.string() << "=" << it.second.string() << std::endl;
  }
}