Thread overview
Expose D Objects as DMDScript Objects
Sep 16, 2005
Kyle Furlong
Sep 16, 2005
Carlos Santander
Sep 16, 2005
Kyle Furlong
Sep 17, 2005
Carlos Santander
Sep 17, 2005
Kyle Furlong
September 16, 2005
How? :-)
September 16, 2005
Kyle Furlong escribió:
> How? :-)

I think this belongs to the DMDScript newsgroup.

Anyway, this should give you an idea of what you need. I haven't tested in a while, but it might just work ;)


/* Digital Mars DMDScript source code.
 * Copyright (c) 2000-2002 by Chromium Communications
 * D version Copyright (c) 2004-2005 by Digital Mars
 * All Rights Reserved
 * written by Walter Bright
 * www.digitalmars.com
 * Use at your own risk. There is no warranty, express or implied.
 * License for redistribution is by the GNU General Public License in gpl.txt.
 *
 * A binary, non-exclusive license for commercial use can be
 * purchased from www.digitalmars.com/dscript/buy.html.
 *
 * DMDScript is implemented in the D Programming Language,
 * www.digitalmars.com/d/
 *
 * For a C++ implementation of DMDScript, including COM support,
 * see www.digitalmars.com/dscript/cpp.html.
 */


module testscript;

import std.conv;
import std.path;
import std.file;
import std.stdio;

import dmdscript.script;

enum
{
	EXITCODE_INIT_ERROR = 1,
	EXITCODE_INVALID_ARGS = 2,
	EXITCODE_RUNTIME_ERROR = 3,
}

class C:Dobject
{
	int _p;
	int _q;

    this(ThreadContext *tc)
    {
	super(tc.Dobject_prototype);

	Put("p",0,0);
	Put("1",0,0);
    }

	int opIndex(int idx)
	{
		if (idx==1) return _q;
		throw new Exception( format("[opIndex] index out of bounds: ",idx));
	}

	void opIndexAssign(int v,int idx)
	{
		if (idx==1)
		{
			_q=v;
			Put("1",_q,0);
		}
		else
			throw new Exception(format("[opIndexAssign] index out of bounds: ",idx));
	}

    void p(int newP)
    {
        _p=newP;
        Put("p",newP,0);
    }

    int p() { return _p; }

    Value* Put(d_string PropertyName, Value* value, uint attributes)
    {
        if (PropertyName=="p")
{
            _p=cast(int)value.toNumber();
writefln("[0: c.p changed to ",_p,"]");
}
else if (PropertyName=="1")
{
            _q=cast(int)value.toNumber();
writefln("[0: c.q changed to ",_q,"]");
}
        return super.Put(PropertyName,value,attributes);
    }

    Value* Put(Identifier* key, Value* value, uint attributes)
    {
        if (key.toString=="p")
{
            _p=cast(int)value.toNumber();
writefln("[1: c.p changed to ",_p,"]");
}
else if (key.toString=="1")
{
            _q=cast(int)value.toNumber();
writefln("[1: c.q changed to ",_q,"]");
}
        return super.Put(key,value,attributes);
    }

    Value* Put(d_string PropertyName, Dobject o, uint attributes)
    {
        if (PropertyName=="p")
{
            _p=cast(int)o.value.toNumber();
writefln("[2: c.p changed to ",_p,"]");
}
else if (PropertyName=="1")
{
            _q=cast(int)o.value.toNumber();
writefln("[2: c.q changed to ",_q,"]");
}
        return super.Put(PropertyName,o,attributes);
    }

    Value* Put(d_string PropertyName, d_number n, uint attributes)
    {
        if (PropertyName=="p")
{
            _p=cast(int)n;
writefln("[3: c.p changed to ",_p,"]");
}
else if (PropertyName=="1")
{
            _q=cast(int)n;
writefln("[3: c.q changed to ",_q,"]");
}
        return super.Put(PropertyName,n,attributes);
    }

    Value* Put(d_string PropertyName, d_string s, uint attributes)
    {
        if (PropertyName=="p")
{
            _p=toInt(s);
writefln("[4: c.p changed to ",_p,"]");
}
else if (PropertyName=="1")
{
            _q=toInt(s);
writefln("[4: c.q changed to ",_q,"]");
}
        return super.Put(PropertyName,s,attributes);
    }



