5.3.18. C++ Integration: Dynamic Scripts
This tutorial shows how to build a daslang program from a string at runtime, compile it from a virtual file (no disk I/O), and interact with its global variables directly through context memory pointers.
The example implements an expression calculator: the host defines variables and a math expression, compiles them into a tiny daslang program, and then evaluates the expression at near-native speed by poking variable values directly into context memory.
Topics covered:
TextWriter— building daslang source code as a C++ stringTextFileInfo+FsFileAccess::setFileInfo— registering a virtual filecompileDaScripton a virtual file namectx.findVariable/ctx.getVariable— locating globals in context memoryDirect pointer writes for maximum-performance variable updates
shared_ptr<Context>for long-lived context reuse
5.3.18.1. Prerequisites
Tutorial 11 completed (tutorial_integration_cpp_context_variables).
Understanding of
compileDaScriptandContextlifecycle.
5.3.18.2. Building source from C++ strings
TextWriter is daslang’s stream builder. We use it to construct a
valid daslang program with global variables and an eval function:
TextWriter ss;
ss << "options gen2\n";
ss << "require math\n\n";
for (auto & v : vars) {
ss << "var " << v.first << " = 0.0f\n";
}
ss << "\n[export]\n"
<< "def eval() : float {\n"
<< " return " << expr << "\n"
<< "}\n";
The generated source is a complete daslang module — it requires
math for trigonometric functions and declares each variable as a
module-level global.
5.3.18.3. Compiling from a virtual file
Instead of writing the source to disk, we register it as a virtual file
using TextFileInfo and setFileInfo:
auto fAccess = make_smart<FsFileAccess>();
auto fileInfo = make_unique<TextFileInfo>(
text.c_str(), uint32_t(text.length()), false);
fAccess->setFileInfo("expr.das", das::move(fileInfo));
ModuleGroup dummyLibGroup;
auto program = compileDaScript("expr.das", fAccess, tout, dummyLibGroup);
compileDaScript treats "expr.das" as a real file, but the file
access layer resolves it to our in-memory buffer. This is useful for
procedurally generated code, REPL implementations, and expression
evaluators.
5.3.18.4. Direct context memory access
After simulation, we locate each variable in context memory and cache its pointer for fast writes:
for (auto & v : vars) {
auto idx = ctx->findVariable(v.first.c_str());
if (idx != -1) {
variables[v.first] = (float *)ctx->getVariable(idx);
}
}
To evaluate the expression with new values, we write directly into context memory — no function calls, no marshalling:
float ExprCalc::compute(ExprVars & vars) {
for (auto & v : vars) {
auto it = variables.find(v.first);
if (it != variables.end()) {
*(it->second) = v.second;
}
}
auto res = ctx->evalWithCatch(fni, nullptr);
return cast<float>::to(res);
}
5.3.18.5. Build & run
cmake --build build --config Release --target integration_cpp_18
bin/Release/integration_cpp_18
Expected output:
a=1 b=2 c=3 => a*b+c = 5
All 60 evaluations correct
sin(0)+cos(0) = 1
sin(pi)+cos(pi) = -1
See also
Full source:
18_dynamic_scripts.cpp
Previous tutorial: tutorial_integration_cpp_coroutines
Next tutorial: tutorial_integration_cpp_class_adapters
Related: tutorial_integration_cpp_context_variables