A simple scripting language in C++
Ferenc Szontágh
2025-04-13 86904d513734134beffc29c6f4012d53a99f25c5
src/BaseFunction.hpp
@@ -1,26 +1,78 @@
// ScriptFunction.hpp
#ifndef SCRIPT_FUNCTION_HPP
#define SCRIPT_FUNCTION_HPP
#include <functional>
#include <stdexcept>
#include <unordered_map>
#include <utility>
#include <vector>
#include "ScriptExceptionMacros.h"
#include "ScriptInterpreterHelpers.hpp"
#include "Token.hpp"
#include "Value.hpp"
class ScriptInterpreter;
using CallbackFunction = std::function<Value(const std::vector<Value> &)>;
using CallBackStorage  = std::unordered_map<std::string, CallbackFunction>;
class BaseFunction {
  protected:
    std::string name;
    std::string     name;
    CallBackStorage functionMap;
  public:
    virtual ~BaseFunction()                                                       = default;
    virtual void  validate(const std::vector<Token> & tokens, size_t & i) const   = 0;
    BaseFunction(const std::string & functionName) : name(functionName) {}
    virtual void addFunction(const std::string & name, std::function<Value(const std::vector<Value> &)> callback) {
        functionMap[name] = std::move(callback);
    }
    virtual void validate(const std::vector<Token> & tokens, size_t & i,
                          const std::unordered_map<std::string, Value> & variables) {
        size_t index = i;
        if (tokens[index].type != TokenType::Identifier) {
            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "identifier");
        }
        index++;  // skip function name
        if (tokens[index].type != TokenType::LeftParenthesis) {
            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "(");
        }
        index++;  // skip '('
        std::vector<Token> args;
        while (tokens[index].type != TokenType::RightParenthesis) {
            if (tokens[index].type == TokenType::Comma) {
                index++;
                continue;
            }
            if (tokens[index].type == TokenType::Variable && !variables.contains(tokens[index].lexeme)) {
                THROW_UNDEFINED_VARIABLE_ERROR(tokens[index].lexeme, tokens[index]);
            }
            args.push_back(tokens[index]);
            index++;
        }
        index++;  // skip ')'
        if (tokens[index].type != TokenType::Semicolon) {
            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], ";");
        }
        this->validateArgs(args, variables);
        ScriptInterpreterHelpers::expectSemicolon(tokens, index, "function call");
    }
    virtual void validateArgs(const std::vector<Token> &                     args,
                              const std::unordered_map<std::string, Value> & variables) = 0;
    virtual Value call(const std::vector<Value> & args, bool debug = false) const = 0;
    template <typename FuncClass> void registerFunctionTo(ScriptInterpreter & interp) {
        FuncClass::registerTo(interp);
    }
    template <typename FuncClass> void registerFunctionTo(ScriptInterpreter & interp) { FuncClass::registerTo(interp); }
};
#endif  // SCRIPT_FUNCTION_HPP