    Value* Put(d_uint32 index, Value* vindex, Value* value, uint attributes)
    {
        if (index==1)
{
            _q=cast(int)value.toNumber();
writefln("[5: c.q changed to ",_q,"]");
}
        return super.Put(index,vindex,value,attributes);
    }

    Value* Put(d_uint32 index, Value* value, uint attributes)
    {
        if (index==1)
{
            _q=cast(int)value.toNumber();
writefln("[5: c.q changed to ",_q,"]");
}
        return super.Put(index,value,attributes);
    }


    /*
    Value* Get(d_string PropertyName)
    {
	return Get(PropertyName, Value.calcHash(PropertyName));
    }

    Value* Get(Identifier* id)
    {
	Value* v;

	//writefln("Dobject.Get(this = %x, '%s', hash = %x)", cast(uint)cast(void*)this, PropertyName, hash);
	//writef("\tinternal_prototype = %p\n", this.internal_prototype);
	//writef("\tDfunction.getPrototype() = %p\n", Dfunction.getPrototype());
	v = proptable.get(&id.value, id.value.hash);
	//if (v) writef("found it %p\n", v.object);
	return v;
    }

    Value* Get(d_string PropertyName, uint hash)
    {
	Value* v;

	//writefln("Dobject.Get(this = %x, '%s', hash = %x)", cast(uint)cast(void*)this, PropertyName, hash);
	//writef("\tinternal_prototype = %p\n", this.internal_prototype);
	//writef("\tDfunction.getPrototype() = %p\n", Dfunction.getPrototype());
	v = proptable.get(PropertyName, hash);
	//if (v) writef("found it %p\n", v.object);
	return v;
    }

    Value* Get(d_uint32 index)
    {
	Value* v;

	v = proptable.get(index);
    //    if (!v)
    //	v = &vundefined;
	return v;
    }

    Value* Get(d_uint32 index, Value* vindex)
    {
        return proptable.get(vindex, Value.calcHash(index));
    }
    */
}

C c;

int main(char[][] args)
{

	char [][] src;

	do
	{
		writef(">");
		fflush(stdout);
		char * buf = new char[80];
		gets(buf);
		char [] tmp  = std.string.toString(buf);
		if (tmp.length>0 && tmp[0]=='#')
		{
			delete buf;
			break;
		}
		src ~= tmp.dup;
	}
	while (true)
	SrcFile f=new SrcFile(std.string.join(src,"\r\n"));
	f.compile();

	ThreadContext *tc;
	tc = ThreadContext.getThreadContext();
	assert(tc != null);
	c=new C(tc);
	c.p=4;
	c[1]=9;
	PutValue(f.program.callcontext,"c",&c.value);
	writefln("[c.p=",c.p,"]");
	writefln("[c[1]=",c[1],"]");

	f.execute();

	writefln("[c.p=",c.p,"]");
	writefln("[c[1]=",c[1],"]");

    return EXIT_SUCCESS;
}

class SrcFile
{
    char[] srcfile;
    char[][] includes;

    Program program;
    char[] buffer;

	this (char [] src)
	{
		buffer = /*"var c=new Object();c.p="~format(c.p)~" ;" ~*/ src.dup ~ ";";
	}

    this(char[] srcfilename, char[][] includes)
    {
	/* DMDScript source files default to a '.ds' extension
	 */

	srcfile = std.path.defaultExt(srcfilename, "ds");
	this.includes = includes;
    }

