}
return rc;
}
+/**
+ * @brief Returns the list of the variables.
+ *
+ * @return the list of the variables
+ */
+RplSymbolSpace::VariableList RplSymbolSpace::listOfVars() const
+{
+ return m_listOfVars;
+}
+
/**
* @brief Returns the parent of the symbol space.
*
}
return rc;
}
+/**
+ * @brief Adds a class to the instance.
+ *
+ * @param clazz the class to add
+ * @return NULL: success<br>
+ * otherwise: the already defined class
+ */
+RplASUserClass* RplSymbolSpace::addClass(RplASUserClass* clazz)
+{
+ RplASUserClass* rc = NULL;
+ const QString& name = clazz->name();
+ if (m_classes.contains(name)){
+ rc = dynamic_cast<RplASUserClass*>(m_classes[name]);
+ } else {
+ m_classes[name] = clazz;
+ }
+ return rc;
+}
/**
* @brief Returns the name of the symbol space.
*
* @param name name of the user defined class
*/
-RplASUserClass::RplASUserClass(const QString& name, RplASTree& tree) :
- RplASClass(name, tree)
+RplASUserClass::RplASUserClass(const QString& name,
+ const RplSourcePosition* position,
+ RplASTree& tree) :
+ RplASClass(name, tree),
+ m_position(position)
{
}
* @param maxLength maximum length of the string
* @return
*/
-QString RplASUserClass::toString(void* object, int maxLength) const
+QString RplASUserClass::toString(void*, int) const
{
return m_name;
}
+/**
+ * @brief Returns the source position of the instance.
+ *
+ * @return the source position
+ */
+const RplSourcePosition* RplASUserClass::position() const
+{
+ return m_position;
+}
+
/** @class RplASUserObject rplastree.hpp "rplexpr/rplastree.hpp"
RplASClass* m_type;
};
+class RplASUserClass;
class RplSymbolSpace
{
public:
typedef QMap<QString, RplASVarDefinition*> VariableMap;
typedef QMap<QString, RplASClass*> ClassMap;
typedef QMap<QString, RplASMethod*> MethodMap;
+ typedef QList<RplASVarDefinition*> VariableList;
public:
RplSymbolSpace(SymbolSpaceType type, const QString& name,
RplSymbolSpace* parent);
void setBody(RplASItem* body);
RplASItem* addVariable(RplASVarDefinition* variable);
RplASItem* addMethod(RplASMethod* method);
- RplASClass* addClass(RplASClass* clazz);
+ RplASUserClass* addClass(RplASUserClass* clazz);
+ RplSymbolSpace* parent() const;
+ VariableList listOfVars() const;
public:
static void initGlobal(RplSymbolSpace& global);
static const char* spaceTypeName(SymbolSpaceType type);
MethodMap m_methods;
RplSymbolSpace* m_parent;
RplASItem* m_body;
+ VariableList m_listOfVars;
+
public:
static RplSymbolSpace m_global;
- RplSymbolSpace* parent() const;
};
class RplASBoolean : public RplASClass {
class RplASUserClass : public RplASClass {
public:
- RplASUserClass(const QString& name, RplASTree& tree);
+ RplASUserClass(const QString& name, const RplSourcePosition* position,
+ RplASTree& tree);
public:
void* newValueInstance(void* source = NULL) const;
void destroyValueInstance(void* object) const;
virtual bool boolValueOf(void* object) const;
virtual QString toString(void *object, int maxLength = 80) const;
+ const RplSourcePosition* position() const;
+
+private:
+ const RplSourcePosition* m_position;
};
class RplASUserObject {
{
return m_nodeType;
}
-
-/** @class RplASExpr rplastree.hpp "rplexpr/rplastree.hpp"
+/**
+ * @brief Returns the flags of the node.
*
- * @brief Implements the abstract base class of value containing items.
+ * @return the bitmask with the flags
+ */
+int RplASItem::flags() const
+{
+ return m_flags;
+}
+/**
+ * @brief Sets the flags of the node.
*
+ * @param flags the new value of the bitmask
*/
-RplASCalculable::RplASCalculable()
+void RplASItem::setFlags(int flags)
{
+ m_flags = flags;
}
+
+/** @class RplASCalculable rplastree.hpp "rplexpr/rplastree.hpp"
+ *
+ * @brief An abstract base class for items which calculates a value.
+ *
+ */
+
+/** @class RplASStorable rplastree.hpp "rplexpr/rplastree.hpp"
+ *
+ * @brief Implements the abstract base class of value containing items.
+ *
+ */
+
/** @class RplASConstant rplastree.hpp "rplexpr/rplastree.hpp"
*
* @brief Implements a constant for the Abstract Syntax Tree.
/**
* @brief Calculates the expression.
*
- * @param value OUT: the calculated value
+ * @param value IN/OUT: the calculated value
+ * @return NULL
*/
-void RplASConstant::calc(RplASVariant& value)
+RplASNode1* RplASConstant::calc(RplASVariant& value)
{
value = m_value;
+ return NULL;
}
/**
/**
* @brief Calculates the expression.
*
- * @param value OUT: the calculated value
+ * @param value IN/OUT: the calculated value
+ * @return NULL
*/
-void RplASListConstant::calc(RplASVariant& value)
+RplASNode1* RplASListConstant::calc(RplASVariant& value)
{
value = m_value;
+ return NULL;
}
/**
/**
* @brief Calculates the value.
*
- * @param value OUT: the value of the instance
+ * @param value IN/OUT: the value of the instance
+ * @return NULL
*/
-void RplASMapConstant::calc(RplASVariant&)
+RplASNode1* RplASMapConstant::calc(RplASVariant& value)
{
-
+ // ToDo: copy map:
+ //value = m_value;
+ return NULL;
}
/**
* @brief Writes the internals into a file.
/**
* @brief Calculates the value.
*
- * @param value OUT: the value of the instance
+ * @param value In/OUT: the value of the instance
+ * @return NULL
*/
-void RplASNamedValue::calc(RplASVariant& )
+RplASNode1* RplASNamedValue::calc(RplASVariant& value, RplStackFrame* frame)
{
-
+ return NULL;
}
/**
* @brief Writes the internals into a file.
/**
* @brief Calculates the initialation value while interpreting.
*
- * @param value OUT: ignored
+ * @param value IN/OUT: ignored
+ * @return NULL
*/
-void RplASVarDefinition::calc(RplASVariant&)
+RplASNode1* RplASVarDefinition::calc(RplASVariant&)
{
+ return NULL;
}
/** @class RplASExprStatement rplastree.hpp "rplexpr/rplastree.hpp"
/**
* @brief Calculates the value of the expression.
*
- * @param value OUT: the calculated value
+ * @param value IN/OUT: the calculated value
+ * @return NULL
*/
-void RplASExprStatement::calc(RplASVariant& )
+RplASNode1* RplASExprStatement::calc(RplASVariant& )
{
+ return NULL;
}
/**
{
}
-void RplASCondition::calc(RplASVariant& value)
+/**
+ * @brief Calculates the value of the condition
+ * @param value IN/OUT: the bool value of the condition
+ * @return NULL
+ */
+RplASNode1* RplASCondition::calc(RplASVariant& value)
{
value.setBool(false);
RplASCalculable* node = dynamic_cast<RplASCalculable*>(m_child);
if (node == NULL)
throw RplASException(m_position, "child of condition is not calculable");
node->calc(value);
+ return NULL;
}
/**
* @brief Calculates the boolean value and returns it.
- *
- * @return the boolean value of the expression
*/
bool RplASCondition::calcAsBool()
{
public:
enum NodeFlags {
NF_UNDEF,
- /// Debugger: this node is a breakpoint
- NF_BREAKPOINT = 1 << 1,
+ /// the node calculates a value:
+ VF_CALCULABLE = 1 << 1,
/// the tree under this node is complete checked for data type correctness
- NF_TYPECHECK_COMPLETE = 1 << 2
+ NF_TYPECHECK_COMPLETE = 1 << 2,
+ /// debugger: this node is a breakpoint
+ NF_BREAKPOINT = 1 << 5
};
public:
static void reset();
RplASItemType nodeType() const;
+ int flags() const;
+ void setFlags(int flags);
+
protected:
unsigned int m_id:16;
RplASItemType m_nodeType:8;
static unsigned int m_nextId;
};
+class RplASNode1;
class RplASCalculable
{
public:
- RplASCalculable();
+ virtual RplASNode1* calc(RplASVariant& value) = 0;
+};
+
+class RplStackFrame;
+class RplASStorable{
public:
- virtual void calc(RplASVariant& value) = 0;
+ virtual RplASNode1* calc(RplASVariant& value, RplStackFrame* frame) = 0;
};
class RplASConstant : public RplASItem, public RplASCalculable
public:
RplASConstant();
public:
- virtual void calc(RplASVariant& value);
+ virtual RplASNode1* calc(RplASVariant& value);
public:
virtual void dump(FILE* fp, int indent);
RplASVariant& value();
RplASVariant m_value;
};
+
class RplASNode1 : public RplASItem
{
public:
public:
RplASListConstant();
public:
- virtual void calc(RplASVariant& value);
+ virtual RplASNode1* calc(RplASVariant& value);
public:
virtual void dump(FILE* fp, int indent);
RplASVariant& value();
public:
RplASMapConstant();
public:
- virtual void calc(RplASVariant& value);
+ virtual RplASNode1* calc(RplASVariant& value);
public:
virtual void dump(FILE* fp, int indent);
RplASVariant& value();
RplASVariant m_value;
};
-class RplASNamedValue : public RplASNode1, public RplASCalculable
+class RplASNamedValue : public RplASNode1, public RplASStorable
{
public:
enum Attributes {
public:
const QString& name() const;
public:
- virtual void calc(RplASVariant& value);
+ virtual RplASNode1* calc(RplASVariant& value, RplStackFrame* frame);
void dump(FILE* fp, int indent);
RplASClass* dataType() const;
RplASVarDefinition();
public:
virtual void execute();
- virtual void calc(RplASVariant& value);
+ virtual RplASNode1* calc(RplASVariant& value);
void dump(FILE* fp, int indent);
const QString& name() const;
RplASClass* datatype() const;
RplASExprStatement();
public:
virtual void execute();
- virtual void calc(RplASVariant& value);
+ virtual RplASNode1* calc(RplASVariant& value);
void dump(FILE* fp, int indent);
};
public:
RplASCondition();
public:
- void calc(RplASVariant& value);
+ RplASNode1* calc(RplASVariant& value);
bool calcAsBool();
void dump(FILE* fp, int indent);
};
L_PARSE_VAR_DEF_ALREADY_DEFINED,
L_PARSE_VAR_DEF_ALREADY_DEFINED2,
L_PARSE_CLASS_NO_NAME,
- L_PARSE_CLASS_LOWERCASE = 2050
+ L_PARSE_CLASS_LOWERCASE = 2050,
+ L_PARSE_CLASS_ALREADY_DEFINED,
+ L_PARSE_CLASS_ALREADY_DEFINED2
};
if (! token->isCapitalizedId())
syntaxError(L_PARSE_CLASS_LOWERCASE, "class name must start with an uppercase character");
QString name = token->toString();
- RplASUserClass* clazz = new RplASUserClass(name, m_tree);
+ RplASUserClass* clazz = new RplASUserClass(name, startPos, m_tree);
RplSymbolSpace* parent = m_tree.currentSpace();
- parent->addClass(clazz);
+ RplASUserClass* alreadyDefined = parent->addClass(clazz);
+ if (alreadyDefined != NULL){
+ error(L_PARSE_CLASS_ALREADY_DEFINED, alreadyDefined->position(),
+ "class already defined", "previous defined class");
+ }
m_tree.startClassOrMethod(name, RplSymbolSpace::SST_CLASS);
clazz->setSymbols();
#include "rplcore/rplcore.hpp"
#include "rplexpr/rplexpr.hpp"
+/** @class RplVmException rplvm.hpp "rplexpr/rplvm.hpp"
+ *
+ * @brief Implements an exception for the virtual machine.
+ *
+ */
+/**
+ * @brief Constructor
+ * @param format the message with placeholders
+ * @param ... the values for the placeholders
+ */
+RplVmException::RplVmException(const char* format, ...) :
+ RplException("")
+{
+ char buffer[16000];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(buffer, sizeof buffer, format, ap);
+ va_end(ap);
+ m_message = buffer;
+}
+/** @class RplStackFrame rplvm.hpp "rplexpr/rplvm.hpp"
+ *
+ * @brief Implements the storage for a symbol space.
+ *
+ * The owner of a symbol space can be "global", a module, a class, or a method.
+ * Some symbol spaces have more than one stack frame, e.g. a recursive called
+ * method.
+ */
+
+
+/**
+ * @brief Constructor.
+ *
+ * @param symbols the symbol space belonging to the stack frame
+ */
+
+RplStackFrame::RplStackFrame(RplSymbolSpace* symbols) :
+ m_countVariables(symbols->listOfVars().size()),
+ m_variables(NULL),
+ m_symbols(symbols)
+{
+ if (m_countLocals > 0)
+ m_variables = new RplASVariant[m_countVariables];
+}
+
+/**
+ * @brief Destructor.
+ */
+RplStackFrame::~RplStackFrame()
+{
+ delete[] m_variables;
+ m_variables = NULL;
+}
+
+/**
+ * @brief Returns the storage of a variable given by the index.
+ *
+ * @param index the index of the variable
+ *
+ * @return the storage of the variable
+ */
+RplASVariant& RplStackFrame::valueOfVariable(int index)
+{
+ if (index < 0 || index >= m_countVariables)
+ throw RplVmException("valueOfVariable(): invalid index: %d", index);
+ return *m_listOfVariables[index];
+}
+
+/** @class RplVMThread rplvm.hpp "rplexpr/rplvm.hpp"
+ *
+ * @brief Implements a thread of the virtual machine.
+ *
+ * The virtual machine can execute many threads at the same time.
+ * Each thread has its own stack.
+ */
+
+/**
+ * @brief Constructor.
+ *
+ * @param maxStack the maximal number of nested stack frames
+ * @param vm the parent, the virtual machine
+ */
+RplVMThread::RplVMThread(int maxStack, RplVirtualMachine* vm) :
+ m_singleStep(false),
+ m_debugMode(false),
+ m_maxStack(maxStack),
+ m_stack(),
+ m_currentValue(),
+ m_vm(vm)
+{
+ m_stack.reserve(maxStack);
+}
+
+/**
+ * @brief Executes a statement list.
+ *
+ * @param statements the first statement of a statement list chained by
+ * <code>m_child</code>
+ * @param space the current symbol space
+ */
+void RplVMThread::execute(RplASNode1* statements, RplSymbolSpace* space)
+{
+ bool debugMode = m_debugMode;
+ while(statements != NULL){
+ if (debugMode
+ && (m_singleStep || (statement->flags() & NF_BREAKPOINT) != 0))
+ debug(statements);
+ RplASCalculable* statement = dynamic_cast<RplASCalculable*>(statements);
+ statement->calc(&m_currentValue);
+ }
+}
+
+/**
+ * @brief Handles a debugger break.
+ *
+ * @param statement the current statement (not yet executed)
+ */
+void RplVMThread::debug(RplASNode1* statement)
+{
+
+}
+
+/** @class RplVirtualMachine rplvm.hpp "rplexpr/rplvm.hpp"
+ *
+ * @brief Implements a virtual machine.
+ *
+ * This is a execution unit which interprets an abstract syntax tree.
+ */
+RplVirtualMachine::RplVirtualMachine(RplASTree* tree, RplSource* source,
+ int maxStack) :
+ m_maxStack(maxStack),
+ m_threads(),
+ m_flags(VF_UNDEF),
+ m_source(source),
+ m_tree(tree)
+{
+ m_threads.reserve(8);
+}
+
+/**
+ * @brief Adds a thread to the instance.
+ *
+ * @param program the statement list to execute
+ * @param space the current symbol space
+ * @param maxStack the maximal number of nested stack frames.
+ * <= 0: use the default
+ */
+void RplVirtualMachine::addThread(RplASItem* program, RplSymbolSpace* space,
+ int maxStack)
+{
+ RplVMThread* thread = new RplVMThread(
+ maxThread <= 0 ? m_maxStack : maxStack, self);
+ m_threads.append(thread);
+ thread->execute(program, space);
+}
+/**
+ * @brief Tests whether a given flag is set.
+ *
+ * @param flag flag to test
+ * @return true: the flag is set<br>
+ * false: otherwise
+ */
+bool RplVirtualMachine::hasFlag(RplVirtualMachine::VMFlag flag) const
+{
+ bool rc = (m_flags & flag) != 0;
+}
+/**
+ * @brief Adds a flag to the current flags.
+ *
+ * @param flag
+ * @return
+ */
+void RplVirtualMachine::setFlag(RplVirtualMachine::VMFlag flag)
+{
+ m_flag |= flag;
+}
+
+/**
+ * @brief Adds a flag to the current flags.
+ *
+ * @param flag
+ * @return
+ */
+void RplVirtualMachine::clearFlag(RplVirtualMachine::VMFlag flag)
+{
+ m_flag &= ~flag;
+}
#ifndef RPLVM_HPP
#define RPLVM_HPP
+class RplVmException : public RplException {
+public:
+ RplVmException(const char* message, ...);
+};
class RplStackFrame {
public:
- RplStackFrame(int countArgs, int countLocals);
+ RplStackFrame(RplSymbolSpace* symbols);
~RplStackFrame();
public:
+ RplASVariant& valueOfVariable(int index);
private:
+ int m_countVariables;
RplASVariant* m_variables;
+ RplSymbolSpace* m_symbols;
};
-class RPLVirtualMachine
+class RplVirtualMachine;
+class RplVMThread
{
public:
- RPLVirtualMachine();
+ typedef QList<RplStackFrame*> StackFrameList;
+public:
+ RplVMThread(int maxStack, RplVirtualMachine* vm);
+public:
+ void execute(RplASNode1* statements, RplSymbolSpace* space);
+ virtual void debug(RplASNode1* statement);
+protected:
+ bool m_debugMode;
+ bool m_singleStep;
+ int m_maxStack;
+ StackFrameList m_stack;
+ RplASVariant m_currentValue;
+ RplVirtualMachine* m_vm;
+};
+
+class RplVirtualMachine
+{
+public:
+ enum VMFlag {
+ VF_UNDEF,
+ VF_TRACE_STATEMENTS = 1<<1,
+ VF_TRACE_LOCALS = 1<<2,
+ VF_TRACE_AUTO_VARIABLES = 1<<3
+
+ };
+
+public:
+ RplVirtualMachine(RplASTree& tree, RplSource& source, int maxStack = 1024);
public:
- void calc();
+ void addThread(RplASItem* program, RplSymbolSpace* space, int maxStack = 0);
+ bool hasFlag(VMFlag flag) const;
+ void setFlag(VMFlag flag);
+ void clearFlag(VMFlag flag);
private:
- RplStackFrame m_global;
- QMap<QString, RplSymbolSpace*> m_modules;
- QList<RplStackFrame*> m_stack;
+ int m_maxStack;
+ QList<RplVMThread*> m_threads;
+ int m_flags;
+ RplSource& m_source;
+ RplASTree& m_tree;
};
#endif // RPLVM_HPP
--- /dev/null
+/*
+ * Licence:
+ * You can use and modify this file without any restriction.
+ * There is no warranty.
+ * You also can use the licence from http://www.wtfpl.net/.
+ * The original sources can be found on https://github.com/republib.
+*/
+
+#include "rplcore/rplcore.hpp"
+#include "rplexpr/rplexpr.hpp"
+#include "rplcore/rpltest.hpp"
+
+class TestRplVM : public RplTest{
+private:
+ RplSource m_source;
+ RplASTree m_tree;
+ RplStringReader m_reader;
+ const char* m_currentSource;
+public:
+ TestRplVM() :
+ RplTest("RplVM"),
+ m_source(),
+ m_tree(),
+ m_reader(m_source)
+ {
+ m_source.addReader(&m_reader);
+ }
+protected:
+ void setSource(const char* content){
+ RplASItem::reset();
+ m_currentSource = content;
+ m_tree.clear();
+ m_source.clear();
+ m_reader.clear();
+ m_reader.addSource("<test>", content);
+ m_source.addReader(&m_reader);
+ m_source.addSourceUnit(m_reader.currentSourceUnit());
+ }
+
+private:
+ void checkAST(RplVirtualMachine& vm, const char* fileExpected, int lineNo){
+ QByteArray fnExpected = "test";
+ fnExpected += QDir::separator().toLatin1();
+ fnExpected += "rplvm";
+ fnExpected += (char) QDir::separator().toLatin1();
+ fnExpected += fileExpected;
+ QByteArray fnCurrent = getTempFile(fileExpected, "rplvm");
+ vm.dump(fnCurrent, RplASTree::DMP_NO_GLOBALS);
+ assertEqualFiles(fnExpected.constData(), fnCurrent.constData(),
+ __FILE__, lineNo);
+ }
+public:
+ void baseTest(){
+ setSource("Int a=2+3*4");
+ RplMFParser parser(m_source, m_tree);
+ parser.parse();
+ RplVirtualMachine vm(m_tree, m_source);
+ checkAST(vm, "baseTest.txt", __LINE__);
+ }
+ virtual void doIt(void) {
+ baseTest();
+ }
+};
+void testRplVM() {
+ TestRplVM test;
+ test.run();
+}
+
}
void testExpr(){
+ extern void testRplVM();
+ testRplVM();
+
extern void testRplMFParser();
testRplMFParser();
#include "rplexpr/rplexpr.hpp"
#include "rplcore/rpltest.hpp"
-class TestRplMFParser : public RplTest{
+class TestRplVM : public RplTest{
private:
RplSource m_source;
RplASTree m_tree;
RplStringReader m_reader;
const char* m_currentSource;
public:
- TestRplMFParser() :
+ TestRplVM() :
RplTest("RplMFParser"),
m_source(),
m_tree(),
}
};
void testRplMFParser() {
- TestRplMFParser test;
+ TestRplVM test;
test.run();
}
../rplcore/rplqstring.cpp \
../rplexpr/rplasclasses.cpp \
rplastree_test.cpp \
- rplmfparser_test.cpp
+ rplmfparser_test.cpp \
+ ../rplexpr/rplvm_test.cpp