Commit 56de3c98 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

move property list parse into a standalone'libplist' library

parent 922c90ed
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ enable_testing()

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/extra/cmake")
set(CMAKE_CXX_STANDARD 17)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/toolkit)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/core)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})

find_package(Threads)
@@ -17,6 +17,8 @@ include_directories(${CAIRO_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} ${HARFBUZZ_IN
add_definitions(-DFNORDMETRIC_VERSION="unstable")

add_library(signaltk STATIC
    core/plist/plist.cc
    core/plist/plist_parser.cc
    graphics/path.cc
    graphics/brush.cc
    graphics/colour.cc
@@ -28,7 +30,6 @@ add_library(signaltk STATIC
    graphics/rasterize.cc
    graphics/png.cc
    elements/element_factory.cc
    elements/element_spec_parser.cc
    elements/element_tree.cc
    elements/plot/gridlines.cc
    elements/plot/plot_axis.cc
+10 −4
Original line number Diff line number Diff line
@@ -16,17 +16,23 @@ namespace signaltk {
template <typename T>
using PropertyDefinitions = std::unordered_map<
    std::string,
    std::function<ReturnCode (const std::string&, T*)>>;
    std::function<ReturnCode (const plist::Property&, T*)>>;

template<typename T>
ReturnCode configureProperties(
    const PropertyList& plist,
    const PropertyDefinitions<T>& pdefs,
    T* config) {
  for (const auto& prop : plist.properties) {
    const auto& pdef = pdefs.find(prop.first);
  for (const auto& prop : plist) {
    const auto& pdef = pdefs.find(prop.name);
    if (pdef != pdefs.end()) {
      if (auto rc = pdef->second(prop.second, config); !rc.isSuccess()) {
      if (auto rc = pdef->second(prop, config); !rc.isSuccess()) {
        return ReturnCode::errorf(
            "EPARSE",
            "error while parsing property '$0': $1",
            prop.name,
            rc.getMessage());

        return rc;
      }
    }

core/plist/plist.cc

0 → 100644
+34 −0
Original line number Diff line number Diff line
/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2018 Paul Asmuth
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <assert.h>
#include "plist.h"

namespace plist {

PropertyValue::operator const std::string&() const {
  return data;
}

const PropertyValue& Property::get(size_t i) const {
  assert(i < values.size());
  return values[i];
}

const PropertyValue& Property::operator[](size_t i) const {
  assert(i < values.size());
  return values[i];
}

size_t Property::size() const {
  return values.size();
}

} // namespace plist
+37 −0
Original line number Diff line number Diff line
@@ -8,17 +8,30 @@
 * <http://www.gnu.org/licenses/>.
 */
#pragma once
#include <atomic>
#include <memory>
#include <string>
#include <vector>

namespace signaltk {
namespace plist {
struct Property;

struct PropertyList {
  std::vector<std::pair<std::string, std::string>> properties;
  std::vector<std::pair<std::string, std::unique_ptr<PropertyList>>> children;
using PropertyList = std::vector<Property>;

struct PropertyValue {
  std::string data;
  bool is_literal;
  operator const std::string&() const;
};

struct Property {
  std::string name;
  std::vector<PropertyValue> values;
  std::unique_ptr<std::vector<Property>> child;

  const PropertyValue& get(size_t i) const;
  const PropertyValue& operator[](size_t i) const;
  size_t size() const;
};

} // namespace signaltk
} // namespace plist
+89 −40
Original line number Diff line number Diff line
@@ -9,11 +9,13 @@
 */
#include <regex>
#include <iostream>
#include "element_spec_parser.h"
#include "plist_parser.h"
#include "utils/stringutil.h"

namespace signaltk {
namespace plist {
using signaltk::StringUtil;

SpecParser::SpecParser(
PropertyListParser::PropertyListParser(
    const char* input,
    size_t input_len) :
    input_(input),
@@ -22,21 +24,11 @@ SpecParser::SpecParser(
    has_token_(false),
    has_error_(false) {}

ReturnCode SpecParser::parse(PropertyList* plist) {
  if (!parseDefinitions(plist)) {
    return ReturnCode::error(
        "EPARSE",
        StringUtil::format(
            "<$0:$1> $2",
            error_lineno_,
            error_colno_,
            error_msg_));
  }

  return ReturnCode::success();
const std::string& PropertyListParser::get_error() const {
  return error_msg_;
}

bool SpecParser::parseDefinitions(PropertyList* plist) {
bool PropertyListParser::parse(PropertyList* plist) {
  TokenType ttype;
  std::string tbuf;
  while (getToken(&ttype, &tbuf)) {
@@ -48,7 +40,7 @@ bool SpecParser::parseDefinitions(PropertyList* plist) {
  return true;
}

bool SpecParser::parsePropertyOrList(PropertyList* plist) {
bool PropertyListParser::parsePropertyOrList(PropertyList* plist) {
  std::string pname;
  if (!expectAndConsumeString(&pname)) {
    return false;
@@ -83,32 +75,65 @@ bool SpecParser::parsePropertyOrList(PropertyList* plist) {
  }
}

bool SpecParser::parseProperty(const std::string& pname, PropertyList* plist) {
  std::string pvalue;
  if (!expectAndConsumeString(&pvalue)) {
bool PropertyListParser::parseProperty(const std::string& pname, PropertyList* plist) {
  Property prop;
  prop.name = pname;

  TokenType ttype;
  std::string tbuf;
  for (; getToken(&ttype, &tbuf) && ttype != T_SEMICOLON; consumeToken()) {
    PropertyValue pval;
    pval.is_literal = true;

    switch (ttype) {
      case T_COMMA:
        pval.data = ",";
        break;
      case T_LPAREN:
        pval.data = "(";
        break;
      case T_RPAREN:
        pval.data = ")";
        break;
      case T_STRING_QUOTED:
        pval.is_literal = false;
        /* fallthrough */
      case T_STRING:
        pval.data = tbuf;
        break;
      default:
        setError(
            StringUtil::format(
                "unexpected token '$0'; expected one of: STRING, COMMA, LPAREN, RPAREN",
                printToken(ttype, tbuf)));
        return false;
    }

  plist->properties.emplace_back(pname, pvalue);
    prop.values.emplace_back(pval);
  }

  plist->emplace_back(std::move(prop));
  return true;
}

bool SpecParser::parsePropertyList(const std::string& pname, PropertyList* plist) {
  auto pvalue = std::make_unique<PropertyList>();
bool PropertyListParser::parsePropertyList(const std::string& pname, PropertyList* plist) {
  Property prop;
  prop.name = pname;
  prop.child = std::make_unique<PropertyList>();

  TokenType ttype;
  std::string tbuf;
  while (getToken(&ttype, &tbuf) && ttype != T_RCBRACE) {
    if (!parsePropertyOrList(pvalue.get())) {
    if (!parsePropertyOrList(prop.child.get())) {
      return false;
    }
  }

  plist->children.emplace_back(pname, std::move(pvalue));
  plist->emplace_back(std::move(prop));
  return true;
}

bool SpecParser::getToken(
bool PropertyListParser::getToken(
    TokenType* ttype,
    std::string* tbuf) const {
  const char* tbuf_cstr = nullptr;
@@ -124,7 +149,7 @@ bool SpecParser::getToken(
  return ret;
}

bool SpecParser::getToken(
bool PropertyListParser::getToken(
    TokenType* ttype,
    const char** tbuf,
    size_t* tbuf_len) const {
@@ -172,6 +197,12 @@ bool SpecParser::getToken(
  /* single character tokens */
  switch (*input_cur_) {

    case ',': {
      token_type_ = T_COMMA;
      input_cur_++;
      goto return_token;
    }

    case ':': {
      token_type_ = T_COLON;
      input_cur_++;
@@ -184,6 +215,18 @@ bool SpecParser::getToken(
      goto return_token;
    }

    case '(': {
      token_type_ = T_LPAREN;
      input_cur_++;
      goto return_token;
    }

    case ')': {
      token_type_ = T_RPAREN;
      input_cur_++;
      goto return_token;
    }

    case '{': {
      token_type_ = T_LCBRACE;
      input_cur_++;
@@ -209,7 +252,7 @@ bool SpecParser::getToken(
  }

  /* [un]quoted strings */
  token_type_ = T_STRING;
  token_type_ = quote_char ? T_STRING_QUOTED : T_STRING;

  if (quote_char) {
    bool escaped = false;
@@ -258,6 +301,8 @@ bool SpecParser::getToken(
        *input_cur_ != ',' &&
        *input_cur_ != ':' &&
        *input_cur_ != ';' &&
        *input_cur_ != '(' &&
        *input_cur_ != ')' &&
        *input_cur_ != '{' &&
        *input_cur_ != '}' &&
        *input_cur_ != '"' &&
@@ -273,18 +318,18 @@ bool SpecParser::getToken(
return_token:
  has_token_ = true;
  *ttype = token_type_;
  *tbuf = token_buf_.data();
  *tbuf = token_buf_.c_str();
  *tbuf_len = token_buf_.size();
  return true;
}

bool SpecParser::consumeToken() {
bool PropertyListParser::consumeToken() {
  has_token_ = false;
  token_buf_.clear();
  return true;
}

bool SpecParser::expectAndConsumeToken(TokenType desired_type) {
bool PropertyListParser::expectAndConsumeToken(TokenType desired_type) {
  TokenType actual_type;
  const char* tbuf = nullptr;
  size_t tbuf_len = 0;
@@ -307,13 +352,13 @@ bool SpecParser::expectAndConsumeToken(TokenType desired_type) {
  return true;
}

bool SpecParser::expectAndConsumeString(std::string* buf) {
bool PropertyListParser::expectAndConsumeString(std::string* buf) {
  TokenType ttype;
  if (!getToken(&ttype, buf)) {
    return false;
  }

  if (ttype != T_STRING) {
  if (ttype != T_STRING && ttype != T_STRING_QUOTED) {
    setError(
        StringUtil::format(
            "unexpected token; expected: STRING, got: $0",
@@ -326,25 +371,29 @@ bool SpecParser::expectAndConsumeString(std::string* buf) {
  return true;
}

std::string SpecParser::printToken(TokenType type) {
std::string PropertyListParser::printToken(TokenType type) {
  return printToken(type, nullptr, 0);
}

std::string SpecParser::printToken(
std::string PropertyListParser::printToken(
    TokenType type,
    const std::string& buf) {
  return printToken(type, buf.data(), buf.size());
  return printToken(type, buf.c_str(), buf.size());
}

std::string SpecParser::printToken(
std::string PropertyListParser::printToken(
    TokenType type,
    const char* buf,
    size_t buf_len) {
  std::string out;
  switch (type) {
    case T_STRING: out = "STRING"; break;
    case T_STRING_QUOTED: out = "STRING"; break;
    case T_COLON: out = "COLON"; break;
    case T_COMMA: out = "COMMA"; break;
    case T_SEMICOLON: out = "SEMICOLON"; break;
    case T_LPAREN: out = "LPAREN"; break;
    case T_RPAREN: out = "RPAREN"; break;
    case T_LCBRACE: out = "LCBRACE"; break;
    case T_RCBRACE: out = "RCBRACE"; break;
  }
@@ -358,11 +407,11 @@ std::string SpecParser::printToken(
  return out;
}

void SpecParser::setError(const std::string& error) {
void PropertyListParser::setError(const std::string& error) {
  has_error_ = true;
  error_msg_ = error;
  error_lineno_ = 0;
  error_colno_ = 0;
}

} // namespace signaltk
} // namespace plist
Loading