/*
* Copyright (c) 2002-2006 Samit Basu
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __Context_hpp__
#define __Context_hpp__
#include "Scope.hpp"
#include "Array.hpp"
#include "FunctionDef.hpp"
#include "Exception.hpp"
#include "Types.hpp"
#include <vector>
#include <QDebug>
#include <QList>
#include <QMutex>
#include <stdarg.h>
/**
* This class represents a reference to an array in a scope
*/
class ArrayReference {
Array *m_ptr;
bool m_global;
ScopePtr m_scope;
public:
inline ArrayReference() : m_ptr(NULL), m_global(false), m_scope(NULL) {
}
inline ArrayReference(Array *ptr) : m_ptr(ptr), m_global(false), m_scope(NULL) {
}
inline ArrayReference(Array *ptr, bool global, ScopePtr scope) :
m_ptr(ptr), m_global(global), m_scope(scope) {
if (m_global)
m_scope->lock();
}
inline ~ArrayReference() {
if (m_global)
m_scope->unlock();
}
inline ArrayReference(const ArrayReference& copy) {
m_ptr = copy.m_ptr;
m_global = copy.m_global;
m_scope = copy.m_scope;
if (m_global)
m_scope->lock();
}
inline ArrayReference& operator=(Array* ptr) {
if (m_global)
m_scope->unlock();
m_ptr = ptr;
m_global = false;
m_scope = NULL;
}
inline ArrayReference& operator=(const ArrayReference& copy) {
if (m_global)
m_scope->unlock();
m_ptr = copy.m_ptr;
m_global = copy.m_global;
m_scope = copy.m_scope;
if (m_global)
m_scope->lock();
return *this;
}
inline Array& operator*() {
return *m_ptr;
}
inline Array* operator->() {
return m_ptr;
}
inline bool valid() {
return (m_ptr != NULL);
}
};
/**
* A Context is a stack of scopes with the (peculiar) property that
* the top and bottom of the stack (global and local scopes respectively)
* are searched regularly. The context is responsible for determining
* if variables and functions exist, and if so, for returning their
* values and accepting modifications to them. A context also keeps
* track of loop depth. Furthermore, the context (starting with 3.1)
* now "owns" all of the functions that are defined. These used to be
* tracked with the scopes, but that lead to a memory leak of FunctionDef
* objects.
*/
typedef SymbolTable<FuncPtr> CodeTable;
class Context {
/**
* The normal stack of scopes.
*/
std::vector<ScopePtr> scopestack;
/**
* The stack of scopes that have been "bypassed"
*/
QList<ScopePtr> bypassstack;
/**
* The table of functions
*/
CodeTable codeTab;
/**
* List of functions that are "temporary" and should be flushed
*/
stringVector tempFunctions;
/**
* Mutex to control access to this context class.
*/
QMutex mutex;
/**
* Pointer to current scope
*/
ScopePtr bottomScope;
/**
* Pointer to global scope
*/
ScopePtr topScope;
public:
/**
* Create a context and initialize it with a global scope and a
* base scope.
*/
Context(ScopePtr global) : mutex(QMutex::Recursive) {
scopestack.push_back(global);
pushScope("base");
topScope = scopestack.front();
bottomScope = scopestack.back();
}
/**
* Get the pointer to the mutex that protects this context.
* This mutex only needs to be used when the GUI thread wants
* to access the context (which is "owned" by the Interpreter
* thread).
*/
inline QMutex* getMutex() {
return &mutex;
}
/**
* Bypass the prescribed number of scopes. These scopes are
* placed on the bypassstack. This effectively makes a different
* scope active, and then allows us to restore the bypass scopes.
* a count of -1 means all scopes are bypassed (except the base scope)
*/
inline void bypassScope(int count) {
if (count < 0)
count = scopestack.size();
while ((count > 0) && (scopestack.back()->getName() != "base")) {
bypassstack.push_back(scopestack.back());
scopestack.pop_back();
count--;
}
bottomScope = scopestack.back();
}
inline void restoreScope(int count) {
for (int i=0;i<count;i++) {
scopestack.push_back(bypassstack.back());
bypassstack.pop_back();
}
bottomScope = scopestack.back();
}
/**
* Every call to bypassScope should be matched by a call to
* restoreBypassedScopes, or memory leaks will occur.
*/
inline void restoreBypassedScopes() {
for (int i=0;i<bypassstack.size();i++)
scopestack.push_back(bypassstack[bypassstack.size()-1-i]);
bypassstack.clear();
bottomScope = scopestack.back();
}
/**
* Push the given scope onto the bottom of the scope stack.
*/
inline void pushScope(std::string name, bool nestflag = false) {
if (scopestack.size() > 100)
throw Exception("Allowable stack depth exceeded...");
scopestack.push_back(new Scope(name,nestflag));
bottomScope = scopestack.back();
}
/**
* Pop the bottom scope off of the scope stack. The scope is
* deleted.
* Throws an Exception if the global scope is popped.
*/
inline void popScope() {
if (scopestack.size() == 1)
throw Exception("Attempt to pop global scope off of context stack!");
scopestack.pop_back();
delete bottomScope;
bottomScope = scopestack.back();
}
/**
* Insert the given variable into the right scope - the global
* scope if the array is in the global list, and mangled in
* global list if it is persistent.
*/
inline void insertVariable(const std::string& varName, const Array& var) {
ScopePtr active;
std::string mapName;
if (bottomScope->isVariablePersistent(varName)) {
mapName = bottomScope->getMangledName(varName);
active = topScope;
} else if (bottomScope->isVariableGlobal(varName)) {
mapName = varName;
active = topScope;
} else {
bottomScope->insertVariable(varName,var);
return;
}
active->insertVariable(mapName,var);
}
/**
* Insert a variable into the local scope - do not check the
* global list.
*/
inline void insertVariableLocally(std::string varName, const Array& var) {
bottomScope->insertVariable(varName,var);
}
/**
* Return a pointer to the given variable. The search
* logic is:
* - If the variable is persistent in the current scope
* (at the bottom of the scope stack), mangle its name
* using the scope, and look for it in the global scope.
* - If the variable is global in the current scope (at the
* bottom of the scope stack, look for it in the global
* scope.
* - Look for the variable in the local scope.
* If the variable is not found, an empty variable is constructed
* with the given name, and inserted into the scope that was
* searched. A pointer to this newly created variable is returned.
*/
inline ArrayReference lookupVariable(const std::string& varName) {
ScopePtr active;
std::string mapName;
bool global = false;
if (bottomScope->isVariablePersistent(varName)) {
mapName = bottomScope->getMangledName(varName);
active = topScope;
} else if (bottomScope->isVariableGlobal(varName)) {
mapName = varName;
active = topScope;
global = true;
} else {
Array *dp = bottomScope->lookupVariable(varName);
if (!dp && bottomScope->isnested()) {
// If the variable is not defined in the current scope,
// loop up through the scope stack, checking for nested
// scopes that may have the variable defined.
int i=scopestack.size()-2;
while ((!dp) && (i>=0) && scopestack[i]->nests(scopestack[i+1]->getName())) {
dp = scopestack[i]->lookupVariable(varName);
if (!dp) i--;
}
if (dp)
return (ArrayReference(dp,false,scopestack[i]));
else
return (ArrayReference(dp,false,bottomScope));
} else
return (ArrayReference(dp,false,bottomScope));
}
return (ArrayReference(active->lookupVariable(mapName),global,active));
}
inline bool variableLocalToCurrentScope(string varName) {
return bottomScope->variableLocal(varName);
}
inline void setVariablesAccessed(stringVector va) {
bottomScope->setVariablesAccessed(va);
}
inline void setLocalVariablesList(stringVector rv) {
bottomScope->setLocalVariables(rv);
}
/**
* Look for a variable, but only locally.
*/
inline Array* lookupVariableLocally(std::string varName) {
return bottomScope->lookupVariable(varName);
}
/**
* Insert a function definition into the code table.
*/
inline void insertFunction(FuncPtr f, bool temporary) {
codeTab.insertSymbol(f->name,f);
if (temporary)
tempFunctions.push_back(f->name);
}
/**
* Remove a function definition from the code table.
*/
inline void deleteFunction(const std::string& funcName) {
codeTab.deleteSymbol(funcName);
}
/**
* Flush temporary function definitions from the global context
*/
inline void flushTemporaryGlobalFunctions() {
for (int i=0;i<tempFunctions.size();i++)
deleteFunction(tempFunctions[i]);
tempFunctions.clear();
}
/**
* Add a built in function to the global scope with the given name.
*/
inline void addFunction(char *name, BuiltInFuncPtr fptr, int argc_in, int argc_out, ...) {
stringVector args;
va_list argp;
if (argc_in>0) {
va_start(argp,argc_out);
for (int i=0;i<argc_in;i++) {
const char *t = va_arg(argp, const char *);
if (!t) {
qDebug() << "addFunction for function " << name << " is wrong!\n";
exit(1);
}
args.push_back(t);
}
if (va_arg(argp,const char *) != NULL) {
qDebug() << "addFunction for function " << name << " is wrong!\n";
exit(1);
}
va_end(argp);
}
BuiltInFunctionDef *f2def;
f2def = new BuiltInFunctionDef;
f2def->retCount = argc_out;
f2def->argCount = argc_in;
f2def->name = name;
f2def->fptr = fptr;
f2def->arguments = args;
insertFunction(f2def,false);
}
/**
* Add a special function to the global scope with the given name.
*/
inline void addSpecialFunction(char*name, SpecialFuncPtr fptr, int argc_in, int argc_out, ...) {
stringVector args;
va_list argp;
if (argc_in>0) {
va_start(argp,argc_out);
for (int i=0;i<argc_in;i++) {
const char *t = va_arg(argp, const char *);
if (!t) {
qDebug() << "addSpecialFunction for function " << name << " is wrong!\n";
exit(1);
}
args.push_back(t);
}
if (va_arg(argp,const char *) != NULL) {
qDebug() << "addSpecialFunction for function " << name << " is wrong!\n";
exit(1);
}
va_end(argp);
}
SpecialFunctionDef *f2def;
f2def = new SpecialFunctionDef;
f2def->retCount = argc_out;
f2def->argCount = argc_in;
f2def->name = name;
f2def->fptr = fptr;
f2def->arguments = args;
insertFunction(f2def,false);
}
/**
* Add a built in function to the global scope with the given name
* and tag it as a graphics function
*/
inline void addGfxFunction(char*name, BuiltInFuncPtr fptr, int argc_in, int argc_out, ...) {
stringVector args;
va_list argp;
if (argc_in>0) {
va_start(argp,argc_out);
for (int i=0;i<argc_in;i++) {
const char *t = va_arg(argp, const char *);
if (!t) {
qDebug() << "addGfxFunction for function " << name << " is wrong!\n";
exit(1);
}
args.push_back(t);
}
if (va_arg(argp,const char *) != NULL) {
qDebug() << "addGfxFunction for function " << name << " is wrong!\n";
exit(1);
}
va_end(argp);
}
BuiltInFunctionDef *f2def;
f2def = new BuiltInFunctionDef;
f2def->retCount = argc_out;
f2def->argCount = argc_in;
f2def->name = name;
f2def->fptr = fptr;
f2def->arguments = args;
f2def->graphicsFunction = true;
insertFunction(f2def,false);
}
/**
* Add a special function to the global scope with the given name, and
* tag it as a graphics function
*/
inline void addGfxSpecialFunction(char*name, SpecialFuncPtr fptr, int argc_in, int argc_out, ...) {
stringVector args;
va_list argp;
if (argc_in>0) {
va_start(argp,argc_out);
for (int i=0;i<argc_in;i++) {
const char *t = va_arg(argp, const char *);
if (!t) {
qDebug() << "addGfxSpecialFunction for function " << name << " is wrong!\n";
exit(1);
}
args.push_back(t);
}
if (va_arg(argp,const char *) != NULL) {
qDebug() << "addGfxSpecialFunction for function " << name << " is wrong!\n";
exit(1);
}
va_end(argp);
}
SpecialFunctionDef *f2def;
f2def = new SpecialFunctionDef;
f2def->retCount = argc_out;
f2def->argCount = argc_in;
f2def->name = name;
f2def->fptr = fptr;
f2def->arguments = args;
f2def->graphicsFunction = true;
insertFunction(f2def,false);
}
inline stringVector listAllFunctions() {
return codeTab.getCompletions("");
}
inline stringVector listGlobalVariables() {
return topScope->listAllVariables();
}
inline stringVector listAllVariables() {
return bottomScope->listAllVariables();
}
inline void clearGlobalVariableList() {
topScope->clearGlobalVariableList();
bottomScope->clearGlobalVariableList();
}
inline void clearPersistentVariableList() {
topScope->clearPersistentVariableList();
bottomScope->clearPersistentVariableList();
}
inline stringVector getCompletions(const std::string& prefix) {
stringVector local_completions = bottomScope->getCompletions(prefix);
stringVector global_completions = topScope->getCompletions(prefix);
stringVector code_completions = codeTab.getCompletions(prefix);
stringVector completions(local_completions);
for (int i=0;i<global_completions.size();i++)
completions.push_back(global_completions[i]);
for (int i=0;i<code_completions.size();i++)
completions.push_back(code_completions[i]);
return completions;
}
inline bool lookupFunction(std::string funcName, FuncPtr& val) {
FuncPtr* ret = codeTab.findSymbol(funcName);
if (ret) {
val = *ret;
return true;
}
return false;
}
/**
* Add a persistent variable to the local stack. This involves
* two steps:
* - the name of the variable is added to the persistent variable list
* in the current scope.
* - the global scope is checked for the mangled name of the
* persistent variable. If the variable does not exist in the
* global scope, then an empty variable is inserted.
*/
inline void addPersistentVariable(std::string var) {
// Delete local variables with this name
bottomScope->deleteVariable(var);
// Delete global variables with this name
// topScope->deleteVariable(var);
bottomScope->addPersistentVariablePointer(var);
}
/**
* Add a variable name into the global variable list of the current
* scope. If the variable does not exist in the global scope, an
* empty variable is added.
*/
inline void addGlobalVariable(std::string var) {
// Delete local variables with this name
bottomScope->deleteVariable(var);
// Delete global persistent variables with this name
topScope->deleteVariable(bottomScope->getMangledName(var));
// Add a point in the local scope to the global variable
bottomScope->addGlobalVariablePointer(var);
}
inline void deleteGlobalVariable(std::string var) {
topScope->deleteVariable(var);
}
/**
* Delete a variable if its defined. Handles global and persistent
* variables also.
*/
inline void deleteVariable(std::string var) {
if (isVariableGlobal(var)) {
topScope->deleteVariable(var);
bottomScope->deleteGlobalVariablePointer(var);
return;
}
if (isVariablePersistent(var)) {
topScope->deleteVariable(bottomScope->getMangledName(var));
bottomScope->deletePersistentVariablePointer(var);
return;
}
bottomScope->deleteVariable(var);
}
/**
* Increment the loop depth counter in the local scope.
*/
inline void enterLoop() {
bottomScope->enterLoop();
}
/**
* Decrement the loop depth counter in the local scope.
*/
inline void exitLoop() {
bottomScope->exitLoop();
}
inline bool isCurrentScopeNested() {
return bottomScope->isnested();
}
inline string scopeName() {
return bottomScope->getName();
}
inline bool currentScopeNests(string name) {
return bottomScope->nests(name);
}
inline bool currentScopeVariableAccessed(string name) {
return bottomScope->variableAccessed(name);
}
/**
* Returns true if the current (local) scope indicates a
* non-zero loop depth.
*/
inline bool inLoop() {
return bottomScope->inLoop();
}
/**
* Returns true if the given variable is global.
*/
inline bool isVariableGlobal(const std::string& varName) {
return bottomScope->isVariableGlobal(varName);
}
/**
* Returns true if the given variable is persistent
*/
inline bool isVariablePersistent(const std::string& varName) {
return bottomScope->isVariablePersistent(varName);
}
};
#endif
syntax highlighted by Code2HTML, v. 0.9.1