    void read()
    {
	/* Read the source file, prepend the include files,
	 * and put it all in buffer[]. Allocate an extra byte
	 * to buffer[] and terminate it with a 0x1A.
	 * (If the 0x1A isn't at the end, the lexer will put
	 * one there, forcing an extra copy to be made of the
	 * source text.)
	 */

	//writef("read file '%s'\n",srcfile);

	// Read the includes[] files
	uint i;
	void[] buf;
	ulong len;

	len = std.file.getSize(srcfile);
	foreach (char[] filename; includes)
	{
	    len += std.file.getSize(filename);
	}
	len++;				// leave room for sentinal

	assert(len < uint.max);

	// Prefix the includes[] files

	buffer = new tchar[len];

	foreach (char[] filename; includes)
	{
	    buf = std.file.read(filename);
	    buffer[i .. i + buf.length] = cast(char[])buf[];
	    i += buf.length;
	}

	buf = std.file.read(srcfile);
	buffer[i .. i + buf.length] = cast(char[])buf[];
	i += buf.length;

	buffer[i] = 0x1A;		// ending sentinal
	i++;
	assert(i == len);
    }

    void compile()
    {
	/* Create a DMDScript program, and compile our text buffer.
	 */

	program = new Program();
	program.compile(srcfile, buffer, null);
    }

    void execute()
    {
	/* Execute the resulting program.
	 */

	program.execute(null);
    }
}

