зеркало из https://github.com/mozilla/pjs.git
Added XMLparsing to load a class. Fixed handling of forward references to
class methods/fields.
This commit is contained in:
Родитель
e820a765e4
Коммит
e30ce8373b
|
@ -38,6 +38,8 @@
|
|||
#include "jsclasses.h"
|
||||
#include "icodegenerator.h"
|
||||
#include "interpreter.h"
|
||||
#include "xmlparser.h"
|
||||
#include "icodeasm.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
|
@ -49,6 +51,7 @@ using namespace VM;
|
|||
using namespace JSTypes;
|
||||
using namespace JSClasses;
|
||||
using namespace Interpreter;
|
||||
using namespace ICodeASM;
|
||||
|
||||
inline char narrow(char16 ch) { return char(ch); }
|
||||
|
||||
|
@ -82,7 +85,8 @@ ICodeGenerator::ICodeGenerator(World *world, JSScope *global, JSClass *aClass, I
|
|||
mFlags(flags),
|
||||
pLabels(NULL),
|
||||
mHasRestParameter(false),
|
||||
mHasNamedRestParameter(false)
|
||||
mHasNamedRestParameter(false),
|
||||
mInitName(world->identifiers["__init__"])
|
||||
{
|
||||
iCode = new InstructionStream();
|
||||
iCodeOwner = true;
|
||||
|
@ -1724,8 +1728,9 @@ ICodeModule *ICodeGenerator::genFunction(FunctionStmtNode *f, bool isConstructor
|
|||
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, &args);
|
||||
}
|
||||
}
|
||||
const StringAtom &initName = mWorld->identifiers["__init__"]; // XXXXXXX
|
||||
icg.call(icg.getStatic(mClass, initName), thisValue, &args); // ok, so it's mis-named
|
||||
if (mClass->hasStatic(mInitName))
|
||||
icg.call(icg.getStatic(mClass, mInitName), thisValue, &args); // ok, so it's mis-named
|
||||
|
||||
}
|
||||
if (f->function.body)
|
||||
icg.genStmt(f->function.body);
|
||||
|
@ -1765,48 +1770,118 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
// to handle recursive types, such as linked list nodes.
|
||||
mGlobal->defineVariable(nameExpr->name, &Type_Type, JSValue(thisClass));
|
||||
|
||||
// Have to have this declared ahead of time so that it's slot can
|
||||
// be discoverd when compiling constructors in the loop below. Could
|
||||
// do a pre-processing loop to discover whether it is in fact empty
|
||||
// and then pass that info through to the genFunction() call for each
|
||||
// constructor.
|
||||
const StringAtom &initName = mWorld->identifiers["__init__"];
|
||||
thisClass->defineStatic(initName, &Function_Type);
|
||||
|
||||
bool hasDefaultConstructor = false;
|
||||
|
||||
/*
|
||||
Pre-pass to declare all the methods & fields
|
||||
*/
|
||||
bool needsInstanceInitializer = false;
|
||||
TypedRegister thisRegister = TypedRegister(0, thisClass);
|
||||
if (classStmt->body) {
|
||||
JSScope* thisScope = thisClass->getScope();
|
||||
ICodeGenerator ccg(mWorld, thisScope, thisClass, kNoFlags); // constructor code generator.
|
||||
ccg.allocateParameter(mWorld->identifiers["this"], thisClass); // always parameter #0
|
||||
ICodeGenerator scg(mWorld, thisScope, thisClass, kIsStaticMethod); // static initializer code generator.
|
||||
StmtNode* s = classStmt->body->statements;
|
||||
while (s) {
|
||||
switch (s->getKind()) {
|
||||
case StmtNode::Const:
|
||||
case StmtNode::Var:
|
||||
{
|
||||
// FIXME: should preprocess all variable declarations, to prepare for method codegen.
|
||||
VariableStmtNode *vs = static_cast<VariableStmtNode *>(s);
|
||||
bool isStatic = hasAttribute(vs->attributes, Token::Static);
|
||||
VariableBinding *v = vs->bindings;
|
||||
TypedRegister thisRegister = TypedRegister(0, thisClass);
|
||||
while (v) {
|
||||
if (v->name) {
|
||||
ASSERT(v->name->getKind() == ExprNode::identifier);
|
||||
IdentifierExprNode* idExpr = static_cast<IdentifierExprNode*>(v->name);
|
||||
JSType* type = extractType(v->type);
|
||||
if (isStatic) {
|
||||
if (isStatic)
|
||||
thisClass->defineStatic(idExpr->name, type);
|
||||
if (v->initializer) {
|
||||
else {
|
||||
thisClass->defineSlot(idExpr->name, type);
|
||||
if (v->initializer)
|
||||
needsInstanceInitializer = true;
|
||||
}
|
||||
}
|
||||
v = v->next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StmtNode::Constructor:
|
||||
case StmtNode::Function:
|
||||
{
|
||||
FunctionStmtNode *f = static_cast<FunctionStmtNode *>(s);
|
||||
bool isStatic = hasAttribute(f->attributes, Token::Static);
|
||||
bool isConstructor = (s->getKind() == StmtNode::Constructor);
|
||||
if (f->function.name->getKind() == ExprNode::identifier) {
|
||||
const StringAtom& name = (static_cast<IdentifierExprNode *>(f->function.name))->name;
|
||||
if (isConstructor)
|
||||
thisClass->defineConstructor(name);
|
||||
else
|
||||
if (isStatic)
|
||||
thisClass->defineStatic(name, &Function_Type);
|
||||
else {
|
||||
switch (f->function.prefix) {
|
||||
// XXXX these dummy place holders for the getters/setters are leaking
|
||||
case FunctionName::Get:
|
||||
thisClass->setGetter(name, new JSFunction(), extractType(f->function.resultType));
|
||||
break;
|
||||
case FunctionName::Set:
|
||||
thisClass->setSetter(name, new JSFunction(), extractType(f->function.resultType));
|
||||
break;
|
||||
case FunctionName::normal:
|
||||
thisClass->defineMethod(name, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("unimplemented class member statement");
|
||||
break;
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
if (needsInstanceInitializer)
|
||||
thisClass->defineStatic(mInitName, &Function_Type);
|
||||
/*
|
||||
Now gen code for each
|
||||
*/
|
||||
|
||||
bool hasDefaultConstructor = false;
|
||||
if (classStmt->body) {
|
||||
JSScope* thisScope = thisClass->getScope();
|
||||
ICodeGenerator *ccg = NULL;
|
||||
if (needsInstanceInitializer) {
|
||||
// constructor code generator. Slot variable
|
||||
// initializers get added to this function.
|
||||
ccg = new ICodeGenerator(mWorld, thisScope, thisClass, kNoFlags);
|
||||
ccg->allocateParameter(mWorld->identifiers["this"], thisClass); // always parameter #0
|
||||
}
|
||||
|
||||
ICodeGenerator scg(mWorld, thisScope, thisClass, kIsStaticMethod); // static initializer code generator.
|
||||
// static field inits, plus code to initialize
|
||||
// static method slots.
|
||||
StmtNode* s = classStmt->body->statements;
|
||||
while (s) {
|
||||
switch (s->getKind()) {
|
||||
case StmtNode::Const:
|
||||
case StmtNode::Var:
|
||||
{
|
||||
VariableStmtNode *vs = static_cast<VariableStmtNode *>(s);
|
||||
bool isStatic = hasAttribute(vs->attributes, Token::Static);
|
||||
VariableBinding *v = vs->bindings;
|
||||
while (v) {
|
||||
if (v->name) {
|
||||
ASSERT(v->name->getKind() == ExprNode::identifier);
|
||||
if (v->initializer) {
|
||||
IdentifierExprNode* idExpr = static_cast<IdentifierExprNode*>(v->name);
|
||||
JSType* type = extractType(v->type);
|
||||
if (isStatic) {
|
||||
scg.setStatic(thisClass, idExpr->name, scg.genExpr(v->initializer));
|
||||
scg.resetStatement();
|
||||
}
|
||||
} else {
|
||||
const JSSlot& slot = thisClass->defineSlot(idExpr->name, type);
|
||||
if (v->initializer) {
|
||||
// generate code for the default constructor, which initializes the slots.
|
||||
ccg.setSlot(thisRegister, slot.mIndex, ccg.genExpr(v->initializer));
|
||||
ccg.resetStatement();
|
||||
} else {
|
||||
const JSSlot& slot = thisClass->getSlot(idExpr->name);
|
||||
ccg->setSlot(thisRegister, slot.mIndex, ccg->genExpr(v->initializer));
|
||||
ccg->resetStatement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1829,14 +1904,11 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
if (isConstructor) {
|
||||
if (name == nameExpr->name)
|
||||
hasDefaultConstructor = true;
|
||||
thisClass->defineConstructor(name);
|
||||
scg.setStatic(thisClass, name, scg.newFunction(icm));
|
||||
}
|
||||
else
|
||||
if (isStatic) {
|
||||
thisClass->defineStatic(name, &Function_Type);
|
||||
if (isStatic)
|
||||
scg.setStatic(thisClass, name, scg.newFunction(icm));
|
||||
}
|
||||
else {
|
||||
switch (f->function.prefix) {
|
||||
case FunctionName::Get:
|
||||
|
@ -1861,7 +1933,10 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
}
|
||||
|
||||
// add the instance initializer
|
||||
scg.setStatic(thisClass, initName, scg.newFunction(ccg.complete(&Void_Type)));
|
||||
if (ccg) {
|
||||
scg.setStatic(thisClass, mInitName, scg.newFunction(ccg->complete(&Void_Type)));
|
||||
delete ccg;
|
||||
}
|
||||
// invent a default constructor if necessary, it just calls the
|
||||
// initializer and the superclass default constructor
|
||||
if (!hasDefaultConstructor) {
|
||||
|
@ -1871,7 +1946,8 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
icg.allocateParameter(mWorld->identifiers["this"], thisClass); // always parameter #0
|
||||
if (superclass)
|
||||
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, &args);
|
||||
icg.call(icg.getStatic(thisClass, initName), thisValue, &args);
|
||||
if (thisClass->hasStatic(mInitName))
|
||||
icg.call(icg.getStatic(thisClass, mInitName), thisValue, &args);
|
||||
icg.returnStmt(thisValue);
|
||||
thisClass->defineConstructor(nameExpr->name);
|
||||
scg.setStatic(thisClass, nameExpr->name, scg.newFunction(icg.complete(&Void_Type)));
|
||||
|
@ -2312,6 +2388,136 @@ Formatter& ICodeModule::print(Formatter& f)
|
|||
return VM::operator<<(f, *its_iCode);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
Formatter& operator<<(Formatter &f, string &s)
|
||||
{
|
||||
f << s.c_str();
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
void ICodeGenerator::readICode(const char *fileName)
|
||||
{
|
||||
String buffer;
|
||||
int ch;
|
||||
|
||||
FILE *f = fopen(fileName, "r");
|
||||
while ((ch = getc(f)) != EOF) buffer += static_cast<char>(ch);
|
||||
fclose(f);
|
||||
|
||||
XMLParser xp(buffer, fileName);
|
||||
XMLNode *top = xp.parseDocument();
|
||||
stdOut << *top;
|
||||
|
||||
XMLNodeList &kids = top->children();
|
||||
for (XMLNodeList::const_iterator i = kids.begin(); i != kids.end(); i++) {
|
||||
XMLNode *node = *i;
|
||||
|
||||
if (node->name().compare(widenCString("class")) == 0) {
|
||||
String className;
|
||||
String superName;
|
||||
JSClass* superclass = 0;
|
||||
|
||||
node->getValue(widenCString("name"), className);
|
||||
if (node->getValue(widenCString("super"), superName)) {
|
||||
const JSValue& superclassValue = mGlobal->getVariable(superName);
|
||||
superclass = static_cast<JSClass*>(superclassValue.object);
|
||||
}
|
||||
JSClass* thisClass = new JSClass(mGlobal, className, superclass);
|
||||
JSScope* thisScope = thisClass->getScope();
|
||||
ICodeGenerator scg(mWorld, thisScope, thisClass, kIsStaticMethod);
|
||||
ICodeGenerator ccg(mWorld, thisScope, thisClass, kNoFlags);
|
||||
ccg.allocateParameter(mWorld->identifiers["this"], thisClass);
|
||||
thisClass->defineStatic(mInitName, &Function_Type);
|
||||
|
||||
mGlobal->defineVariable(className, &Type_Type, JSValue(thisClass));
|
||||
|
||||
bool hasDefaultConstructor = false;
|
||||
XMLNodeList &elements = node->children();
|
||||
for (XMLNodeList::const_iterator j = elements.begin(); j != elements.end(); j++) {
|
||||
XMLNode *element = *j;
|
||||
|
||||
if (element->name().compare(widenCString("method")) == 0) {
|
||||
String methodName, resultTypeName;
|
||||
element->getValue(widenCString("name"), methodName);
|
||||
element->getValue(widenCString("result"), resultTypeName);
|
||||
JSType *resultType = findType(mWorld->identifiers[resultTypeName]);
|
||||
String &body = element->body();
|
||||
if (body.length()) {
|
||||
std::string str(body.length(), char());
|
||||
std::transform(body.begin(), body.end(), str.begin(), narrow);
|
||||
ICodeParser icp;
|
||||
|
||||
stdOut << "Calling ICodeParser with :\n" << str << "\n";
|
||||
|
||||
icp.ParseSourceFromString(str);
|
||||
|
||||
#ifdef ROB_DONE
|
||||
ICodeModule *icm = new ICodeModule(icp.instructionStream(),
|
||||
NULL, /* VariableList *variables */
|
||||
icp.maxRegister,
|
||||
1, /* uint32 maxParameter, */
|
||||
NULL, /* InstructionMap *instructionMap */
|
||||
false, /* bool hasRestParameter, */
|
||||
false, /* bool hasNamedRestParameter */
|
||||
resultType);
|
||||
thisClass->defineMethod(methodName, new JSFunction(icm));
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
if (element->name().compare(widenCString("field")) == 0) {
|
||||
String fieldName;
|
||||
String fieldType;
|
||||
|
||||
element->getValue(widenCString("name"), fieldName);
|
||||
element->getValue(widenCString("type"), fieldType);
|
||||
JSType *type = findType(mWorld->identifiers[fieldType]);
|
||||
|
||||
if (element->hasAttribute(widenCString("static")))
|
||||
thisClass->defineStatic(fieldName, type);
|
||||
else {
|
||||
const JSSlot& slot = thisClass->defineSlot(fieldName, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scg.setStatic(thisClass, mInitName, scg.newFunction(ccg.complete(&Void_Type)));
|
||||
|
||||
if (!hasDefaultConstructor) {
|
||||
TypedRegister thisValue = TypedRegister(0, thisClass);
|
||||
ArgumentList args;
|
||||
ICodeGenerator icg(mWorld, thisScope, thisClass, kIsStaticMethod);
|
||||
icg.allocateParameter(mWorld->identifiers["this"], thisClass); // always parameter #0
|
||||
if (superclass)
|
||||
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, &args);
|
||||
if (thisClass->hasStatic(mInitName))
|
||||
icg.call(icg.getStatic(thisClass, mInitName), thisValue, &args);
|
||||
icg.returnStmt(thisValue);
|
||||
thisClass->defineConstructor(className);
|
||||
scg.setStatic(thisClass, mWorld->identifiers[className], scg.newFunction(icg.complete(&Void_Type)));
|
||||
}
|
||||
thisClass->complete();
|
||||
|
||||
if (scg.getICode()->size()) {
|
||||
Interpreter::Context cx(*mWorld, thisScope);
|
||||
ICodeModule* clinit = scg.complete(&Void_Type);
|
||||
cx.interpret(clinit, JSValues());
|
||||
delete clinit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (node->name().compare(widenCString("script")) == 0) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace ICG
|
||||
|
||||
|
|
|
@ -189,6 +189,8 @@ namespace ICG {
|
|||
bool mHasRestParameter; // true if this function has a ... parameter
|
||||
bool mHasNamedRestParameter; // true if this function has a named ... parameter
|
||||
|
||||
const StringAtom &mInitName;
|
||||
|
||||
std::vector<bool> mPermanentRegister;
|
||||
|
||||
Register getTempRegister()
|
||||
|
@ -243,7 +245,6 @@ namespace ICG {
|
|||
|
||||
void setFlag(uint32 flag, bool v) { mFlags = (ICodeGeneratorFlags)((v) ? mFlags | flag : mFlags & ~flag); }
|
||||
|
||||
|
||||
typedef enum { Var, Property, Slot, Static, Constructor, Name, Method } LValueKind;
|
||||
|
||||
LValueKind resolveIdentifier(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, bool lvalue);
|
||||
|
@ -265,6 +266,7 @@ namespace ICG {
|
|||
}
|
||||
|
||||
ICodeModule *complete(JSType *resultType);
|
||||
void readICode(const char *fileName);
|
||||
|
||||
JSType *extractType(ExprNode *t);
|
||||
|
||||
|
@ -346,6 +348,7 @@ namespace ICG {
|
|||
|
||||
Formatter& operator<<(Formatter &f, ICodeGenerator &i);
|
||||
Formatter& operator<<(Formatter &f, ICodeModule &i);
|
||||
Formatter& operator<<(Formatter &f, std::string &s);
|
||||
/*
|
||||
std::ostream &operator<<(std::ostream &s, ICodeGenerator &i);
|
||||
std::ostream &operator<<(std::ostream &s, StringAtom &str);
|
||||
|
|
|
@ -242,10 +242,15 @@ ICodeModule* Context::genCode(StmtNode *p, const String &fileName)
|
|||
icg.returnStmt(ret);
|
||||
|
||||
ICodeModule *icm = icg.complete(&Void_Type);
|
||||
icm->setFileName (fileName);
|
||||
icm->setFileName(fileName);
|
||||
return icm;
|
||||
}
|
||||
|
||||
void Context::loadClass(const char *fileName)
|
||||
{
|
||||
ICodeGenerator icg(&getWorld(), getGlobalObject());
|
||||
icg.readICode(fileName); // loads it into the global object
|
||||
}
|
||||
|
||||
JSValues& Context::getRegisters() { return mActivation->mRegisters; }
|
||||
ICodeModule* Context::getICode() { return mActivation->mICode; }
|
||||
|
|
|
@ -81,6 +81,8 @@ namespace Interpreter {
|
|||
ICodeModule* genCode(StmtNode *p, const String &fileName);
|
||||
JSValue readEvalFile(FILE* in, const String& fileName);
|
||||
|
||||
void loadClass(const char *fileName);
|
||||
|
||||
void addBinaryOperator(BinaryOperator::BinaryOp op, BinaryOperator *fn) { mBinaryOperators[op].push_back(fn); }
|
||||
const JSValue findBinaryOverride(JSValue &operand1, JSValue &operand2, BinaryOperator::BinaryOp op);
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "world.h"
|
||||
#include "interpreter.h"
|
||||
#include "icodegenerator.h"
|
||||
#include "xmlparser.h"
|
||||
|
||||
#ifdef DEBUGGER_FOO
|
||||
#include "debugger.h"
|
||||
|
@ -211,6 +212,7 @@ class Tracer : public Context::Listener {
|
|||
}
|
||||
};
|
||||
|
||||
//#define HAVE_GEORGE_TRACE_IT
|
||||
|
||||
static void readEvalPrint(FILE *in, World &world)
|
||||
{
|
||||
|
@ -232,7 +234,11 @@ static void readEvalPrint(FILE *in, World &world)
|
|||
Tracer *george = new Tracer();
|
||||
cx.addListener(george);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TEST_XML_LOADER
|
||||
cx.loadClass("class.xml");
|
||||
#endif
|
||||
|
||||
while (promptLine(inReader, line, buffer.empty() ? "js> " : "> ")) {
|
||||
appendChars(buffer, line.data(), line.size());
|
||||
try {
|
||||
|
@ -309,6 +315,12 @@ static void testCompile()
|
|||
{
|
||||
JSScope glob;
|
||||
Context cx(world, &glob);
|
||||
|
||||
#ifdef HAVE_GEORGE_TRACE_IT
|
||||
Tracer *george = new Tracer();
|
||||
cx.addListener(george);
|
||||
#endif
|
||||
|
||||
glob.defineNativeFunction(world.identifiers["print"], print);
|
||||
for (uint i = 0; i < sizeof(tests) / sizeof(char *); i++) {
|
||||
String testScript = widenCString(tests[i]);
|
||||
|
@ -326,6 +338,7 @@ static void testCompile()
|
|||
}
|
||||
|
||||
|
||||
|
||||
} /* namespace Shell */
|
||||
} /* namespace JavaScript */
|
||||
|
||||
|
@ -341,10 +354,12 @@ int main(int , char **)
|
|||
|
||||
using namespace JavaScript;
|
||||
using namespace Shell;
|
||||
|
||||
#if 1
|
||||
testCompile();
|
||||
#endif
|
||||
readEvalPrint(stdin, world);
|
||||
|
||||
return 0;
|
||||
// return ProcessArgs(argv + 1, argc - 1);
|
||||
}
|
||||
|
|
|
@ -488,7 +488,6 @@ namespace JSTypes {
|
|||
ICodeModule* mICode;
|
||||
|
||||
protected:
|
||||
JSFunction() : mICode(0) {}
|
||||
|
||||
typedef JavaScript::gc_traits_finalizable<JSFunction> traits;
|
||||
typedef gc_allocator<JSFunction, traits> allocator;
|
||||
|
@ -496,6 +495,8 @@ namespace JSTypes {
|
|||
public:
|
||||
static void initFunctionObject(JSScope *g);
|
||||
|
||||
JSFunction() : mICode(0) {}
|
||||
|
||||
JSFunction(ICodeModule* iCode)
|
||||
: JSObject(FunctionPrototypeObject),
|
||||
mICode(iCode)
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
#include "parser.h"
|
||||
#include "world.h"
|
||||
#include "xmlparser.h"
|
||||
|
||||
namespace JavaScript {
|
||||
|
||||
|
||||
bool XMLTag::getValue(String &name, String &value)
|
||||
{
|
||||
AttributeList::iterator i = mAttributeList.find(name);
|
||||
if (i == mAttributeList.end())
|
||||
return false;
|
||||
else {
|
||||
value = i->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void XMLParser::syntaxError(const char *msg, uint backUp)
|
||||
{
|
||||
mReader.unget(backUp);
|
||||
mReader.error(Exception::syntaxError, widenCString(msg), mReader.getPos());
|
||||
}
|
||||
|
||||
void XMLParser::parseName(String &id)
|
||||
{
|
||||
char16 ch = mReader.peek();
|
||||
CharInfo chi(ch);
|
||||
if (isAlpha(chi)) {
|
||||
mReader.beginRecording(id);
|
||||
mReader.recordChar(mReader.get());
|
||||
while (true) {
|
||||
ch = mReader.peek();
|
||||
CharInfo chi(ch);
|
||||
if (isAlphanumeric(chi))
|
||||
mReader.recordChar(mReader.get());
|
||||
else
|
||||
break;
|
||||
}
|
||||
mReader.endRecording();
|
||||
}
|
||||
else
|
||||
syntaxError("Invalid name");
|
||||
}
|
||||
|
||||
void XMLParser::parseWhiteSpace()
|
||||
{
|
||||
char16 ch = mReader.peek();
|
||||
CharInfo chi(ch);
|
||||
while (isSpace(chi)) {
|
||||
ch = mReader.get();
|
||||
if (isLineBreak(ch))
|
||||
mReader.beginLine();
|
||||
chi = CharInfo(mReader.peek());
|
||||
}
|
||||
}
|
||||
|
||||
void XMLParser::parseAttrValue(String &val)
|
||||
{
|
||||
char16 ch = mReader.get();
|
||||
if (ch != '\"')
|
||||
syntaxError("'\"' expected");
|
||||
else {
|
||||
mReader.beginRecording(val);
|
||||
while (true) {
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated value");
|
||||
if (ch == '\"')
|
||||
break;
|
||||
mReader.recordChar(ch);
|
||||
}
|
||||
mReader.endRecording();
|
||||
}
|
||||
}
|
||||
|
||||
XMLTag *XMLParser::parseTag()
|
||||
{
|
||||
char16 ch;
|
||||
XMLTag *tag = new XMLTag();
|
||||
ch = mReader.peek();
|
||||
if (ch == '/') {
|
||||
tag->setEndTag();
|
||||
mReader.get();
|
||||
}
|
||||
else {
|
||||
if (ch == '!') {
|
||||
mReader.get();
|
||||
if (mReader.peek() != '-')
|
||||
syntaxError("badly formed tag");
|
||||
mReader.get();
|
||||
if (mReader.peek() != '-')
|
||||
syntaxError("badly formed tag");
|
||||
mReader.get();
|
||||
while (true) {
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated comment tag");
|
||||
if (ch == '-') {
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated comment tag");
|
||||
if (ch != '-')
|
||||
mReader.unget();
|
||||
else {
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated comment tag");
|
||||
if (ch != '>')
|
||||
syntaxError("encountered '--' in comment");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isLineBreak(ch))
|
||||
mReader.beginLine();
|
||||
}
|
||||
tag->setComment();
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
parseName(tag->name());
|
||||
parseWhiteSpace();
|
||||
CharInfo chi(mReader.peek());
|
||||
while (isAlpha(chi)) {
|
||||
String attrValue;
|
||||
String attrName;
|
||||
parseName(attrName);
|
||||
if (mReader.peek() == '=') {
|
||||
mReader.get();
|
||||
parseAttrValue(attrValue);
|
||||
}
|
||||
tag->addAttribute(attrName, attrValue);
|
||||
parseWhiteSpace();
|
||||
chi = CharInfo(mReader.peek());
|
||||
}
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated tag");
|
||||
if (ch == '/') {
|
||||
tag->setEmpty();
|
||||
ch = mReader.get();
|
||||
}
|
||||
if (ch != '>')
|
||||
syntaxError("'>' expected");
|
||||
return tag;
|
||||
}
|
||||
|
||||
void XMLParser::parseTagBody(XMLNode *parent, XMLTag *startTag)
|
||||
{
|
||||
while (true) {
|
||||
char16 ch = mReader.get();
|
||||
while (ch != '<') {
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated tag body");
|
||||
parent->addToBody(ch);
|
||||
ch = mReader.get();
|
||||
}
|
||||
XMLTag *tag = parseTag();
|
||||
if (tag->isEndTag() && (tag->name().compare(startTag->name()) == 0))
|
||||
break;
|
||||
else {
|
||||
XMLNode *child = new XMLNode(parent, tag);
|
||||
if (!tag->isEmpty())
|
||||
parseTagBody(child, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XMLNode *XMLParser::parseDocument()
|
||||
{
|
||||
XMLNode *top = new XMLNode(NULL, new XMLTag(widenCString("TOP")));
|
||||
char16 ch = mReader.get();
|
||||
while (ch == '<') {
|
||||
XMLTag *tag = parseTag();
|
||||
XMLNode *n = new XMLNode(top, tag);
|
||||
if (!tag->isEmpty()) {
|
||||
parseTagBody(n, tag);
|
||||
}
|
||||
parseWhiteSpace();
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
break;
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
Formatter& operator<<(Formatter& f, const XMLTag& tag)
|
||||
{
|
||||
tag.print(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
Formatter& operator<<(Formatter& f, const XMLNode& node)
|
||||
{
|
||||
node.print(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
} /* namespace JavaScript */
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "parser.h"
|
||||
#include "world.h"
|
||||
|
||||
|
||||
namespace JavaScript {
|
||||
|
||||
typedef enum {Tag, EmptyTag, EndTag, CommentTag, DocTypeTag, ProcessInstructionTag } TagFlag;
|
||||
|
||||
|
||||
class XMLNode;
|
||||
typedef std::vector<XMLNode *> XMLNodeList;
|
||||
typedef std::multimap<String, String, std::less<String> > AttributeList;
|
||||
typedef AttributeList::value_type AttributeValue;
|
||||
|
||||
class XMLTag {
|
||||
public:
|
||||
XMLTag() : mFlag(Tag) { }
|
||||
XMLTag(String name) : mName(name), mFlag(Tag) { }
|
||||
|
||||
void addAttribute(String name, String value) { mAttributeList.insert(AttributeValue(name, value) ); }
|
||||
bool getValue(String &name, String &value);
|
||||
bool hasAttribute(String &name) { return (mAttributeList.find(name) != mAttributeList.end()); }
|
||||
|
||||
String &name() { return mName; }
|
||||
|
||||
void setEmpty() { mFlag = EmptyTag; }
|
||||
void setComment() { mFlag = EmptyTag; }
|
||||
void setEndTag() { mFlag = EndTag; }
|
||||
|
||||
bool isEmpty() const { return mFlag == EmptyTag; }
|
||||
bool isEndTag() const { return mFlag == EndTag; }
|
||||
bool isComment() const { return mFlag == CommentTag; }
|
||||
|
||||
String mName;
|
||||
TagFlag mFlag;
|
||||
AttributeList mAttributeList;
|
||||
|
||||
void print(Formatter& f) const
|
||||
{
|
||||
if (isComment())
|
||||
f << "XML Comment tag\n";
|
||||
else
|
||||
f << "XMLTag '" << mName << "'\n";
|
||||
for (AttributeList::const_iterator i = mAttributeList.begin(); i != mAttributeList.end(); i++) {
|
||||
f << "\t'" << i->first << "' = '" << i->second << "'\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Formatter& operator<<(Formatter& f, const XMLTag& tag);
|
||||
Formatter& operator<<(Formatter& f, const XMLNode& node);
|
||||
|
||||
class XMLNode
|
||||
{
|
||||
public:
|
||||
XMLNode(XMLNode *parent, XMLTag *tag) : mParent(parent), mTag(tag) { if (parent) parent->addChild(this); }
|
||||
|
||||
String &name() { return mTag->name(); }
|
||||
XMLTag *tag() { return mTag; }
|
||||
|
||||
String &body() { return mBody; }
|
||||
|
||||
void addToBody(String contents) { mBody += contents; }
|
||||
void addToBody(char16 ch) { mBody += ch; }
|
||||
|
||||
void addChild(XMLNode *child) { mChildren.push_back(child); }
|
||||
XMLNodeList &children() { return mChildren; }
|
||||
|
||||
bool getValue(String &name, String &value)
|
||||
{ return mTag->getValue(name, value); }
|
||||
bool hasAttribute(String &name) { return mTag->hasAttribute(name); }
|
||||
|
||||
|
||||
void print(Formatter& f) const
|
||||
{
|
||||
f << *mTag;
|
||||
if (mParent)
|
||||
f << "parent = '" << mParent->name() << "'";
|
||||
else
|
||||
f << "parent = NULL";
|
||||
|
||||
XMLNodeList::const_iterator i = mChildren.begin();
|
||||
XMLNodeList::const_iterator end = mChildren.end();
|
||||
if (i != end) {
|
||||
f << "\nChildren :\n";
|
||||
while (i != end) {
|
||||
f << **i;
|
||||
f << "\n";
|
||||
i++;
|
||||
}
|
||||
}
|
||||
f << "\n";
|
||||
}
|
||||
|
||||
XMLNodeList mChildren;
|
||||
XMLNode *mParent;
|
||||
XMLTag *mTag;
|
||||
String mBody;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class XMLParser {
|
||||
public:
|
||||
void syntaxError(const char *message, uint backUp = 1);
|
||||
char16 doEscape();
|
||||
|
||||
XMLParser(const String &source, const char *fileName) : mReader(source, widenCString(fileName)) { }
|
||||
|
||||
|
||||
void parseName(String &id);
|
||||
void parseWhiteSpace();
|
||||
void parseAttrValue(String &val);
|
||||
XMLTag *parseTag();
|
||||
void parseTagBody(XMLNode *parent, XMLTag *startTag);
|
||||
XMLNode *parseDocument();
|
||||
|
||||
|
||||
Reader mReader;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} /* namespace JavaScript */
|
|
@ -38,6 +38,8 @@
|
|||
#include "jsclasses.h"
|
||||
#include "icodegenerator.h"
|
||||
#include "interpreter.h"
|
||||
#include "xmlparser.h"
|
||||
#include "icodeasm.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
|
@ -49,6 +51,7 @@ using namespace VM;
|
|||
using namespace JSTypes;
|
||||
using namespace JSClasses;
|
||||
using namespace Interpreter;
|
||||
using namespace ICodeASM;
|
||||
|
||||
inline char narrow(char16 ch) { return char(ch); }
|
||||
|
||||
|
@ -82,7 +85,8 @@ ICodeGenerator::ICodeGenerator(World *world, JSScope *global, JSClass *aClass, I
|
|||
mFlags(flags),
|
||||
pLabels(NULL),
|
||||
mHasRestParameter(false),
|
||||
mHasNamedRestParameter(false)
|
||||
mHasNamedRestParameter(false),
|
||||
mInitName(world->identifiers["__init__"])
|
||||
{
|
||||
iCode = new InstructionStream();
|
||||
iCodeOwner = true;
|
||||
|
@ -1724,8 +1728,9 @@ ICodeModule *ICodeGenerator::genFunction(FunctionStmtNode *f, bool isConstructor
|
|||
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, &args);
|
||||
}
|
||||
}
|
||||
const StringAtom &initName = mWorld->identifiers["__init__"]; // XXXXXXX
|
||||
icg.call(icg.getStatic(mClass, initName), thisValue, &args); // ok, so it's mis-named
|
||||
if (mClass->hasStatic(mInitName))
|
||||
icg.call(icg.getStatic(mClass, mInitName), thisValue, &args); // ok, so it's mis-named
|
||||
|
||||
}
|
||||
if (f->function.body)
|
||||
icg.genStmt(f->function.body);
|
||||
|
@ -1765,48 +1770,118 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
// to handle recursive types, such as linked list nodes.
|
||||
mGlobal->defineVariable(nameExpr->name, &Type_Type, JSValue(thisClass));
|
||||
|
||||
// Have to have this declared ahead of time so that it's slot can
|
||||
// be discoverd when compiling constructors in the loop below. Could
|
||||
// do a pre-processing loop to discover whether it is in fact empty
|
||||
// and then pass that info through to the genFunction() call for each
|
||||
// constructor.
|
||||
const StringAtom &initName = mWorld->identifiers["__init__"];
|
||||
thisClass->defineStatic(initName, &Function_Type);
|
||||
|
||||
bool hasDefaultConstructor = false;
|
||||
|
||||
/*
|
||||
Pre-pass to declare all the methods & fields
|
||||
*/
|
||||
bool needsInstanceInitializer = false;
|
||||
TypedRegister thisRegister = TypedRegister(0, thisClass);
|
||||
if (classStmt->body) {
|
||||
JSScope* thisScope = thisClass->getScope();
|
||||
ICodeGenerator ccg(mWorld, thisScope, thisClass, kNoFlags); // constructor code generator.
|
||||
ccg.allocateParameter(mWorld->identifiers["this"], thisClass); // always parameter #0
|
||||
ICodeGenerator scg(mWorld, thisScope, thisClass, kIsStaticMethod); // static initializer code generator.
|
||||
StmtNode* s = classStmt->body->statements;
|
||||
while (s) {
|
||||
switch (s->getKind()) {
|
||||
case StmtNode::Const:
|
||||
case StmtNode::Var:
|
||||
{
|
||||
// FIXME: should preprocess all variable declarations, to prepare for method codegen.
|
||||
VariableStmtNode *vs = static_cast<VariableStmtNode *>(s);
|
||||
bool isStatic = hasAttribute(vs->attributes, Token::Static);
|
||||
VariableBinding *v = vs->bindings;
|
||||
TypedRegister thisRegister = TypedRegister(0, thisClass);
|
||||
while (v) {
|
||||
if (v->name) {
|
||||
ASSERT(v->name->getKind() == ExprNode::identifier);
|
||||
IdentifierExprNode* idExpr = static_cast<IdentifierExprNode*>(v->name);
|
||||
JSType* type = extractType(v->type);
|
||||
if (isStatic) {
|
||||
if (isStatic)
|
||||
thisClass->defineStatic(idExpr->name, type);
|
||||
if (v->initializer) {
|
||||
else {
|
||||
thisClass->defineSlot(idExpr->name, type);
|
||||
if (v->initializer)
|
||||
needsInstanceInitializer = true;
|
||||
}
|
||||
}
|
||||
v = v->next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StmtNode::Constructor:
|
||||
case StmtNode::Function:
|
||||
{
|
||||
FunctionStmtNode *f = static_cast<FunctionStmtNode *>(s);
|
||||
bool isStatic = hasAttribute(f->attributes, Token::Static);
|
||||
bool isConstructor = (s->getKind() == StmtNode::Constructor);
|
||||
if (f->function.name->getKind() == ExprNode::identifier) {
|
||||
const StringAtom& name = (static_cast<IdentifierExprNode *>(f->function.name))->name;
|
||||
if (isConstructor)
|
||||
thisClass->defineConstructor(name);
|
||||
else
|
||||
if (isStatic)
|
||||
thisClass->defineStatic(name, &Function_Type);
|
||||
else {
|
||||
switch (f->function.prefix) {
|
||||
// XXXX these dummy place holders for the getters/setters are leaking
|
||||
case FunctionName::Get:
|
||||
thisClass->setGetter(name, new JSFunction(), extractType(f->function.resultType));
|
||||
break;
|
||||
case FunctionName::Set:
|
||||
thisClass->setSetter(name, new JSFunction(), extractType(f->function.resultType));
|
||||
break;
|
||||
case FunctionName::normal:
|
||||
thisClass->defineMethod(name, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("unimplemented class member statement");
|
||||
break;
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
if (needsInstanceInitializer)
|
||||
thisClass->defineStatic(mInitName, &Function_Type);
|
||||
/*
|
||||
Now gen code for each
|
||||
*/
|
||||
|
||||
bool hasDefaultConstructor = false;
|
||||
if (classStmt->body) {
|
||||
JSScope* thisScope = thisClass->getScope();
|
||||
ICodeGenerator *ccg = NULL;
|
||||
if (needsInstanceInitializer) {
|
||||
// constructor code generator. Slot variable
|
||||
// initializers get added to this function.
|
||||
ccg = new ICodeGenerator(mWorld, thisScope, thisClass, kNoFlags);
|
||||
ccg->allocateParameter(mWorld->identifiers["this"], thisClass); // always parameter #0
|
||||
}
|
||||
|
||||
ICodeGenerator scg(mWorld, thisScope, thisClass, kIsStaticMethod); // static initializer code generator.
|
||||
// static field inits, plus code to initialize
|
||||
// static method slots.
|
||||
StmtNode* s = classStmt->body->statements;
|
||||
while (s) {
|
||||
switch (s->getKind()) {
|
||||
case StmtNode::Const:
|
||||
case StmtNode::Var:
|
||||
{
|
||||
VariableStmtNode *vs = static_cast<VariableStmtNode *>(s);
|
||||
bool isStatic = hasAttribute(vs->attributes, Token::Static);
|
||||
VariableBinding *v = vs->bindings;
|
||||
while (v) {
|
||||
if (v->name) {
|
||||
ASSERT(v->name->getKind() == ExprNode::identifier);
|
||||
if (v->initializer) {
|
||||
IdentifierExprNode* idExpr = static_cast<IdentifierExprNode*>(v->name);
|
||||
JSType* type = extractType(v->type);
|
||||
if (isStatic) {
|
||||
scg.setStatic(thisClass, idExpr->name, scg.genExpr(v->initializer));
|
||||
scg.resetStatement();
|
||||
}
|
||||
} else {
|
||||
const JSSlot& slot = thisClass->defineSlot(idExpr->name, type);
|
||||
if (v->initializer) {
|
||||
// generate code for the default constructor, which initializes the slots.
|
||||
ccg.setSlot(thisRegister, slot.mIndex, ccg.genExpr(v->initializer));
|
||||
ccg.resetStatement();
|
||||
} else {
|
||||
const JSSlot& slot = thisClass->getSlot(idExpr->name);
|
||||
ccg->setSlot(thisRegister, slot.mIndex, ccg->genExpr(v->initializer));
|
||||
ccg->resetStatement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1829,14 +1904,11 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
if (isConstructor) {
|
||||
if (name == nameExpr->name)
|
||||
hasDefaultConstructor = true;
|
||||
thisClass->defineConstructor(name);
|
||||
scg.setStatic(thisClass, name, scg.newFunction(icm));
|
||||
}
|
||||
else
|
||||
if (isStatic) {
|
||||
thisClass->defineStatic(name, &Function_Type);
|
||||
if (isStatic)
|
||||
scg.setStatic(thisClass, name, scg.newFunction(icm));
|
||||
}
|
||||
else {
|
||||
switch (f->function.prefix) {
|
||||
case FunctionName::Get:
|
||||
|
@ -1861,7 +1933,10 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
}
|
||||
|
||||
// add the instance initializer
|
||||
scg.setStatic(thisClass, initName, scg.newFunction(ccg.complete(&Void_Type)));
|
||||
if (ccg) {
|
||||
scg.setStatic(thisClass, mInitName, scg.newFunction(ccg->complete(&Void_Type)));
|
||||
delete ccg;
|
||||
}
|
||||
// invent a default constructor if necessary, it just calls the
|
||||
// initializer and the superclass default constructor
|
||||
if (!hasDefaultConstructor) {
|
||||
|
@ -1871,7 +1946,8 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
icg.allocateParameter(mWorld->identifiers["this"], thisClass); // always parameter #0
|
||||
if (superclass)
|
||||
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, &args);
|
||||
icg.call(icg.getStatic(thisClass, initName), thisValue, &args);
|
||||
if (thisClass->hasStatic(mInitName))
|
||||
icg.call(icg.getStatic(thisClass, mInitName), thisValue, &args);
|
||||
icg.returnStmt(thisValue);
|
||||
thisClass->defineConstructor(nameExpr->name);
|
||||
scg.setStatic(thisClass, nameExpr->name, scg.newFunction(icg.complete(&Void_Type)));
|
||||
|
@ -2312,6 +2388,136 @@ Formatter& ICodeModule::print(Formatter& f)
|
|||
return VM::operator<<(f, *its_iCode);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
Formatter& operator<<(Formatter &f, string &s)
|
||||
{
|
||||
f << s.c_str();
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
void ICodeGenerator::readICode(const char *fileName)
|
||||
{
|
||||
String buffer;
|
||||
int ch;
|
||||
|
||||
FILE *f = fopen(fileName, "r");
|
||||
while ((ch = getc(f)) != EOF) buffer += static_cast<char>(ch);
|
||||
fclose(f);
|
||||
|
||||
XMLParser xp(buffer, fileName);
|
||||
XMLNode *top = xp.parseDocument();
|
||||
stdOut << *top;
|
||||
|
||||
XMLNodeList &kids = top->children();
|
||||
for (XMLNodeList::const_iterator i = kids.begin(); i != kids.end(); i++) {
|
||||
XMLNode *node = *i;
|
||||
|
||||
if (node->name().compare(widenCString("class")) == 0) {
|
||||
String className;
|
||||
String superName;
|
||||
JSClass* superclass = 0;
|
||||
|
||||
node->getValue(widenCString("name"), className);
|
||||
if (node->getValue(widenCString("super"), superName)) {
|
||||
const JSValue& superclassValue = mGlobal->getVariable(superName);
|
||||
superclass = static_cast<JSClass*>(superclassValue.object);
|
||||
}
|
||||
JSClass* thisClass = new JSClass(mGlobal, className, superclass);
|
||||
JSScope* thisScope = thisClass->getScope();
|
||||
ICodeGenerator scg(mWorld, thisScope, thisClass, kIsStaticMethod);
|
||||
ICodeGenerator ccg(mWorld, thisScope, thisClass, kNoFlags);
|
||||
ccg.allocateParameter(mWorld->identifiers["this"], thisClass);
|
||||
thisClass->defineStatic(mInitName, &Function_Type);
|
||||
|
||||
mGlobal->defineVariable(className, &Type_Type, JSValue(thisClass));
|
||||
|
||||
bool hasDefaultConstructor = false;
|
||||
XMLNodeList &elements = node->children();
|
||||
for (XMLNodeList::const_iterator j = elements.begin(); j != elements.end(); j++) {
|
||||
XMLNode *element = *j;
|
||||
|
||||
if (element->name().compare(widenCString("method")) == 0) {
|
||||
String methodName, resultTypeName;
|
||||
element->getValue(widenCString("name"), methodName);
|
||||
element->getValue(widenCString("result"), resultTypeName);
|
||||
JSType *resultType = findType(mWorld->identifiers[resultTypeName]);
|
||||
String &body = element->body();
|
||||
if (body.length()) {
|
||||
std::string str(body.length(), char());
|
||||
std::transform(body.begin(), body.end(), str.begin(), narrow);
|
||||
ICodeParser icp;
|
||||
|
||||
stdOut << "Calling ICodeParser with :\n" << str << "\n";
|
||||
|
||||
icp.ParseSourceFromString(str);
|
||||
|
||||
#ifdef ROB_DONE
|
||||
ICodeModule *icm = new ICodeModule(icp.instructionStream(),
|
||||
NULL, /* VariableList *variables */
|
||||
icp.maxRegister,
|
||||
1, /* uint32 maxParameter, */
|
||||
NULL, /* InstructionMap *instructionMap */
|
||||
false, /* bool hasRestParameter, */
|
||||
false, /* bool hasNamedRestParameter */
|
||||
resultType);
|
||||
thisClass->defineMethod(methodName, new JSFunction(icm));
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
if (element->name().compare(widenCString("field")) == 0) {
|
||||
String fieldName;
|
||||
String fieldType;
|
||||
|
||||
element->getValue(widenCString("name"), fieldName);
|
||||
element->getValue(widenCString("type"), fieldType);
|
||||
JSType *type = findType(mWorld->identifiers[fieldType]);
|
||||
|
||||
if (element->hasAttribute(widenCString("static")))
|
||||
thisClass->defineStatic(fieldName, type);
|
||||
else {
|
||||
const JSSlot& slot = thisClass->defineSlot(fieldName, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scg.setStatic(thisClass, mInitName, scg.newFunction(ccg.complete(&Void_Type)));
|
||||
|
||||
if (!hasDefaultConstructor) {
|
||||
TypedRegister thisValue = TypedRegister(0, thisClass);
|
||||
ArgumentList args;
|
||||
ICodeGenerator icg(mWorld, thisScope, thisClass, kIsStaticMethod);
|
||||
icg.allocateParameter(mWorld->identifiers["this"], thisClass); // always parameter #0
|
||||
if (superclass)
|
||||
icg.call(icg.getStatic(superclass, superclass->getName()), thisValue, &args);
|
||||
if (thisClass->hasStatic(mInitName))
|
||||
icg.call(icg.getStatic(thisClass, mInitName), thisValue, &args);
|
||||
icg.returnStmt(thisValue);
|
||||
thisClass->defineConstructor(className);
|
||||
scg.setStatic(thisClass, mWorld->identifiers[className], scg.newFunction(icg.complete(&Void_Type)));
|
||||
}
|
||||
thisClass->complete();
|
||||
|
||||
if (scg.getICode()->size()) {
|
||||
Interpreter::Context cx(*mWorld, thisScope);
|
||||
ICodeModule* clinit = scg.complete(&Void_Type);
|
||||
cx.interpret(clinit, JSValues());
|
||||
delete clinit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (node->name().compare(widenCString("script")) == 0) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace ICG
|
||||
|
||||
|
|
|
@ -189,6 +189,8 @@ namespace ICG {
|
|||
bool mHasRestParameter; // true if this function has a ... parameter
|
||||
bool mHasNamedRestParameter; // true if this function has a named ... parameter
|
||||
|
||||
const StringAtom &mInitName;
|
||||
|
||||
std::vector<bool> mPermanentRegister;
|
||||
|
||||
Register getTempRegister()
|
||||
|
@ -243,7 +245,6 @@ namespace ICG {
|
|||
|
||||
void setFlag(uint32 flag, bool v) { mFlags = (ICodeGeneratorFlags)((v) ? mFlags | flag : mFlags & ~flag); }
|
||||
|
||||
|
||||
typedef enum { Var, Property, Slot, Static, Constructor, Name, Method } LValueKind;
|
||||
|
||||
LValueKind resolveIdentifier(const StringAtom &name, TypedRegister &v, uint32 &slotIndex, bool lvalue);
|
||||
|
@ -265,6 +266,7 @@ namespace ICG {
|
|||
}
|
||||
|
||||
ICodeModule *complete(JSType *resultType);
|
||||
void readICode(const char *fileName);
|
||||
|
||||
JSType *extractType(ExprNode *t);
|
||||
|
||||
|
@ -346,6 +348,7 @@ namespace ICG {
|
|||
|
||||
Formatter& operator<<(Formatter &f, ICodeGenerator &i);
|
||||
Formatter& operator<<(Formatter &f, ICodeModule &i);
|
||||
Formatter& operator<<(Formatter &f, std::string &s);
|
||||
/*
|
||||
std::ostream &operator<<(std::ostream &s, ICodeGenerator &i);
|
||||
std::ostream &operator<<(std::ostream &s, StringAtom &str);
|
||||
|
|
|
@ -242,10 +242,15 @@ ICodeModule* Context::genCode(StmtNode *p, const String &fileName)
|
|||
icg.returnStmt(ret);
|
||||
|
||||
ICodeModule *icm = icg.complete(&Void_Type);
|
||||
icm->setFileName (fileName);
|
||||
icm->setFileName(fileName);
|
||||
return icm;
|
||||
}
|
||||
|
||||
void Context::loadClass(const char *fileName)
|
||||
{
|
||||
ICodeGenerator icg(&getWorld(), getGlobalObject());
|
||||
icg.readICode(fileName); // loads it into the global object
|
||||
}
|
||||
|
||||
JSValues& Context::getRegisters() { return mActivation->mRegisters; }
|
||||
ICodeModule* Context::getICode() { return mActivation->mICode; }
|
||||
|
|
|
@ -81,6 +81,8 @@ namespace Interpreter {
|
|||
ICodeModule* genCode(StmtNode *p, const String &fileName);
|
||||
JSValue readEvalFile(FILE* in, const String& fileName);
|
||||
|
||||
void loadClass(const char *fileName);
|
||||
|
||||
void addBinaryOperator(BinaryOperator::BinaryOp op, BinaryOperator *fn) { mBinaryOperators[op].push_back(fn); }
|
||||
const JSValue findBinaryOverride(JSValue &operand1, JSValue &operand2, BinaryOperator::BinaryOp op);
|
||||
|
||||
|
|
|
@ -488,7 +488,6 @@ namespace JSTypes {
|
|||
ICodeModule* mICode;
|
||||
|
||||
protected:
|
||||
JSFunction() : mICode(0) {}
|
||||
|
||||
typedef JavaScript::gc_traits_finalizable<JSFunction> traits;
|
||||
typedef gc_allocator<JSFunction, traits> allocator;
|
||||
|
@ -496,6 +495,8 @@ namespace JSTypes {
|
|||
public:
|
||||
static void initFunctionObject(JSScope *g);
|
||||
|
||||
JSFunction() : mICode(0) {}
|
||||
|
||||
JSFunction(ICodeModule* iCode)
|
||||
: JSObject(FunctionPrototypeObject),
|
||||
mICode(iCode)
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
#include "parser.h"
|
||||
#include "world.h"
|
||||
#include "xmlparser.h"
|
||||
|
||||
namespace JavaScript {
|
||||
|
||||
|
||||
bool XMLTag::getValue(String &name, String &value)
|
||||
{
|
||||
AttributeList::iterator i = mAttributeList.find(name);
|
||||
if (i == mAttributeList.end())
|
||||
return false;
|
||||
else {
|
||||
value = i->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void XMLParser::syntaxError(const char *msg, uint backUp)
|
||||
{
|
||||
mReader.unget(backUp);
|
||||
mReader.error(Exception::syntaxError, widenCString(msg), mReader.getPos());
|
||||
}
|
||||
|
||||
void XMLParser::parseName(String &id)
|
||||
{
|
||||
char16 ch = mReader.peek();
|
||||
CharInfo chi(ch);
|
||||
if (isAlpha(chi)) {
|
||||
mReader.beginRecording(id);
|
||||
mReader.recordChar(mReader.get());
|
||||
while (true) {
|
||||
ch = mReader.peek();
|
||||
CharInfo chi(ch);
|
||||
if (isAlphanumeric(chi))
|
||||
mReader.recordChar(mReader.get());
|
||||
else
|
||||
break;
|
||||
}
|
||||
mReader.endRecording();
|
||||
}
|
||||
else
|
||||
syntaxError("Invalid name");
|
||||
}
|
||||
|
||||
void XMLParser::parseWhiteSpace()
|
||||
{
|
||||
char16 ch = mReader.peek();
|
||||
CharInfo chi(ch);
|
||||
while (isSpace(chi)) {
|
||||
ch = mReader.get();
|
||||
if (isLineBreak(ch))
|
||||
mReader.beginLine();
|
||||
chi = CharInfo(mReader.peek());
|
||||
}
|
||||
}
|
||||
|
||||
void XMLParser::parseAttrValue(String &val)
|
||||
{
|
||||
char16 ch = mReader.get();
|
||||
if (ch != '\"')
|
||||
syntaxError("'\"' expected");
|
||||
else {
|
||||
mReader.beginRecording(val);
|
||||
while (true) {
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated value");
|
||||
if (ch == '\"')
|
||||
break;
|
||||
mReader.recordChar(ch);
|
||||
}
|
||||
mReader.endRecording();
|
||||
}
|
||||
}
|
||||
|
||||
XMLTag *XMLParser::parseTag()
|
||||
{
|
||||
char16 ch;
|
||||
XMLTag *tag = new XMLTag();
|
||||
ch = mReader.peek();
|
||||
if (ch == '/') {
|
||||
tag->setEndTag();
|
||||
mReader.get();
|
||||
}
|
||||
else {
|
||||
if (ch == '!') {
|
||||
mReader.get();
|
||||
if (mReader.peek() != '-')
|
||||
syntaxError("badly formed tag");
|
||||
mReader.get();
|
||||
if (mReader.peek() != '-')
|
||||
syntaxError("badly formed tag");
|
||||
mReader.get();
|
||||
while (true) {
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated comment tag");
|
||||
if (ch == '-') {
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated comment tag");
|
||||
if (ch != '-')
|
||||
mReader.unget();
|
||||
else {
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated comment tag");
|
||||
if (ch != '>')
|
||||
syntaxError("encountered '--' in comment");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isLineBreak(ch))
|
||||
mReader.beginLine();
|
||||
}
|
||||
tag->setComment();
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
parseName(tag->name());
|
||||
parseWhiteSpace();
|
||||
CharInfo chi(mReader.peek());
|
||||
while (isAlpha(chi)) {
|
||||
String attrValue;
|
||||
String attrName;
|
||||
parseName(attrName);
|
||||
if (mReader.peek() == '=') {
|
||||
mReader.get();
|
||||
parseAttrValue(attrValue);
|
||||
}
|
||||
tag->addAttribute(attrName, attrValue);
|
||||
parseWhiteSpace();
|
||||
chi = CharInfo(mReader.peek());
|
||||
}
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated tag");
|
||||
if (ch == '/') {
|
||||
tag->setEmpty();
|
||||
ch = mReader.get();
|
||||
}
|
||||
if (ch != '>')
|
||||
syntaxError("'>' expected");
|
||||
return tag;
|
||||
}
|
||||
|
||||
void XMLParser::parseTagBody(XMLNode *parent, XMLTag *startTag)
|
||||
{
|
||||
while (true) {
|
||||
char16 ch = mReader.get();
|
||||
while (ch != '<') {
|
||||
if (mReader.getEof(ch))
|
||||
syntaxError("Unterminated tag body");
|
||||
parent->addToBody(ch);
|
||||
ch = mReader.get();
|
||||
}
|
||||
XMLTag *tag = parseTag();
|
||||
if (tag->isEndTag() && (tag->name().compare(startTag->name()) == 0))
|
||||
break;
|
||||
else {
|
||||
XMLNode *child = new XMLNode(parent, tag);
|
||||
if (!tag->isEmpty())
|
||||
parseTagBody(child, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XMLNode *XMLParser::parseDocument()
|
||||
{
|
||||
XMLNode *top = new XMLNode(NULL, new XMLTag(widenCString("TOP")));
|
||||
char16 ch = mReader.get();
|
||||
while (ch == '<') {
|
||||
XMLTag *tag = parseTag();
|
||||
XMLNode *n = new XMLNode(top, tag);
|
||||
if (!tag->isEmpty()) {
|
||||
parseTagBody(n, tag);
|
||||
}
|
||||
parseWhiteSpace();
|
||||
ch = mReader.get();
|
||||
if (mReader.getEof(ch))
|
||||
break;
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
Formatter& operator<<(Formatter& f, const XMLTag& tag)
|
||||
{
|
||||
tag.print(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
Formatter& operator<<(Formatter& f, const XMLNode& node)
|
||||
{
|
||||
node.print(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
} /* namespace JavaScript */
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "parser.h"
|
||||
#include "world.h"
|
||||
|
||||
|
||||
namespace JavaScript {
|
||||
|
||||
typedef enum {Tag, EmptyTag, EndTag, CommentTag, DocTypeTag, ProcessInstructionTag } TagFlag;
|
||||
|
||||
|
||||
class XMLNode;
|
||||
typedef std::vector<XMLNode *> XMLNodeList;
|
||||
typedef std::multimap<String, String, std::less<String> > AttributeList;
|
||||
typedef AttributeList::value_type AttributeValue;
|
||||
|
||||
class XMLTag {
|
||||
public:
|
||||
XMLTag() : mFlag(Tag) { }
|
||||
XMLTag(String name) : mName(name), mFlag(Tag) { }
|
||||
|
||||
void addAttribute(String name, String value) { mAttributeList.insert(AttributeValue(name, value) ); }
|
||||
bool getValue(String &name, String &value);
|
||||
bool hasAttribute(String &name) { return (mAttributeList.find(name) != mAttributeList.end()); }
|
||||
|
||||
String &name() { return mName; }
|
||||
|
||||
void setEmpty() { mFlag = EmptyTag; }
|
||||
void setComment() { mFlag = EmptyTag; }
|
||||
void setEndTag() { mFlag = EndTag; }
|
||||
|
||||
bool isEmpty() const { return mFlag == EmptyTag; }
|
||||
bool isEndTag() const { return mFlag == EndTag; }
|
||||
bool isComment() const { return mFlag == CommentTag; }
|
||||
|
||||
String mName;
|
||||
TagFlag mFlag;
|
||||
AttributeList mAttributeList;
|
||||
|
||||
void print(Formatter& f) const
|
||||
{
|
||||
if (isComment())
|
||||
f << "XML Comment tag\n";
|
||||
else
|
||||
f << "XMLTag '" << mName << "'\n";
|
||||
for (AttributeList::const_iterator i = mAttributeList.begin(); i != mAttributeList.end(); i++) {
|
||||
f << "\t'" << i->first << "' = '" << i->second << "'\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Formatter& operator<<(Formatter& f, const XMLTag& tag);
|
||||
Formatter& operator<<(Formatter& f, const XMLNode& node);
|
||||
|
||||
class XMLNode
|
||||
{
|
||||
public:
|
||||
XMLNode(XMLNode *parent, XMLTag *tag) : mParent(parent), mTag(tag) { if (parent) parent->addChild(this); }
|
||||
|
||||
String &name() { return mTag->name(); }
|
||||
XMLTag *tag() { return mTag; }
|
||||
|
||||
String &body() { return mBody; }
|
||||
|
||||
void addToBody(String contents) { mBody += contents; }
|
||||
void addToBody(char16 ch) { mBody += ch; }
|
||||
|
||||
void addChild(XMLNode *child) { mChildren.push_back(child); }
|
||||
XMLNodeList &children() { return mChildren; }
|
||||
|
||||
bool getValue(String &name, String &value)
|
||||
{ return mTag->getValue(name, value); }
|
||||
bool hasAttribute(String &name) { return mTag->hasAttribute(name); }
|
||||
|
||||
|
||||
void print(Formatter& f) const
|
||||
{
|
||||
f << *mTag;
|
||||
if (mParent)
|
||||
f << "parent = '" << mParent->name() << "'";
|
||||
else
|
||||
f << "parent = NULL";
|
||||
|
||||
XMLNodeList::const_iterator i = mChildren.begin();
|
||||
XMLNodeList::const_iterator end = mChildren.end();
|
||||
if (i != end) {
|
||||
f << "\nChildren :\n";
|
||||
while (i != end) {
|
||||
f << **i;
|
||||
f << "\n";
|
||||
i++;
|
||||
}
|
||||
}
|
||||
f << "\n";
|
||||
}
|
||||
|
||||
XMLNodeList mChildren;
|
||||
XMLNode *mParent;
|
||||
XMLTag *mTag;
|
||||
String mBody;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class XMLParser {
|
||||
public:
|
||||
void syntaxError(const char *message, uint backUp = 1);
|
||||
char16 doEscape();
|
||||
|
||||
XMLParser(const String &source, const char *fileName) : mReader(source, widenCString(fileName)) { }
|
||||
|
||||
|
||||
void parseName(String &id);
|
||||
void parseWhiteSpace();
|
||||
void parseAttrValue(String &val);
|
||||
XMLTag *parseTag();
|
||||
void parseTagBody(XMLNode *parent, XMLTag *startTag);
|
||||
XMLNode *parseDocument();
|
||||
|
||||
|
||||
Reader mReader;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} /* namespace JavaScript */
|
|
@ -35,6 +35,7 @@
|
|||
#include "world.h"
|
||||
#include "interpreter.h"
|
||||
#include "icodegenerator.h"
|
||||
#include "xmlparser.h"
|
||||
|
||||
#ifdef DEBUGGER_FOO
|
||||
#include "debugger.h"
|
||||
|
@ -211,6 +212,7 @@ class Tracer : public Context::Listener {
|
|||
}
|
||||
};
|
||||
|
||||
//#define HAVE_GEORGE_TRACE_IT
|
||||
|
||||
static void readEvalPrint(FILE *in, World &world)
|
||||
{
|
||||
|
@ -232,7 +234,11 @@ static void readEvalPrint(FILE *in, World &world)
|
|||
Tracer *george = new Tracer();
|
||||
cx.addListener(george);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TEST_XML_LOADER
|
||||
cx.loadClass("class.xml");
|
||||
#endif
|
||||
|
||||
while (promptLine(inReader, line, buffer.empty() ? "js> " : "> ")) {
|
||||
appendChars(buffer, line.data(), line.size());
|
||||
try {
|
||||
|
@ -309,6 +315,12 @@ static void testCompile()
|
|||
{
|
||||
JSScope glob;
|
||||
Context cx(world, &glob);
|
||||
|
||||
#ifdef HAVE_GEORGE_TRACE_IT
|
||||
Tracer *george = new Tracer();
|
||||
cx.addListener(george);
|
||||
#endif
|
||||
|
||||
glob.defineNativeFunction(world.identifiers["print"], print);
|
||||
for (uint i = 0; i < sizeof(tests) / sizeof(char *); i++) {
|
||||
String testScript = widenCString(tests[i]);
|
||||
|
@ -326,6 +338,7 @@ static void testCompile()
|
|||
}
|
||||
|
||||
|
||||
|
||||
} /* namespace Shell */
|
||||
} /* namespace JavaScript */
|
||||
|
||||
|
@ -341,10 +354,12 @@ int main(int , char **)
|
|||
|
||||
using namespace JavaScript;
|
||||
using namespace Shell;
|
||||
|
||||
#if 1
|
||||
testCompile();
|
||||
#endif
|
||||
readEvalPrint(stdin, world);
|
||||
|
||||
return 0;
|
||||
// return ProcessArgs(argv + 1, argc - 1);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче