/* ** Copyright (C) 2020 Dirk-Jan C. Binnema ** ** This program 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, or (at your option) any ** later version. ** ** This program 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 this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #ifndef MU_SEXP_HH__ #define MU_SEXP_HH__ #include #include #include #include "utils/mu-utils.hh" #include "utils/mu-error.hh" namespace Mu { namespace Sexp { /// Simple s-expression parser & builder that parses lists () and atoms (strings /// ("-quoted), (positive) integers ([0..9]+) and symbol starting with alpha or /// ':', then alphanum and '-') /// /// (:foo (1234 "bar" nil) :quux (a b c)) /// Parse node struct Node { /// Node type enum struct Type { List, String, Number, Symbol }; /** * Make a node of out of an s-expression string. * * @param expr a string containing an s-expression * * @return the parsed s-expression, or throw Error. */ static Node make (const std::string& expr); /** * Make a node for a string/integer/symbol/list value * * @param val some value * * @return a node */ static Node make_string (std::string&& val) { return Node{Type::String, std::move(val)}; } static Node make_string (const std::string& val) { return Node{Type::String, std::string{val}}; } static Node make_number (int val) { return Node{Type::Number, format("%d", val)}; } static Node make_symbol (std::string&& val) { return Node{Type::Symbol, std::move(val)}; } /// sequence of node objects struct Seq { /** * Add an item to a node-sequence. * * @param node item to add; moved/consumed. */ void add(Node&& node) {nodes_.emplace_back(std::move(node));} void add(Seq&& seq) {add(make_list(std::move(seq)));} void add(std::string&& s) {add(make_string(std::move(s)));} void add(int i) {add(make_number(i));} // disambiguate. void add_symbol(std::string&& s) {add(make_symbol(std::move(s)));} /** * Add a property to tne node sequence; i.e. a property-symbol * (starting with ':') and some node * * @param name name (must start with ":"), and some value or list * @param val */ template void add_prop(std::string&& name, T&& val) { if (name.empty() || name[0] != ':') throw Error{Error::Code::InvalidArgument, "property names must start with ':'"}; add(make_symbol(std::move(name))); add(std::move(val)); } // deliberately limited stl-like /** * Is this an empty sequence? * * @return true or false */ bool empty() const {return nodes_.empty();} /** * Get the number of elsements in the sequence * * @return number of elmement. */ size_t size() const {return nodes_.size();} /** * Get begin iterator of the sequence * * @return iterator */ const auto begin() const { return nodes_.begin(); } /** * Get the end iterator of the sequnce * * @return an iterator */ const auto end() const { return nodes_.end(); } /** * Get a const ref to the item at idx. * * @param idx index, must be < size() * * @return const ref to the item. */ const auto at(size_t idx) const { return nodes_.at(idx);} private: std::vector nodes_; }; /** * Make a list node from a sequence * * @param seq a sequence of nodes * * @return a node */ static Node make_list (Seq&& seq) { return Node{std::move(seq)}; } /** * Convert a Sexp::Node to its string representation * * @return the string representation */ std::string to_string() const; /** * Return the type of this Node. * * @return the type */ Type type() const { return type_; } /// Some type helpers bool is_list() const { return type() == Type::List; }; bool is_string() const { return type() == Type::String; } bool is_number() const { return type() == Type::Number; } bool is_symbol() const { return type() == Type::Symbol; } bool is_nil() const { return is_symbol() && value() == "nil"; } bool is_t() const { return is_symbol() && value() == "t"; } /** * The elements of this node; invalid unless this is a list node. * * @return */ const Seq& elements() const { if (type() != Type::List) throw Error(Error::Code::InvalidArgument, "no elements for non-list"); return seq_; } /** * The value of this node; invalid for list nodes. * * @return */ const std::string& value() const { if (type_ == Type::List) throw Error(Error::Code::InvalidArgument, "no value for list"); return value_; } private: /** * Construct a new non-list node * * @param typearg the type of node * @param valuearg the value */ Node(Type typearg, std::string&& valuearg): type_{typearg}, value_{std::move(valuearg)} { if (typearg == Type::List) throw Error(Error::Code::Parsing, "atomic type cannot be a "); } /** * Construct a list node * * @param kids the list's children */ explicit Node(Seq&& seq): type_{Type::List}, seq_{std::move(seq)} {} const Type type_; /**< Type of node */ const std::string value_; /**< String value of node (only for * non-Type::Lst)*/ const Seq seq_; /**< Children of node (only for * Type::Lst) */ }; static inline std::ostream& operator<<(std::ostream& os, Sexp::Node::Type id) { switch (id) { case Sexp::Node::Type::List: os << ""; break; case Sexp::Node::Type::String: os << ""; break; case Sexp::Node::Type::Number: os << ""; break; case Sexp::Node::Type::Symbol: os << ""; break; default: throw std::runtime_error ("unknown node type"); } return os; } static inline std::ostream& operator<<(std::ostream& os, const Sexp::Node& node) { os << node.to_string(); return os; } } // Sexp } // Mu #endif /* MU_SEXP_HH__ */