-- 
Carlos Santander Bernal
September 16, 2005
Carlos Santander wrote:
> Kyle Furlong escribió:
> 
>> How? :-)
> 
> 
> I think this belongs to the DMDScript newsgroup.
> 
> Anyway, this should give you an idea of what you need. I haven't tested in a while, but it might just work ;)
> 
> 
> /* Digital Mars DMDScript source code.
>  * Copyright (c) 2000-2002 by Chromium Communications
>  * D version Copyright (c) 2004-2005 by Digital Mars
>  * All Rights Reserved
>  * written by Walter Bright
>  * www.digitalmars.com
>  * Use at your own risk. There is no warranty, express or implied.
>  * License for redistribution is by the GNU General Public License in gpl.txt.
>  *
>  * A binary, non-exclusive license for commercial use can be
>  * purchased from www.digitalmars.com/dscript/buy.html.
>  *
>  * DMDScript is implemented in the D Programming Language,
>  * www.digitalmars.com/d/
>  *
>  * For a C++ implementation of DMDScript, including COM support,
>  * see www.digitalmars.com/dscript/cpp.html.
>  */
> 
> 
> module testscript;
> 
> import std.conv;
> import std.path;
> import std.file;
> import std.stdio;
> 
> import dmdscript.script;
> 
> enum
> {
>     EXITCODE_INIT_ERROR = 1,
>     EXITCODE_INVALID_ARGS = 2,
>     EXITCODE_RUNTIME_ERROR = 3,
> }
> 
> class C:Dobject
> {
>     int _p;
>     int _q;
> 
>     this(ThreadContext *tc)
>     {
>     super(tc.Dobject_prototype);
> 
>     Put("p",0,0);
>     Put("1",0,0);
>     }
> 
>     int opIndex(int idx)
>     {
>         if (idx==1) return _q;
>         throw new Exception( format("[opIndex] index out of bounds: ",idx));
>     }
> 
>     void opIndexAssign(int v,int idx)
>     {
>         if (idx==1)
>         {
>             _q=v;
>             Put("1",_q,0);
>         }
>         else
>             throw new Exception(format("[opIndexAssign] index out of bounds: ",idx));
>     }
> 
>     void p(int newP)
>     {
>         _p=newP;
>         Put("p",newP,0);
>     }
> 
>     int p() { return _p; }
> 
>     Value* Put(d_string PropertyName, Value* value, uint attributes)
>     {
>         if (PropertyName=="p")
> {
>             _p=cast(int)value.toNumber();
> writefln("[0: c.p changed to ",_p,"]");
> }
> else if (PropertyName=="1")
> {
>             _q=cast(int)value.toNumber();
> writefln("[0: c.q changed to ",_q,"]");
> }
>         return super.Put(PropertyName,value,attributes);
>     }
> 
>     Value* Put(Identifier* key, Value* value, uint attributes)
>     {
>         if (key.toString=="p")
> {
>             _p=cast(int)value.toNumber();
> writefln("[1: c.p changed to ",_p,"]");
> }
> else if (key.toString=="1")
> {
>             _q=cast(int)value.toNumber();
> writefln("[1: c.q changed to ",_q,"]");
> }
>         return super.Put(key,value,attributes);
>     }
> 
>     Value* Put(d_string PropertyName, Dobject o, uint attributes)
>     {
>         if (PropertyName=="p")
> {
>             _p=cast(int)o.value.toNumber();
> writefln("[2: c.p changed to ",_p,"]");
> }
> else if (PropertyName=="1")
> {
>             _q=cast(int)o.value.toNumber();
> writefln("[2: c.q changed to ",_q,"]");
> }
>         return super.Put(PropertyName,o,attributes);
>     }
> 
>     Value* Put(d_string PropertyName, d_number n, uint attributes)
>     {
>         if (PropertyName=="p")
> {
>             _p=cast(int)n;
> writefln("[3: c.p changed to ",_p,"]");
> }
> else if (PropertyName=="1")
> {
>             _q=cast(int)n;
> writefln("[3: c.q changed to ",_q,"]");
> }
>         return super.Put(PropertyName,n,attributes);
>     }
> 
>     Value* Put(d_string PropertyName, d_string s, uint attributes)
>     {
>         if (PropertyName=="p")
> {
>             _p=toInt(s);
> writefln("[4: c.p changed to ",_p,"]");
> }
> else if (PropertyName=="1")
> {
>             _q=toInt(s);
> writefln("[4: c.q changed to ",_q,"]");
> }
>         return super.Put(PropertyName,s,attributes);
>     }
> 
> 
> 
>     Value* Put(d_uint32 index, Value* vindex, Value* value, uint attributes)
>     {
>         if (index==1)
> {
>             _q=cast(int)value.toNumber();
> writefln("[5: c.q changed to ",_q,"]");
> }
>         return super.Put(index,vindex,value,attributes);
>     }
> 
>     Value* Put(d_uint32 index, Value* value, uint attributes)
>     {
>         if (index==1)
> {
>             _q=cast(int)value.toNumber();
> writefln("[5: c.q changed to ",_q,"]");
> }
>         return super.Put(index,value,attributes);
>     }
> 
> 
>     /*
>     Value* Get(d_string PropertyName)
>     {
>     return Get(PropertyName, Value.calcHash(PropertyName));
>     }
> 
>     Value* Get(Identifier* id)
>     {
>     Value* v;
> 
>     //writefln("Dobject.Get(this = %x, '%s', hash = %x)", cast(uint)cast(void*)this, PropertyName, hash);
>     //writef("\tinternal_prototype = %p\n", this.internal_prototype);
>     //writef("\tDfunction.getPrototype() = %p\n", Dfunction.getPrototype());
>     v = proptable.get(&id.value, id.value.hash);
>     //if (v) writef("found it %p\n", v.object);
>     return v;
>     }
> 
>     Value* Get(d_string PropertyName, uint hash)
>     {
>     Value* v;
> 
>     //writefln("Dobject.Get(this = %x, '%s', hash = %x)", cast(uint)cast(void*)this, PropertyName, hash);
>     //writef("\tinternal_prototype = %p\n", this.internal_prototype);
>     //writef("\tDfunction.getPrototype() = %p\n", Dfunction.getPrototype());
>     v = proptable.get(PropertyName, hash);
>     //if (v) writef("found it %p\n", v.object);
>     return v;
>     }
> 
>     Value* Get(d_uint32 index)
>     {
>     Value* v;
> 
>     v = proptable.get(index);
>     //    if (!v)
>     //    v = &vundefined;
>     return v;
>     }
> 
>     Value* Get(d_uint32 index, Value* vindex)
>     {
>         return proptable.get(vindex, Value.calcHash(index));
>     }
>     */
> }
> 
> C c;
> 
> int main(char[][] args)
> {
> 
>     char [][] src;
> 
>     do
>     {
>         writef(">");
>         fflush(stdout);
>         char * buf = new char[80];
>         gets(buf);
>         char [] tmp  = std.string.toString(buf);
>         if (tmp.length>0 && tmp[0]=='#')
>         {
>             delete buf;
>             break;
>         }
>         src ~= tmp.dup;
>     }
>     while (true)
>     SrcFile f=new SrcFile(std.string.join(src,"\r\n"));
>     f.compile();
> 
>     ThreadContext *tc;
>     tc = ThreadContext.getThreadContext();
>     assert(tc != null);
>     c=new C(tc);
>     c.p=4;
>     c[1]=9;
>     PutValue(f.program.callcontext,"c",&c.value);
>     writefln("[c.p=",c.p,"]");
>     writefln("[c[1]=",c[1],"]");
> 
>     f.execute();
> 
>     writefln("[c.p=",c.p,"]");
>     writefln("[c[1]=",c[1],"]");
> 
>     return EXIT_SUCCESS;
> }
> 
> class SrcFile
> {
>     char[] srcfile;
>     char[][] includes;
> 
>     Program program;
>     char[] buffer;
> 
>     this (char [] src)
>     {
>         buffer = /*"var c=new Object();c.p="~format(c.p)~" ;" ~*/ src.dup ~ ";";
>     }
> 
>     this(char[] srcfilename, char[][] includes)
>     {
>     /* DMDScript source files default to a '.ds' extension
>      */
> 
>     srcfile = std.path.defaultExt(srcfilename, "ds");
>     this.includes = includes;
>     }
> 
>     void read()
>     {
>     /* Read the source file, prepend the include files,
>      * and put it all in buffer[]. Allocate an extra byte
>      * to buffer[] and terminate it with a 0x1A.
>      * (If the 0x1A isn't at the end, the lexer will put
>      * one there, forcing an extra copy to be made of the
>      * source text.)
>      */
> 
>     //writef("read file '%s'\n",srcfile);
> 
>     // Read the includes[] files
>     uint i;
>     void[] buf;
>     ulong len;
> 
>     len = std.file.getSize(srcfile);
>     foreach (char[] filename; includes)
>     {
>         len += std.file.getSize(filename);
>     }
>     len++;                // leave room for sentinal
> 
>     assert(len < uint.max);
> 
>     // Prefix the includes[] files
> 
>     buffer = new tchar[len];
> 
>     foreach (char[] filename; includes)
>     {
>         buf = std.file.read(filename);
>         buffer[i .. i + buf.length] = cast(char[])buf[];
>         i += buf.length;
>     }
> 
>     buf = std.file.read(srcfile);
>     buffer[i .. i + buf.length] = cast(char[])buf[];
>     i += buf.length;
> 
>     buffer[i] = 0x1A;        // ending sentinal
>     i++;
>     assert(i == len);
>     }
> 
>     void compile()
>     {
>     /* Create a DMDScript program, and compile our text buffer.
>      */
> 
>     program = new Program();
>     program.compile(srcfile, buffer, null);
>     }
> 
>     void execute()
>     {
>     /* Execute the resulting program.
>      */
> 
>     program.execute(null);
>     }
> }
> 

