module dsext; /* Let's try adding a new object type to DMDScript. I shall call my new object... Monkey! It shall have a property called "furColor" with the value "brown". It shall have a property called "hungry" with the value 'true'. It shall also have a method called "eatBanana" which sets "hungry" to 'false'... of course. */ import std.file; import dmdscript.script; import dmdscript.threadcontext; import dmdscript.text; /* The constructor... as a class... Interesting. */ class DS_Monkey_Ctor : Dfunction { this(ThreadContext *tc) { super(1, tc.Dfunction_prototype); } void* Construct(CallContext *cc, Value *ret, Value[] arglist) { // construct a new Monkey! Muahahaha! ret.putVobject(new DS_Monkey); return null; } void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist) { return Construct(cc, ret, arglist); } } /* The 'prototype' object... I'm still not 100% positive exactly what this thing's existance is all about, but I see what to do with it... */ class DS_Monkey_Prototype : Dobject { this(ThreadContext *tc) { // set up the core object stuff, I guess super(tc.Dobject_prototype); // set up this class Put(TEXT_constructor, tc.ctorTable["Monkey"], 0); Put(TEXT_name, "Monkey", 0); // stuff under here translates to a DMDScript object property Put("furColor", "brown", 0); Put("hungry", true, 0); } } /* Finally, the actual Monkey class! Ooh ooh aah aah. */ class DS_Monkey : Dobject { // Looks like this, and the init(), are how DMDScript learns about Monkey static this() { ThreadContext.initTable ~= &(DS_Monkey.init); } static void init(ThreadContext *tc) { Dfunction ctor = new DS_Monkey_Ctor(tc); Dobject proto = new DS_Monkey_Prototype(tc); tc.ctorTable["Monkey"] = ctor; tc.protoTable["Monkey"] = proto; ctor.Put(TEXT_prototype, proto, 0); // this stuff is funky, but its how we add methods, apparently // I'm not sure why the first field has to be a pointer... // Is that supposed to facilitate... renaming the function?? // Or...??? static char[] TEXT_eatBanana = "eatBanana"; static NativeFunctionData nfd[] = [ { &TEXT_eatBanana, &DS_Monkey_eatBanana, 0 } ]; DnativeFunction.init(proto, nfd, 0); } // because... constructors are sorta important, y'know? static Dfunction getConstructor() { ThreadContext *tc = ThreadContext.getThreadContext(); return tc.ctorTable["Monkey"]; } // still wish I knew exactly why we have this guy static Dobject getPrototype() { ThreadContext *tc = ThreadContext.getThreadContext(); return tc.protoTable["Monkey"]; } // the /real/ Monkey ctor this() { this(DS_Monkey.getPrototype()); } this(Dobject prototype) { super(prototype); classname = "Monkey"; } } /* The Monkey.eatBanana method Its odd that DMDScript object methods must be defined as functions... But then again, that might facilitate loading from a DLL. Can you say, plugin engine? I thought you could. */ void* DS_Monkey_eatBanana(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) { othis.Put("hungry", false, 0); return null; } /* The main function is pretty simple... In fact, I'm reasonably impressed by how simple. */ void main() { char[] source = cast(char[]) read("dsext.ds"); Program script = Program.getProgram(); if (script is null) { script = new Program; } // I guess I could've gotten the filename from the command line.. // but... its just an experiment, yeesh script.compile("dsext.ds", source, null); script.execute(null); }