2020-06-01 18:01:03 +02:00
|
|
|
/*
|
2022-05-05 00:27:08 +02:00
|
|
|
** Copyright (C) 2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
2020-06-01 18:01:03 +02:00
|
|
|
**
|
|
|
|
** 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__
|
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
#include "mu-utils.hh"
|
|
|
|
|
|
|
|
#include <stdexcept>
|
2020-06-01 18:01:03 +02:00
|
|
|
#include <vector>
|
2022-11-07 17:13:14 +01:00
|
|
|
#include <string>
|
|
|
|
#include <string_view>
|
|
|
|
#include <iostream>
|
|
|
|
#include <variant>
|
|
|
|
#include <cinttypes>
|
|
|
|
#include <ostream>
|
|
|
|
#include <cassert>
|
2020-06-01 18:01:03 +02:00
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
#include <utils/mu-result.hh>
|
2020-06-01 18:01:03 +02:00
|
|
|
|
|
|
|
namespace Mu {
|
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
/**
|
|
|
|
* A structure somewhat similar to a Lisp s-expression and which can be
|
|
|
|
* constructed from/to an s-expressing string representation.
|
|
|
|
*
|
|
|
|
* A sexp is either an atom (String, Number Symbol) or a List
|
|
|
|
*/
|
2020-07-12 14:09:05 +02:00
|
|
|
struct Sexp {
|
2022-11-07 17:13:14 +01:00
|
|
|
/// Types
|
|
|
|
using List = std::vector<Sexp>;
|
|
|
|
using String = std::string;
|
|
|
|
using Number = int64_t;
|
|
|
|
struct Symbol { // distinguish from String.
|
|
|
|
Symbol(const std::string& s): name{s} {}
|
|
|
|
Symbol(std::string&& s): name(std::move(s)) {}
|
|
|
|
Symbol(const char* str): Symbol(std::string{str}) {}
|
|
|
|
Symbol(std::string_view sv): Symbol(std::string{sv}) {}
|
|
|
|
operator const std::string&() const {return name; }
|
|
|
|
std::string name;
|
|
|
|
};
|
|
|
|
enum struct Type { List, String, Number, Symbol };
|
|
|
|
using Data = std::variant<List, String, Number, Symbol>;
|
2021-10-20 11:18:15 +02:00
|
|
|
|
|
|
|
/**
|
2022-11-07 17:13:14 +01:00
|
|
|
* Get the type of data
|
2021-10-20 11:18:15 +02:00
|
|
|
*
|
2022-11-07 17:13:14 +01:00
|
|
|
* @return type
|
2021-10-20 11:18:15 +02:00
|
|
|
*/
|
2022-11-07 17:13:14 +01:00
|
|
|
constexpr Type type() const { return static_cast<Type>(data.index()); }
|
2022-02-19 18:00:19 +01:00
|
|
|
|
2022-05-05 00:27:08 +02:00
|
|
|
|
|
|
|
/**
|
2022-11-07 17:13:14 +01:00
|
|
|
* Get the name for some type
|
2022-05-05 00:27:08 +02:00
|
|
|
*
|
2022-11-07 17:13:14 +01:00
|
|
|
* @param t type
|
2022-05-05 00:27:08 +02:00
|
|
|
*
|
2022-11-07 17:13:14 +01:00
|
|
|
* @return name
|
2022-05-05 00:27:08 +02:00
|
|
|
*/
|
2022-11-07 17:13:14 +01:00
|
|
|
static constexpr std::string_view type_name(Type t) {
|
|
|
|
switch(t) {
|
|
|
|
case Type::String:
|
|
|
|
return "string";
|
|
|
|
case Type::Number:
|
|
|
|
return "number";
|
|
|
|
case Type::Symbol:
|
|
|
|
return "symbol";
|
|
|
|
case Type::List:
|
|
|
|
return "list";
|
|
|
|
default:
|
|
|
|
return "<error>";
|
|
|
|
}
|
2022-05-05 00:27:08 +02:00
|
|
|
}
|
|
|
|
|
2021-10-20 11:18:15 +02:00
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
constexpr bool stringp() const { return std::holds_alternative<String>(data); }
|
|
|
|
constexpr bool numberp() const { return std::holds_alternative<Number>(data); }
|
|
|
|
constexpr bool listp() const { return std::holds_alternative<List>(data); }
|
|
|
|
constexpr bool symbolp() const { return std::holds_alternative<Symbol>(data); }
|
|
|
|
|
|
|
|
constexpr bool nilp() const { return symbolp() && symbol() == "nil"; }
|
|
|
|
static const Sexp& nil() { static const Sexp nilsym(Symbol{"nil"}); return nilsym; }
|
|
|
|
static const Sexp& t() { static const Sexp tsym(Symbol{"t"}); return tsym; }
|
|
|
|
|
|
|
|
// Get the specific variant type.
|
|
|
|
const List& list() const { return std::get<List>(data); }
|
|
|
|
List& list() { return std::get<List>(data); }
|
|
|
|
const String& string() const { return std::get<String>(data); }
|
|
|
|
String& string() { return std::get<String>(data); }
|
|
|
|
const Number& number() const { return std::get<Number>(data); }
|
|
|
|
Number& number() { return std::get<Number>(data); }
|
|
|
|
const String& symbol() const { return std::get<Symbol>(data).name; }
|
|
|
|
String& symbol() { return std::get<Symbol>(data).name; }
|
|
|
|
|
|
|
|
/// Default ctor
|
|
|
|
Sexp():data{List{}} {} // default: an empty list.
|
|
|
|
|
|
|
|
// Copy & move ctors
|
|
|
|
Sexp(const Sexp& other):data{other.data}{}
|
|
|
|
Sexp(Sexp&& other):data{std::move(other.data)}{}
|
|
|
|
|
|
|
|
// Assignment
|
|
|
|
Sexp& operator=(const Sexp& rhs) {
|
|
|
|
if (this != &rhs)
|
|
|
|
data = rhs.data;
|
|
|
|
return *this;
|
2021-10-20 11:18:15 +02:00
|
|
|
}
|
2022-11-07 17:13:14 +01:00
|
|
|
Sexp& operator=(Sexp&& rhs) {
|
|
|
|
if (this != &rhs)
|
|
|
|
data = std::move(rhs.data);
|
|
|
|
return *this;
|
2021-10-20 11:18:15 +02:00
|
|
|
}
|
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
/// Type specific ctors
|
|
|
|
Sexp(const List& lst): data{lst} {}
|
|
|
|
Sexp(List&& lst): data{std::move(lst)} {}
|
2021-10-20 11:18:15 +02:00
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
Sexp(const String& str): data{str} {}
|
|
|
|
Sexp(String&& str): data{std::move(str)} {}
|
|
|
|
Sexp(const char *str): Sexp{std::string{str}} {}
|
|
|
|
Sexp(std::string_view sv): Sexp{std::string{sv}} {}
|
2021-10-20 11:18:15 +02:00
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
template<typename N, typename = std::enable_if_t<std::is_integral_v<N>> >
|
|
|
|
Sexp(N n):data{static_cast<Number>(n)} {}
|
2021-10-20 11:18:15 +02:00
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
Sexp(const Symbol& sym): data{sym} {}
|
|
|
|
Sexp(Symbol&& sym): data{std::move(sym)} {}
|
2021-10-20 11:18:15 +02:00
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
///
|
|
|
|
template<typename S, typename T, typename... Args>
|
|
|
|
Sexp(S&& s, T&& t, Args&&... args): data{List()} {
|
|
|
|
auto& l{std::get<List>(data)};
|
|
|
|
l.emplace_back(Sexp(std::forward<S>(s)));
|
|
|
|
l.emplace_back(Sexp(std::forward<T>(t)));
|
|
|
|
(l.emplace_back(Sexp(std::forward<Args>(args))), ...);
|
2021-10-20 11:18:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-11-07 17:13:14 +01:00
|
|
|
* Parse sexp from string
|
2021-10-20 11:18:15 +02:00
|
|
|
*
|
2022-11-07 17:13:14 +01:00
|
|
|
* @param str a string
|
2021-10-20 11:18:15 +02:00
|
|
|
*
|
2022-11-07 17:13:14 +01:00
|
|
|
* @return either an Sexp or an error
|
2021-10-20 11:18:15 +02:00
|
|
|
*/
|
2022-11-07 17:13:14 +01:00
|
|
|
static Result<Sexp> parse(const std::string& str);
|
|
|
|
|
|
|
|
|
|
|
|
/// List specific
|
|
|
|
using iterator = List::iterator;
|
|
|
|
using const_iterator = List::const_iterator;
|
|
|
|
iterator begin() { return list().begin(); }
|
|
|
|
const_iterator begin() const { return list().begin(); }
|
|
|
|
const_iterator cbegin() const { return list().cbegin(); }
|
|
|
|
|
|
|
|
iterator end() { return list().end(); }
|
|
|
|
const_iterator end() const { return list().end(); }
|
|
|
|
const_iterator cend() const { return list().cend(); }
|
|
|
|
|
|
|
|
bool empty() const { return list().empty(); }
|
|
|
|
size_t size() const { return list().size(); }
|
|
|
|
void clear() { list().clear(); }
|
|
|
|
|
|
|
|
/// Adding to lists
|
|
|
|
Sexp& add(const Sexp& s) { list().emplace_back(s); return *this; }
|
|
|
|
Sexp& add(Sexp&& s) { list().emplace_back(std::move(s)); return *this; }
|
|
|
|
Sexp& add() { return *this; }
|
|
|
|
|
|
|
|
template <typename V1, typename V2, typename... Args>
|
|
|
|
Sexp& add(V1&& v1, V2&& v2, Args... args) {
|
|
|
|
return add(std::forward<V1>(v1))
|
|
|
|
.add(std::forward<V2>(v2))
|
|
|
|
.add(std::forward<Args>(args)...);
|
2021-10-20 11:18:15 +02:00
|
|
|
}
|
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
// Plist (property lists)
|
|
|
|
bool plistp() const { return listp() && plistp(cbegin(), cend()); }
|
|
|
|
Sexp& put_props() { return *this; } // Final case for template pack.
|
|
|
|
template <class PropType, class SexpType, typename... Args>
|
|
|
|
Sexp& put_props(PropType&& prop, SexpType&& sexp, Args... args) {
|
|
|
|
auto&& propname{std::string(prop)};
|
|
|
|
return del_prop(propname)
|
|
|
|
.add(Symbol(std::move(propname)),
|
|
|
|
std::forward<SexpType>(sexp))
|
|
|
|
.put_props(std::forward<Args>(args)...);
|
2021-10-20 11:18:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-11-07 17:13:14 +01:00
|
|
|
* Find the property value for some property by name
|
2021-10-20 11:18:15 +02:00
|
|
|
*
|
2022-11-07 17:13:14 +01:00
|
|
|
* @param p property name
|
2021-10-20 11:18:15 +02:00
|
|
|
*
|
2022-11-07 17:13:14 +01:00
|
|
|
* @return the property if found, or the symbol nil otherwise.
|
2021-10-20 11:18:15 +02:00
|
|
|
*/
|
2022-11-07 17:13:14 +01:00
|
|
|
const Sexp& get_prop(const std::string& p) const {
|
|
|
|
if (auto&& it = find_prop(p, cbegin(), cend()); it != cend())
|
|
|
|
return *(std::next(it));
|
2021-10-20 11:18:15 +02:00
|
|
|
else
|
2022-11-07 17:13:14 +01:00
|
|
|
return Sexp::nil();
|
|
|
|
}
|
|
|
|
bool has_prop(const std::string& s) const {
|
|
|
|
return find_prop(s, cbegin(), cend())!= cend();
|
2022-02-18 09:49:56 +01:00
|
|
|
}
|
2021-10-20 11:18:15 +02:00
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
/// Output to string
|
|
|
|
enum struct Format {
|
2022-05-06 21:07:15 +02:00
|
|
|
Default = 0, /**< Nothing in particular */
|
|
|
|
SplitList = 1 << 0, /**< Insert newline after list item */
|
2022-11-07 17:13:14 +01:00
|
|
|
TypeInfo = 1 << 1, /**< Show type-info */
|
2022-05-06 21:07:15 +02:00
|
|
|
};
|
|
|
|
|
2021-10-20 11:18:15 +02:00
|
|
|
/**
|
2022-11-07 17:13:14 +01:00
|
|
|
* Get a string representation of the sexp
|
2021-10-20 11:18:15 +02:00
|
|
|
*
|
2022-11-07 17:13:14 +01:00
|
|
|
* @return str
|
2021-10-20 11:18:15 +02:00
|
|
|
*/
|
2022-11-07 17:13:14 +01:00
|
|
|
std::string to_string(Format fopts=Format::Default) const;
|
|
|
|
std::string to_json_string(Format fopts=Format::Default) const;
|
|
|
|
|
|
|
|
Sexp& del_prop(const std::string& pname);
|
|
|
|
protected:
|
|
|
|
const_iterator find_prop(const std::string& s, const_iterator b,
|
|
|
|
const_iterator e) const;
|
|
|
|
bool plistp(const_iterator b, const_iterator e) const;
|
|
|
|
private:
|
|
|
|
iterator find_prop(const std::string& s,iterator b,
|
|
|
|
iterator e);
|
|
|
|
Data data;
|
2020-06-01 18:01:03 +02:00
|
|
|
};
|
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
MU_ENABLE_BITOPS(Sexp::Format);
|
2021-10-20 11:18:15 +02:00
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
/**
|
|
|
|
* String-literal; allow for ":foo"_sym to be a symbol
|
|
|
|
*/
|
|
|
|
static inline Sexp::Symbol
|
|
|
|
operator"" _sym(const char* str, std::size_t n)
|
|
|
|
{
|
|
|
|
return Sexp::Symbol{str};
|
2020-06-01 18:01:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline std::ostream&
|
2022-11-07 17:13:14 +01:00
|
|
|
operator<<(std::ostream& os, const Sexp::Type& stype)
|
2020-06-01 18:01:03 +02:00
|
|
|
{
|
2022-11-07 17:13:14 +01:00
|
|
|
os << Sexp::type_name(stype);
|
2021-10-20 11:18:15 +02:00
|
|
|
return os;
|
2020-06-01 18:01:03 +02:00
|
|
|
}
|
|
|
|
|
2022-11-07 17:13:14 +01:00
|
|
|
|
2021-05-02 22:07:03 +02:00
|
|
|
static inline std::ostream&
|
2022-11-07 17:13:14 +01:00
|
|
|
operator<<(std::ostream& os, const Sexp& sexp)
|
2021-05-02 22:07:03 +02:00
|
|
|
{
|
2022-11-07 17:13:14 +01:00
|
|
|
os << sexp.to_string();
|
2021-10-20 11:18:15 +02:00
|
|
|
return os;
|
2021-05-02 22:07:03 +02:00
|
|
|
}
|
|
|
|
|
2021-10-20 11:18:15 +02:00
|
|
|
} // namespace Mu
|
2020-06-01 18:01:03 +02:00
|
|
|
|
|
|
|
#endif /* MU_SEXP_HH__ */
|