Last year, my theme was "yes, D *can* do that" and "just try it!", with a focus on lower level code. This year, I'd like to keep the same idea, but on the other end of the spectrum: the same D language that we can use to write bare-metal code can also write Javascript-style code.
I'll talk primarily about my jsvar.d, and some about its (optional) companion, script.d, showing what they can do and how I was able to do it.
Kind:Outline of highlights:
// this is valid D code! var a = 10; var b = "20"; var c = a + b; var d = json!q{ "foo": { "bar": 10.2 } }; writeln(d.foo); // {"bar":10.2} d.foo.bar = (var a) => a ~ b; writeln(d.foo.bar()("hello! "));
External API or script interaction are practical uses of the result, and the techniques are applicable to many other places.
import std.variant; Variant a = "test"; string s = a.get!string;
public var opBinary(string op, T)(T t) { var n; if(payloadType() == Type.Object) { var* operator = this._payload._object._peekMember("opBinary", true); if(operator !is null && operator._type == Type.Function) { return operator.call(this, op, t); } } return _op!(n, this, op, T)(t); } public var opBinaryRight(string op, T)(T s) { return var(s).opBinary!op(this); }
} else static if(isCallable!T) { this._type = Type.Function; static if(is(T == typeof(this._payload._function))) { this._payload._function = t; } else this._payload._function = delegate var(var _this, var[] args) { var ret; ParameterTypeTuple!T fargs; foreach(idx, a; fargs) { if(idx == args.length) break; cast(Unqual!(typeof(a))) fargs[idx] = args[idx].get!(typeof(a)); } static if(is(ReturnType!t == void)) { t(fargs); } else { ret = t(fargs); } return ret; }; // and also wrapped native classes, automatically WrappedNativeObject wrapNativeObject(Class)(Class obj) if(is(Class == class)) { return new class WrappedNativeObject { override Object getObject() { return obj; } this() { wrappedType = typeid(obj); // wrap the other methods // and wrap members as scriptable properties foreach(memberName; __traits(allMembers, Class)) { static if(is(typeof(__traits(getMember, obj, memberName)) type)) static if(is(typeof(__traits(getMember, obj, memberName)))) { static if(is(type == function)) { _properties[memberName] = &__traits(getMember, obj, memberName); } else { // if it has a type but is not a function, it is prolly a member _properties[memberName] = new PropertyPrototype( () => var(__traits(getMember, obj, memberName)), (var v) { static if(memberName == "handleCharEvent") { import std.stdio; writeln("setting ", memberName, " to ", v.get!type.ptr); } __traits(getMember, obj, memberName) = v.get!(type); }); } } } } }; }
public @property ref var opDispatch(string name, string file = __FILE__, size_t line = __LINE__)() { return this[name]; } public @property ref var opDispatch(string name, string file = __FILE__, size_t line = __LINE__, T)(T r) { return this.opIndexAssign!T(r, name); } public ref var NULL() { var* v = new var; *v = null; return *v; }
else static if(isDelegate!T) { // making a local copy because otherwise the delegate might refer to a struct on the stack and get corrupted later or something auto func = this._payload._function; // the static helper lets me pass specific variables to the closure static T helper(typeof(func) func) { return delegate ReturnType!T (ParameterTypeTuple!T args) { var[] arr; foreach(arg; args) arr ~= var(arg); var ret = func(var(null), arr); static if(is(ReturnType!T == void)) return; else return ret.get!(ReturnType!T); }; } return helper(func);
class CastExpression : Expression { string type; Expression e1; override string toString() { return "cast(" ~ type ~ ") " ~ e1.toString(); } override InterpretResult interpret(PrototypeObject sc) { var n = e1.interpret(sc).value; foreach(possibleType; CtList!("int", "long", "float", "double", "real", "char", "dchar", "string", "int[]", "string[]", "float[]")) { if(type == possibleType) n = mixin("cast(" ~ possibleType ~ ") n"); } return InterpretResult(n, sc); } }
// why an argument? It needs to initialize and there's no default ctor! auto terminal = Terminal(ConsoleOutputMode.linear); terminal.getline(); // supports editing
var globals = var.emptyObject; globals.loadJsonFile = delegate var(string name) { import std.file; return var.fromJson(readText(name)); }; globals.saveJsonFile = delegate var(string name, var obj) { import std.file; write(name, obj.toJson()); return obj; }; // wrapping my http2.d was easy too! globals["get"] = delegate var(string path) { auto request = client.navigateTo(Uri(path)); request.send(); return var(request.waitForCompletion()); }; writeln(interpret(line, globals));Speaker bio:
Adam D. Ruppe is a master programmer and serious contender - if not winner - for world champion of D for seven consecutive years and counting. He is among the most prolific D programmers on the planet, having written all manner of code in it from bare metal programs to professional web projects, and is also the author of the critically praised and community renowned "D Cookbook".
With expert level knowledge in several programming languages, techniques, and domains coupled with versatile practicality and humble generosity, Adam rarely finds a problem he cannot solve and is a common sight in the D community, always willing - and able - to help on nearly any user question.
Recent D projects: