Skip to content

expression_parser.hpp

SECTIONData Structure INCLUDEnoya/expression_parser.hpp

解析并计算带括号和运算符优先级的表达式;适合题目要求实现算术语法或把字符串转为表达式树。

Complexity: Time: O(t log o) for t tokens and ordered operator tables. Space: O(t) parse stacks/output.

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include

/// @complexity Time: O(t log o) for t tokens and ordered operator tables.
/// Space: O(t) parse stacks/output.

#include <algorithm>
#include <cctype>
#include <cstddef>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

namespace noya {

struct expression_binary_operator {
  std::string opr;
  int pri = 0;
  bool ras = false;
};

struct expression_unary_operator {
  std::string opr;
  int pri = 0;
};

struct expression_node {
  enum class kind { operand, unary, binary } tp = kind::operand;
  std::string tok;
  int ls = -1;
  int rs = -1;
};

struct expression_tree {
  std::vector<expression_node> tr;
  int rt = -1;
};

struct expression_parse_error {
  std::size_t ps = 0;
  std::string msg;
};

struct expression_parse_result {
  std::optional<expression_tree> t;
  std::optional<expression_parse_error> err;

  explicit operator bool() const { return t.has_value(); }
};

/// @brief Parse identifiers, integers, parentheses, configurable binary
/// operators, and configurable prefix unary operators into an expression tree.
inline expression_parse_result
parse_expression(std::string_view s,
                 const std::vector<expression_binary_operator> &bop,
                 const std::vector<expression_unary_operator> &uop = {}) {
  struct token {
    enum class kind {
      operand,
      operation,
      left_parenthesis,
      right_parenthesis,
      end
    } tp = kind::end;
    std::string s0;
    std::size_t pos = 0;
  };

  std::vector<std::string> ops;
  for (const auto &op : bop) {
    if (op.opr.empty() || op.pri < 0) {
      return {{}, expression_parse_error{0, "invalid binary operator table"}};
    }
    ops.push_back(op.opr);
  }
  for (const auto &op : uop) {
    if (op.opr.empty() || op.pri < 0) {
      return {{}, expression_parse_error{0, "invalid unary operator table"}};
    }
    ops.push_back(op.opr);
  }
  std::sort(ops.begin(), ops.end(), [](const auto &a, const auto &b) {
    if (a.size() != b.size()) {
      return a.size() > b.size();
    }
    return a < b;
  });
  ops.erase(std::unique(ops.begin(), ops.end()), ops.end());

  std::vector<token> ts;
  for (std::size_t pos = 0; pos < s.size();) {
    unsigned char ch = s[pos];
    if (std::isspace(ch)) {
      pos++;
      continue;
    }
    if (ch == '(' || ch == ')') {
      ts.push_back({ch == '(' ? token::kind::left_parenthesis
                              : token::kind::right_parenthesis,
                    std::string(1, char(ch)), pos});
      pos++;
      continue;
    }
    if (std::isalnum(ch) || ch == '_') {
      std::size_t end = pos + 1;
      while (end < s.size()) {
        unsigned char nxt = s[end];
        if (!std::isalnum(nxt) && nxt != '_') {
          break;
        }
        end++;
      }
      ts.push_back(
          {token::kind::operand, std::string(s.substr(pos, end - pos)), pos});
      pos = end;
      continue;
    }
    auto it0 =
        std::find_if(ops.begin(), ops.end(), [&](const std::string &opr) {
          return s.substr(pos, opr.size()) == opr;
        });
    if (it0 == ops.end()) {
      return {{}, expression_parse_error{pos, "unknown token"}};
    }
    ts.push_back({token::kind::operation, *it0, pos});
    pos += it0->size();
  }
  ts.push_back({token::kind::end, {}, s.size()});

  expression_tree t;
  std::size_t cur = 0;
  std::optional<expression_parse_error> err;
  auto bad = [&](std::string msg) {
    if (!err) {
      err = expression_parse_error{ts[cur].pos, std::move(msg)};
    }
    return -1;
  };
  auto bin = [&](const std::string &opr) -> const expression_binary_operator * {
    auto it = std::find_if(bop.begin(), bop.end(),
                           [&](const auto &op) { return op.opr == opr; });
    return it == bop.end() ? nullptr : &*it;
  };
  auto una = [&](const std::string &opr) -> const expression_unary_operator * {
    auto it = std::find_if(uop.begin(), uop.end(),
                           [&](const auto &op) { return op.opr == opr; });
    return it == uop.end() ? nullptr : &*it;
  };
  auto ins = [&](expression_node u) {
    t.tr.push_back(std::move(u));
    return int(t.tr.size()) - 1;
  };

  auto dfs = [&](auto &f, int lo) -> int {
    int ls = -1;
    if (ts[cur].tp == token::kind::operand) {
      ls = ins({expression_node::kind::operand, ts[cur].s0, -1, -1});
      cur++;
    } else if (ts[cur].tp == token::kind::left_parenthesis) {
      cur++;
      ls = f(f, 0);
      if (ls == -1) {
        return -1;
      }
      if (ts[cur].tp != token::kind::right_parenthesis) {
        return bad("missing closing parenthesis");
      }
      cur++;
    } else if (ts[cur].tp == token::kind::operation) {
      const auto *op = una(ts[cur].s0);
      if (op == nullptr) {
        return bad("expected operand or prefix unary operator");
      }
      std::string opr = ts[cur++].s0;
      int ch0 = f(f, op->pri);
      if (ch0 == -1) {
        return -1;
      }
      ls = ins({expression_node::kind::unary, std::move(opr), ch0, -1});
    } else {
      return bad("expected operand");
    }

    while (ts[cur].tp == token::kind::operation) {
      const auto *op = bin(ts[cur].s0);
      if (op == nullptr || op->pri < lo) {
        break;
      }
      std::string opr = ts[cur++].s0;
      int np = op->pri + (op->ras ? 0 : 1);
      int rs = f(f, np);
      if (rs == -1) {
        return -1;
      }
      ls = ins({expression_node::kind::binary, std::move(opr), ls, rs});
    }
    return ls;
  };

  t.rt = dfs(dfs, 0);
  if (t.rt == -1) {
    return {{}, err};
  }
  if (ts[cur].tp != token::kind::end) {
    bad(ts[cur].tp == token::kind::right_parenthesis
            ? "unmatched closing parenthesis"
            : "unexpected token after expression");
    return {{}, err};
  }
  return {std::move(t), {}};
}

} // namespace noya
#ifndef NOYA_EXPRESSION_PARSER_HPP
#define NOYA_EXPRESSION_PARSER_HPP 1

