diff --git a/CLAUDE.md b/CLAUDE.md index a09194e..e555f67 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ ## Project overview -Archetype is a message-passing, object-oriented programming language for writing text-based adventure games. The interpreter is implemented in C++11. Game source files use the `.arch` extension; compiled binaries use `.acx`. +Archetype is a message-passing, object-oriented programming language for writing text-based adventure games. The interpreter is implemented in C++20. Game source files use the `.arch` extension; compiled binaries use `.acx`. ## Build diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 76f022b..06f06aa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,11 @@ cmake_minimum_required(VERSION 3.10) project(archetype) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +add_compile_options(-Wall -Wextra) + add_executable(archetype Capture.cc ConsoleInput.cc diff --git a/src/Expression.cc b/src/Expression.cc index 0ff6fbb..58a749d 100644 --- a/src/Expression.cc +++ b/src/Expression.cc @@ -22,6 +22,8 @@ using namespace std; namespace archetype { + using enum Keywords::Operators_e; + bool IExpression::Debug = false; enum ExpressionType_e { @@ -47,15 +49,15 @@ namespace archetype { inline bool is_binary(Keywords::Operators_e op) { switch (op) { - case Keywords::OP_LPAREN: - case Keywords::OP_CHS: - case Keywords::OP_NUMERIC: - case Keywords::OP_NOT: - case Keywords::OP_STRING: - case Keywords::OP_RANDOM: - case Keywords::OP_LENGTH: - case Keywords::OP_HEAD: - case Keywords::OP_TAIL: + case OP_LPAREN: + case OP_CHS: + case OP_NUMERIC: + case OP_NOT: + case OP_STRING: + case OP_RANDOM: + case OP_LENGTH: + case OP_HEAD: + case OP_TAIL: return false; default: return true; @@ -68,14 +70,14 @@ namespace archetype { // all others are left-associative, unless // special-cased. switch (op) { - case Keywords::OP_POWER: - case Keywords::OP_C_MULTIPLY: - case Keywords::OP_C_DIVIDE: - case Keywords::OP_C_PLUS: - case Keywords::OP_C_MINUS: - case Keywords::OP_C_CONCAT: - case Keywords::OP_ASSIGN: - case Keywords::OP_PAIR: + case OP_POWER: + case OP_C_MULTIPLY: + case OP_C_DIVIDE: + case OP_C_PLUS: + case OP_C_MINUS: + case OP_C_CONCAT: + case OP_ASSIGN: + case OP_PAIR: return true; default: return not is_binary(op); @@ -84,54 +86,54 @@ namespace archetype { inline int precedence(Keywords::Operators_e op) { switch (op) { - case Keywords::OP_LPAREN: return 14; - case Keywords::OP_DOT: return 13; + case OP_LPAREN: return 14; + case OP_DOT: return 13; - case Keywords::OP_CHS: return 12; - case Keywords::OP_NUMERIC: return 12; - case Keywords::OP_STRING: return 12; - case Keywords::OP_RANDOM: return 12; - case Keywords::OP_LENGTH: return 12; + case OP_CHS: return 12; + case OP_NUMERIC: return 12; + case OP_STRING: return 12; + case OP_RANDOM: return 12; + case OP_LENGTH: return 12; - case Keywords::OP_POWER: return 11; + case OP_POWER: return 11; - case Keywords::OP_MULTIPLY: return 10; - case Keywords::OP_DIVIDE: return 10; + case OP_MULTIPLY: return 10; + case OP_DIVIDE: return 10; - case Keywords::OP_PLUS: return 9; - case Keywords::OP_MINUS: return 9; - case Keywords::OP_CONCAT: return 9; + case OP_PLUS: return 9; + case OP_MINUS: return 9; + case OP_CONCAT: return 9; - case Keywords::OP_WITHIN: return 8; + case OP_WITHIN: return 8; - case Keywords::OP_LEFTFROM: return 7; - case Keywords::OP_RIGHTFROM: return 7; + case OP_LEFTFROM: return 7; + case OP_RIGHTFROM: return 7; - case Keywords::OP_SEND: return 6; - case Keywords::OP_PASS: return 6; + case OP_SEND: return 6; + case OP_PASS: return 6; - case Keywords::OP_EQ: return 5; - case Keywords::OP_NE: return 5; - case Keywords::OP_GT: return 5; - case Keywords::OP_LT: return 5; - case Keywords::OP_GE: return 5; - case Keywords::OP_LE: return 5; + case OP_EQ: return 5; + case OP_NE: return 5; + case OP_GT: return 5; + case OP_LT: return 5; + case OP_GE: return 5; + case OP_LE: return 5; - case Keywords::OP_NOT: return 4; - case Keywords::OP_AND: return 3; - case Keywords::OP_OR: return 2; + case OP_NOT: return 4; + case OP_AND: return 3; + case OP_OR: return 2; // TODO: What's the right precedence here? LISP never has to decide - case Keywords::OP_PAIR: return 2; - case Keywords::OP_HEAD: return 2; - case Keywords::OP_TAIL: return 2; + case OP_PAIR: return 2; + case OP_HEAD: return 2; + case OP_TAIL: return 2; - case Keywords::OP_C_MULTIPLY: return 1; - case Keywords::OP_C_DIVIDE: return 1; - case Keywords::OP_C_PLUS: return 1; - case Keywords::OP_C_MINUS: return 1; - case Keywords::OP_C_CONCAT: return 1; - case Keywords::OP_ASSIGN: return 1; + case OP_C_MULTIPLY: return 1; + case OP_C_DIVIDE: return 1; + case OP_C_PLUS: return 1; + case OP_C_MINUS: return 1; + case OP_C_CONCAT: return 1; + case OP_ASSIGN: return 1; default: throw logic_error("Attempt to look up precedence of nonexistent operator"); @@ -141,16 +143,16 @@ namespace archetype { Keywords::Operators_e non_assignment_equivalent(Keywords::Operators_e op) { switch (op) { - case Keywords::OP_C_PLUS: - return Keywords::OP_PLUS; - case Keywords::OP_C_MINUS: - return Keywords::OP_MINUS; - case Keywords::OP_C_MULTIPLY: - return Keywords::OP_MULTIPLY; - case Keywords::OP_C_DIVIDE: - return Keywords::OP_DIVIDE; - case Keywords::OP_C_CONCAT: - return Keywords::OP_CONCAT; + case OP_C_PLUS: + return OP_PLUS; + case OP_C_MINUS: + return OP_MINUS; + case OP_C_MULTIPLY: + return OP_MULTIPLY; + case OP_C_DIVIDE: + return OP_DIVIDE; + case OP_C_CONCAT: + return OP_CONCAT; default: throw logic_error("Unexpected cumulative assignment operator"); } @@ -191,8 +193,8 @@ namespace archetype { result = Value(new AttributeValue(selfObject->id(), id_)); } else { // Next: an object in the Universe - auto id_obj_p = Universe::instance().ObjectIdentifiers.find(id_); - if (id_obj_p != Universe::instance().ObjectIdentifiers.end()) { + auto& object_ids = Universe::instance().ObjectIdentifiers; + if (auto id_obj_p = object_ids.find(id_); id_obj_p != object_ids.end()) { result = Value{new ObjectValue{id_obj_p->second}}; } } @@ -255,7 +257,7 @@ namespace archetype { Value rv = right_->evaluate()->valueConversion(); Value result; switch (op()) { - case Keywords::OP_CHS: { + case OP_CHS: { Value rv_n = rv->numericConversion(); if (rv_n->isDefined()) { result = Value{new NumericValue{-rv_n->getNumber()}}; @@ -264,16 +266,16 @@ namespace archetype { } break; } - case Keywords::OP_NUMERIC: + case OP_NUMERIC: result = rv->numericConversion(); break; - case Keywords::OP_NOT: + case OP_NOT: result = Value{new BooleanValue{not rv->isTrueEnough()}}; break; - case Keywords::OP_STRING: + case OP_STRING: result = rv->stringConversion(); break; - case Keywords::OP_RANDOM: { + case OP_RANDOM: { Value rv_n = rv->numericConversion(); if (rv_n->isDefined() and rv_n->getNumber() > 0) { random_device rd; @@ -286,7 +288,7 @@ namespace archetype { } break; } - case Keywords::OP_LENGTH: { + case OP_LENGTH: { Value rv_s = rv->stringConversion(); if (rv_s->isDefined()) { result = Value{new NumericValue{static_cast(rv_s->getString().size())}}; @@ -295,12 +297,12 @@ namespace archetype { } break; } - case Keywords::OP_HEAD: { + case OP_HEAD: { Value rv_v = rv->valueConversion(); result = rv_v->head(); break; } - case Keywords::OP_TAIL: { + case OP_TAIL: { Value rv_v = rv->valueConversion(); result = rv_v->tail(); break; @@ -328,11 +330,11 @@ namespace archetype { virtual int nodeCount() const override { return 1 + right_->nodeCount(); } virtual Expression anyFewerNodeEquivalent() override { right_ = tighten(std::move(right_)); - return op() != Keywords::OP_LPAREN ? nullptr : std::move(right_); + return op() != OP_LPAREN ? nullptr : std::move(right_); } virtual void prefixDisplay(ostream& out) const override { - if (op() == Keywords::OP_LPAREN) { + if (op() == OP_LPAREN) { right_->prefixDisplay(out); } else { out << '('; @@ -345,9 +347,9 @@ namespace archetype { Value eval_ss(Keywords::Operators_e op, string lv_s, string rv_s) { switch (op) { - case Keywords::OP_CONCAT: + case OP_CONCAT: return Value(new StringValue(lv_s + rv_s)); - case Keywords::OP_WITHIN: { + case OP_WITHIN: { size_t where = rv_s.find(lv_s); if (lv_s.empty() or where == string::npos) { return Value{new UndefinedValue}; @@ -362,9 +364,9 @@ namespace archetype { Value eval_sn(Keywords::Operators_e op, string lv_s, int rv_n) { switch (op) { - case Keywords::OP_LEFTFROM: + case OP_LEFTFROM: return Value(new StringValue(lv_s.substr(0, rv_n))); - case Keywords::OP_RIGHTFROM: { + case OP_RIGHTFROM: { int n = min(int(lv_s.size() + 1), max(0, rv_n)); return Value(new StringValue(lv_s.substr(n - 1))); } @@ -376,11 +378,11 @@ namespace archetype { Value eval_nn(Keywords::Operators_e op, int lv_n, int rv_n) { int result; switch (op) { - case Keywords::OP_PLUS: result = lv_n + rv_n; break; - case Keywords::OP_MINUS: result = lv_n - rv_n; break; - case Keywords::OP_MULTIPLY: result = lv_n * rv_n; break; - case Keywords::OP_DIVIDE: result = lv_n / rv_n; break; - case Keywords::OP_POWER: result = static_cast(pow(lv_n, rv_n)); break; + case OP_PLUS: result = lv_n + rv_n; break; + case OP_MINUS: result = lv_n - rv_n; break; + case OP_MULTIPLY: result = lv_n * rv_n; break; + case OP_DIVIDE: result = lv_n / rv_n; break; + case OP_POWER: result = static_cast(pow(lv_n, rv_n)); break; default: throw logic_error("number-op-number attempted on this operator"); } @@ -389,19 +391,19 @@ namespace archetype { bool eval_compare(Keywords::Operators_e op, const Value& lv, const Value& rv) { // Quick shortcut for identity. Will also catch (v = UNDEFINED) and (UNDEFINED = v). - if (op == Keywords::OP_EQ and lv->isSameValueAs(rv)) { + if (op == OP_EQ and lv->isSameValueAs(rv)) { return true; } // If one side or the other is UNDEFINED, only ~= is possible to be true. if ((not lv->isDefined()) xor (not rv->isDefined())) { - return op == Keywords::OP_NE; + return op == OP_NE; } // But if they are both UNDEFINED, then they are only equal if testing for // that equality. if ((not lv->isDefined()) and (not rv->isDefined())) { - return op == Keywords::OP_EQ; + return op == OP_EQ; } Value lv_n = lv->numericConversion(); @@ -410,12 +412,12 @@ namespace archetype { int ln = lv_n->getNumber(); int rn = rv_n->getNumber(); switch (op) { - case Keywords::OP_EQ: return ln == rn; - case Keywords::OP_NE: return ln != rn; - case Keywords::OP_LT: return ln < rn; - case Keywords::OP_LE: return ln <= rn; - case Keywords::OP_GE: return ln >= rn; - case Keywords::OP_GT: return ln > rn; + case OP_EQ: return ln == rn; + case OP_NE: return ln != rn; + case OP_LT: return ln < rn; + case OP_LE: return ln <= rn; + case OP_GE: return ln >= rn; + case OP_GT: return ln > rn; default: throw logic_error("Unexpected numeric eval_compare case"); } } @@ -425,12 +427,12 @@ namespace archetype { string ls = lv_s->getString(); string rs = rv_s->getString(); switch (op) { - case Keywords::OP_EQ: return ls == rs; - case Keywords::OP_NE: return ls != rs; - case Keywords::OP_LT: return ls < rs; - case Keywords::OP_LE: return ls <= rs; - case Keywords::OP_GE: return ls >= rs; - case Keywords::OP_GT: return ls > rs; + case OP_EQ: return ls == rs; + case OP_NE: return ls != rs; + case OP_LT: return ls < rs; + case OP_LE: return ls <= rs; + case OP_GE: return ls >= rs; + case OP_GT: return ls > rs; default: throw logic_error("Unexpected numeric eval_compare case"); } } @@ -440,15 +442,15 @@ namespace archetype { int l_obj = lv_o->getObject(); int r_obj = rv_o->getObject(); switch (op) { - case Keywords::OP_EQ: return l_obj == r_obj; - case Keywords::OP_NE: return l_obj != r_obj; + case OP_EQ: return l_obj == r_obj; + case OP_NE: return l_obj != r_obj; default: return false; } } // Special case: if the two values aren't comparable, that is, the comparison // fails no matter the conversion, then they definitely aren't equal. - if (op == Keywords::OP_NE) { + if (op == OP_NE) { return not lv->isSameValueAs(rv); } else { return false; @@ -473,7 +475,7 @@ namespace archetype { } bool result = true; switch (op()) { - case Keywords::OP_DOT: + case OP_DOT: if (auto id_node = dynamic_cast(right_.get())) { Universe::instance().classify(t, id_node->id(), REFERENCED_ATTRIBUTE_ID); } else { @@ -481,16 +483,16 @@ namespace archetype { result = false; } break; - case Keywords::OP_ASSIGN: - case Keywords::OP_C_CONCAT: - case Keywords::OP_C_DIVIDE: - case Keywords::OP_C_MINUS: - case Keywords::OP_C_MULTIPLY: - case Keywords::OP_C_PLUS: { + case OP_ASSIGN: + case OP_C_CONCAT: + case OP_C_DIVIDE: + case OP_C_MINUS: + case OP_C_MULTIPLY: + case OP_C_PLUS: { if (auto id_node = dynamic_cast(left_.get())) { Universe::instance().classify(t, id_node->id(), REFERENCED_ATTRIBUTE_ID); } else if (auto binary = dynamic_cast(left_.get())) { - if (binary->op() != Keywords::OP_DOT) { + if (binary->op() != OP_DOT) { t.errorMessage("Left-hand side of assignment must be an attribute"); result = false; } @@ -515,14 +517,14 @@ namespace archetype { Value result; // Sort evaluations by "signature" switch (op()) { - case Keywords::OP_PAIR: { + case OP_PAIR: { Value lv_v = left_->evaluate()->valueConversion(); Value rv_v = right_->evaluate()->valueConversion(); result = Value{new PairValue{std::move(lv_v), std::move(rv_v)}}; break; } - case Keywords::OP_CONCAT: - case Keywords::OP_WITHIN: { + case OP_CONCAT: + case OP_WITHIN: { Value lv_s = left_->evaluate()->stringConversion(); Value rv_s = right_->evaluate()->stringConversion(); if (lv_s->isDefined() and rv_s->isDefined()) { @@ -532,8 +534,8 @@ namespace archetype { } break; } - case Keywords::OP_LEFTFROM: - case Keywords::OP_RIGHTFROM: { + case OP_LEFTFROM: + case OP_RIGHTFROM: { Value lv_s = left_->evaluate()->stringConversion(); Value rv_n = right_->evaluate()->numericConversion(); if (lv_s->isDefined() and rv_n->isDefined()) { @@ -543,23 +545,23 @@ namespace archetype { } break; } - case Keywords::OP_AND: { + case OP_AND: { Value lv = left_->evaluate(); Value rv = right_->evaluate(); result = Value{new BooleanValue{lv->isTrueEnough() and rv->isTrueEnough()}}; break; } - case Keywords::OP_OR: { + case OP_OR: { Value lv = left_->evaluate(); Value rv = right_->evaluate(); result = Value{new BooleanValue{lv->isTrueEnough() or rv->isTrueEnough()}}; break; } - case Keywords::OP_PLUS: - case Keywords::OP_MINUS: - case Keywords::OP_MULTIPLY: - case Keywords::OP_DIVIDE: - case Keywords::OP_POWER: { + case OP_PLUS: + case OP_MINUS: + case OP_MULTIPLY: + case OP_DIVIDE: + case OP_POWER: { Value lv_n = left_->evaluate()->numericConversion(); Value rv_n = right_->evaluate()->numericConversion(); if (lv_n->isDefined() and rv_n->isDefined()) { @@ -570,10 +572,10 @@ namespace archetype { break; } - case Keywords::OP_C_PLUS: - case Keywords::OP_C_MINUS: - case Keywords::OP_C_MULTIPLY: - case Keywords::OP_C_DIVIDE: { + case OP_C_PLUS: + case OP_C_MINUS: + case OP_C_MULTIPLY: + case OP_C_DIVIDE: { Value lv = left_->evaluate(); Value rv = right_->evaluate(); Value lv_a = lv->attributeConversion(); @@ -589,7 +591,7 @@ namespace archetype { break; } - case Keywords::OP_C_CONCAT: { + case OP_C_CONCAT: { Value lv = left_->evaluate(); Value rv = right_->evaluate(); Value lv_a = lv->attributeConversion(); @@ -605,25 +607,25 @@ namespace archetype { break; } - case Keywords::OP_EQ: - case Keywords::OP_NE: - case Keywords::OP_LT: - case Keywords::OP_LE: - case Keywords::OP_GE: - case Keywords::OP_GT: + case OP_EQ: + case OP_NE: + case OP_LT: + case OP_LE: + case OP_GE: + case OP_GT: result = as_boolean_value(eval_compare(op(), left_->evaluate()->valueConversion(), right_->evaluate()->valueConversion())); break; - case Keywords::OP_ASSIGN: { + case OP_ASSIGN: { Value lv_a = left_->evaluate()->attributeConversion(); Value rv_v = right_->evaluate()->valueConversion(); result = lv_a->assign(std::move(rv_v)); break; } - case Keywords::OP_DOT: { + case OP_DOT: { Value lv_o = left_->evaluate()->objectConversion(); if (not lv_o->isDefined()) { result = Value{new UndefinedValue}; @@ -640,8 +642,8 @@ namespace archetype { break; } - case Keywords::OP_SEND: - case Keywords::OP_PASS: { + case OP_SEND: + case OP_PASS: { Value lv_v = left_->evaluate()->valueConversion(); Value rv_o = right_->evaluate()->objectConversion(); if (not rv_o->isDefined()) { @@ -650,7 +652,7 @@ namespace archetype { ObjectPtr recipient = Universe::instance().getObject(rv_o->getObject()); if (not recipient) { result = Value{new UndefinedValue}; - } else if (op() == Keywords::OP_PASS or recipient->isPrototype()) { + } else if (op() == OP_PASS or recipient->isPrototype()) { result = Object::pass(recipient, std::move(lv_v)); } else { result = Object::send(recipient, std::move(lv_v)); @@ -828,11 +830,11 @@ namespace archetype { } Expression list_expr{new ValueExpression{Value{new UndefinedValue}}}; while (not elements.empty()) { - list_expr = Expression{new BinaryOperator{std::move(elements.top()), Keywords::OP_PAIR, std::move(list_expr)}}; + list_expr = Expression{new BinaryOperator{std::move(elements.top()), OP_PAIR, std::move(list_expr)}}; elements.pop(); } // Ensure that everything inside the braces is grouped together - return Expression{new UnaryOperator{Keywords::OP_LPAREN, std::move(list_expr)}}; + return Expression{new UnaryOperator{OP_LPAREN, std::move(list_expr)}}; } Expression get_operand_node(TokenStream& t) { @@ -843,7 +845,7 @@ namespace archetype { return nullptr; case '(': if (Expression expr = form_expr(t)) { - Expression result(new UnaryOperator(Keywords::OP_LPAREN, std::move(expr))); + Expression result(new UnaryOperator(OP_LPAREN, std::move(expr))); return result; } else { return nullptr; @@ -858,14 +860,14 @@ namespace archetype { Keywords::Operators_e op_name = Keywords::Operators_e(t.token().number()); // Check for special cases, operators that can be unary in this context switch (op_name) { - case Keywords::OP_PLUS: - op_name = Keywords::OP_NUMERIC; + case OP_PLUS: + op_name = OP_NUMERIC; break; - case Keywords::OP_MINUS: - op_name = Keywords::OP_CHS; + case OP_MINUS: + op_name = OP_CHS; break; - case Keywords::OP_CONCAT: - op_name = Keywords::OP_STRING; + case OP_CONCAT: + op_name = OP_STRING; break; default: if (is_binary(op_name)) { @@ -883,7 +885,7 @@ namespace archetype { } default: { /* some constant or keyword */ if (Expression scalar = get_scalar(t)) { - return Expression(new UnaryOperator(Keywords::OP_LPAREN, std::move(scalar))); + return Expression(new UnaryOperator(OP_LPAREN, std::move(scalar))); } else { return nullptr; } diff --git a/src/Expression.hh b/src/Expression.hh index 9f665a2..2872c4a 100644 --- a/src/Expression.hh +++ b/src/Expression.hh @@ -36,12 +36,12 @@ namespace archetype { virtual void write(Storage& out) const = 0; - virtual bool bindsBefore(Keywords::Operators_e op) const { return true; } - virtual void tieOnRightSide(Keywords::Operators_e op, Expression rightSide) { } + virtual bool bindsBefore(Keywords::Operators_e /*op*/) const { return true; } + virtual void tieOnRightSide(Keywords::Operators_e /*op*/, Expression /*rightSide*/) { } virtual Expression anyFewerNodeEquivalent() { return nullptr; } virtual int nodeCount() const { return 1; } - virtual bool verify(TokenStream& t) const { return true; } + virtual bool verify(TokenStream& /*t*/) const { return true; } virtual void prefixDisplay(std::ostream& out) const = 0; virtual Value evaluate() const = 0; @@ -66,8 +66,8 @@ namespace archetype { bool eval_compare(Keywords::Operators_e op, const Value& lv, const Value& rv); - Expression make_expr(TokenStream& t); - Expression make_expr_from_str(std::string src_str); + [[nodiscard]] Expression make_expr(TokenStream& t); + [[nodiscard]] Expression make_expr_from_str(std::string src_str); Storage& operator<<(Storage& out, const Expression& expr); Storage& operator>>(Storage& in, Expression& expr); diff --git a/src/FileStorage.cc b/src/FileStorage.cc index 86cb7b9..973a0b4 100644 --- a/src/FileStorage.cc +++ b/src/FileStorage.cc @@ -37,7 +37,7 @@ namespace archetype { return bytes_read; } - void InFileStorage::write(const Byte *buf, int nbytes) { + void InFileStorage::write(const Byte* /*buf*/, int /*nbytes*/) { } OutFileStorage::OutFileStorage(std::string filename) @@ -53,7 +53,7 @@ namespace archetype { return 0; } - int OutFileStorage::read(Byte *buf, int nbytes) { + int OutFileStorage::read(Byte* /*buf*/, int /*nbytes*/) { return 0; } diff --git a/src/FileStorage.hh b/src/FileStorage.hh index b3c4f49..c3a9644 100644 --- a/src/FileStorage.hh +++ b/src/FileStorage.hh @@ -22,7 +22,7 @@ namespace archetype { InFileStorage(const InFileStorage&) = delete; InFileStorage& operator=(const InFileStorage&) = delete; - bool ok() const; + [[nodiscard]] bool ok() const; virtual ~InFileStorage() { } virtual int remaining() const override; virtual int read(Byte* buf, int nbytes) override; @@ -38,7 +38,7 @@ namespace archetype { OutFileStorage(const OutFileStorage&) = delete; OutFileStorage& operator=(const OutFileStorage&) = delete; - bool ok() const; + [[nodiscard]] bool ok() const; virtual ~OutFileStorage() { } virtual int remaining() const override; virtual int read(Byte* buf, int nbytes) override; diff --git a/src/IdIndex.hh b/src/IdIndex.hh index d929193..14d9454 100644 --- a/src/IdIndex.hh +++ b/src/IdIndex.hh @@ -26,7 +26,7 @@ namespace archetype { T sentinel_; int holes_; public: - static const int npos = -1; + static constexpr int npos = -1; IdIndex(const T& sentinel = T{}): sentinel_{sentinel}, @@ -88,7 +88,7 @@ namespace archetype { } bool has(const T& obj) const { - return index_.count(obj); + return index_.contains(obj); } bool hasIndex(int i) const { @@ -105,8 +105,8 @@ namespace archetype { int total_entries = static_cast(registry_.size()); int indexed_entries = static_cast(index_.size()); out << total_entries << indexed_entries; - for (auto const& entry : index_) { - out << entry.second << entry.first; + for (auto const& [obj, obj_index] : index_) { + out << obj_index << obj; } } diff --git a/src/Object.cc b/src/Object.cc index dc53781..1830981 100644 --- a/src/Object.cc +++ b/src/Object.cc @@ -30,16 +30,15 @@ namespace archetype { bool Object::hasAttribute(int attribute_id) const { ObjectPtr p = parent(); - return attributes_.count(attribute_id) > 0 or (p and p->hasAttribute(attribute_id)); + return attributes_.contains(attribute_id) or (p and p->hasAttribute(attribute_id)); } bool Object::hasLocalAttribute(int attribute_id) const { - return attributes_.count(attribute_id) > 0; + return attributes_.contains(attribute_id); } Value Object::getAttributeValue(int attribute_id) const { - auto where = attributes_.find(attribute_id); - if (where != attributes_.end()) { + if (auto where = attributes_.find(attribute_id); where != attributes_.end()) { return where->second->evaluate(); } ObjectPtr p = parent(); @@ -105,8 +104,7 @@ namespace archetype { } Value Object::executeMethod(int message_id) { - auto where = methods_.find(message_id); - if (where != methods_.end()) { + if (auto where = methods_.find(message_id); where != methods_.end()) { return where->second->execute(); } ObjectPtr p = parent(); @@ -138,12 +136,12 @@ namespace archetype { void Object::write(Storage& out) { out << parentId_ << id_ << static_cast(prototype_); out << static_cast(attributes_.size()); - for (auto const& attribute : attributes_) { - out << attribute.first << attribute.second; + for (auto const& [attribute_id, expr] : attributes_) { + out << attribute_id << expr; } out << static_cast(methods_.size()); - for (auto const& method : methods_) { - out << method.first << method.second; + for (auto const& [message_id, stmt] : methods_) { + out << message_id << stmt; } } diff --git a/src/Object.hh b/src/Object.hh index 4689dc5..8db61b8 100644 --- a/src/Object.hh +++ b/src/Object.hh @@ -21,7 +21,7 @@ namespace archetype { - const int DefaultMethod = std::numeric_limits::max(); + inline constexpr int DefaultMethod = std::numeric_limits::max(); class Object; typedef std::shared_ptr ObjectPtr; diff --git a/src/ReadEvalPrintLoop.cc b/src/ReadEvalPrintLoop.cc index 5402cc1..6bca662 100644 --- a/src/ReadEvalPrintLoop.cc +++ b/src/ReadEvalPrintLoop.cc @@ -33,7 +33,7 @@ namespace archetype { if (t.type() != Token::IDENTIFIER) { return false; } - if (not Universe::instance().ObjectIdentifiers.count(t.number())) { + if (not Universe::instance().ObjectIdentifiers.contains(t.number())) { return false; } int object_id = Universe::instance().ObjectIdentifiers[t.number()]; diff --git a/src/Statement.hh b/src/Statement.hh index 77b41bd..00cc45a 100644 --- a/src/Statement.hh +++ b/src/Statement.hh @@ -163,8 +163,8 @@ namespace archetype { virtual Value execute() const override; }; - Statement make_statement(TokenStream& t); - Statement make_stmt_from_str(std::string src_str); + [[nodiscard]] Statement make_statement(TokenStream& t); + [[nodiscard]] Statement make_stmt_from_str(std::string src_str); } #endif /* defined(__archetype__Statement__) */ diff --git a/src/SystemObject.cc b/src/SystemObject.cc index 2fd17cd..7c98706 100644 --- a/src/SystemObject.cc +++ b/src/SystemObject.cc @@ -52,8 +52,7 @@ namespace archetype { } Value message_literal = message->messageConversion(); if (message_literal->isDefined()) { - auto where = stateByMessage_.find(message_literal->getMessage()); - if (where != stateByMessage_.end()) { + if (auto where = stateByMessage_.find(message_literal->getMessage()); where != stateByMessage_.end()) { state_ = where->second; return true; } @@ -61,7 +60,7 @@ namespace archetype { return false; } - Value SystemObject::executeMethod(int message_id) { + Value SystemObject::executeMethod(int /*message_id*/) { return Value{new AbsentValue}; } diff --git a/src/SystemParser.cc b/src/SystemParser.cc index b9b2ecd..99bae45 100644 --- a/src/SystemParser.cc +++ b/src/SystemParser.cc @@ -87,22 +87,20 @@ namespace archetype { inline void remove_fillers(list& wordValues) { auto fillers = {"a", "an", "the"}; - wordValues.erase(remove_if(begin(wordValues), end(wordValues), - [fillers](const Value& v) { - return find(begin(fillers), end(fillers), v->getString()) != end(fillers); - }), - end(wordValues)); + erase_if(wordValues, [fillers](const Value& v) { + return ranges::find(fillers, v->getString()) != end(fillers); + }); } void SystemParser::matchVerbs_(std::list& wordValues) { - for (auto vp = begin(verbMatches_); vp != end(verbMatches_); ++vp) { + for (const auto& [phrase, verb_id] : verbMatches_) { auto match = search(begin(wordValues), end(wordValues), - begin(vp->first), end(vp->first), equal_string_values); + begin(phrase), end(phrase), equal_string_values); if (match != end(wordValues)) { auto match_end = match; - advance(match_end, vp->first.size()); + advance(match_end, phrase.size()); wordValues.erase(match, match_end); - wordValues.insert(match_end, Value{new ObjectValue{vp->second}}); + wordValues.insert(match_end, Value{new ObjectValue{verb_id}}); } } } @@ -119,11 +117,11 @@ namespace archetype { // At this point we have at least one match. If it's proximate, we're completely // done. But if it isn't, then we want to check all remaining matches at this // place, of this size, for a better match that is proximate. - if (not proximate_.count(matched_obj_id)) { + if (not proximate_.contains(matched_obj_id)) { auto next_np = np; while (++next_np != end(nounMatches_) and next_np->first.size() == phrase_size) { if (equal(match, match_end, begin(next_np->first), equal_string_values) and - proximate_.count(next_np->second)) { + proximate_.contains(next_np->second)) { // This is a nearer version of the same match phrase matched_obj_id = next_np->second; break; diff --git a/src/Token.cc b/src/Token.cc index f4d9073..e7f3dd4 100644 --- a/src/Token.cc +++ b/src/Token.cc @@ -21,40 +21,42 @@ namespace archetype { { } std::ostream& operator<<(std::ostream& out, const Token& t) { + using enum Token::Type_e; switch (t.type()) { - case Token::RESERVED_WORD: + case RESERVED_WORD: out << "reserved word '" << Keywords::instance().Reserved.get(t.number()) << "'"; break; - case Token::PUNCTUATION: + case PUNCTUATION: out << "punctuation '" << char(t.number()) << "'"; break; default: out << "Token("; switch (t.type()) { - case Token::RESERVED_WORD: case Token::PUNCTUATION: + case RESERVED_WORD: case PUNCTUATION: assert(0); - case Token::IDENTIFIER: + break; + case IDENTIFIER: out << "identifier"; break; - case Token::MESSAGE: + case MESSAGE: out << "message"; break; - case Token::OPERATOR: + case OPERATOR: out << "operator"; break; - case Token::TEXT_LITERAL: + case TEXT_LITERAL: out << "text literal"; break; - case Token::QUOTE_LITERAL: + case QUOTE_LITERAL: out << "quote literal"; break; - case Token::NUMERIC: + case NUMERIC: out << "numeric"; break; - case Token::BAD_TOKEN: + case BAD_TOKEN: out << "bad token"; break; - case Token::NEWLINE: + case NEWLINE: out << "newline"; break; } @@ -64,12 +66,4 @@ namespace archetype { return out; } - bool operator==(const Token& t1, const Token& t2) { - return t1.type() == t2.type() and t1.number() == t2.number(); - } - - bool operator!=(const Token& t1, const Token& t2) { - return not (t1 == t2); - } - } diff --git a/src/Token.hh b/src/Token.hh index 47b999e..559a943 100644 --- a/src/Token.hh +++ b/src/Token.hh @@ -31,13 +31,13 @@ namespace archetype { Token(Type_e type, int number); Type_e type() const { return type_; } int number() const { return number_; } + + bool operator==(const Token&) const = default; private: Type_e type_; int number_; }; - bool operator==(const Token& t1, const Token& t2); - bool operator!=(const Token& t1, const Token& t2); std::ostream& operator<<(std::ostream& out, const Token& t); } diff --git a/src/TokenStream.cc b/src/TokenStream.cc index 2e0b5de..c1db028 100644 --- a/src/TokenStream.cc +++ b/src/TokenStream.cc @@ -29,8 +29,8 @@ namespace archetype { bool isIDStart(char c) const { return isalpha(c) or c == '_'; } bool isIDChar(char c) const { return isIDStart(c) or isDigit(c); } bool isDigit(char c) const { return isdigit(c); } - bool isOperator(char c) const { return opers_.count(c) > 0; } - bool isLongOperator(char c) const { return longOpers_.count(c) > 0; } + bool isOperator(char c) const { return opers_.contains(c); } + bool isLongOperator(char c) const { return longOpers_.contains(c); } } TypeCheck; TypeChecker::TypeChecker(): diff --git a/src/TokenStream.hh b/src/TokenStream.hh index e236459..cb2be9c 100644 --- a/src/TokenStream.hh +++ b/src/TokenStream.hh @@ -24,7 +24,7 @@ namespace archetype { bool keepLooking_; public: TokenStream(SourceFilePtr source); - bool fetch(); + [[nodiscard]] bool fetch(); bool isNewlineSignificant() const { return newlineIsToken_.front(); } void considerNewline() { newlineIsToken_.push_front(true); } @@ -35,7 +35,7 @@ namespace archetype { void expectGeneral(std::string expected); void expected(Token required); void hitEOF(Token required); - bool insistOn(Token required); + [[nodiscard]] bool insistOn(Token required); void errorMessage(std::string message); void stopLooking(); }; diff --git a/src/Universe.cc b/src/Universe.cc index c4b5c14..000ee37 100644 --- a/src/Universe.cc +++ b/src/Universe.cc @@ -115,8 +115,7 @@ namespace archetype { } void Universe::classify(TokenStream& t, int identifier, IdentifierKind_e kind) { - auto existing_p = kinds_.find(identifier); - if (existing_p == kinds_.end()) { + if (auto existing_p = kinds_.find(identifier); existing_p == kinds_.end()) { kinds_[identifier] = kind; } else if (kind != UNKNOWN_ID and existing_p->second == UNKNOWN_ID) { kinds_[identifier] = kind; @@ -133,11 +132,11 @@ namespace archetype { } void Universe::reportUndefinedIdentifiers() const { - for (auto const& p : kinds_) { - if (p.second == UNKNOWN_ID or p.second == REFERENCED_ATTRIBUTE_ID) { + for (auto const& [identifier, kind] : kinds_) { + if (kind == UNKNOWN_ID or kind == REFERENCED_ATTRIBUTE_ID) { ostringstream out; out << "Identifier \""; - out << Identifiers.get(p.first); + out << Identifiers.get(identifier); out << "\" was never defined"; output()->put(out.str()); output()->endLine(); @@ -222,7 +221,9 @@ namespace archetype { } int attribute_id = t.token().number(); Universe::instance().classify(t, attribute_id, DEFINED_ATTRIBUTE_ID); - t.insistOn(Token(Token::PUNCTUATION, ':')); + if (not t.insistOn(Token(Token::PUNCTUATION, ':'))) { + return nullptr; + } Expression expr = make_expr(t); if (not expr) { return nullptr; @@ -240,7 +241,9 @@ namespace archetype { return nullptr; } int message_id = (t.token().type() == Token::MESSAGE) ? t.token().number() : numeric_limits::max(); - t.insistOn(Token(Token::PUNCTUATION, ':')); + if (not t.insistOn(Token(Token::PUNCTUATION, ':'))) { + return nullptr; + } Statement stmt = make_statement(t); if (not stmt) { return nullptr; @@ -283,8 +286,10 @@ namespace archetype { ObjectPtr obj = Universe::instance().defineNewObject(); obj->setPrototype(true); Universe::instance().assignObjectIdentifier(obj, t.token().number()); - t.insistOn(Token(Token::RESERVED_WORD, Keywords::RW_BASED)); - t.insistOn(Token(Token::RESERVED_WORD, Keywords::RW_ON)); + if (not (t.insistOn(Token(Token::RESERVED_WORD, Keywords::RW_BASED)) and + t.insistOn(Token(Token::RESERVED_WORD, Keywords::RW_ON)))) { + return nullptr; + } if (not t.fetch()) { t.expectGeneral("name of a previously defined type"); return nullptr; diff --git a/src/Value.cc b/src/Value.cc index 1ce1bb4..de0b455 100644 --- a/src/Value.cc +++ b/src/Value.cc @@ -7,6 +7,8 @@ // #include +#include +#include #include #include #include @@ -19,7 +21,7 @@ using namespace std; namespace archetype { // Escape a string for safe embedding in a quoted literal. - static std::string escape_string(const std::string& s) { + static std::string escape_string(std::string_view s) { std::string result; result.reserve(s.size() + 2); result += '"'; @@ -37,10 +39,10 @@ namespace archetype { return result; } - // Look up the identifier name bound to an object, or empty string if none. - static std::string identifier_of(int object_id) { + // Look up the identifier name bound to an object, if there is one. + static std::optional identifier_of(int object_id) { int identifier_id = Universe::instance().identifierForObject(object_id); - if (identifier_id < 0) return ""; + if (identifier_id < 0) return std::nullopt; return Universe::instance().Identifiers.get(identifier_id); } @@ -113,7 +115,7 @@ namespace archetype { return Value{new UndefinedValue}; } - Value IValue::assign(Value new_value) { + Value IValue::assign(Value /*new_value*/) { return Value{new UndefinedValue}; } @@ -371,13 +373,13 @@ namespace archetype { } std::string ObjectValue::asRDF() const { - std::string name = identifier_of(objectId_); - if (name.empty()) { + auto name = identifier_of(objectId_); + if (not name) { return "_:object_" + std::to_string(objectId_); } ObjectPtr obj = Universe::instance().getObject(objectId_); std::string prefix = obj->isPrototype() ? "type:" : "obj:"; - return prefix + name; + return prefix + *name; } void ObjectValue::write(Storage& out) const { diff --git a/src/Wellspring.cc b/src/Wellspring.cc index cd7cf3e..9513d21 100644 --- a/src/Wellspring.cc +++ b/src/Wellspring.cc @@ -6,27 +6,25 @@ // Copyright (c) 2014, 2022 Derek Jones. All rights reserved. // +#include #include #include -#include #include #include #include "Wellspring.hh" using namespace std; +namespace fs = std::filesystem; namespace archetype { SourceFilePtr Wellspring::primarySource(string file_path) { - string::size_type last_slash = file_path.rfind('/'); - string basename = file_path.substr(0, last_slash + 1); - if (basename.empty()) { - basename = "./"; - } - paths_.push_front(basename); + fs::path source_path{file_path}; + fs::path directory = source_path.parent_path(); + paths_.push_front(directory.empty() ? "." : directory.string()); // Run the filename alone through the regular search now, since that // will also supply the default extension. - return open(file_path.substr(last_slash + 1)); + return open(source_path.filename().string()); } void Wellspring::addSearchPath(std::string directory_path) { @@ -34,27 +32,21 @@ namespace archetype { } SourceFilePtr Wellspring::open(string source_name) { - auto result = sources_.find(source_name); - if (result != sources_.end()) { + if (auto result = sources_.find(source_name); result != sources_.end()) { everBeenOpened_.insert(source_name); return result->second; } - for (auto p : paths_) { - string try_path = p; - assert(!try_path.empty()); - if (try_path.rfind('/') != (try_path.size() - 1)) { - try_path += '/'; - } - try_path += source_name; - if (source_name.find('.') == string::npos) { + for (const auto& p : paths_) { + fs::path try_path = fs::path{p} / source_name; + if (try_path.extension().empty()) { try_path += ".arch"; } - unique_ptr input(new ifstream(try_path.c_str())); + unique_ptr input(new ifstream(try_path)); if (input->is_open()) { // Now that it's been tested for openness, move it to a higher abstraction stream_ptr source_stream{input.release()}; - SourceFilePtr source{make_shared(try_path, source_stream)}; - sources_[try_path] = source; + SourceFilePtr source{make_shared(try_path.string(), source_stream)}; + sources_[try_path.string()] = source; return source; } } @@ -62,7 +54,7 @@ namespace archetype { } bool Wellspring::hasNeverBeenOpened(std::string source_name) const { - return everBeenOpened_.count(source_name) == 0; + return not everBeenOpened_.contains(source_name); } void Wellspring::put(std::string source_name, SourceFilePtr source) { @@ -70,12 +62,7 @@ namespace archetype { } void Wellspring::close(SourceFilePtr source) { - for (auto p = sources_.begin(); p != sources_.end(); ++p) { - if (p->second == source) { - sources_.erase(p); - return; - } - } + erase_if(sources_, [&source](auto const& entry) { return entry.second == source; }); } void Wellspring::closeAll() { diff --git a/src/inspect_universe.cc b/src/inspect_universe.cc index 281a243..8910321 100644 --- a/src/inspect_universe.cc +++ b/src/inspect_universe.cc @@ -1,9 +1,11 @@ #include #include -#include +#include #include +#include #include #include +#include #include "inspect_universe.hh" #include "Universe.hh" @@ -15,7 +17,7 @@ namespace archetype { // Escape a string for safe embedding in a quoted Turtle literal. // Mirrors escape_string in Value.cc; kept local to avoid a cross-TU dep. - static std::string escape_literal(const std::string& s) { + static std::string escape_literal(std::string_view s) { std::string result; result.reserve(s.size() + 2); result += '"'; @@ -34,19 +36,18 @@ namespace archetype { } // URI-encode a message name following RFC 3986 section 2.3. - static std::string uri_encode(const std::string& s) { - std::ostringstream encoded; + static std::string uri_encode(std::string_view s) { + std::string encoded; for (auto ch : s) { if ((ch >= 'A' and ch <= 'Z') or (ch >= 'a' and ch <= 'z') or (ch >= '0' and ch <= '9') or ch == '.' or ch == '_' or ch == '-' or ch == '~') { - encoded << ch; + encoded += ch; } else { - encoded << '%' << std::setw(2) << std::setfill('0') << std::hex - << (static_cast(static_cast(ch))); + encoded += std::format("%{:02x}", static_cast(ch)); } } - return encoded.str(); + return encoded; } static std::string obj_name_for(int obj_id) { @@ -100,35 +101,33 @@ namespace archetype { // -- Vocabulary: invert match tables so phrases are grouped by object -- std::map> phrases_by_object; - for (const auto& vm : parser.verbMatches_) { - phrases_by_object[vm.second].insert(join_phrase(vm.first)); + for (const auto& [phrase, verb_id] : parser.verbMatches_) { + phrases_by_object[verb_id].insert(join_phrase(phrase)); } - for (const auto& nm : parser.nounMatches_) { - phrases_by_object[nm.second].insert(join_phrase(nm.first)); + for (const auto& [phrase, noun_id] : parser.nounMatches_) { + phrases_by_object[noun_id].insert(join_phrase(phrase)); } // "Effective" phrases: noun phrases whose referent is currently in // scope (proximate). Pre-baked here so consumers without SPARQL can // answer "what can the player type right now?" with a simple scan. std::map> live_phrases_by_object; - for (const auto& nm : parser.nounMatches_) { - if (parser.proximate_.count(nm.second)) { - live_phrases_by_object[nm.second].insert(join_phrase(nm.first)); + for (const auto& [phrase, noun_id] : parser.nounMatches_) { + if (parser.proximate_.contains(noun_id)) { + live_phrases_by_object[noun_id].insert(join_phrase(phrase)); } } out << "# Vocabulary\n\n"; - for (const auto& entry : phrases_by_object) { - int obj_id = entry.first; + for (const auto& [obj_id, phrases] : phrases_by_object) { out << obj_name_for(obj_id); bool first = true; - for (const auto& p : entry.second) { + for (const auto& p : phrases) { out << "\n " << (first ? "" : "; ") << "archetype:matchesPhrase " << escape_literal(p); first = false; } - auto live = live_phrases_by_object.find(obj_id); - if (live != live_phrases_by_object.end()) { + if (auto live = live_phrases_by_object.find(obj_id); live != live_phrases_by_object.end()) { for (const auto& p : live->second) { out << "\n ; archetype:matchesNow " << escape_literal(p); } @@ -196,24 +195,24 @@ namespace archetype { out << " a " << obj_name_for(Universe::NullObjectId); } - for (auto const& attr : obj->attributes_) { - auto* val_expr = dynamic_cast(attr.second.get()); + for (auto const& [attribute_id, expr] : obj->attributes_) { + auto* val_expr = dynamic_cast(expr.get()); if (not val_expr) continue; ContextScope c; c->selfObject = obj; Value value = val_expr->evaluate(); if (value->isDefined()) { - out << "\n ; attr:" << Universe::instance().Identifiers.get(attr.first) + out << "\n ; attr:" << Universe::instance().Identifiers.get(attribute_id) << " " << value->asRDF(); } } if (include_methods) { - for (auto const& method : obj->methods_) { - if (method.first == DefaultMethod) { + for (int message_id : obj->methods_ | std::views::keys) { + if (message_id == DefaultMethod) { out << "\n ; archetype:respondsTo archetype:default"; } else { - std::string message = Universe::instance().Messages.get(method.first); + std::string message = Universe::instance().Messages.get(message_id); out << "\n ; archetype:respondsTo msg:" << uri_encode(message); } } diff --git a/src/main.cc b/src/main.cc index 57458f5..5caa77c 100644 --- a/src/main.cc +++ b/src/main.cc @@ -10,9 +10,12 @@ #include #include #include +#include #include #include +#include #include +#include #include #include "TestRegistry.hh" @@ -39,7 +42,7 @@ namespace archetype { - static const char VersionString[] = "3.0"; + static constexpr std::string_view VersionString = "3.0"; class CompilationFailure : public std::runtime_error { public: @@ -103,10 +106,9 @@ static void from_source(map &opts) { opts.erase(it_source); SourceFilePtr source = Wellspring::instance().primarySource(source_path); if (not source) { - throw invalid_argument("Cannot open \"" + source_path + "\""); + throw invalid_argument(format("Cannot open \"{}\"", source_path)); } - auto it_include = opts.find("include"); - if (it_include != opts.end()) { + if (auto it_include = opts.find("include"); it_include != opts.end()) { string includes = it_include->second; opts.erase(it_include); istringstream in(includes); @@ -120,8 +122,7 @@ static void from_source(map &opts) { throw CompilationFailure(); } Universe::instance().reportUndefinedIdentifiers(); - auto it_create = opts.find("create"); - if (it_create == opts.end()) { + if (auto it_create = opts.find("create"); it_create == opts.end()) { dispatch_to_universe("START"); } else { string filename_out = it_create->second; @@ -130,19 +131,18 @@ static void from_source(map &opts) { auto iext = source_path.rfind('.'); filename_out = source_path.substr(0, iext); } - string acx = ".acx"; - if (filename_out.rfind(acx) != filename_out.length() - acx.length()) { - filename_out += acx; + if (not filename_out.ends_with(".acx")) { + filename_out += ".acx"; } if (source_path == filename_out) { - throw invalid_argument("Cannot use " + filename_out + " as output"); + throw invalid_argument(format("Cannot use {} as output", filename_out)); } OutFileStorage save_file(filename_out); if (save_file.ok()) { save_file << Universe::instance(); - cout << "Created " + filename_out << endl; + cout << format("Created {}", filename_out) << endl; } else { - throw runtime_error("Could not write to " + filename_out); + throw runtime_error(format("Could not write to {}", filename_out)); } } } @@ -152,10 +152,10 @@ int main(int argc, const char* argv[]) { list args(argv + 1, argv + argc); map opts; for (auto a = args.begin(); a != args.end();) { - if (a->find("--") != 0) { + if (not a->starts_with("--")) { ++a; } else { - auto iequal = find(a->begin(), a->end(), '='); + auto iequal = ranges::find(*a, '='); string opt_name(a->begin() + 2, iequal); string opt_value; if (iequal != a->end()) { @@ -172,33 +172,29 @@ int main(int argc, const char* argv[]) { auto unknown_options_error = [&]() -> int { if (opts.empty() and args.empty()) return 0; cerr << "ERROR: unknown options or arguments:"; - for (auto const& kv : opts) cerr << " --" << kv.first; + for (auto const& opt_name : opts | views::keys) cerr << " --" << opt_name; for (auto const& a : args) cerr << " " << a; cerr << endl; return 1; }; - auto it_help = opts.find("help"); - if (it_help != opts.end()) { + if (auto it_help = opts.find("help"); it_help != opts.end()) { opts.erase(it_help); if (int e = unknown_options_error()) return e; usage(); return 0; } - auto it_silent = opts.find("silent"); - if (it_silent != opts.end()) { + if (auto it_silent = opts.find("silent"); it_silent != opts.end()) { session.silent(true); opts.erase(it_silent); } - auto it_test = opts.find("test"); - if (it_test != opts.end()) { + if (auto it_test = opts.find("test"); it_test != opts.end()) { opts.erase(it_test); if (int e = unknown_options_error()) return e; bool success = TestRegistry::instance().runAllTestSuites(cout); int exit_code = success ? 0 : 1; return exit_code; } - auto it_repl = opts.find("repl"); - if (it_repl != opts.end()) { + if (auto it_repl = opts.find("repl"); it_repl != opts.end()) { opts.erase(it_repl); if (!args.empty()) { std::string filename = args.front(); @@ -206,7 +202,7 @@ int main(int argc, const char* argv[]) { cout << "Loading " << filename << endl; InFileStorage in(filename); if (!in.ok()) { - throw runtime_error("Cannot open \"" + filename + "\""); + throw runtime_error(format("Cannot open \"{}\"", filename)); } in >> Universe::instance(); } @@ -215,7 +211,7 @@ int main(int argc, const char* argv[]) { return errors; } - if (opts.find("source") != opts.end()) { + if (opts.contains("source")) { try { from_source(opts); } catch (const archetype::QuitGame&) { @@ -227,10 +223,7 @@ int main(int argc, const char* argv[]) { } } - auto it_perform = opts.find("perform"); - auto it_update = opts.find("update"); - auto it_inspect = opts.find("inspect"); - if (it_perform != opts.end()) { + if (auto it_perform = opts.find("perform"); it_perform != opts.end()) { string filename = it_perform->second; opts.erase(it_perform); if (filename.rfind('.') == string::npos) { @@ -239,7 +232,7 @@ int main(int argc, const char* argv[]) { try { InFileStorage in(filename); if (!in.ok()) { - throw runtime_error("Cannot open \"" + filename + "\""); + throw runtime_error(format("Cannot open \"{}\"", filename)); } in >> Universe::instance(); dispatch_to_universe("START"); @@ -250,15 +243,14 @@ int main(int argc, const char* argv[]) { cerr << "ERROR: " << e.what() << endl; return 1; } - } else if (it_update != opts.end()) { + } else if (auto it_update = opts.find("update"); it_update != opts.end()) { string filename = it_update->second; opts.erase(it_update); if (filename.rfind('.') == string::npos) { filename += ".acx"; } int width = 80; - auto it_width = opts.find("width"); - if (it_width != opts.end()) { + if (auto it_width = opts.find("width"); it_width != opts.end()) { width = stoi(it_width->second); opts.erase(it_width); } @@ -267,23 +259,20 @@ int main(int argc, const char* argv[]) { { ifstream f_in(filename.c_str()); if (!f_in) { - throw invalid_argument("Cannot read from " + filename); + throw invalid_argument(format("Cannot read from {}", filename)); } copy(istreambuf_iterator{f_in}, {}, back_inserter(in_mem.bytes())); } - auto it_sitrep = opts.find("sitrep"); - bool sitrep = (it_sitrep != opts.end()); - if (sitrep) opts.erase(it_sitrep); + bool sitrep = opts.erase("sitrep") > 0; // --inspect with an empty value pairs with --update; a non-empty value // selects the standalone --inspect=file.acx path handled below. bool inspect_after = false; - if (it_inspect != opts.end()) { + if (auto it_inspect = opts.find("inspect"); it_inspect != opts.end()) { inspect_after = it_inspect->second.empty(); opts.erase(it_inspect); } string input; - auto it_input = opts.find("input"); - if (it_input != opts.end()) { + if (auto it_input = opts.find("input"); it_input != opts.end()) { input = it_input->second; opts.erase(it_input); } @@ -291,14 +280,14 @@ int main(int argc, const char* argv[]) { cout << update_universe(in_mem, out_mem, input, width, sitrep, inspect_after); ofstream f_out(filename.c_str()); if (!f_out) { - throw invalid_argument("Cannot write to " + filename); + throw invalid_argument(format("Cannot write to {}", filename)); } - copy(out_mem.bytes().begin(), out_mem.bytes().end(), ostreambuf_iterator{f_out}); + ranges::copy(out_mem.bytes(), ostreambuf_iterator{f_out}); } catch (const std::exception& e) { cerr << "ERROR: " << e.what() << endl; return 1; } - } else if (it_inspect != opts.end()) { + } else if (auto it_inspect = opts.find("inspect"); it_inspect != opts.end()) { string filename = it_inspect->second; opts.erase(it_inspect); if (filename.rfind('.') == string::npos) { @@ -307,11 +296,9 @@ int main(int argc, const char* argv[]) { try { InFileStorage in(filename); if (!in.ok()) { - throw runtime_error("Cannot open \"" + filename + "\""); + throw runtime_error(format("Cannot open \"{}\"", filename)); } - auto it_full = opts.find("full"); - bool full = (it_full != opts.end()); - if (full) opts.erase(it_full); + bool full = opts.erase("full") > 0; inspect_universe(in, cout, full); } catch (const std::exception& e) { cerr << "ERROR: " << e.what() << endl; diff --git a/src/update_universe.cc b/src/update_universe.cc index 376cc8e..36e078f 100644 --- a/src/update_universe.cc +++ b/src/update_universe.cc @@ -10,7 +10,9 @@ #include "Value.hh" #include "inspect_universe.hh" +#include #include +#include namespace archetype { @@ -51,7 +53,7 @@ using namespace std; // convention is that keys are short identifier-ish words ("location", // "exits"), so anything outside [A-Za-z0-9_-] is replaced with '_'. Leading // digits get an underscore prefix. An empty or all-invalid key becomes "_". -static string sanitize_local_name(const string& raw) { +static string sanitize_local_name(string_view raw) { if (raw.empty()) return "_"; string out; out.reserve(raw.size()); @@ -98,7 +100,7 @@ Value dispatch_to_universe(string message) { Value start{new MessageValue{start_id}}; Value result = Object::send(main_object, std::move(start)); if (result->isSameValueAs(Value{new AbsentValue})) { - throw invalid_argument("No method for '" + message + "' on main object"); + throw invalid_argument(format("No method for '{}' on main object", message)); } return result; }