Commit 70793e74 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

improved sexpr representation

parent b3ebc227
Loading
Loading
Loading
Loading
+63 −39
Original line number Diff line number Diff line
@@ -30,67 +30,91 @@
#include <assert.h>
#include "sexpr.h"

using namespace std::placeholders;

namespace plotfx {

const Expr& Expr::operator[](size_t i) const {
  assert(next);
  assert(i < next->size());
  return (*next)[i];
enum class ExprType {
  LIST, VALUE_LITERAL, VALUE
};

struct Expr {
  ExprType type;
  std::string value;
  ExprStorage list;
  ExprStorage next;
};

void expr_destroy(Expr* expr) {
  delete expr;
}

ExprStorage expr_create_list() {
  auto e = ExprStorage(new Expr, bind(&expr_destroy, _1));
  e->type = ExprType::LIST;
  return e;
}

size_t Expr::size() const {
  if (next) {
    return next->size();
  } else {
    return 0;
ExprStorage expr_create_value(const std::string& str) {
  auto e = ExprStorage(new Expr, bind(&expr_destroy, _1));
  e->type = ExprType::VALUE;
  e->value = str;
  return e;
}

ExprStorage expr_create_value_literal(const std::string& str) {
  auto e = ExprStorage(new Expr, bind(&expr_destroy, _1));
  e->type = ExprType::VALUE_LITERAL;
  e->value = str;
  return e;
}

Expr::operator const std::string&() const {
  return value;
const Expr* expr_next(const Expr* expr) {
  return expr->next.get();
}

ExprStorage* expr_get_next_storage(Expr* expr) {
  return &expr->next;
}

bool is_list(const Expr& e) {
  return e.kind == ExprKind::LIST;
bool expr_is_list(const Expr* expr) {
  return expr->type == ExprType::LIST;
}

bool is_value(const Expr& e) {
  switch (e.kind) {
    case ExprKind::VALUE_LITERAL:
    case ExprKind::VALUE:
      return true;
    default:
      return false;
const Expr* expr_get_list(const Expr* expr) {
  return expr->list.get();
}

ExprStorage* expr_get_list_storage(Expr* expr) {
  return &expr->list;
}

bool expr_is_value(const Expr* expr) {
  return expr->type == ExprType::VALUE || expr->type == ExprType::VALUE_LITERAL;
}

bool is_value(const Expr& e, const std::string& cmp) {
  switch (e.kind) {
    case ExprKind::VALUE_LITERAL:
    case ExprKind::VALUE:
      break;
    default:
      return false;
bool expr_is_value(const Expr* expr, const std::string& cmp) {
  return expr_is_value(expr) && expr->value == cmp;
}

  return e.value == cmp;
bool expr_is_value_literal(const Expr* expr) {
  return expr->type == ExprType::VALUE_LITERAL;
}

bool is_value_literal(const Expr& e) {
  return e.kind == ExprKind::VALUE_LITERAL;
bool expr_is_value_literal(const Expr* expr, const std::string& cmp) {
  return expr->type == ExprType::VALUE_LITERAL && expr->value == cmp;
}

bool is_value_literal(const Expr& e, const std::string& cmp) {
  return e.kind == ExprKind::VALUE_LITERAL && e.value == cmp;
bool expr_is_value_quoted(const Expr* expr) {
  return expr->type == ExprType::VALUE;
}

bool is_value_quoted(const Expr& e) {
  return e.kind == ExprKind::VALUE;
bool expr_is_value_quoted(const Expr* expr, const std::string& cmp) {
  return expr->type == ExprType::VALUE && expr->value == cmp;
}

bool is_value_quoted(const Expr& e, const std::string& cmp) {
  return e.kind == ExprKind::VALUE && e.value == cmp;
const std::string& expr_get_value(const Expr* expr) {
  return expr->value;
}

} // namespace plotfx
+18 −21
Original line number Diff line number Diff line
@@ -28,36 +28,33 @@
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <vector>

namespace plotfx {
struct Property;

enum class ExprKind {
  LIST, VALUE, VALUE_LITERAL
};
struct Expr;
using ExprStorage = std::unique_ptr<Expr, std::function<void (Expr*)>>;

struct Expr {
  ExprKind kind;
  std::unique_ptr<std::vector<Expr>> next;
  std::string value;
ExprStorage expr_create_list();
ExprStorage expr_create_value(const std::string& str);
ExprStorage expr_create_value_literal(const std::string& str);

  const Expr& operator[](size_t i) const;
  size_t size() const;
const Expr* expr_next(const Expr* expr);
ExprStorage* expr_get_next_storage(Expr* expr);

  operator const std::string&() const;
bool expr_is_list(const Expr* expr);
const Expr* expr_get_list(const Expr* expr);
ExprStorage* expr_get_list_storage(Expr* expr);

};

bool is_list(const Property& prop);
bool is_value(const Property& prop);
bool is_value(const Property& prop, const std::string& cmp);
bool is_value_literal(const Property& prop);
bool is_value_literal(const Property& prop, const std::string& cmp);
bool is_value_quoted(const Property& prop);
bool is_value_quoted(const Property& prop, const std::string& cmp);
bool expr_is_value(const Expr* expr);
bool expr_is_value(const Expr* expr, const std::string& cmp);
bool expr_is_value_literal(const Expr* expr);
bool expr_is_value_literal(const Expr* expr, const std::string& cmp);
bool expr_is_value_quoted(const Expr* expr);
bool expr_is_value_quoted(const Expr* expr, const std::string& cmp);
const std::string& expr_get_value(const Expr* expr);

} // namespace plotfx
+29 −23
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ namespace plotfx {
ReturnCode expr_parse_literal(
    const char** cur,
    const char* end,
    Expr* parent) {
    ExprStorage* expr) {
  std::string literal;

  for (bool match = true; *cur != end && match;) {
@@ -46,6 +46,9 @@ ReturnCode expr_parse_literal(
      case '\n':
      case '\t':
      case '\r':
      case '\\':
      case '\'':
      case '\"':
      case '(':
      case ')':
        match = false;
@@ -57,11 +60,7 @@ ReturnCode expr_parse_literal(
    }
  }

  Expr e;
  e.kind = ExprKind::VALUE_LITERAL;
  e.value = std::move(literal);
  parent->next->emplace_back(std::move(e));

  *expr = expr_create_value_literal(std::move(literal));
  return OK;
}

@@ -89,7 +88,7 @@ ReturnCode expr_parse_string_escape(
ReturnCode expr_parse_string(
    const char** cur,
    const char* end,
    Expr* parent) {
    ExprStorage* expr) {
  auto quote_char = *(*cur)++;

  std::string string;
@@ -113,24 +112,18 @@ ReturnCode expr_parse_string(
    ++(*cur);
  }

  Expr e;
  e.kind = ExprKind::VALUE;
  e.value = std::move(string);
  parent->next->emplace_back(std::move(e));
  *expr = expr_create_value(std::move(string));
  return OK;
}

ReturnCode expr_parse(
    const char* input,
    size_t input_len,
    Expr* expr) {
    ExprStorage* expr) {
  auto cur = input;
  auto end = input + input_len;

  expr->kind = ExprKind::LIST;
  expr->next.reset(new std::vector<Expr>());

  std::stack<Expr*> stack;
  std::stack<ExprStorage*> stack;
  stack.push(expr);

  while (cur != end) {
@@ -143,12 +136,14 @@ ReturnCode expr_parse(
        continue;

      case '(': {
        Expr e;
        e.kind = ExprKind::LIST;
        e.next.reset(new std::vector<Expr>());
        *stack.top() = expr_create_list();

        auto next = expr_get_next_storage(stack.top()->get());
        auto list = expr_get_list_storage(stack.top()->get());

        stack.top()->next->emplace_back(std::move(e));
        stack.push(&stack.top()->next->back());
        stack.pop();
        stack.push(next);
        stack.push(list);

        ++cur;
        continue;
@@ -167,6 +162,9 @@ ReturnCode expr_parse(
      case '\'':
      case '"': {
        if (auto rc = expr_parse_string(&cur, end, stack.top()); rc) {
          auto next = expr_get_next_storage(stack.top()->get());
          stack.pop();
          stack.push(next);
          continue;
        } else {
          return rc;
@@ -175,13 +173,21 @@ ReturnCode expr_parse(

      default:
        if (auto rc = expr_parse_literal(&cur, end, stack.top()); rc) {
          continue;
          auto next = expr_get_next_storage(stack.top()->get());
          stack.pop();
          stack.push(next);
        } else {
          return rc;
        }
    }
  }

  stack.pop();

  if (!stack.empty()) {
    return ReturnCode::error("EARG", "unbalanced parens");
  }

  return OK;
}

+1 −1
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ namespace plotfx {
ReturnCode expr_parse(
    const char* input,
    size_t input_len,
    Expr* expr);
    ExprStorage* expr);

} // namespace plotfx
+53 −53
Original line number Diff line number Diff line
@@ -58,76 +58,76 @@ void test_parse_literals() {
  std::string confstr =
      R"(1337 hello world)";

  Expr e;
  EXPECT_OK(expr_parse(confstr.data(), confstr.size(), &e));

  EXPECT_EQ(e.kind, ExprKind::LIST);
  EXPECT_EQ(e.size(), 3);
  EXPECT_EQ(e[0].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[0], "1337");
  EXPECT_EQ(e[1].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[1], "hello");
  EXPECT_EQ(e[2].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[2], "world");
  ExprStorage es;
  EXPECT_OK(expr_parse(confstr.data(), confstr.size(), &es));

  const Expr* e = es.get();
  EXPECT(expr_is_value_literal(e, "1337"));
  EXPECT((e = expr_next(e)) != nullptr);
  EXPECT(expr_is_value_literal(e, "hello"));
  EXPECT((e = expr_next(e)) != nullptr);
  EXPECT(expr_is_value_literal(e, "world"));
  EXPECT((e = expr_next(e)) == nullptr);
}

void test_parse_lists() {
  std::string confstr =
      R"((1337 hello (world galaxy) xxx) yyy)";

  Expr e;
  EXPECT_OK(expr_parse(confstr.data(), confstr.size(), &e));

  EXPECT_EQ(e.kind, ExprKind::LIST);
  EXPECT_EQ(e.size(), 2);
  EXPECT_EQ(e[0].kind, ExprKind::LIST);
  EXPECT_EQ(e[0].size(), 4);
  EXPECT_EQ(e[0][0].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[0][0], "1337");
  EXPECT_EQ(e[0][1].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[0][1], "hello");
  EXPECT_EQ(e[0][2].kind, ExprKind::LIST);
  EXPECT_EQ(e[0][2].size(), 2);
  EXPECT_EQ(e[0][2][0].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[0][2][0], "world");
  EXPECT_EQ(e[0][2][1].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[0][2][1], "galaxy");
  EXPECT_EQ(e[0][3].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[0][3], "xxx");
  EXPECT_EQ(e[1].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[1], "yyy");
      R"(aaa (1337 hello (world galaxy) xxx) yyy)";

  ExprStorage es;
  EXPECT_OK(expr_parse(confstr.data(), confstr.size(), &es));

  const Expr* e = es.get();
  EXPECT(expr_is_value_literal(e, "aaa"));
  EXPECT((e = expr_next(e)) != nullptr);
  EXPECT(expr_is_list(e));
  auto l1 = expr_get_list(e);
  EXPECT(expr_is_value_literal(l1, "1337"));
  EXPECT((l1 = expr_next(l1)) != nullptr);
  EXPECT(expr_is_value_literal(l1, "hello"));
  EXPECT((l1 = expr_next(l1)) != nullptr);
  EXPECT(expr_is_list(l1));
  auto l2 = expr_get_list(l1);
  EXPECT(expr_is_value_literal(l2, "world"));
  EXPECT((l2 = expr_next(l2)) != nullptr);
  EXPECT(expr_is_value_literal(l2, "galaxy"));
  EXPECT((l2 = expr_next(l2)) == nullptr);
  EXPECT((l1 = expr_next(l1)) != nullptr);
  EXPECT(expr_is_value_literal(l1, "xxx"));
  EXPECT((l1 = expr_next(l1)) == nullptr);
  EXPECT((e = expr_next(e)) != nullptr);
  EXPECT(expr_is_value_literal(e, "yyy"));
  EXPECT((e = expr_next(e)) == nullptr);
}

void test_parse_strings() {
  std::string confstr =
      R"(1337 "hello" 'world')";

  Expr e;
  EXPECT_OK(expr_parse(confstr.data(), confstr.size(), &e));

  EXPECT_EQ(e.kind, ExprKind::LIST);
  EXPECT_EQ(e.size(), 3);
  EXPECT_EQ(e[0].kind, ExprKind::VALUE_LITERAL);
  EXPECT_STREQ(e[0], "1337");
  EXPECT_EQ(e[1].kind, ExprKind::VALUE);
  EXPECT_STREQ(e[1], "hello");
  EXPECT_EQ(e[2].kind, ExprKind::VALUE);
  EXPECT_STREQ(e[2], "world");
  ExprStorage es;
  EXPECT_OK(expr_parse(confstr.data(), confstr.size(), &es));

  const Expr* e = es.get();
  EXPECT(expr_is_value_literal(e, "1337"));
  EXPECT((e = expr_next(e)) != nullptr);
  EXPECT(expr_is_value_quoted(e, "hello"));
  EXPECT((e = expr_next(e)) != nullptr);
  EXPECT(expr_is_value_quoted(e, "world"));
  EXPECT((e = expr_next(e)) == nullptr);
}

void test_parse_string_escapes() {
  std::string confstr =
      R"("escape \" me" "hello \\ world")";

  Expr e;
  EXPECT_OK(expr_parse(confstr.data(), confstr.size(), &e));
  ExprStorage es;
  EXPECT_OK(expr_parse(confstr.data(), confstr.size(), &es));

  EXPECT_EQ(e.kind, ExprKind::LIST);
  EXPECT_EQ(e.size(), 2);
  EXPECT_EQ(e[0].kind, ExprKind::VALUE);
  EXPECT_STREQ(e[0], "escape \" me");
  EXPECT_EQ(e[1].kind, ExprKind::VALUE);
  EXPECT_STREQ(e[1], "hello \\ world");
  const Expr* e = es.get();
  EXPECT(expr_is_value(e, "escape \" me"));
  EXPECT((e = expr_next(e)) != nullptr);
  EXPECT(expr_is_value(e, "hello \\ world"));
  EXPECT((e = expr_next(e)) == nullptr);
}

int main() {