Okay, but I have a more complicated design requirement. The object cannot inherit from Dobject. The D object cannot know anything about DMDScript. Basically the scripting package is implemented as a plugin, with the main code only knowing about certain interfaces. An ideal solution of course would be to use reflection, but thats a ways away I guess.
September 17, 2005
Kyle Furlong escribió:
> 
> Okay, but I have a more complicated design requirement. The object cannot inherit from Dobject. The D object cannot know anything about DMDScript. Basically the scripting package is implemented as a plugin, with the main code only knowing about certain interfaces. An ideal solution of course would be to use reflection, but thats a ways away I guess.

Wrap your D object in a Dobject subclass. I did that when (in another sample) I changed a button's caption using DMDScript. The button knew nothing about DMDScript, of course. I don't know, there should be a way to get it done.

-- 
Carlos Santander Bernal
September 17, 2005
Carlos Santander wrote:
> Kyle Furlong escribió:
> 
>>
>> Okay, but I have a more complicated design requirement. The object cannot inherit from Dobject. The D object cannot know anything about DMDScript. Basically the scripting package is implemented as a plugin, with the main code only knowing about certain interfaces. An ideal solution of course would be to use reflection, but thats a ways away I guess.
> 
> 
> Wrap your D object in a Dobject subclass. I did that when (in another sample) I changed a button's caption using DMDScript. The button knew nothing about DMDScript, of course. I don't know, there should be a way to get it done.
> 

What I meant was that the code which has the D object must be completely  scripting language agnostic. So it could implement an IScriptable interface or such but that is about the extent of it.

The design is for a game engine. So I am trying to implement a plugin system whereby any subsystem can be changed out for a different implementation. So you could use DirectX or OpenGL, or use Lua or DMDScript, all you would have to do is write a dll with certain requirements.

The pertinent classes/interfaces are:

<code>

/****************************************
	ScriptingEngine.d - ScriptingEngine Implementation
	Author: Kyle Furlong
	Date:	Sept. 13, 2005
****************************************/

module Dluge.Plugings.Scripting.ScriptingEngine;

private
{
	import Dluge.All;
	import Dluge.Plugins.Scripting.Script;
	import std.c.windows.windows;
	import std.stdio;
	import std.string;
	import dmdscript.program;
	import dmdscript.dglobal;
}

extern(C)
{
	export IScriptingEngine Create()
	{
		return new ScriptingEngineImpl();
	}
}

private class ScriptingEngineImpl : IScriptingEngine
{		
	public IScript Create(char[] source)
	{
		return new Script(source);
	}
	
	public bit Initialize()
	{
		writefln("Scripting Engine Initialize");
		return true;
	}
	
	public bit Initialized()
	{
		return true;
	}
	
	private int i = 0;
	public void Frame()
	{
		//writefln(format(i++));
	}
	
	public void Shutdown()
	{
	}
}

extern (C)
{
	void gc_init();
	void gc_term();
	void _minit();
	void _moduleCtor();
	void _moduleUnitTests();
}

extern (Windows)
BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
{
    switch (ulReason)
    {
		case DLL_PROCESS_ATTACH:
		    gc_init();			// initialize GC
		    _minit();			// initialize module list
		    _moduleCtor();		// run module constructors
		    _moduleUnitTests();		// run module unit tests
		    break;
	
		case DLL_PROCESS_DETACH:
		    gc_term();			// shut down GC
		    break;
	
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		    // Multiple threads not supported yet
		    return false;
    }
    return true;
}

/****************************************
	Script.d - IScript Implementation for DMDScript
	Author: Kyle Furlong
	Date:	Sept. 15, 2005
****************************************/

module Dluge.Plugins.Scripting.Script;

private
{
	import Dluge.All;
 	import dmdscript.program;
 	import std.date;
}

public class Script : IScript
{
	private char[] name = "";
	public char[] Name() { return name; }
	private char[] source = "";
	public char[] Source() { return source; }
	private Program program;
	
	this(char[] source)
	{
		this.program = new Program();
		this.source = source;
		this.name = std.string.toString(getUTCtime());
	}
	
	bit Run(char[][] args)
	{
		try
		{
			program.execute(args);
			return true;
		}
		catch(ScriptException e)
		{
			return false;
		}
	}
	
	bit Compile()
	{
		try
		{
			program.compile(name, source, null);
			return true;
		}
		catch(ScriptException e)
		{
			return false;
		}		
	}
	
	void AddObject(char[] name, *IScriptable* o)
		// This needs to be  worked on
	{
		program.callcontext.global.Put(name, o, DontEnum);
	}
	
	void RemoveObject(char[] name)
	{
		program.callcontext.global.Delete(name);
	}
}

/****************************************
	IScript.d - Script interface
	Author: Kyle Furlong
	Date:	Sept. 12, 2005
****************************************/

module Dluge.Scripting.IScript;

private
{
	import Dluge.Common;
}

public interface IScript
{
	bit Run(char[][] args);
	bit Compile();
	void AddObject(char[] name, Object o);
	void RemoveObject(char[] name);
}

/****************************************
	ScriptingEngine.d - Scripting engine interface
	Author: Kyle Furlong
	Date:	Sept. 12, 2005
****************************************/

module Dluge.Scripting.ScriptingEngine;

private
{
	import Dluge.Plugin;
	import Dluge.Common;
	import Dluge.Scripting.IScript;
}

public static class ScriptingEngine
{
	mixin PluginProxy!(IScriptingEngine);
}

public interface IScriptingEngine : IPlugin
{
	IScript Create(char[] script);
}

</code>
_______________________________________________________

Let me know if you have any insight.