Version 0.29 : Added new object via functions

Fixed getString() for double on some platforms
master
pur3mail 13 years ago
parent 8c790516ea
commit ecc4cc8fbd

@ -92,9 +92,10 @@
Rudimentary call stack on error Rudimentary call stack on error
Added String Character functions Added String Character functions
Added shift operators Added shift operators
Version 0.29 : Added new object via functions
Fixed getString() for double on some platforms
NOTE:
NOTE: This doesn't support constructors for objects
Constructing an array with an initial length 'Array(5)' doesn't work Constructing an array with an initial length 'Array(5)' doesn't work
Recursive loops of data such as a.foo = a; fail to be garbage collected Recursive loops of data such as a.foo = a; fail to be garbage collected
length variable cannot be set length variable cannot be set
@ -951,7 +952,7 @@ const string &CScriptVar::getString() {
} }
if (isDouble()) { if (isDouble()) {
char buffer[32]; char buffer[32];
sprintf_s(buffer, sizeof(buffer), "%lf", doubleData); sprintf_s(buffer, sizeof(buffer), "%f", doubleData);
data = buffer; data = buffer;
return data; return data;
} }
@ -1429,6 +1430,104 @@ CScriptVarLink *CTinyJS::parseFunctionDefinition() {
return funcVar; return funcVar;
} }
/** Handle a function call (assumes we've parsed the function name and we're
* on the start bracket). 'parent' is the object that contains this method,
* if there was one (otherwise it's just a normnal function).
*/
CScriptVarLink *CTinyJS::functionCall(bool &execute, CScriptVarLink *function, CScriptVar *parent) {
if (execute) {
if (!function->var->isFunction()) {
string errorMsg = "Expecting '";
errorMsg = errorMsg + function->name + "' to be a function";
throw new CScriptException(errorMsg.c_str());
}
l->match('(');
// create a new symbol table entry for execution of this function
CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION);
if (parent)
functionRoot->addChildNoDup("this", parent);
// grab in all parameters
CScriptVarLink *v = function->var->firstChild;
while (v) {
CScriptVarLink *value = base(execute);
if (execute) {
if (value->var->isBasic()) {
// pass by value
functionRoot->addChild(v->name, value->var->deepCopy());
} else {
// pass by reference
functionRoot->addChild(v->name, value->var);
}
}
CLEAN(value);
if (l->tk!=')') l->match(',');
v = v->nextSibling;
}
l->match(')');
// setup a return variable
CScriptVarLink *returnVar = NULL;
// execute function!
// add the function's execute space to the symbol table so we can recurse
CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR);
scopes.push_back(functionRoot);
#ifdef TINYJS_CALL_STACK
call_stack.push_back(function->name + " from " + l->getPosition());
#endif
if (function->var->isNative()) {
ASSERT(function->var->jsCallback);
function->var->jsCallback(functionRoot, function->var->jsCallbackUserData);
} else {
/* we just want to execute the block, but something could
* have messed up and left us with the wrong ScriptLex, so
* we want to be careful here... */
CScriptException *exception = 0;
CScriptLex *oldLex = l;
CScriptLex *newLex = new CScriptLex(function->var->getString());
l = newLex;
try {
block(execute);
// because return will probably have called this, and set execute to false
execute = true;
} catch (CScriptException *e) {
exception = e;
}
delete newLex;
l = oldLex;
if (exception)
throw exception;
}
#ifdef TINYJS_CALL_STACK
if (!call_stack.empty()) call_stack.pop_back();
#endif
scopes.pop_back();
/* get the real return var before we remove it from our function */
returnVar = new CScriptVarLink(returnVarLink->var);
functionRoot->removeLink(returnVarLink);
delete functionRoot;
if (returnVar)
return returnVar;
else
return new CScriptVarLink(new CScriptVar());
} else {
// function, but not executing - just parse args and be done
l->match('(');
while (l->tk != ')') {
CScriptVarLink *value = base(execute);
CLEAN(value);
if (l->tk!=')') l->match(',');
}
l->match(')');
if (l->tk == '{') {
block(execute);
}
/* function will be a blank scriptvarlink if we're not executing,
* so just return it rather than an alloc/free */
return function;
}
}
CScriptVarLink *CTinyJS::factor(bool &execute) { CScriptVarLink *CTinyJS::factor(bool &execute) {
if (l->tk=='(') { if (l->tk=='(') {
l->match('('); l->match('(');
@ -1466,94 +1565,7 @@ CScriptVarLink *CTinyJS::factor(bool &execute) {
l->match(LEX_ID); l->match(LEX_ID);
while (l->tk=='(' || l->tk=='.' || l->tk=='[') { while (l->tk=='(' || l->tk=='.' || l->tk=='[') {
if (l->tk=='(') { // ------------------------------------- Function Call if (l->tk=='(') { // ------------------------------------- Function Call
if (execute) { a = functionCall(execute, a, parent);
if (!a->var->isFunction()) {
string errorMsg = "Expecting '";
errorMsg = errorMsg + a->name + "' to be a function";
throw new CScriptException(errorMsg.c_str());
}
l->match('(');
// create a new symbol table entry for execution of this function
CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION);
if (parent)
functionRoot->addChildNoDup("this", parent);
// grab in all parameters
CScriptVarLink *v = a->var->firstChild;
while (v) {
CScriptVarLink *value = base(execute);
if (execute) {
if (value->var->isBasic()) {
// pass by value
functionRoot->addChild(v->name, value->var->deepCopy());
} else {
// pass by reference
functionRoot->addChild(v->name, value->var);
}
}
CLEAN(value);
if (l->tk!=')') l->match(',');
v = v->nextSibling;
}
l->match(')');
// setup a return variable
CScriptVarLink *returnVar = NULL;
// execute function!
// add the function's execute space to the symbol table so we can recurse
CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR);
scopes.push_back(functionRoot);
#ifdef TINYJS_CALL_STACK
call_stack.push_back(a->name + " from " + l->getPosition());
#endif
if (a->var->isNative()) {
ASSERT(a->var->jsCallback);
a->var->jsCallback(functionRoot, a->var->jsCallbackUserData);
} else {
/* we just want to execute the block, but something could
* have messed up and left us with the wrong ScriptLex, so
* we want to be careful here... */
CScriptException *exception = 0;
CScriptLex *oldLex = l;
CScriptLex *newLex = new CScriptLex(a->var->getString());
l = newLex;
try {
block(execute);
// because return will probably have called this, and set execute to false
execute = true;
} catch (CScriptException *e) {
exception = e;
}
delete newLex;
l = oldLex;
if (exception)
throw exception;
}
#ifdef TINYJS_CALL_STACK
if (!call_stack.empty()) call_stack.pop_back();
#endif
scopes.pop_back();
/* get the real return var before we remove it from our function */
returnVar = new CScriptVarLink(returnVarLink->var);
functionRoot->removeLink(returnVarLink);
delete functionRoot;
if (returnVar)
a = returnVar;
else
a = new CScriptVarLink(new CScriptVar());
} else {
// function, but not executing - just parse args and be done
l->match('(');
while (l->tk != ')') {
CScriptVarLink *value = base(execute);
CLEAN(value);
if (l->tk!=')') l->match(',');
}
l->match(')');
if (l->tk == '{') {
block(execute);
}
}
} else if (l->tk == '.') { // ------------------------------------- Record Access } else if (l->tk == '.') { // ------------------------------------- Record Access
l->match('.'); l->match('.');
if (execute) { if (execute) {
@ -1656,20 +1668,24 @@ CScriptVarLink *CTinyJS::factor(bool &execute) {
l->match(LEX_R_NEW); l->match(LEX_R_NEW);
const string &className = l->tkStr; const string &className = l->tkStr;
if (execute) { if (execute) {
CScriptVarLink *objClass = findInScopes(className); CScriptVarLink *objClassOrFunc = findInScopes(className);
if (!objClass) { if (!objClassOrFunc) {
TRACE("%s is not a valid class name", className.c_str()); TRACE("%s is not a valid class name", className.c_str());
return new CScriptVarLink(new CScriptVar()); return new CScriptVarLink(new CScriptVar());
} }
l->match(LEX_ID); l->match(LEX_ID);
CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT); CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
obj->addChild(TINYJS_PROTOTYPE_CLASS, objClass->var); CScriptVarLink *objLink = new CScriptVarLink(obj);
if (l->tk == '(') { if (objClassOrFunc->var->isFunction()) {
l->match('('); CLEAN(functionCall(execute, objClassOrFunc, obj));
l->match(')'); } else {
obj->addChild(TINYJS_PROTOTYPE_CLASS, objClassOrFunc->var);
if (l->tk == '(') {
l->match('(');
l->match(')');
}
} }
// TODO: Object constructors return objLink;
return new CScriptVarLink(obj);
} else { } else {
l->match(LEX_ID); l->match(LEX_ID);
if (l->tk == '(') { if (l->tk == '(') {

@ -333,6 +333,7 @@ private:
CScriptVar *arrayClass; /// Built in array class CScriptVar *arrayClass; /// Built in array class
// parsing - in order of precedence // parsing - in order of precedence
CScriptVarLink *functionCall(bool &execute, CScriptVarLink *function, CScriptVar *parent);
CScriptVarLink *factor(bool &execute); CScriptVarLink *factor(bool &execute);
CScriptVarLink *unary(bool &execute); CScriptVarLink *unary(bool &execute);
CScriptVarLink *term(bool &execute); CScriptVarLink *term(bool &execute);

@ -0,0 +1,10 @@
// the 'lf' in the printf caused issues writing doubles on some compilers
function Person(name) {
this.name = name;
this.kill = function() { this.name += " is dead"; };
}
var a = new Person("Kenny");
a.kill();
result = a.name == "Kenny is dead";

@ -0,0 +1,9 @@
// the 'lf' in the printf caused issues writing doubles on some compilers
var a=5.0/10.0*100.0;
var b=5.0*110.0;
var c=50.0/10.0;
a.dump();
b.dump();
c.dump();
result = a==50 && b==550 && c==5;
Loading…
Cancel
Save