mirror of https://github.com/adamdruppe/arsd.git
slightly better erros, throw runtime possibility
This commit is contained in:
parent
8edcd7ad8b
commit
246aa4c486
97
jsvar.d
97
jsvar.d
|
@ -76,6 +76,16 @@ import std.traits;
|
||||||
import std.conv;
|
import std.conv;
|
||||||
import std.json;
|
import std.json;
|
||||||
|
|
||||||
|
version(jsvar_throw)
|
||||||
|
/++
|
||||||
|
Variable to decide if jsvar throws on certain invalid
|
||||||
|
operations or continues on propagating null vars.
|
||||||
|
+/
|
||||||
|
bool jsvar_throw = true;
|
||||||
|
else
|
||||||
|
/// ditto
|
||||||
|
bool jsvar_throw = false;
|
||||||
|
|
||||||
// uda for wrapping classes
|
// uda for wrapping classes
|
||||||
enum scriptable = "arsd_jsvar_compatible";
|
enum scriptable = "arsd_jsvar_compatible";
|
||||||
|
|
||||||
|
@ -838,7 +848,7 @@ struct var {
|
||||||
public var apply(var _this, var[] args) {
|
public var apply(var _this, var[] args) {
|
||||||
if(this.payloadType() == Type.Function) {
|
if(this.payloadType() == Type.Function) {
|
||||||
if(this._payload._function is null) {
|
if(this._payload._function is null) {
|
||||||
version(jsvar_throw)
|
if(jsvar_throw)
|
||||||
throw new DynamicTypeException(this, Type.Function);
|
throw new DynamicTypeException(this, Type.Function);
|
||||||
else
|
else
|
||||||
return var(null);
|
return var(null);
|
||||||
|
@ -846,7 +856,7 @@ struct var {
|
||||||
return this._payload._function(_this, args);
|
return this._payload._function(_this, args);
|
||||||
} else if(this.payloadType() == Type.Object) {
|
} else if(this.payloadType() == Type.Object) {
|
||||||
if(this._payload._object is null) {
|
if(this._payload._object is null) {
|
||||||
version(jsvar_throw)
|
if(jsvar_throw)
|
||||||
throw new DynamicTypeException(this, Type.Function);
|
throw new DynamicTypeException(this, Type.Function);
|
||||||
else
|
else
|
||||||
return var(null);
|
return var(null);
|
||||||
|
@ -856,14 +866,13 @@ struct var {
|
||||||
return operator.apply(_this, args);
|
return operator.apply(_this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
version(jsvar_throw)
|
|
||||||
throw new DynamicTypeException(this, Type.Function);
|
|
||||||
|
|
||||||
if(this.payloadType() == Type.Integral || this.payloadType() == Type.Floating) {
|
if(this.payloadType() == Type.Integral || this.payloadType() == Type.Floating) {
|
||||||
if(args.length)
|
if(args.length)
|
||||||
return var(this.get!double * args[0].get!double);
|
return var(this.get!double * args[0].get!double);
|
||||||
else
|
else
|
||||||
return this;
|
return this;
|
||||||
|
} else if(jsvar_throw) {
|
||||||
|
throw new DynamicTypeException(this, Type.Function);
|
||||||
}
|
}
|
||||||
|
|
||||||
//return this;
|
//return this;
|
||||||
|
@ -1310,7 +1319,7 @@ struct var {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(from is null) {
|
if(from is null) {
|
||||||
version(jsvar_throw)
|
if(jsvar_throw)
|
||||||
throw new DynamicTypeException(var(null), Type.Object, file, line);
|
throw new DynamicTypeException(var(null), Type.Object, file, line);
|
||||||
else
|
else
|
||||||
return *(new var);
|
return *(new var);
|
||||||
|
@ -1356,10 +1365,12 @@ struct var {
|
||||||
*n = this["opIndex"](idx);
|
*n = this["opIndex"](idx);
|
||||||
return *n;
|
return *n;
|
||||||
}
|
}
|
||||||
version(jsvar_throw)
|
if(jsvar_throw) {
|
||||||
throw new DynamicTypeException(this, Type.Array, file, line);
|
throw new DynamicTypeException(this, Type.Array, file, line);
|
||||||
var* n = new var();
|
} else {
|
||||||
return *n;
|
var* n = new var();
|
||||||
|
return *n;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref var opIndexAssign(T)(T t, size_t idx, string file = __FILE__, size_t line = __LINE__) {
|
public ref var opIndexAssign(T)(T t, size_t idx, string file = __FILE__, size_t line = __LINE__) {
|
||||||
|
@ -1371,10 +1382,12 @@ struct var {
|
||||||
} else if(_type == Type.Object) {
|
} else if(_type == Type.Object) {
|
||||||
return opIndexAssign(t, to!string(idx), file, line);
|
return opIndexAssign(t, to!string(idx), file, line);
|
||||||
}
|
}
|
||||||
version(jsvar_throw)
|
if(jsvar_throw) {
|
||||||
throw new DynamicTypeException(this, Type.Array, file, line);
|
throw new DynamicTypeException(this, Type.Array, file, line);
|
||||||
var* n = new var();
|
} else {
|
||||||
return *n;
|
var* n = new var();
|
||||||
|
return *n;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var _getOwnProperty(string name, string file = __FILE__, size_t line = __LINE__) {
|
ref var _getOwnProperty(string name, string file = __FILE__, size_t line = __LINE__) {
|
||||||
|
@ -1385,8 +1398,8 @@ struct var {
|
||||||
return *peek;
|
return *peek;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
version(jsvar_throw)
|
//if(jsvar_throw)
|
||||||
throw new DynamicTypeException(this, Type.Object, file, line);
|
//throw new DynamicTypeException(this, Type.Object, file, line);
|
||||||
var* n = new var();
|
var* n = new var();
|
||||||
return *n;
|
return *n;
|
||||||
}
|
}
|
||||||
|
@ -1772,7 +1785,7 @@ class PrototypeObject {
|
||||||
|
|
||||||
// if we're here, the property was not found, so let's implicitly create it
|
// if we're here, the property was not found, so let's implicitly create it
|
||||||
if(throwOnFailure)
|
if(throwOnFailure)
|
||||||
throw new Exception("no such property " ~ name, file, line);
|
throw new DynamicTypeException("no such property " ~ name, file, line);
|
||||||
var n;
|
var n;
|
||||||
this._properties[name] = n;
|
this._properties[name] = n;
|
||||||
return this._properties[name];
|
return this._properties[name];
|
||||||
|
@ -1803,7 +1816,7 @@ class PrototypeObject {
|
||||||
|
|
||||||
// if we're here, the property was not found, so let's implicitly create it
|
// if we're here, the property was not found, so let's implicitly create it
|
||||||
if(throwOnFailure)
|
if(throwOnFailure)
|
||||||
throw new Exception("no such property " ~ name, file, line);
|
throw new DynamicTypeException("no such property " ~ name, file, line);
|
||||||
this._properties[name] = t;
|
this._properties[name] = t;
|
||||||
return this._properties[name];
|
return this._properties[name];
|
||||||
}
|
}
|
||||||
|
@ -1860,15 +1873,62 @@ class PropertyPrototype : PrototypeObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
struct ScriptLocation {
|
||||||
|
string scriptFilename; ///
|
||||||
|
int lineNumber; ///
|
||||||
|
}
|
||||||
|
|
||||||
class DynamicTypeException : Exception {
|
class DynamicTypeException : Exception {
|
||||||
|
this(string msg, string file = __FILE__, size_t line = __LINE__) {
|
||||||
|
super(msg, file, line);
|
||||||
|
}
|
||||||
this(var v, var.Type required, string file = __FILE__, size_t line = __LINE__) {
|
this(var v, var.Type required, string file = __FILE__, size_t line = __LINE__) {
|
||||||
import std.string;
|
import std.string;
|
||||||
if(v.payloadType() == required)
|
if(v.payloadType() == required)
|
||||||
super(format("Tried to use null as a %s", required), file, line);
|
super(format("Tried to use null as a %s", required), file, line);
|
||||||
else
|
else {
|
||||||
super(format("Tried to use %s as a %s", v.payloadType(), required), file, line);
|
super(format("Tried to use %s%s as a %s", v == null ? "null ": "", v.payloadType(), required), file, line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override void toString(scope void delegate(in char[]) sink) const {
|
||||||
|
import std.format;
|
||||||
|
if(varName.length)
|
||||||
|
sink(varName);
|
||||||
|
if(callStack.length) {
|
||||||
|
sink("arsd.jsvar.DynamicTypeException@");
|
||||||
|
sink(file);
|
||||||
|
sink("(");
|
||||||
|
sink(to!string(line));
|
||||||
|
sink("): ");
|
||||||
|
sink(msg);
|
||||||
|
foreach(cs; callStack)
|
||||||
|
sink(format("\n\t%s:%s", cs.scriptFilename, cs.lineNumber));
|
||||||
|
|
||||||
|
bool found;
|
||||||
|
void hiddenSink(in char[] str) {
|
||||||
|
// I just want to hide the call stack until the interpret call...
|
||||||
|
// since the script stack above is more meaningful to users.
|
||||||
|
//
|
||||||
|
// but then I will go back to the D functions once on the outside.
|
||||||
|
import std.string;
|
||||||
|
if(found)
|
||||||
|
sink(str);
|
||||||
|
else if(str.indexOf("arsd.script.interpret(") != -1)
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sink("\n--------");
|
||||||
|
|
||||||
|
super.toString(&hiddenSink);
|
||||||
|
} else {
|
||||||
|
super.toString(sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptLocation[] callStack;
|
||||||
|
string varName;
|
||||||
}
|
}
|
||||||
|
|
||||||
template makeAscii() {
|
template makeAscii() {
|
||||||
|
@ -1957,7 +2017,6 @@ var subclassable(T)() if(is(T == class) || is(T == interface)) {
|
||||||
}~memberName~q{
|
}~memberName~q{
|
||||||
(Parameters!(__traits(getMember, T, memberName)) p)
|
(Parameters!(__traits(getMember, T, memberName)) p)
|
||||||
{
|
{
|
||||||
//pragma(msg, T,".",memberName, " ", typeof(__traits(getMember, super, memberName)).stringof);
|
|
||||||
//import std.stdio; writeln("calling ", T.stringof, ".", memberName, " - ", methodOverriddenByScript(memberName), "/", _next_devirtualized, " on ", cast(size_t) cast(void*) this);
|
//import std.stdio; writeln("calling ", T.stringof, ".", memberName, " - ", methodOverriddenByScript(memberName), "/", _next_devirtualized, " on ", cast(size_t) cast(void*) this);
|
||||||
if(_next_devirtualized || !methodOverriddenByScript(memberName))
|
if(_next_devirtualized || !methodOverriddenByScript(memberName))
|
||||||
return __traits(getMember, super, memberName)(p);
|
return __traits(getMember, super, memberName)(p);
|
||||||
|
|
28
script.d
28
script.d
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
I kinda like the javascript foo`blargh` template literals too.
|
I kinda like the javascript foo`blargh` template literals too.
|
||||||
|
|
||||||
|
++ and -- are not implemented.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/++
|
/++
|
||||||
|
@ -475,12 +477,6 @@ class ScriptRuntimeException : Exception {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
struct ScriptLocation {
|
|
||||||
string scriptFilename; ///
|
|
||||||
int lineNumber; ///
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This represents an exception thrown by `throw x;` inside the script as it is interpreted.
|
/// This represents an exception thrown by `throw x;` inside the script as it is interpreted.
|
||||||
class ScriptException : Exception {
|
class ScriptException : Exception {
|
||||||
///
|
///
|
||||||
|
@ -1469,9 +1465,11 @@ class AssignExpression : Expression {
|
||||||
}
|
}
|
||||||
class VariableExpression : Expression {
|
class VariableExpression : Expression {
|
||||||
string identifier;
|
string identifier;
|
||||||
|
ScriptLocation loc;
|
||||||
|
|
||||||
this(string identifier) {
|
this(string identifier, ScriptLocation loc = ScriptLocation.init) {
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
|
this.loc = loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
override string toString() {
|
override string toString() {
|
||||||
|
@ -1483,7 +1481,12 @@ class VariableExpression : Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var getVar(PrototypeObject sc, bool recurse = true) {
|
ref var getVar(PrototypeObject sc, bool recurse = true) {
|
||||||
return sc._getMember(identifier, true /* FIXME: recurse?? */, true);
|
try {
|
||||||
|
return sc._getMember(identifier, true /* FIXME: recurse?? */, true);
|
||||||
|
} catch(DynamicTypeException dte) {
|
||||||
|
dte.callStack ~= loc;
|
||||||
|
throw dte;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var setVar(PrototypeObject sc, var t, bool recurse = true, bool suppressOverloading = false) {
|
ref var setVar(PrototypeObject sc, var t, bool recurse = true, bool suppressOverloading = false) {
|
||||||
|
@ -2305,6 +2308,9 @@ class CallExpression : Expression {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return InterpretResult(f.apply(_this, args), sc);
|
return InterpretResult(f.apply(_this, args), sc);
|
||||||
|
} catch(DynamicTypeException dte) {
|
||||||
|
dte.callStack ~= loc;
|
||||||
|
throw dte;
|
||||||
} catch(ScriptException se) {
|
} catch(ScriptException se) {
|
||||||
se.callStack ~= loc;
|
se.callStack ~= loc;
|
||||||
throw se;
|
throw se;
|
||||||
|
@ -2337,7 +2343,7 @@ VariableExpression parseVariableName(MyTokenStreamHere)(ref MyTokenStreamHere to
|
||||||
auto token = tokens.front;
|
auto token = tokens.front;
|
||||||
if(token.type == ScriptToken.Type.identifier) {
|
if(token.type == ScriptToken.Type.identifier) {
|
||||||
tokens.popFront();
|
tokens.popFront();
|
||||||
return new VariableExpression(token.str);
|
return new VariableExpression(token.str, ScriptLocation(token.scriptFilename, token.lineNumber));
|
||||||
}
|
}
|
||||||
throw new ScriptCompileException("Found "~token.str~" when expecting identifier", token.scriptFilename, token.lineNumber);
|
throw new ScriptCompileException("Found "~token.str~" when expecting identifier", token.scriptFilename, token.lineNumber);
|
||||||
}
|
}
|
||||||
|
@ -3389,7 +3395,9 @@ var interpret(string code, PrototypeObject variables, string scriptFilename = nu
|
||||||
Returns:
|
Returns:
|
||||||
the result of the last expression evaluated by the script engine
|
the result of the last expression evaluated by the script engine
|
||||||
+/
|
+/
|
||||||
var interpret(string code, var variables = null, string scriptFilename = null) {
|
var interpret(string code, var variables = null, string scriptFilename = null, string file = __FILE__, size_t line = __LINE__) {
|
||||||
|
if(scriptFilename is null)
|
||||||
|
scriptFilename = file ~ "@" ~ to!string(line);
|
||||||
return interpretStream(
|
return interpretStream(
|
||||||
lexScript(repeat(code, 1), scriptFilename),
|
lexScript(repeat(code, 1), scriptFilename),
|
||||||
(variables.payloadType() == var.Type.Object && variables._payload._object !is null) ? variables._payload._object : new PrototypeObject());
|
(variables.payloadType() == var.Type.Object && variables._payload._object !is null) ? variables._payload._object : new PrototypeObject());
|
||||||
|
|
Loading…
Reference in New Issue