Adding a module

Modules are registered as class handlers — the same mechanism the built-in modules use. You do not need to modify the parser.


1. Create kse_mymodule.cpp

#include "kse.hpp"

static size_t mymodule_handler(Engine& e, size_t i) {
    ++i; // skip "MyModule"
    if (e.tokenText(i) != ":") return e.skipOptSemi(i);
    ++i; // skip ":"

    std::string method = e.tokenText(i);
    ++i; // skip method name
    if (e.tokenText(i) == "(") ++i; // skip (

    if (method == "Hello") {
        // read input argument, then outVar (ByRef convention)
        Value v = e.evalExpr(i);
        if (e.tokenText(i) == ",") ++i; // skip comma
        std::string outVar = e.tokenText(i); ++i; // read receiver name
        if (e.tokenText(i) == ")") ++i; // skip )

        std::string msg = "Hello from MyModule: " + toString(v);
        e.setVar(outVar, Value(msg));
        return e.skipOptSemi(i);
    }

    return e.skipOptSemi(i);
}

void register_mymodule(Engine& e) {
    e.registerClass("MyModule", mymodule_handler);
}

2. Register it in kse.cpp

Declare the registrar at the top:

void register_mymodule(Engine&);

Then call it in the Engine constructor:

Engine::Engine() {
    scopedVars_[""];
    register_math(*this);
    // ... existing registrations ...
    register_mymodule(*this);   // add this line
}

3. Add to Makefile

SRCS = kse.cpp kse_konsol.cpp kse_math.cpp kse_string.cpp \
       kse_file.cpp kse_time.cpp kse_class.cpp kse_try.cpp \
       kse_mymodule.cpp

4. Use it in a script

Var:String result;
MyModule:Hello("world", result);
Konsol:Print(result);

Engine API available to handlers

e.tokenText(i)          // text of token at i
e.tokenCmd(i)           // command/type id of token at i
e.tokenLine(i)          // source line number
e.tokenCount()          // total token count

e.evalExpr(i)           // evaluate expression starting at i; advances i past it
e.setVar("name", v)     // set a variable (use the caller's outVar name for return values)
e.getVar("name")        // get a variable's Value
e.hasVar("name")        // check existence

e.skipOptSemi(i)        // call as final return; skips ; if present
e.skipPast("}", i)      // scan forward to given token, return its index

// Value helpers (free functions from kse_types.hpp)
toString(v)             // Value → std::string
toDouble(v)             // Value → double
toBool(v)               // Value → bool
Value("text")           // make a string Value
Value(3.14)             // make a number Value
Value(true)             // make a bool Value

Multiple methods, multiple arguments

if (method == "Add") {
    Value a = e.evalExpr(i);
    if (e.tokenText(i) == ",") ++i;
    Value b = e.evalExpr(i);
    if (e.tokenText(i) == ",") ++i;
    std::string outVar = e.tokenText(i); ++i; // receiver
    if (e.tokenText(i) == ")") ++i;
    e.setVar(outVar, Value(toDouble(a) + toDouble(b)));
    return e.skipOptSemi(i);
}

The pattern: eval each input argument (leaving i at the following delimiter), skip commas between arguments, read the last token as the receiver variable name, skip ), write the result with e.setVar(outVar, ...).

Exposing the module as a script-side class

If you want script code to instantiate your module type like a class (field storage, method dispatch), register it via Class:Create at runtime from a .ks file, or directly manipulate e.classDefs() and e.classInsts() from C++ — the same maps the built-in Class: keyword uses.

For a simpler plugin-based approach, see Plugin system.