Files
whetstone_DSL/editor/src/AgentCodeGen.h

195 lines
7.4 KiB
C
Raw Normal View History

2026-02-09 19:21:17 -07:00
#pragma once
#include "PrimitivesRegistry.h"
#include "ast/Module.h"
#include "ast/ClassDeclaration.h"
#include "ast/Variable.h"
#include "ast/Type.h"
2026-02-09 19:21:17 -07:00
#include "ast/Function.h"
#include "ast/Parameter.h"
#include "ast/Statement.h"
#include "ast/Expression.h"
#include <string>
#include <vector>
#include <algorithm>
struct AgentCodeGenResult {
ASTNode* node = nullptr;
std::string note;
std::vector<std::string> usedSymbols;
};
class AgentCodeGen {
public:
AgentCodeGenResult generate(const std::string& spec,
PrimitivesRegistry& registry,
const std::string& language,
bool preferImports) const {
AgentCodeGenResult result;
if (looksLikeClassOrModuleSpec(spec)) {
result.node = buildClassOrModule(spec, language);
result.note = "Generated class/module structure from specification.";
return result;
}
2026-02-09 19:21:17 -07:00
auto functions = registry.getAvailableFunctions();
if (functions.empty()) {
result.note = "No available functions; returning empty stub.";
result.node = makeEmptyFunction("generated");
return result;
}
PrimitiveSymbol chosen = chooseFunction(functions, preferImports);
result.usedSymbols.push_back(chosen.name);
auto* fn = new Function(nextId("fn"), "generated");
auto* param = new Parameter(nextId("param"), "input");
param->setChild("type", new PrimitiveType(nextId("type"), "string"));
2026-02-09 19:21:17 -07:00
fn->addChild("parameters", param);
auto* call = new FunctionCall();
call->id = nextId("call");
call->functionName = chosen.name;
call->addChild("arguments", new VariableReference(nextId("var"), "input"));
auto* stmt = new ExpressionStatement();
stmt->id = nextId("exprstmt");
stmt->setChild("expression", call);
fn->addChild("body", stmt);
result.node = fn;
result.note = buildNote(spec, chosen, language);
return result;
}
private:
static PrimitiveSymbol chooseFunction(const std::vector<PrimitiveSymbol>& funcs,
bool preferImports) {
if (preferImports) {
for (const auto& sym : funcs) {
if (sym.source == "import" && !sym.vulnerable) return sym;
}
2026-02-09 19:21:17 -07:00
for (const auto& sym : funcs) {
if (sym.source == "import") return sym;
}
}
for (const auto& sym : funcs) {
if (sym.source == "builtin") return sym;
}
return funcs.front();
}
static Function* makeEmptyFunction(const std::string& name) {
auto* fn = new Function(nextId("fn"), name);
return fn;
}
static std::string buildNote(const std::string& spec,
const PrimitiveSymbol& sym,
const std::string& language) {
std::string note = "Generated using " + sym.name + " (" + sym.source + ")";
if (!spec.empty()) note += " for spec: " + spec;
if (!language.empty()) note += " in " + language;
if (!sym.tags.empty()) {
note += " | tags: ";
for (size_t i = 0; i < sym.tags.size(); ++i) {
if (i > 0) note += ", ";
note += sym.tags[i];
}
}
2026-02-09 19:21:17 -07:00
return note;
}
static std::string nextId(const std::string& prefix) {
static int counter = 0;
return prefix + "_" + std::to_string(counter++);
}
static bool containsInsensitive(const std::string& text,
const std::string& token) {
auto lower = [](const std::string& s) {
std::string out = s;
std::transform(out.begin(), out.end(), out.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return out;
};
std::string t = lower(text);
std::string k = lower(token);
return t.find(k) != std::string::npos;
}
static bool looksLikeClassOrModuleSpec(const std::string& spec) {
return containsInsensitive(spec, "class") ||
containsInsensitive(spec, "struct") ||
containsInsensitive(spec, "priorityqueue") ||
containsInsensitive(spec, "workitem");
}
static Variable* makeField(const std::string& name, const std::string& primitiveKind) {
auto* v = new Variable(nextId("var"), name);
auto* t = new PrimitiveType(nextId("type"), primitiveKind);
v->setChild("type", t);
return v;
}
static MethodDeclaration* makeMethod(const std::string& name,
const std::string& retType,
const std::vector<std::pair<std::string, std::string>>& params = {}) {
auto* m = new MethodDeclaration(nextId("meth"), name);
if (!retType.empty()) {
m->setChild("returnType", new PrimitiveType(nextId("type"), retType));
}
for (const auto& p : params) {
auto* param = new Parameter(nextId("param"), p.first);
param->setChild("type", new PrimitiveType(nextId("type"), p.second));
m->addChild("parameters", param);
}
return m;
}
static ASTNode* buildClassOrModule(const std::string& spec,
const std::string& language) {
auto* mod = new Module(nextId("mod"), "generated_module", language.empty() ? "cpp" : language);
const bool wantsWorkItem = containsInsensitive(spec, "workitem");
const bool wantsPriorityQueue = containsInsensitive(spec, "priorityqueue") ||
containsInsensitive(spec, "queue");
if (wantsWorkItem) {
auto* workItem = new ClassDeclaration(nextId("cls"), "WorkItem");
workItem->addChild("fields", makeField("job_id", "string"));
workItem->addChild("fields", makeField("priority", "int"));
workItem->addChild("fields", makeField("payload", "string"));
mod->addChild("classes", workItem);
}
if (wantsPriorityQueue) {
auto* pq = new ClassDeclaration(nextId("cls"), "PriorityQueue");
pq->addChild("fields", makeField("items", "string"));
if (wantsWorkItem) {
pq->addChild("methods", makeMethod("__init__", "", {}));
pq->addChild("methods", makeMethod("enqueue", "void", {{"item", "string"}}));
pq->addChild("methods", makeMethod("dequeue", "string", {}));
pq->addChild("methods", makeMethod("peek", "string", {}));
pq->addChild("methods", makeMethod("size", "int", {}));
pq->addChild("methods", makeMethod("empty", "bool", {}));
} else {
pq->addChild("methods", makeMethod("enqueue", "void", {{"item", "string"}}));
pq->addChild("methods", makeMethod("dequeue", "string", {}));
}
mod->addChild("classes", pq);
}
if (!wantsWorkItem && !wantsPriorityQueue) {
auto* cls = new ClassDeclaration(nextId("cls"), "GeneratedClass");
cls->addChild("methods", makeMethod("run", "void", {}));
mod->addChild("classes", cls);
}
(void)language;
return mod;
}
2026-02-09 19:21:17 -07:00
};