/// @complexity Time: O(t log o) for t tokens and ordered operator tables.
/// Space: O(t) parse stacks/output.

#include <algorithm>
#include <cctype>
#include <cstddef>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

namespace noya {

struct expression_binary_operator {
  std::string opr;
  int pri = 0;
  bool ras = false;
};

struct expression_unary_operator {
  std::string opr;
  int pri = 0;
};

struct expression_node {
  enum class kind { operand, unary, binary } tp = kind::operand;
  std::string tok;
  int ls = -1;
  int rs = -1;
};

struct expression_tree {
  std::vector<expression_node> tr;
  int rt = -1;
};

struct expression_parse_error {
  std::size_t ps = 0;
  std::string msg;
};

struct expression_parse_result {
  std::optional<expression_tree> t;
  std::optional<expression_parse_error> err;

  explicit operator bool() const { return t.has_value(); }
};

/// @brief Parse identifiers, integers, parentheses, configurable binary
/// operators, and configurable prefix unary operators into an expression tree.
inline expression_parse_result
parse_expression(std::string_view s,
                 const std::vector<expression_binary_operator> &bop,
                 const std::vector<expression_unary_operator> &uop = {}) {
  struct token {
    enum class kind {
      operand,
      operation,
      left_parenthesis,
      right_parenthesis,
      end
    } tp = kind::end;
    std::string s0;
    std::size_t pos = 0;
  };

  std::vector<std::string> ops;
  for (const auto &op : bop) {
    if (op.opr.empty() || op.pri < 0) {
      return {{}, expression_parse_error{0, "invalid binary operator table"}};
    }
    ops.push_back(op.opr);
  }
  for (const auto &op : uop) {
    if (op.opr.empty() || op.pri < 0) {
      return {{}, expression_parse_error{0, "invalid unary operator table"}};
    }
    ops.push_back(op.opr);
  }
  std::sort(ops.begin(), ops.end(), [](const auto &a, const auto &b) {
    if (a.size() != b.size()) {
      return a.size() > b.size();
    }
    return a < b;
  });
  ops.erase(std::unique(ops.begin(), ops.end()), ops.end());

  std::vector<token> ts;
  for (std::size_t pos = 0; pos < s.size();) {
    unsigned char ch = s[pos];
    if (std::isspace(ch)) {
      pos++;
      continue;
    }
    if (ch == '(' || ch == ')') {
      ts.push_back({ch == '(' ? token::kind::left_parenthesis
                              : token::kind::right_parenthesis,
                    std::string(1, char(ch)), pos});
      pos++;
      continue;
    }
    if (std::isalnum(ch) || ch == '_') {
      std::size_t end = pos + 1;
      while (end < s.size()) {
        unsigned char nxt = s[end];
        if (!std::isalnum(nxt) && nxt != '_') {
          break;
        }
        end++;
      }
      ts.push_back(
          {token::kind::operand, std::string(s.substr(pos, end - pos)), pos});
      pos = end;
      continue;
    }
    auto it0 =
        std::find_if(ops.begin(), ops.end(), [&](const std::string &opr) {
          return s.substr(pos, opr.size()) == opr;
        });
    if (it0 == ops.end()) {
      return {{}, expression_parse_error{pos, "unknown token"}};
    }
    ts.push_back({token::kind::operation, *it0, pos});
    pos += it0->size();
  }
  ts.push_back({token::kind::end, {}, s.size()});

  expression_tree t;
  std::size_t cur = 0;
  std::optional<expression_parse_error> err;
  auto bad = [&](std::string msg) {
    if (!err) {
      err = expression_parse_error{ts[cur].pos, std::move(msg)};
    }
    return -1;
  };
  auto bin = [&](const std::string &opr) -> const expression_binary_operator * {
    auto it = std::find_if(bop.begin(), bop.end(),
                           [&](const auto &op) { return op.opr == opr; });
    return it == bop.end() ? nullptr : &*it;
  };
  auto una = [&](const std::string &opr) -> const expression_unary_operator * {
    auto it = std::find_if(uop.begin(), uop.end(),
                           [&](const auto &op) { return op.opr == opr; });
    return it == uop.end() ? nullptr : &*it;
  };
  auto ins = [&](expression_node u) {
    t.tr.push_back(std::move(u));
    return int(t.tr.size()) - 1;
  };

  auto dfs = [&](auto &f, int lo) -> int {
    int ls = -1;
    if (ts[cur].tp == token::kind::operand) {
      ls = ins({expression_node::kind::operand, ts[cur].s0, -1, -1});
      cur++;
    } else if (ts[cur].tp == token::kind::left_parenthesis) {
      cur++;
      ls = f(f, 0);
      if (ls == -1) {
        return -1;
      }
      if (ts[cur].tp != token::kind::right_parenthesis) {
        return bad("missing closing parenthesis");
      }
      cur++;
    } else if (ts[cur].tp == token::kind::operation) {
      const auto *op = una(ts[cur].s0);
      if (op == nullptr) {
        return bad("expected operand or prefix unary operator");
      }
      std::string opr = ts[cur++].s0;
      int ch0 = f(f, op->pri);
      if (ch0 == -1) {
        return -1;
      }
      ls = ins({expression_node::kind::unary, std::move(opr), ch0, -1});
    } else {
      return bad("expected operand");
    }

    while (ts[cur].tp == token::kind::operation) {
      const auto *op = bin(ts[cur].s0);
      if (op == nullptr || op->pri < lo) {
        break;
      }
      std::string opr = ts[cur++].s0;
      int np = op->pri + (op->ras ? 0 : 1);
      int rs = f(f, np);
      if (rs == -1) {
        return -1;
      }
      ls = ins({expression_node::kind::binary, std::move(opr), ls, rs});
    }
    return ls;
  };

  t.rt = dfs(dfs, 0);
  if (t.rt == -1) {
    return {{}, err};
  }
  if (ts[cur].tp != token::kind::end) {
    bad(ts[cur].tp == token::kind::right_parenthesis
            ? "unmatched closing parenthesis"
            : "unexpected token after expression");
    return {{}, err};
  }
  return {std::move(t), {}};
}

} // namespace noya

