mirror of https://github.com/adamdruppe/arsd.git
script macro
This commit is contained in:
parent
9c04174573
commit
bb4619715d
29
jsvar.d
29
jsvar.d
|
@ -688,6 +688,11 @@ struct var {
|
||||||
if(this.payloadType() == Type.Function) {
|
if(this.payloadType() == Type.Function) {
|
||||||
assert(this._payload._function !is null);
|
assert(this._payload._function !is null);
|
||||||
return this._payload._function(_this, args);
|
return this._payload._function(_this, args);
|
||||||
|
} else if(this.payloadType() == Type.Object) {
|
||||||
|
assert(this._payload._object !is null);
|
||||||
|
var* operator = this._payload._object._peekMember("opCall", true);
|
||||||
|
if(operator !is null && operator._type == Type.Function)
|
||||||
|
return operator.apply(_this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
version(jsvar_throw)
|
version(jsvar_throw)
|
||||||
|
@ -1075,13 +1080,19 @@ struct var {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property PrototypeObject prototypeObject() {
|
||||||
|
var v = prototype();
|
||||||
|
if(v._type == Type.Object)
|
||||||
|
return v._payload._object;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// what I call prototype is more like what Mozilla calls __proto__, but tbh I think this is better so meh
|
// what I call prototype is more like what Mozilla calls __proto__, but tbh I think this is better so meh
|
||||||
@property ref var prototype() {
|
@property ref var prototype() {
|
||||||
static var _arrayPrototype;
|
static var _arrayPrototype;
|
||||||
static var _functionPrototype;
|
static var _functionPrototype;
|
||||||
static var _stringPrototype;
|
static var _stringPrototype;
|
||||||
|
|
||||||
|
|
||||||
final switch(payloadType()) {
|
final switch(payloadType()) {
|
||||||
case Type.Array:
|
case Type.Array:
|
||||||
assert(_arrayPrototype._type == Type.Object);
|
assert(_arrayPrototype._type == Type.Object);
|
||||||
|
@ -1100,7 +1111,20 @@ struct var {
|
||||||
case Type.String:
|
case Type.String:
|
||||||
assert(_stringPrototype._type == Type.Object);
|
assert(_stringPrototype._type == Type.Object);
|
||||||
if(_stringPrototype._payload._object is null) {
|
if(_stringPrototype._payload._object is null) {
|
||||||
_stringPrototype._object = new PrototypeObject();
|
auto p = new PrototypeObject();
|
||||||
|
_stringPrototype._object = p;
|
||||||
|
|
||||||
|
var replaceFunction;
|
||||||
|
replaceFunction._type = Type.Function;
|
||||||
|
replaceFunction._function = (var _this, var[] args) {
|
||||||
|
string s = _this.toString();
|
||||||
|
import std.array : replace;
|
||||||
|
return var(std.array.replace(s,
|
||||||
|
args[0].toString(),
|
||||||
|
args[1].toString()));
|
||||||
|
};
|
||||||
|
|
||||||
|
p._properties["replace"] = replaceFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _stringPrototype;
|
return _stringPrototype;
|
||||||
|
@ -1115,6 +1139,7 @@ struct var {
|
||||||
// these types don't have prototypes
|
// these types don't have prototypes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var* v = new var(null);
|
var* v = new var(null);
|
||||||
return *v;
|
return *v;
|
||||||
}
|
}
|
||||||
|
|
92
script.d
92
script.d
|
@ -1,4 +1,6 @@
|
||||||
/**
|
/**
|
||||||
|
FIXME: Also ability to get source code for function something so you can mixin.
|
||||||
|
|
||||||
Script features:
|
Script features:
|
||||||
|
|
||||||
FIXME: add COM support on Windows
|
FIXME: add COM support on Windows
|
||||||
|
@ -104,6 +106,11 @@
|
||||||
args can say var if you want, but don't have to
|
args can say var if you want, but don't have to
|
||||||
default arguments supported in any position
|
default arguments supported in any position
|
||||||
when calling, you can use the default keyword to use the default value in any position
|
when calling, you can use the default keyword to use the default value in any position
|
||||||
|
* macros:
|
||||||
|
A macro is defined just like a function, except with the
|
||||||
|
macro keyword instead of the function keyword. The difference
|
||||||
|
is a macro must interpret its own arguments - it is passed
|
||||||
|
AST objects instead of values. Still a WIP.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -172,7 +179,7 @@ private enum string[] keywords = [
|
||||||
"__FILE__", "__LINE__", // these two are special to the lexer
|
"__FILE__", "__LINE__", // these two are special to the lexer
|
||||||
"foreach", "json!q{", "default", "finally",
|
"foreach", "json!q{", "default", "finally",
|
||||||
"return", "static", "struct", "import", "module", "assert", "switch",
|
"return", "static", "struct", "import", "module", "assert", "switch",
|
||||||
"while", "catch", "throw", "scope", "break", "super", "class", "false", "mixin", "super",
|
"while", "catch", "throw", "scope", "break", "super", "class", "false", "mixin", "super", "macro",
|
||||||
"auto", // provided as an alias for var right now, may change later
|
"auto", // provided as an alias for var right now, may change later
|
||||||
"null", "else", "true", "eval", "goto", "enum", "case", "cast",
|
"null", "else", "true", "eval", "goto", "enum", "case", "cast",
|
||||||
"var", "for", "try", "new",
|
"var", "for", "try", "new",
|
||||||
|
@ -412,6 +419,20 @@ TokenStream!TextStream lexScript(TextStream)(TextStream textStream, string scrip
|
||||||
return new TokenStream!TextStream(textStream, scriptFilename);
|
return new TokenStream!TextStream(textStream, scriptFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MacroPrototype : PrototypeObject {
|
||||||
|
var func;
|
||||||
|
|
||||||
|
// macros are basically functions that get special treatment for their arguments
|
||||||
|
// they are passed as AST objects instead of interpreted
|
||||||
|
// calling an AST object will interpret it in the script
|
||||||
|
this(var func) {
|
||||||
|
this.func = func;
|
||||||
|
this._properties["opCall"] = (var _this, var[] args) {
|
||||||
|
return func.apply(_this, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct InterpretResult {
|
struct InterpretResult {
|
||||||
var value;
|
var value;
|
||||||
PrototypeObject sc;
|
PrototypeObject sc;
|
||||||
|
@ -422,6 +443,26 @@ struct InterpretResult {
|
||||||
|
|
||||||
class Expression {
|
class Expression {
|
||||||
abstract InterpretResult interpret(PrototypeObject sc);
|
abstract InterpretResult interpret(PrototypeObject sc);
|
||||||
|
|
||||||
|
// this returns an AST object that can be inspected and possibly altered
|
||||||
|
// by the script. Calling the returned object will interpret the object in
|
||||||
|
// the original scope passed
|
||||||
|
var toScriptExpressionObject(PrototypeObject sc) {
|
||||||
|
var obj = var.emptyObject;
|
||||||
|
|
||||||
|
obj["type"] = typeid(this).name;
|
||||||
|
obj["toString"] = (var _this, var[] args) {
|
||||||
|
return var(this.toString());
|
||||||
|
};
|
||||||
|
obj["opCall"] = (var _this, var[] args) {
|
||||||
|
Expression e = this;
|
||||||
|
// FIXME: if they changed the properties in the
|
||||||
|
// script, we should update them here too.
|
||||||
|
return e.interpret(sc).value;
|
||||||
|
};
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MixinExpression : Expression {
|
class MixinExpression : Expression {
|
||||||
|
@ -608,7 +649,7 @@ class FunctionLiteralExpression : Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
override string toString() {
|
override string toString() {
|
||||||
string s = "function (";
|
string s = (isMacro ? "macro" : "function") ~ " (";
|
||||||
s ~= arguments.toString();
|
s ~= arguments.toString();
|
||||||
|
|
||||||
s ~= ") ";
|
s ~= ") ";
|
||||||
|
@ -631,6 +672,8 @@ class FunctionLiteralExpression : Expression {
|
||||||
|
|
||||||
PrototypeObject lexicalScope;
|
PrototypeObject lexicalScope;
|
||||||
|
|
||||||
|
bool isMacro;
|
||||||
|
|
||||||
override InterpretResult interpret(PrototypeObject sc) {
|
override InterpretResult interpret(PrototypeObject sc) {
|
||||||
assert(DefaultArgumentDummyObject !is null);
|
assert(DefaultArgumentDummyObject !is null);
|
||||||
var v;
|
var v;
|
||||||
|
@ -666,6 +709,11 @@ class FunctionLiteralExpression : Expression {
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if(isMacro) {
|
||||||
|
var n = var.emptyObject;
|
||||||
|
n._object = new MacroPrototype(v);
|
||||||
|
v = n;
|
||||||
|
}
|
||||||
return InterpretResult(v, sc);
|
return InterpretResult(v, sc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1144,6 +1192,22 @@ class ForExpression : Expression {
|
||||||
|
|
||||||
return InterpretResult(result, sc, flowControl);
|
return InterpretResult(result, sc, flowControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string toString() {
|
||||||
|
string code = "for(";
|
||||||
|
if(initialization !is null)
|
||||||
|
code ~= initialization.toString();
|
||||||
|
code ~= "; ";
|
||||||
|
if(condition !is null)
|
||||||
|
code ~= condition.toString();
|
||||||
|
code ~= "; ";
|
||||||
|
if(advancement !is null)
|
||||||
|
code ~= advancement.toString();
|
||||||
|
code ~= ") ";
|
||||||
|
code ~= loopBody.toString();
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IfExpression : Expression {
|
class IfExpression : Expression {
|
||||||
|
@ -1169,6 +1233,19 @@ class IfExpression : Expression {
|
||||||
}
|
}
|
||||||
return InterpretResult(result.value, sc, result.flowControl);
|
return InterpretResult(result.value, sc, result.flowControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string toString() {
|
||||||
|
string code = "if(";
|
||||||
|
code ~= condition.toString();
|
||||||
|
code ~= ") ";
|
||||||
|
if(ifTrue !is null)
|
||||||
|
code ~= ifTrue.toString();
|
||||||
|
else
|
||||||
|
code ~= " { }";
|
||||||
|
if(ifFalse !is null)
|
||||||
|
code ~= " else " ~ ifFalse.toString();
|
||||||
|
return code;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is kinda like a placement new, and currently isn't exposed inside the language,
|
// this is kinda like a placement new, and currently isn't exposed inside the language,
|
||||||
|
@ -1320,10 +1397,14 @@ class CallExpression : Expression {
|
||||||
|
|
||||||
override InterpretResult interpret(PrototypeObject sc) {
|
override InterpretResult interpret(PrototypeObject sc) {
|
||||||
auto f = func.interpret(sc).value;
|
auto f = func.interpret(sc).value;
|
||||||
|
bool isMacro = (f.payloadType == var.Type.Object && ((cast(MacroPrototype) f._payload._object) !is null));
|
||||||
var[] args;
|
var[] args;
|
||||||
foreach(argument; arguments)
|
foreach(argument; arguments)
|
||||||
if(argument !is null) {
|
if(argument !is null) {
|
||||||
args ~= argument.interpret(sc).value;
|
if(isMacro) // macro, pass the argument as an expression object
|
||||||
|
args ~= argument.toScriptExpressionObject(sc);
|
||||||
|
else // regular function, interpret the arguments
|
||||||
|
args ~= argument.interpret(sc).value;
|
||||||
} else {
|
} else {
|
||||||
if(DefaultArgumentDummyObject is null)
|
if(DefaultArgumentDummyObject is null)
|
||||||
DefaultArgumentDummyObject = new PrototypeObject();
|
DefaultArgumentDummyObject = new PrototypeObject();
|
||||||
|
@ -1471,6 +1552,7 @@ Expression parsePart(MyTokenStreamHere)(ref MyTokenStreamHere tokens) {
|
||||||
obj.elements[key.str] = value;
|
obj.elements[key.str] = value;
|
||||||
|
|
||||||
goto moreKeys;
|
goto moreKeys;
|
||||||
|
case "macro":
|
||||||
case "function":
|
case "function":
|
||||||
tokens.requireNextToken(ScriptToken.Type.symbol, "(");
|
tokens.requireNextToken(ScriptToken.Type.symbol, "(");
|
||||||
|
|
||||||
|
@ -1481,6 +1563,7 @@ Expression parsePart(MyTokenStreamHere)(ref MyTokenStreamHere tokens) {
|
||||||
tokens.requireNextToken(ScriptToken.Type.symbol, ")");
|
tokens.requireNextToken(ScriptToken.Type.symbol, ")");
|
||||||
|
|
||||||
exp.functionBody = parseExpression(tokens);
|
exp.functionBody = parseExpression(tokens);
|
||||||
|
exp.isMacro = token.str == "macro";
|
||||||
|
|
||||||
e = exp;
|
e = exp;
|
||||||
break;
|
break;
|
||||||
|
@ -2066,6 +2149,7 @@ Expression parseStatement(MyTokenStreamHere)(ref MyTokenStreamHere tokens, strin
|
||||||
goto skip;
|
goto skip;
|
||||||
// literals
|
// literals
|
||||||
case "function":
|
case "function":
|
||||||
|
case "macro":
|
||||||
// function can be a literal, or a declaration.
|
// function can be a literal, or a declaration.
|
||||||
|
|
||||||
tokens.popFront(); // we're peeking ahead
|
tokens.popFront(); // we're peeking ahead
|
||||||
|
@ -2091,6 +2175,8 @@ Expression parseStatement(MyTokenStreamHere)(ref MyTokenStreamHere tokens, strin
|
||||||
e.identifiers ~= ident.str;
|
e.identifiers ~= ident.str;
|
||||||
e.initializers ~= exp;
|
e.initializers ~= exp;
|
||||||
|
|
||||||
|
exp.isMacro = token.str == "macro";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue