#include "EngineSEXP.h" #include "sexp_lookup.h" #include "parse/sexp.h" namespace sexp { EngineSEXPFactory::ArgumentListBuilder::ArgumentListBuilder(EngineSEXPFactory* parent) : _parent(parent) {} EngineSEXPFactory::ArgumentListBuilder& EngineSEXPFactory::ArgumentListBuilder::arg(int type, SCP_string help_text) { argument arg; arg.type = type; arg.help_text = std::move(help_text); _parent->_arguments.emplace_back(std::move(arg)); return *this; } EngineSEXPFactory::ArgumentListBuilder& EngineSEXPFactory::ArgumentListBuilder::beginOptional() { Assertion(std::find_if(_parent->_arguments.begin(), _parent->_arguments.end(), EngineSEXPFactory::isArgumentOptional) == _parent->_arguments.end(), "Optional marker was already added!"); Assertion(std::find_if(_parent->_arguments.begin(), _parent->_arguments.end(), EngineSEXPFactory::isArgumentVarargsMarker) == _parent->_arguments.end(), "Adding optional arguments after varags specifier is not allowed!"); argument arg; arg.optional_marker = true; _parent->_arguments.emplace_back(std::move(arg)); return *this; } EngineSEXPFactory::ArgumentListBuilder& EngineSEXPFactory::ArgumentListBuilder::beginVarargs() { Assertion(std::find_if(_parent->_arguments.begin(), _parent->_arguments.end(), EngineSEXPFactory::isArgumentVarargsMarker) == _parent->_arguments.end(), "Varargs marker was already added!"); argument arg; arg.varargs_marker = true; _parent->_arguments.emplace_back(std::move(arg)); return *this; } EngineSEXPFactory& EngineSEXPFactory::ArgumentListBuilder::finishArguments() { return *_parent; } EngineSEXPFactory::EngineSEXPFactory(std::unique_ptr sexp) : m_sexp(std::move(sexp)) {} EngineSEXPFactory& EngineSEXPFactory::category(int cat) { _category = cat; return *this; } EngineSEXPFactory& EngineSEXPFactory::category(const SCP_string& cat) { _categoryName = cat; return *this; } EngineSEXPFactory& EngineSEXPFactory::subcategory(int cat) { _category = cat; return *this; } EngineSEXPFactory& EngineSEXPFactory::subcategory(const SCP_string& subcat) { _subcategoryName = subcat; return *this; } EngineSEXPFactory::ArgumentListBuilder EngineSEXPFactory::beginArgList() { return {this}; } dummy_return EngineSEXPFactory::finish() { Assertion(_returnType >= 0, "Engine SEXP %s: A return type has to be specified!", m_sexp->getName().c_str()); if (_category >= 0 && _category != OP_CATEGORY_NONE) { m_sexp->setCategory(_category); } else { Assertion(!_categoryName.empty(), "Engine SEXP %s: A category has to be specified!", m_sexp->getName().c_str()); m_sexp->setCategoryName(_categoryName); } if (_subcategory >= 0 && _subcategory != OP_SUBCATEGORY_NONE) { m_sexp->setSubcategory(_subcategory); } else { Assertion(!_subcategoryName.empty(), "Engine SEXP %s: A subcategory has to be specified!", m_sexp->getName().c_str()); m_sexp->setSubcategoryName(_subcategoryName); } m_sexp->setReturnType(_returnType); SCP_stringstream helpStream; helpStream << m_sexp->getName() << "\r\n"; helpStream << "\t" << _helpText << "\r\n"; // Now build the argument help text and min and max argument count int min_args_counter = 0; int max_args_counter = 0; SCP_vector argument_types; SCP_vector variable_argument_types; int arg_counter = 1; bool varargs = false; bool optional = false; for (const auto& arg : _arguments) { if (arg.varargs_marker) { helpStream << "Rest: (The following pattern repeats)\r\n"; // Number non-vararg and vararg parameters separately arg_counter = 1; varargs = true; continue; } else if (arg.optional_marker) { optional = true; continue; } helpStream << arg_counter << ": " << arg.help_text << "\r\n"; ++arg_counter; if (!varargs && !optional) { // Keep track of minimum number of arguments. If we are either in the varargs part or the optional part then // we have found the minimum number and should not increment it any further. min_args_counter++; } // Always increment max. varargs are handled after the loop max_args_counter++; // Collect argument types if (varargs) { variable_argument_types.push_back(arg.type); } else { argument_types.push_back(arg.type); } } if (varargs) { max_args_counter = std::numeric_limits::max(); } m_sexp->initArguments(min_args_counter, max_args_counter, std::move(argument_types), std::move(variable_argument_types)); m_sexp->setHelpText(helpStream.str()); m_sexp->setAction(std::move(_action)); add_dynamic_sexp(std::move(m_sexp)); return {}; } EngineSEXPFactory::~EngineSEXPFactory() { Assertion(m_sexp == nullptr, "Did not call finish on a EngineSEXP factory!"); } EngineSEXPFactory& EngineSEXPFactory::helpText(SCP_string text) { _helpText = std::move(text); return *this; } EngineSEXPFactory& EngineSEXPFactory::returnType(int type) { _returnType = type; return *this; } EngineSEXPFactory& EngineSEXPFactory::action(EngineSexpAction act) { _action = std::move(act); return *this; } bool EngineSEXPFactory::isArgumentOptional(const EngineSEXPFactory::argument& arg) { return arg.optional_marker; } bool EngineSEXPFactory::isArgumentVarargsMarker(const EngineSEXPFactory::argument& arg) { return arg.varargs_marker; } EngineSEXP::EngineSEXP(const SCP_string& name) : DynamicSEXP(name) {} EngineSEXPFactory EngineSEXP::create(const SCP_string& name) { return { std::unique_ptr(new EngineSEXP(name)) }; } void EngineSEXP::initialize() { // Initialize category now that we know that it is safe to do so if (_category == OP_CATEGORY_NONE) { _category = get_category(_categoryName); if (_category == OP_CATEGORY_NONE) { _category = add_category(_categoryName); } } // Initialize subcategory now that we know that it is safe to do so if (_subcategory == OP_SUBCATEGORY_NONE) { _subcategory = get_subcategory(_subcategoryName, _category); if (_subcategory == OP_SUBCATEGORY_NONE) { _subcategory = add_subcategory(_category, _subcategoryName); } } } int EngineSEXP::getMinimumArguments() const { return _minArgs; } int EngineSEXP::getMaximumArguments() const { return _maxArgs; } int EngineSEXP::getArgumentType(int argnum) const { if (argnum >= static_cast(_argumentTypes.size())) { // We are in the varargs part. Adjust the number and then fit it into the varargs types array argnum -= static_cast(_argumentTypes.size()); // Error checking to avoid division by zero if (_variableArgumentsTypes.empty()) { return OPF_NONE; } argnum = argnum % _variableArgumentsTypes.size(); return _variableArgumentsTypes[argnum]; } else { // Normal arguments so just look up the type return _argumentTypes[argnum]; } } int EngineSEXP::execute(int node, int /*parent_node*/) { SEXPParameterExtractor extractor(node); return _action(&extractor); } int EngineSEXP::getReturnType() { return _returnType; } int EngineSEXP::getSubcategory() { return _subcategory; } int EngineSEXP::getCategory() { return _category; } void EngineSEXP::setCategory(int category) { _category = category; } void EngineSEXP::setCategoryName(SCP_string category) { _categoryName = std::move(category); } void EngineSEXP::setSubcategory(int subcategory) { _subcategory = subcategory; } void EngineSEXP::setSubcategoryName(SCP_string subcategory) { _subcategoryName = std::move(subcategory); } void EngineSEXP::setHelpText(SCP_string helpText) { _help_text = std::move(helpText); } void EngineSEXP::setReturnType(int returnType) { _returnType = returnType; } void EngineSEXP::initArguments(int minArgs, int maxArgs, SCP_vector argTypes, SCP_vector varargsTypes) { _minArgs = minArgs; _maxArgs = maxArgs; _argumentTypes = std::move(argTypes); _variableArgumentsTypes = std::move(varargsTypes); } void EngineSEXP::setAction(EngineSexpAction action) { _action = std::move(action); } } // namespace sexp