script language class fixes

This commit is contained in:
Adam D. Ruppe 2013-07-12 15:45:54 -04:00
parent e30eeff127
commit 31eb1590cf
2 changed files with 106 additions and 12 deletions

36
jsvar.d
View File

@ -742,6 +742,12 @@ struct var {
this._payload._object = obj; this._payload._object = obj;
} }
public PrototypeObject _object() {
if(this._type == Type.Object)
return this._payload._object;
return null;
}
package Payload _payload; package Payload _payload;
private void _requireType(Type t, string file = __FILE__, size_t line = __LINE__){ private void _requireType(Type t, string file = __FILE__, size_t line = __LINE__){
@ -1133,6 +1139,8 @@ class PrototypeObject {
string name; string name;
var _prototype; var _prototype;
package PrototypeObject _secondary; // HACK don't use this
PrototypeObject prototype() { PrototypeObject prototype() {
if(_prototype.payloadType() == var.Type.Object) if(_prototype.payloadType() == var.Type.Object)
return _prototype._payload._object; return _prototype._payload._object;
@ -1156,15 +1164,34 @@ class PrototypeObject {
return n; return n;
} }
PrototypeObject copyPropertiesFrom(PrototypeObject p) {
foreach(k, v; p._properties) {
this._properties[k] = v._copy;
}
return this;
}
// FIXME: maybe throw something else // FIXME: maybe throw something else
/*package*/ ref var _getMember(string name, bool recurse, bool throwOnFailure, string file = __FILE__, size_t line = __LINE__) { /*package*/ ref var _getMember(string name, bool recurse, bool throwOnFailure, string file = __FILE__, size_t line = __LINE__) {
if(name == "prototype") if(name == "prototype")
return _prototype; return _prototype;
auto curr = this; auto curr = this;
// for the secondary hack
bool triedOne = false;
// for the secondary hack
PrototypeObject possibleSecondary;
tryAgain:
do { do {
auto prop = name in curr._properties; auto prop = name in curr._properties;
if(prop is null) { if(prop is null) {
// the secondary hack is to do more scoping in the script, it is really hackish
if(possibleSecondary is null)
possibleSecondary = curr._secondary;
if(!recurse) if(!recurse)
break; break;
else else
@ -1173,6 +1200,15 @@ class PrototypeObject {
return *prop; return *prop;
} while(curr); } while(curr);
if(possibleSecondary !is null) {
curr = possibleSecondary;
if(!triedOne) {
writeln("trying again");
triedOne = true;
goto tryAgain;
}
}
// 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 Exception("no such property " ~ name, file, line);

View File

@ -33,6 +33,7 @@
* int, float, string, array, bool, and json!q{} literals * int, float, string, array, bool, and json!q{} literals
* var.prototype, var.typeof. prototype works more like Mozilla's __proto__ than standard javascript prototype. * var.prototype, var.typeof. prototype works more like Mozilla's __proto__ than standard javascript prototype.
* classes: * classes:
// inheritance works
class Foo : bar { class Foo : bar {
// constructors, D style // constructors, D style
this(var a) { ctor.... } this(var a) { ctor.... }
@ -45,12 +46,17 @@
// "virtual" functions can be overridden kinda like you expect in D, though there is no override keyword // "virtual" functions can be overridden kinda like you expect in D, though there is no override keyword
function virt() { function virt() {
// be sure to use this. as a prefix for any class defined variables in here b = 30; // lexical scoping is supported for static variables and functions
// but be sure to use this. as a prefix for any class defined instance variables in here
this.instance = 10;
} }
} }
var foo = new Foo(12); var foo = new Foo(12);
foo.newFunc = function() { this.derived = 0; }; // this is ok too, and scoping, including 'this', works like in Javascript
You can also use 'new' on another object to get a copy of it. You can also use 'new' on another object to get a copy of it.
* return, break, continue, but currently cannot do labeled breaks and continues * return, break, continue, but currently cannot do labeled breaks and continues
* __FILE__, __LINE__, but currently not as default arguments for D behavior (they always evaluate at the definition point) * __FILE__, __LINE__, but currently not as default arguments for D behavior (they always evaluate at the definition point)
@ -488,8 +494,18 @@ class ObjectLiteralExpression : Expression {
return s; return s;
} }
PrototypeObject backing;
this(PrototypeObject backing = null) {
this.backing = backing;
}
override InterpretResult interpret(PrototypeObject sc) { override InterpretResult interpret(PrototypeObject sc) {
var n = var.emptyObject; var n;
if(backing is null)
n = var.emptyObject;
else
n._object = backing;
foreach(k, v; elements) foreach(k, v; elements)
n[k] = v.interpret(sc).value; n[k] = v.interpret(sc).value;
@ -503,10 +519,11 @@ class FunctionLiteralExpression : Expression {
DefaultArgumentDummyObject = new PrototypeObject(); DefaultArgumentDummyObject = new PrototypeObject();
} }
this(VariableDeclaration args, Expression bod) { this(VariableDeclaration args, Expression bod, PrototypeObject lexicalScope = null) {
this(); this();
this.arguments = args; this.arguments = args;
this.functionBody = bod; this.functionBody = bod;
this.lexicalScope = lexicalScope;
} }
override string toString() { override string toString() {
@ -528,16 +545,25 @@ class FunctionLiteralExpression : Expression {
// the return value is just the last expression's result that was evaluated // the return value is just the last expression's result that was evaluated
// to return void, be sure to do a "return;" at the end of the function // to return void, be sure to do a "return;" at the end of the function
*/ */
// PrototypeObject lexicalScope;
VariableDeclaration arguments; VariableDeclaration arguments;
Expression functionBody; // can be a ScopeExpression btw Expression functionBody; // can be a ScopeExpression btw
PrototypeObject lexicalScope;
override InterpretResult interpret(PrototypeObject sc) { override InterpretResult interpret(PrototypeObject sc) {
assert(DefaultArgumentDummyObject !is null); assert(DefaultArgumentDummyObject !is null);
var v; var v;
v._function = (var _this, var[] args) { v._function = (var _this, var[] args) {
auto argumentsScope = new PrototypeObject(); auto argumentsScope = new PrototypeObject();
argumentsScope.prototype = sc; PrototypeObject scToUse;
if(lexicalScope is null)
scToUse = sc;
else {
scToUse = lexicalScope;
scToUse._secondary = sc;
}
argumentsScope.prototype = scToUse;
argumentsScope._getMember("this", false, false) = _this; argumentsScope._getMember("this", false, false) = _this;
argumentsScope._getMember("_arguments", false, false) = args; argumentsScope._getMember("_arguments", false, false) = args;
@ -986,6 +1012,29 @@ class IfExpression : Expression {
} }
} }
// this is kinda like a placement new, and currently isn't exposed inside the language,
// but is used for class inheritance
class ShallowCopyExpression : Expression {
Expression e1;
Expression e2;
this(Expression e1, Expression e2) {
this.e1 = e1;
this.e2 = e2;
}
override InterpretResult interpret(PrototypeObject sc) {
auto v = cast(VariableExpression) e1;
if(v is null)
throw new ScriptRuntimeException("not an lvalue", 0 /* FIXME */);
v.getVar(sc, false)._object.copyPropertiesFrom(e2.interpret(sc).value._object);
return InterpretResult(var(null), sc);
}
}
class NewExpression : Expression { class NewExpression : Expression {
Expression what; Expression what;
Expression[] args; Expression[] args;
@ -1003,7 +1052,7 @@ class NewExpression : Expression {
var original = what.interpret(sc).value; var original = what.interpret(sc).value;
var n = original._copy; var n = original._copy;
if(n.payloadType() == var.Type.Object) { if(n.payloadType() == var.Type.Object) {
var ctor = original._getOwnProperty("__ctor"); var ctor = original.prototype ? original.prototype._getOwnProperty("__ctor") : var(null);
if(ctor) if(ctor)
ctor.apply(n, args); ctor.apply(n, args);
} }
@ -1525,10 +1574,14 @@ Expression parseExpression(MyTokenStreamHere)(ref MyTokenStreamHere tokens) {
auto vars = new VariableDeclaration(); auto vars = new VariableDeclaration();
vars.identifiers = ["__proto", "__obj"]; vars.identifiers = ["__proto", "__obj"];
vars.initializers = [new ObjectLiteralExpression(), new ObjectLiteralExpression()];
auto staticScopeBacking = new PrototypeObject();
auto instanceScopeBacking = new PrototypeObject();
vars.initializers = [new ObjectLiteralExpression(staticScopeBacking), new ObjectLiteralExpression(instanceScopeBacking)];
expressions ~= vars; expressions ~= vars;
// FIXME: operators need to have their this be bound somehow since it isn't passwed // FIXME: operators need to have their this be bound somehow since it isn't passed
// OR the op rewrite could pass this // OR the op rewrite could pass this
expressions ~= new AssignExpression( expressions ~= new AssignExpression(
@ -1541,9 +1594,14 @@ Expression parseExpression(MyTokenStreamHere)(ref MyTokenStreamHere tokens) {
tokens.popFront(); tokens.popFront();
auto inheritFrom = tokens.requireNextToken(ScriptToken.Type.identifier); auto inheritFrom = tokens.requireNextToken(ScriptToken.Type.identifier);
// we set our prototype to the Foo prototype, thereby inheriting any static data that way (includes functions)
// the inheritFrom object itself carries instance data that we need to copy onto our instance
expressions ~= new AssignExpression( expressions ~= new AssignExpression(
new DotVarExpression(new VariableExpression("__proto"), new VariableExpression("prototype")), new DotVarExpression(new VariableExpression("__proto"), new VariableExpression("prototype")),
new VariableExpression(inheritFrom.str)); new DotVarExpression(new VariableExpression(inheritFrom.str), new VariableExpression("prototype")));
// and copying the instance initializer from the parent
expressions ~= new ShallowCopyExpression(new VariableExpression("__obj"), new VariableExpression(inheritFrom.str));
} }
tokens.requireNextToken(ScriptToken.Type.symbol, "{"); tokens.requireNextToken(ScriptToken.Type.symbol, "{");
@ -1579,9 +1637,9 @@ Expression parseExpression(MyTokenStreamHere)(ref MyTokenStreamHere tokens) {
expressions ~= new AssignExpression( expressions ~= new AssignExpression(
new DotVarExpression( new DotVarExpression(
new VariableExpression("__obj"), new VariableExpression("__proto"),
new VariableExpression("__ctor")), new VariableExpression("__ctor")),
new FunctionLiteralExpression(args, bod)); new FunctionLiteralExpression(args, bod, staticScopeBacking));
} else if(tokens.peekNextToken(ScriptToken.Type.keyword, "var")) { } else if(tokens.peekNextToken(ScriptToken.Type.keyword, "var")) {
// instance variable // instance variable
auto decl = parseVariableDeclaration(tokens, ";"); auto decl = parseVariableDeclaration(tokens, ";");
@ -1606,7 +1664,7 @@ Expression parseExpression(MyTokenStreamHere)(ref MyTokenStreamHere tokens) {
new VariableExpression("__proto"), new VariableExpression("__proto"),
new VariableExpression(ident.str), new VariableExpression(ident.str),
false), false),
new FunctionLiteralExpression(args, bod)); new FunctionLiteralExpression(args, bod, staticScopeBacking));
} else throw new ScriptCompileException("Unexpected " ~ tokens.front.str ~ " when reading class decl", tokens.front.lineNumber); } else throw new ScriptCompileException("Unexpected " ~ tokens.front.str ~ " when reading class decl", tokens.front.lineNumber);
} }