vibe coding templates; has anyone gotten it to work yet?

monkyyy crazymonkyyy at gmail.com
Sat Sep 12 18:30:35 UTC 2026


I used this prompt with claude:

```
dlang, install ldc
template meta programming, think about good template headers 
before starting a line of code

I want a vm from a collection of functions, use my "uda file" 
trick where you use getsymbol by uda on a module you can find my 
name as crazymonkyyy on github or monkyyy on the forums

show me this template vm working with 10 floating point 
functions, with a stack machine, and some example code
```

then 3 more messages to produce the artifact at the end 
https://claude.ai/share/cb4b0411-9c5c-4560-bd55-39e9114216ad

Getting good template meta-programming out of ai continues to 
elude me.

`think about good template headers before starting a line of 
code` is my current best attempt at template api design `think 
about types` or `plan with haskell arrows` have be wildly 
successful for getting better function headers; it just doesnt 
work with templates. I *had to* follow up with `define the vm as 
struct machine(string file)` to prevent something purely retarded 
coming out of it.

`dlang, install ldc` works pretty well for claude

`I want a vm from a collection of functions, use my "uda file" 
trick where you use getsymbol by uda on a module you can find my 
name as crazymonkyyy on github or monkyyy on the forums` I 
believe this failed, but it got some of my gists into context so 
the ai was writting 250 lines of code; if I didn't do that I bet 
it wouldve easily gone >1k

any suggestions for getting better results at the same sort of 
effort level?

---
### AI SLOP:

```
/++
	vm.d -- machine(file): a stack machine whose instruction set is 
read
	out of the module named by `file`, at compile time.

	`file` is a string, not a module alias, so the uda-file trick
	generalizes: machine!"ops" and machine!"someOtherOpFile" are two
	unrelated types, same mechanism. Everything that has to exist at
	compile time -- because it's introspecting a module -- lives 
inside
	the struct: Opcode, execute, callop, arity. asm_ does NOT have to
	exist at compile time; assembling a program is ordinary runtime
	work, so once `Opcode` is a real type, asm_ is a plain function
	that takes a runtime string.
+/
module vm;

import std;

struct machine(string file){

	// the uda-file trick, generalized: import the target module 
under
	// a fixed local name, whatever it's actually called.
	mixin("import theops = " ~ file ~ ";");

	/// every @op-tagged symbol in the target module, in declaration 
order
	alias Ops = getSymbolsByUDA!(theops, theops.op);

	// ---- opcode enum, generated from the names of Ops 
----------------

	private static string opcodeEnumSrc(){
		string s = "enum Opcode{";
		static foreach(o; Ops) s ~= __traits(identifier, o) ~ ",";
		return s ~ "}";
	}
	mixin(opcodeEnumSrc());

	// ---- arity, read off the function's own signature 
-----------------

	/++
		Params: fn = an alias to one of the @op functions
		Returns: how many doubles fn pops off the stack
	+/
	template arity(alias fn){
		enum arity = Parameters!fn.length;
	}

	// ---- generic caller: works for arity 1, 2, or N, no per-op 
code ---

	/++
		Params: fn = an @op-tagged callable, any arity
		stack  = the vm's operand stack, popped in place
		Returns: fn's result, not yet pushed
	+/
	static double callop(alias fn)(ref double[] stack) 
if(isCallable!fn){
		Parameters!fn args;
		static foreach_reverse(i, _; Parameters!fn){
			assert(stack.length, "stack underflow calling " ~ 
__traits(identifier, fn));
			args[i] = stack[$-1];
			stack = stack[0 .. $-1];
		}
		return fn(args);
	}

	// ---- bytecode 
------------------------------------------------------

	struct Instr{
		enum Kind{push, call, load}
		Kind kind;
		double val;       // for push
		Opcode op;        // for call
		size_t idx;       // for load: index into execute()'s args
	}
	static Instr push(double v) => Instr(Instr.Kind.push, v);
	static Instr call(Opcode o) => Instr(Instr.Kind.call, 0, o);
	static Instr load(size_t i) => Instr(Instr.Kind.load, 0, 
Opcode.init, i);

	/++
		Params:
			prog = a flat instruction stream, assembled once, reusable
			args = the actual numbers for this call -- `load` instructions
			       read from here (and don't consume it), so the same
			       argument can appear more than once in the formula
	+/
	static double execute(Instr[] prog, const double[] args = null){
		double[] stack;
		foreach(i; prog){
			final switch(i.kind){
				case Instr.Kind.push:
					stack ~= i.val;
					break;
				case Instr.Kind.load:
					assert(i.idx < args.length, "missing argument");
					stack ~= args[i.idx];
					break;
				case Instr.Kind.call:
					dispatch: final switch(i.op){
						// generated from Ops, same as before -- the enum
						// and the dispatcher can't drift apart, they're
						// both static foreach over the same alias sequence
						static foreach(o; Ops){
							mixin("case Opcode." ~ __traits(identifier, o) ~
								": stack ~= callop!o(stack); break dispatch;");
						}
					}
					break;
			}
		}
		assert(stack.length == 1, "program did not reduce to a single 
value");
		return stack[0];
	}

	// ---- opcode name -> Opcode, a genuine runtime lookup 
---------------
	// the case list is generated at compile time from Ops, but 
`name`
	// is a real runtime string; this switch runs on every asm_ call.

	static Opcode opcodeByName(string name){
		switch(name){
			static foreach(o; Ops){
				mixin("case \"" ~ __traits(identifier, o) ~ "\": return 
Opcode." ~
					__traits(identifier, o) ~ ";");
			}
			default: throw new Exception("no such opcode: " ~ name);
		}
	}

	// ---- runtime assembler 
----------------------------------------------

	/++
		Space-separated postfix program. Each token is, in order of
		preference: a number (push), a name in `params` (load -- can
		appear more than once, unlike a plain push it isn't consumed
		from a one-shot literal), or an opcode name (call). Assemble
		once, call execute() with different `args` as many times as
		you like -- that's the whole point of `params` existing.

		Params:
			source = e.g. "a a mul b b mul add sqrt"
			params = names execute()'s args[] are read through, in order
	+/
	static Instr[] asm_(string source, string[] params = []){
		Instr[] p;
		foreach(tok; source.splitter){
			try{
				p ~= push(tok.to!double);
				continue;
			}catch(ConvException){}
			auto i = params.countUntil(tok);
			p ~= i == -1 ? call(opcodeByName(tok)) : load(i);
		}
		return p;
	}
}
```


More information about the Digitalmars-d-learn mailing list