#endif // NOYA_EXPRESSION_PARSER_HPP
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

/// @complexity Time: O(t log o) for t tokens and ordered operator tables.
/// Space: O(t) parse stacks/output.

namespace noya {

struct expression_binary_operator {
  std::string opr;
  int pri = 0;
  bool ras = false;
};

struct expression_unary_operator {
  std::string opr;
  int pri = 0;
};

struct expression_node {
  enum class kind { operand, unary, binary } tp = kind::operand;
  std::string tok;
  int ls = -1;
  int rs = -1;
};

struct expression_tree {
  std::vector<expression_node> tr;
  int rt = -1;
};

struct expression_parse_error {
  std::size_t ps = 0;
  std::string msg;
};

struct expression_parse_result {
  std::optional<expression_tree> t;
  std::optional<expression_parse_error> err;

  explicit operator bool() const { return t.has_value(); }
};

/// @brief Parse identifiers, integers, parentheses, configurable binary
/// operators, and configurable prefix unary operators into an expression tree.
inline expression_parse_result
parse_expression(std::string_view s,
                 const std::vector<expression_binary_operator> &bop,
                 const std::vector<expression_unary_operator> &uop = {}) {
  struct token {
    enum class kind {
      operand,
      operation,
      left_parenthesis,
      right_parenthesis,
      end
    } tp = kind::end;
    std::string s0;
    std::size_t pos = 0;
  };

  std::vector<std::string> ops;
  for (const auto &op : bop) {
    if (op.opr.empty() || op.pri < 0) {
      return {{}, expression_parse_error{0, "invalid binary operator table"}};
    }
    ops.push_back(op.opr);
  }
  for (const auto &op : uop) {
    if (op.opr.empty() || op.pri < 0) {
      return {{}, expression_parse_error{0, "invalid unary operator table"}};
    }
    ops.push_back(op.opr);
  }
  std::sort(ops.begin(), ops.end(), [](const auto &a, const auto &b) {
    if (a.size() != b.size()) {
      return a.size() > b.size();
    }
    return a < b;
  });
  ops.erase(std::unique(ops.begin(), ops.end()), ops.end());

  std::vector<token> ts;
  for (std::size_t pos = 0; pos < s.size();) {
    unsigned char ch = s[pos];
    if (std::isspace(ch)) {
      pos++;
      continue;
    }
    if (ch == '(' || ch == ')') {
      ts.push_back({ch == '(' ? token::kind::left_parenthesis
                              : token::kind::right_parenthesis,
                    std::string(1, char(ch)), pos});
      pos++;
      continue;
    }
    if (std::isalnum(ch) || ch == '_') {
      std::size_t end = pos + 1;
      while (end < s.size()) {
        unsigned char nxt = s[end];
        if (!std::isalnum(nxt) && nxt != '_') {
          break;
        }
        end++;
      }
      ts.push_back(
          {token::kind::operand, std::string(s.substr(pos, end - pos)), pos});
      pos = end;
      continue;
    }
    auto it0 =
        std::find_if(ops.begin(), ops.end(), [&](const std::string &opr) {
          return s.substr(pos, opr.size()) == opr;
        });
    if (it0 == ops.end()) {
      return {{}, expression_parse_error{pos, "unknown token"}};
    }
    ts.push_back({token::kind::operation, *it0, pos});
    pos += it0->size();
  }
  ts.push_back({token::kind::end, {}, s.size()});

  expression_tree t;
  std::size_t cur = 0;
  std::optional<expression_parse_error> err;
  auto bad = [&](std::string msg) {
    if (!err) {
      err = expression_parse_error{ts[cur].pos, std::move(msg)};
    }
    return -1;
  };
  auto bin = [&](const std::string &opr) -> const expression_binary_operator * {
    auto it = std::find_if(bop.begin(), bop.end(),
                           [&](const auto &op) { return op.opr == opr; });
    return it == bop.end() ? nullptr : &*it;
  };
  auto una = [&](const std::string &opr) -> const expression_unary_operator * {
    auto it = std::find_if(uop.begin(), uop.end(),
                           [&](const auto &op) { return op.opr == opr; });
    return it == uop.end() ? nullptr : &*it;
  };
  auto ins = [&](expression_node u) {
    t.tr.push_back(std::move(u));
    return int(t.tr.size()) - 1;
  };

  auto dfs = [&](auto &f, int lo) -> int {
    int ls = -1;
    if (ts[cur].tp == token::kind::operand) {
      ls = ins({expression_node::kind::operand, ts[cur].s0, -1, -1});
      cur++;
    } else if (ts[cur].tp == token::kind::left_parenthesis) {
      cur++;
      ls = f(f, 0);
      if (ls == -1) {
        return -1;
      }
      if (ts[cur].tp != token::kind::right_parenthesis) {
        return bad("missing closing parenthesis");
      }
      cur++;
    } else if (ts[cur].tp == token::kind::operation) {
      const auto *op = una(ts[cur].s0);
      if (op == nullptr) {
        return bad("expected operand or prefix unary operator");
      }
      std::string opr = ts[cur++].s0;
      int ch0 = f(f, op->pri);
      if (ch0 == -1) {
        return -1;
      }
      ls = ins({expression_node::kind::unary, std::move(opr), ch0, -1});
    } else {
      return bad("expected operand");
    }

    while (ts[cur].tp == token::kind::operation) {
      const auto *op = bin(ts[cur].s0);
      if (op == nullptr || op->pri < lo) {
        break;
      }
      std::string opr = ts[cur++].s0;
      int np = op->pri + (op->ras ? 0 : 1);
      int rs = f(f, np);
      if (rs == -1) {
        return -1;
      }
      ls = ins({expression_node::kind::binary, std::move(opr), ls, rs});
    }
    return ls;
  };

  t.rt = dfs(dfs, 0);
  if (t.rt == -1) {
    return {{}, err};
  }
  if (ts[cur].tp != token::kind::end) {
    bad(ts[cur].tp == token::kind::right_parenthesis
            ? "unmatched closing parenthesis"
            : "unexpected token after expression");
    return {{}, err};
  }
  return {std::move(t), {}};
}

} // namespace noya