Commit 2627db1d authored by Paul Asmuth's avatar Paul Asmuth
Browse files

implement 'var' variable references

parent 6d60fca2
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -38,11 +38,13 @@ Output File (`example_chart.svg`):
Input File (`example_chart.plot`):

    plot {
      data: csv('city_temperatures.csv');
      data: csv('testdata/city_temperatures.csv');
      x: var(month);
      y: var(temperature);
      group: var(city);

      x: $month;
      y: $temperature;
      group: $city;
      axis-y-min: -10;
      axis-y-max: 32;

      layer {
        type: lines;
@@ -50,7 +52,7 @@ Input File (`example_chart.plot`):

      layer {
        type: points;
        point-size: 3pt;
        size: 3.3pt;
      }
    }

+81 −21
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include "config_helpers.h"
#include "utils/fileutil.h"
#include "utils/csv.h"
#include "utils/algo.h"
#include <iostream>

using namespace std::placeholders;
@@ -171,9 +172,9 @@ ReturnCode configure_string(
  return OK;
}

ReturnCode parse_data_frame_csv(
ReturnCode parse_datasource_csv(
    const plist::Property& prop ,
    DataFrame* data) {
    DataContext* ctx) {
  if (!plist::is_enum(prop, "csv")) {
    return ERROR_INVALID_ARGUMENT;
  }
@@ -182,18 +183,23 @@ ReturnCode parse_data_frame_csv(
    return ERROR_INVALID_ARGUMENT; // FIXME
  }

  const auto& csv_path = prop[0].value;
  auto csv_data_str = FileUtil::read(csv_path).toString();
  auto csv_data = CSVData{};
  auto csv_opts = CSVParserConfig{};
  std::string csv_path;
  bool csv_headers = true;
  for (size_t i = 0; i < prop.size(); ++i) {
    if (i == 0) {
      csv_path = prop[i].value;
      continue;
    }

  for (size_t i = 1; i < prop.size(); ++i) {
    if (prop[i].value == "noheaders") {
      csv_opts.headers = false;
      csv_headers = false;
      continue;
    }
  }

  auto csv_data_str = FileUtil::read(csv_path).toString();
  auto csv_data = CSVData{};
  CSVParserConfig csv_opts;
  if (auto rc = parseCSV(csv_data_str, csv_opts, &csv_data); !rc) {
    return rc;
  }
@@ -206,28 +212,52 @@ ReturnCode parse_data_frame_csv(
  }

  for (size_t i = 0; i < column_count; ++i) {
    DataColumn column;
    column.name = std::to_string(i);
    for (const auto& row : csv_data) {
      column.data.push_back(row[i]);
    std::string series_name;

    auto row = csv_data.begin();
    if (csv_headers &&
        row != csv_data.end() &&
        row->size() > i) {
      series_name = (row++)->at(i);
    } else {
      series_name = std::to_string(i);
    }

    data->columns.emplace_back(column);
    auto series = std::make_shared<Series>();
    for (; row != csv_data.end(); ++row) {
      if (row->size() > i) {
        series->emplace_back(row->at(i));
      } else {
        series->emplace_back();
      }
    }

    ctx->by_name[series_name] = series;
  }

  return OK;
}

ReturnCode configure_data_frame(
ReturnCode configure_datasource_prop(
    const plist::Property& prop,
    DataFrame* data) {
    DataContext* data) {
  if (plist::is_enum(prop, "csv")) {
    return parse_data_frame_csv(prop, data);
    return parse_datasource_csv(prop, data);
  }

  return ERROR_INVALID_ARGUMENT;
}

ReturnCode configure_datasource(
    const plist::PropertyList& plist,
    DataContext* data) {
  static const ParserDefinitions pdefs = {
    {"data", bind(&configure_datasource_prop, _1, data)},
  };

  return parseAll(plist, pdefs);
}

ParserFn configure_key(std::string* key) {
  return [key] (const plist::Property& prop) -> ReturnCode {
    if (plist::is_value(prop) && prop.value.size() > 0 && prop.value[0] == '$') {
@@ -269,7 +299,6 @@ ReturnCode parse_data_series_csv(

  for (size_t i = 2; i < prop.size(); ++i) {
    if (prop[i].value == "noheaders") {
      csv_opts.headers = false;
      continue;
    }
  }
@@ -307,13 +336,40 @@ ReturnCode parse_data_series_inline(
  return OK;
}

ReturnCode parse_data_series_var(
    const plist::Property& prop,
    const DataContext& ctx,
    SeriesRef* data) {
  if (!plist::is_enum(prop, "var")) {
    return ERROR_INVALID_ARGUMENT;
  }

  if (prop.size() != 1) {
    return ReturnCode::errorf("EARG", "var() takes exactly one argument, got: $0", prop.size());
  }

  const auto& var_name = prop[0].value;
  auto var_data = find_maybe(ctx.by_name, var_name);
  if (!var_data) {
    return ReturnCode::errorf("EARG", "variable not found: '$0'", var_name);
  }

  *data = var_data;
  return OK;
}

ReturnCode configure_series(
    const plist::Property& prop,
    const DataContext& ctx,
    SeriesRef* data) {
  if (plist::is_enum(prop, "csv")) {
    return parse_data_series_csv(prop, data);
  }

  if (plist::is_enum(prop, "var")) {
    return parse_data_series_var(prop, ctx, data);
  }

  if (plist::is_list(prop)) {
    return parse_data_series_inline(prop, data);
  }
@@ -321,16 +377,20 @@ ReturnCode configure_series(
  return ERROR_INVALID_ARGUMENT;
}

ParserFn configure_series_fn(SeriesRef* series) {
  return bind(&configure_series, _1, series);
ParserFn configure_series_fn(
    const DataContext& ctx,
    SeriesRef* series) {
  return bind(&configure_series, _1, ctx, series);
}

ParserFn configure_var(
    SeriesRef* series,
    const DataContext& ctx,
    ParserFn parser) {
  return [=] (const plist::Property& prop) -> ReturnCode {
    if (plist::is_enum(prop, "csv")) {
      return configure_series(prop, series);
    if (plist::is_enum(prop, "csv") ||
        plist::is_enum(prop, "var")) {
      return configure_series(prop, ctx, series);
    } else {
      return parser(prop);
    }
+14 −4
Original line number Diff line number Diff line
@@ -64,7 +64,10 @@ ReturnCode parse_classlike(
template <typename T>
ParserFn configure_opt(ParserFn parser);

ParserFn configure_var(SeriesRef* series, ParserFn parser);
ParserFn configure_var(
    SeriesRef* series,
    const DataContext& ctx,
    ParserFn parser);

ParserFn configure_multiprop(const std::vector<ParserFn>& parsers);

@@ -95,15 +98,22 @@ ReturnCode configure_float_opt(
    const plist::Property& prop,
    std::optional<double>* value);

ReturnCode configure_data_frame(
ReturnCode configure_datasource_prop(
    const plist::Property& prop,
    DataFrame* data);
    DataContext* data);

ReturnCode configure_datasource(
    const plist::PropertyList& plist,
    DataContext* data);

ReturnCode configure_series(
    const plist::Property& prop,
    const DataContext& ctx,
    SeriesRef* data);

ParserFn configure_series_fn(SeriesRef* data);
ParserFn configure_series_fn(
    const DataContext& ctx,
    SeriesRef* data);

} // namespace plotfx

+0 −10
Original line number Diff line number Diff line
@@ -39,16 +39,6 @@ using Series = std::vector<Value>;
using SeriesRef = std::shared_ptr<const Series>;
using SeriesMap = std::unordered_map<std::string, SeriesRef>;

// FIXME: rename to column?
struct DataColumn {
  std::string name;
  Series data;
};

struct DataFrame {
  std::vector<DataColumn> columns;
};

struct DataContext {
  SeriesMap by_name;
  SeriesMap defaults;
+1 −5
Original line number Diff line number Diff line
@@ -61,11 +61,7 @@ ReturnCode parseCSV(

      if (byte == opts.line_separator) {
        ++row_index;

        if (row_index > 1 || !opts.headers) {
        output->push_back(row);
        }

        row.clear();
      }

Loading