aboutsummaryrefslogtreecommitdiffstats
path: root/3rdparty
diff options
context:
space:
mode:
authorMiodrag Milanovic <mmicko@gmail.com>2018-06-28 13:16:53 +0200
committerMiodrag Milanovic <mmicko@gmail.com>2018-06-28 17:57:26 +0200
commitc63274342f1aaa3d1087c0df5ffdaccfd0afcd56 (patch)
treeb04628f6c8c6768e2d5771737f3f13687cd0ed29 /3rdparty
parent66670831b89d934ef00c47c0527137f6ec38a0b0 (diff)
downloadnextpnr-c63274342f1aaa3d1087c0df5ffdaccfd0afcd56.tar.gz
nextpnr-c63274342f1aaa3d1087c0df5ffdaccfd0afcd56.tar.bz2
nextpnr-c63274342f1aaa3d1087c0df5ffdaccfd0afcd56.zip
initial import of python-console
Diffstat (limited to '3rdparty')
-rw-r--r--3rdparty/python-console/CMakeLists.txt43
-rw-r--r--3rdparty/python-console/ColumnFormatter.cpp141
-rw-r--r--3rdparty/python-console/ColumnFormatter.h77
-rw-r--r--3rdparty/python-console/Console.cpp297
-rw-r--r--3rdparty/python-console/Console.h78
-rw-r--r--3rdparty/python-console/Interpreter.cpp231
-rw-r--r--3rdparty/python-console/Interpreter.h97
-rw-r--r--3rdparty/python-console/LICENSE19
-rw-r--r--3rdparty/python-console/ParseHelper.BlockParseState.cpp174
-rw-r--r--3rdparty/python-console/ParseHelper.BracketParseState.cpp127
-rw-r--r--3rdparty/python-console/ParseHelper.ContinuationParseState.cpp61
-rw-r--r--3rdparty/python-console/ParseHelper.cpp225
-rw-r--r--3rdparty/python-console/ParseHelper.h169
-rw-r--r--3rdparty/python-console/ParseListener.cpp29
-rw-r--r--3rdparty/python-console/ParseListener.h42
-rw-r--r--3rdparty/python-console/ParseMessage.cpp31
-rw-r--r--3rdparty/python-console/ParseMessage.h38
-rw-r--r--3rdparty/python-console/README.md17
-rw-r--r--3rdparty/python-console/Utils.h33
-rw-r--r--3rdparty/python-console/data/test.py2
-rw-r--r--3rdparty/python-console/data/test2.py6
-rw-r--r--3rdparty/python-console/data/test3.py4
-rw-r--r--3rdparty/python-console/data/test4.py4
-rw-r--r--3rdparty/python-console/data/test5.py3
-rw-r--r--3rdparty/python-console/test_cli.cpp63
-rw-r--r--3rdparty/python-console/test_console.cpp36
-rw-r--r--3rdparty/python-console/test_parse_helper.cpp51
-rw-r--r--3rdparty/python-console/test_python_interpreter.cpp22
28 files changed, 2120 insertions, 0 deletions
diff --git a/3rdparty/python-console/CMakeLists.txt b/3rdparty/python-console/CMakeLists.txt
new file mode 100644
index 00000000..dd4d6047
--- /dev/null
+++ b/3rdparty/python-console/CMakeLists.txt
@@ -0,0 +1,43 @@
+cmake_minimum_required( VERSION 2.8 )
+project( PythonInterpreter )
+
+find_package( Qt4 REQUIRED )
+include( ${QT_USE_FILE} )
+find_package( PythonLibs REQUIRED )
+include_directories( ${PYTHON_INCLUDE_DIRS} )
+
+add_executable( test_python_interpreter test_python_interpreter.cpp Interpreter.cpp )
+target_link_libraries( test_python_interpreter ${PYTHON_LIBRARIES} )
+
+qt4_wrap_cpp( Console_MOC Console.h )
+add_executable( test_console test_console.cpp
+ Console.cpp ${Console_MOC}
+ ColumnFormatter.cpp
+ Interpreter.cpp
+ ParseHelper.cpp
+ ParseHelper.BlockParseState.cpp
+ ParseHelper.BracketParseState.cpp
+ ParseHelper.ContinuationParseState.cpp
+ ParseMessage.cpp
+)
+target_link_libraries( test_console ${QT_LIBRARIES} ${PYTHON_LIBRARIES} )
+
+add_executable( test_parse_helper test_parse_helper.cpp
+ ParseHelper.cpp
+ ParseHelper.BlockParseState.cpp
+ ParseHelper.ContinuationParseState.cpp
+ ParseListener.cpp
+ ParseMessage.cpp
+)
+
+add_executable( test_cli test_cli.cpp
+ ParseHelper.cpp
+ ParseHelper.BlockParseState.cpp
+ ParseHelper.ContinuationParseState.cpp
+ ParseListener.cpp
+ ParseMessage.cpp
+ Interpreter.cpp
+)
+target_link_libraries( test_cli
+ ${PYTHON_LIBRARIES}
+)
diff --git a/3rdparty/python-console/ColumnFormatter.cpp b/3rdparty/python-console/ColumnFormatter.cpp
new file mode 100644
index 00000000..adbda96a
--- /dev/null
+++ b/3rdparty/python-console/ColumnFormatter.cpp
@@ -0,0 +1,141 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "ColumnFormatter.h"
+#include <iomanip>
+#include <iostream>
+#include <list>
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <vector>
+#include <cstdlib>
+
+bool ColumnFormatter::load(const std::string& fn)
+{
+ items.clear();
+ std::ifstream ifs(fn.c_str());
+ if (!ifs.is_open())
+ return false;
+ std::string str;
+ while (!ifs.eof())
+ {
+ std::getline( ifs, str );
+ if (!ifs.eof())
+ items.push_back(str);
+ }
+ return true;
+}
+
+int ColumnFormatter::solve(int width)
+{
+ bool fits = true;
+ int i = 1;
+ while (fits)
+ {
+ ++i;
+ std::vector<int> widths = divideItems(i);
+ int columnWidth = width / i;
+ for (int j = 0; j < widths.size(); ++j)
+ {
+ fits &= (widths[j] < columnWidth);
+ }
+ if (!fits)
+ {
+ --i;
+ }
+ }
+ return i;
+}
+
+std::vector<int> ColumnFormatter::divideItems(int numColumns)
+{
+ columns.clear();
+ for (int i = 0; i < numColumns; ++i)
+ columns.push_back(std::list<std::string>());
+ for (int i = 0; i < items.size(); ++i)
+ {
+ columns[i % numColumns].push_back(items[i]);
+ }
+
+ // count the fattest item in each column
+ std::vector<int> res(numColumns);
+ for (int i = 0; i < numColumns; ++i)
+ {
+ for (std::list<std::string>::const_iterator it =
+ columns[i].begin(); it != columns[i].end(); ++it)
+ {
+ if (res[i] < it->size())
+ res[i] = it->size();
+ }
+ }
+
+ return res;
+}
+
+void ColumnFormatter::format(int width)
+{
+ m_formattedOutput.clear();
+ int cols = solve(width);
+ std::vector<int> colWidths(cols, width / cols);
+ int rem = width % cols;
+ for (int i = 0; i < rem; ++i)
+ {
+ colWidths[i]++;
+ }
+ divideItems(cols);
+ std::vector< std::list<std::string>::const_iterator > its;
+ std::vector< std::list<std::string>::const_iterator > it_ends;
+ for (int i = 0; i < columns.size(); ++i)
+ {
+ its.push_back(columns[i].begin());
+ it_ends.push_back(columns[i].end());
+ }
+ bool done = false;
+ while (!done)
+ {
+ std::stringstream row_ss;
+ for (int i = 0; i < columns.size(); ++i)
+ {
+ std::stringstream item_ss;
+ std::string item;
+ if (its[i] != it_ends[i])
+ {
+ item = *its[i];
+ ++its[i];
+ }
+ item_ss << std::left << std::setw(colWidths[i]) << item;
+ row_ss << item_ss.str();
+ }
+ m_formattedOutput.push_back(row_ss.str());
+
+ done = true;
+ for (int i = 0; i < columns.size(); ++i)
+ {
+ done &= (its[i] == it_ends[i]);
+ }
+ }
+}
+
+const std::list<std::string>& ColumnFormatter::formattedOutput() const
+{
+ return m_formattedOutput;
+}
diff --git a/3rdparty/python-console/ColumnFormatter.h b/3rdparty/python-console/ColumnFormatter.h
new file mode 100644
index 00000000..4fedf2ea
--- /dev/null
+++ b/3rdparty/python-console/ColumnFormatter.h
@@ -0,0 +1,77 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#ifndef COLUMN_FORMATTER_H
+#define COLUMN_FORMATTER_H
+#include <list>
+#include <string>
+#include <vector>
+
+/**
+Format a list of items into as many columns as width permits.
+*/
+class ColumnFormatter
+{
+protected:
+ std::vector<std::string> items;
+ std::vector< std::list<std::string> > columns;
+ std::list< std::string > m_formattedOutput;
+
+public:
+ /**
+ Load items from file, one item per line.
+ */
+ bool load(const std::string& fn);
+
+ template <class InputIterator>
+ void setItems(InputIterator begin, InputIterator end)
+ {
+ items.clear();
+ for (InputIterator it = begin; it != end; ++it)
+ {
+ items.push_back(*it);
+ }
+ }
+
+ /**
+ Determine the number of columns that the items can be into, given a
+ width limit.
+ */
+ int solve(int width);
+
+ /**
+ Divide items into numColumns.
+ */
+ std::vector<int> divideItems(int numColumns);
+
+ /**
+ Generate formatted output, the items formatted into as many columns as can
+ be fit in width.
+ */
+ void format(int width);
+
+ /**
+ Get output.
+ */
+ const std::list<std::string>& formattedOutput() const;
+};
+
+#endif // COLUMN_FORMATTER_H
diff --git a/3rdparty/python-console/Console.cpp b/3rdparty/python-console/Console.cpp
new file mode 100644
index 00000000..f9413542
--- /dev/null
+++ b/3rdparty/python-console/Console.cpp
@@ -0,0 +1,297 @@
+#include "Console.h"
+#include "Interpreter.h"
+#include "ColumnFormatter.h"
+
+#include <iostream>
+#include <QKeyEvent>
+#include <QFont>
+
+#include "Utils.h"
+
+const QString Console::PROMPT = ">>> ";
+const QString Console::MULTILINE_PROMPT = "... ";
+const QColor Console::NORMAL_COLOR = QColor::fromRgbF( 0, 0, 0 );
+const QColor Console::ERROR_COLOR = QColor::fromRgbF( 1.0, 0, 0 );
+const QColor Console::OUTPUT_COLOR = QColor::fromRgbF( 0, 0, 1.0 );
+
+Console::Console( QWidget* parent ):
+ QTextEdit( parent ),
+ m_interpreter( new Interpreter )
+{
+ QFont font;
+ font.setFamily("Courier New");
+ setFont(font);
+ m_parseHelper.subscribe( this );
+ displayPrompt( );
+}
+
+Console::~Console( )
+{
+ delete m_interpreter;
+}
+
+void Console::keyPressEvent( QKeyEvent* e )
+{
+ switch ( e->key() )
+ {
+ case Qt::Key_Return:
+ handleReturnKeyPress( );
+ return;
+
+ case Qt::Key_Tab:
+ autocomplete( );
+ return;
+
+ case Qt::Key_Backspace:
+ if ( ! canBackspace( ) )
+ return;
+ break;
+
+ case Qt::Key_Up:
+ previousHistory( );
+ return;
+
+ case Qt::Key_Down:
+ nextHistory( );
+ return;
+
+ case Qt::Key_Left:
+ if ( ! canGoLeft( ) )
+ return;
+ }
+
+ QTextEdit::keyPressEvent( e );
+}
+
+void Console::handleReturnKeyPress( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ {
+ return;
+ }
+
+ QString line = getLine( );
+
+#ifndef NDEBUG
+ std::cout << line.toStdString( ) << "\n";
+#endif
+
+ m_parseHelper.process( line.toStdString( ) );
+ if ( m_parseHelper.buffered( ) )
+ {
+ append("");
+ displayPrompt( );
+ }
+ if ( line.size( ) )
+ {
+ m_historyBuffer.push_back( line.toStdString( ) );
+ m_historyIt = m_historyBuffer.end();
+ }
+ moveCursorToEnd( );
+}
+
+void Console::parseEvent( const ParseMessage& message )
+{
+ // handle invalid user input
+ if ( message.errorCode )
+ {
+ setTextColor( ERROR_COLOR );
+ append(message.message.c_str());
+
+ setTextColor( NORMAL_COLOR );
+ append("");
+ displayPrompt( );
+ return;
+ }
+
+ // interpret valid user input
+ int errorCode;
+ std::string res;
+ if ( message.message.size() )
+ res = m_interpreter->interpret( message.message, &errorCode );
+ if ( errorCode )
+ {
+ setTextColor( ERROR_COLOR );
+ }
+ else
+ {
+ setTextColor( OUTPUT_COLOR );
+ }
+
+ if ( res.size( ) )
+ {
+ append(res.c_str());
+ }
+
+ setTextColor( NORMAL_COLOR );
+
+ // set up the next line on the console
+ append("");
+ displayPrompt( );
+}
+
+QString Console::getLine( )
+{
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::StartOfLine );
+ cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, Console::PROMPT.size( ) );
+ cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
+ QString line = cursor.selectedText( );
+ cursor.clearSelection( );
+ return line;
+}
+
+bool Console::cursorIsOnInputLine( )
+{
+ int cursorBlock = textCursor( ).blockNumber( );
+ QTextCursor bottomCursor = textCursor( );
+ bottomCursor.movePosition( QTextCursor::End );
+ int bottomBlock = bottomCursor.blockNumber( );
+ return ( cursorBlock == bottomBlock );
+}
+
+bool Console::inputLineIsEmpty( )
+{
+ QTextCursor bottomCursor = textCursor( );
+ bottomCursor.movePosition( QTextCursor::End );
+ int col = bottomCursor.columnNumber( );
+ return ( col == Console::PROMPT.size( ) );
+}
+
+bool Console::canBackspace( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ {
+ return false;
+ }
+
+ if ( inputLineIsEmpty( ) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool Console::canGoLeft( )
+{
+ if ( cursorIsOnInputLine( ) )
+ {
+ QTextCursor bottomCursor = textCursor( );
+ int col = bottomCursor.columnNumber( );
+ return (col > Console::PROMPT.size( ));
+ }
+ return true;
+}
+
+void Console::displayPrompt( )
+{
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::End );
+ if ( m_parseHelper.buffered( ) )
+ {
+ cursor.insertText( Console::MULTILINE_PROMPT );
+ }
+ else
+ {
+ cursor.insertText( Console::PROMPT );
+ }
+ cursor.movePosition( QTextCursor::EndOfLine );
+}
+
+void Console::autocomplete( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ return;
+
+ QString line = getLine( );
+ const std::list<std::string>& suggestions =
+ m_interpreter->suggest( line.toStdString( ) );
+ if (suggestions.size() == 1)
+ {
+ line = suggestions.back().c_str();
+ }
+ else
+ {
+ // try to complete to longest common prefix
+ std::string prefix =
+ LongestCommonPrefix(suggestions.begin(), suggestions.end());
+ if (prefix.size() > line.size())
+ {
+ line = prefix.c_str();
+ }
+ else
+ {
+ ColumnFormatter fmt;
+ fmt.setItems(suggestions.begin(), suggestions.end());
+ fmt.format(width() / 10);
+ setTextColor( OUTPUT_COLOR );
+ const std::list<std::string>& formatted = fmt.formattedOutput();
+ for (std::list<std::string>::const_iterator it = formatted.begin();
+ it != formatted.end(); ++it)
+ {
+ append(it->c_str());
+ }
+ std::cout << width() << "\n";
+ setTextColor( NORMAL_COLOR );
+ }
+ }
+
+ // set up the next line on the console
+ append("");
+ displayPrompt( );
+ moveCursorToEnd( );
+ cursor.insertText( line );
+ moveCursorToEnd( );
+}
+
+void Console::previousHistory( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ return;
+
+ if ( ! m_historyBuffer.size( ) )
+ return;
+
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::StartOfLine );
+ cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, Console::PROMPT.size( ) );
+ cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
+ cursor.removeSelectedText( );
+ if ( m_historyIt != m_historyBuffer.begin( ) )
+ {
+ --m_historyIt;
+ }
+ cursor.insertText( m_historyIt->c_str() );
+}
+
+void Console::nextHistory( )
+{
+ if ( ! cursorIsOnInputLine( ) )
+ return;
+
+ if ( ! m_historyBuffer.size( ) )
+ return;
+ if ( m_historyIt == m_historyBuffer.end( ) )
+ {
+ return;
+ }
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::StartOfLine );
+ cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, Console::PROMPT.size( ) );
+ cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
+ cursor.removeSelectedText( );
+ ++m_historyIt;
+ if ( m_historyIt == m_historyBuffer.end( ) )
+ {
+ return;
+ }
+ cursor.insertText( m_historyIt->c_str() );
+}
+
+void Console::moveCursorToEnd( )
+{
+ QTextCursor cursor = textCursor();
+ cursor.movePosition( QTextCursor::End );
+ setTextCursor( cursor );
+}
diff --git a/3rdparty/python-console/Console.h b/3rdparty/python-console/Console.h
new file mode 100644
index 00000000..e4a0730c
--- /dev/null
+++ b/3rdparty/python-console/Console.h
@@ -0,0 +1,78 @@
+/**
+python-console
+Copyright (C) 2018 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef ATSUI_CONSOLE_H
+#define ATSUI_CONSOLE_H
+#include <QTextEdit>
+#include <QColor>
+#include "ParseHelper.h"
+#include "ParseListener.h"
+
+class QWidget;
+class QKeyEvent;
+class Interpreter;
+
+class Console : public QTextEdit, ParseListener
+{
+ Q_OBJECT
+
+public:
+ Console( QWidget* parent = 0 );
+ virtual ~Console( );
+
+protected:
+ // override QTextEdit
+ virtual void keyPressEvent( QKeyEvent* e );
+
+ virtual void handleReturnKeyPress( );
+
+ /**
+ Handle a compilable chunk of Python user input.
+ */
+ virtual void parseEvent( const ParseMessage& message );
+
+ QString getLine( );
+ bool cursorIsOnInputLine( );
+ bool inputLineIsEmpty( );
+ bool canBackspace( );
+ bool canGoLeft( );
+ void displayPrompt( );
+ void autocomplete( );
+ void previousHistory( );
+ void nextHistory( );
+ void moveCursorToEnd( );
+
+ static const QString PROMPT;
+ static const QString MULTILINE_PROMPT;
+
+ static const QColor NORMAL_COLOR;
+ static const QColor ERROR_COLOR;
+ static const QColor OUTPUT_COLOR;
+
+ Interpreter* m_interpreter;
+ ParseHelper m_parseHelper;
+ std::list<std::string> m_historyBuffer;
+ std::list<std::string>::const_iterator m_historyIt;
+};
+
+#endif // ATSUI_CONSOLE_H
diff --git a/3rdparty/python-console/Interpreter.cpp b/3rdparty/python-console/Interpreter.cpp
new file mode 100644
index 00000000..95615ed2
--- /dev/null
+++ b/3rdparty/python-console/Interpreter.cpp
@@ -0,0 +1,231 @@
+#include "Interpreter.h"
+#include <iostream>
+#include <map>
+#include <boost/format.hpp>
+
+PyThreadState* Interpreter::MainThreadState = NULL;
+
+Interpreter::Interpreter( )
+{
+ PyEval_AcquireLock( );
+ m_threadState = Py_NewInterpreter( );
+
+ PyObject *module = PyImport_ImportModule("__main__");
+ loc = glb = PyModule_GetDict(module);
+ SetupRedirector( m_threadState );
+ PyRun_SimpleString("import sys\n"
+ "import redirector\n"
+ "sys.path.insert(0, \".\")\n" // add current path
+ "sys.stdout = redirector.redirector()\n"
+ "sys.stderr = sys.stdout\n"
+ "import rlcompleter\n"
+ "sys.completer = rlcompleter.Completer()\n"
+ );
+
+ PyEval_ReleaseThread( m_threadState );
+}
+
+Interpreter::~Interpreter( )
+{
+#ifndef NDEBUG
+ std::cout << "delete interpreter\n";
+#endif
+ PyEval_AcquireThread( m_threadState );
+ Py_EndInterpreter( m_threadState );
+ PyEval_ReleaseLock( );
+}
+
+void
+Interpreter::test( )
+{
+ PyEval_AcquireThread( m_threadState );
+
+ PyObject* py_result;
+ PyObject* dum;
+ std::string command = "print 'Hello world'\n";
+ py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
+ if ( py_result == 0 )
+ {
+ std::cout << "Huh?\n";
+ PyEval_ReleaseThread( m_threadState );
+ return;
+ }
+ dum = PyEval_EvalCode ((PyCodeObject *)py_result, glb, loc);
+ Py_XDECREF (dum);
+ Py_XDECREF (py_result);
+
+ std::cout << GetResultString( m_threadState );
+ GetResultString( m_threadState ) = "";
+
+ PyEval_ReleaseThread( m_threadState );
+}
+
+std::string
+Interpreter::interpret( const std::string& command, int* errorCode )
+{
+ PyEval_AcquireThread( m_threadState );
+ *errorCode = 0;
+
+ PyObject* py_result;
+ PyObject* dum;
+ std::string res;
+#ifndef NDEBUG
+ std::cout << "interpreting (" << command << ")\n";
+#endif
+ py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
+ if ( py_result == 0 )
+ {
+#ifndef NDEBUG
+ std::cout << "Huh?\n";
+#endif
+ if ( PyErr_Occurred( ) )
+ {
+ *errorCode = 1;
+ PyErr_Print( );
+ res = GetResultString( m_threadState );
+ GetResultString( m_threadState ) = "";
+ }
+
+ PyEval_ReleaseThread( m_threadState );
+ return res;
+ }
+ dum = PyEval_EvalCode ((PyCodeObject *)py_result, glb, loc);
+ Py_XDECREF (dum);
+ Py_XDECREF (py_result);
+ if ( PyErr_Occurred( ) )
+ {
+ *errorCode = 1;
+ PyErr_Print( );
+ }
+
+ res = GetResultString( m_threadState );
+ GetResultString( m_threadState ) = "";
+
+ PyEval_ReleaseThread( m_threadState );
+ return res;
+}
+
+const std::list<std::string>& Interpreter::suggest( const std::string& hint )
+{
+ PyEval_AcquireThread( m_threadState );
+ m_suggestions.clear();
+ int i = 0;
+ std::string command = boost::str(
+ boost::format("sys.completer.complete('%1%', %2%)\n")
+ % hint
+ % i);
+#ifndef NDEBUG
+ std::cout << command << "\n";
+#endif
+ std::string res;
+ do
+ {
+ PyObject* py_result;
+ PyObject* dum;
+ py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
+ dum = PyEval_EvalCode ((PyCodeObject *)py_result, glb, loc);
+ Py_XDECREF (dum);
+ Py_XDECREF (py_result);
+ res = GetResultString( m_threadState );
+ GetResultString( m_threadState ) = "";
+ ++i;
+ command = boost::str(
+ boost::format("sys.completer.complete('%1%', %2%)\n")
+ % hint
+ % i);
+ if (res.size())
+ {
+ // throw away the newline
+ res = res.substr(1, res.size() - 3);
+ m_suggestions.push_back(res);
+ }
+ }
+ while (res.size());
+
+ PyEval_ReleaseThread( m_threadState );
+ return m_suggestions;
+}
+
+void
+Interpreter::Initialize( )
+{
+ Py_Initialize( );
+ PyEval_InitThreads( );
+ MainThreadState = PyEval_SaveThread( );
+}
+
+void
+Interpreter::Finalize( )
+{
+ PyEval_RestoreThread( MainThreadState );
+ Py_Finalize( );
+}
+
+std::string& Interpreter::GetResultString( PyThreadState* threadState )
+{
+ static std::map< PyThreadState*, std::string > ResultStrings;
+ if ( !ResultStrings.count( threadState ) )
+ {
+ ResultStrings[ threadState ] = "";
+ }
+ return ResultStrings[ threadState ];
+}
+
+PyObject* Interpreter::RedirectorInit(PyObject *, PyObject *)
+{
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject* Interpreter::RedirectorWrite(PyObject *, PyObject *args)
+{
+ char* output;
+ PyObject *selfi;
+
+ if (!PyArg_ParseTuple(args,"Os",&selfi,&output))
+ {
+ return NULL;
+ }
+
+ std::string outputString( output );
+ PyThreadState* currentThread = PyThreadState_Get( );
+ std::string& resultString = GetResultString( currentThread );
+ resultString = resultString + outputString;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyMethodDef Interpreter::ModuleMethods[] = { {NULL,NULL,0,NULL} };
+PyMethodDef Interpreter::RedirectorMethods[] =
+{
+ {"__init__", Interpreter::RedirectorInit, METH_VARARGS,
+ "initialize the stdout/err redirector"},
+ {"write", Interpreter::RedirectorWrite, METH_VARARGS,
+ "implement the write method to redirect stdout/err"},
+ {NULL,NULL,0,NULL},
+};
+
+void Interpreter::SetupRedirector( PyThreadState* threadState )
+{
+ PyMethodDef *def;
+
+ /* create a new module and class */
+ PyObject *module = Py_InitModule("redirector", ModuleMethods);
+ PyObject *moduleDict = PyModule_GetDict(module);
+ PyObject *classDict = PyDict_New();
+ PyObject *className = PyString_FromString("redirector");
+ PyObject *fooClass = PyClass_New(NULL, classDict, className);
+ PyDict_SetItemString(moduleDict, "redirector", fooClass);
+ Py_DECREF(classDict);
+ Py_DECREF(className);
+ Py_DECREF(fooClass);
+
+ /* add methods to class */
+ for (def = RedirectorMethods; def->ml_name != NULL; def++) {
+ PyObject *func = PyCFunction_New(def, NULL);
+ PyObject *method = PyMethod_New(func, NULL, fooClass);
+ PyDict_SetItemString(classDict, def->ml_name, method);
+ Py_DECREF(func);
+ Py_DECREF(method);
+ }
+}
diff --git a/3rdparty/python-console/Interpreter.h b/3rdparty/python-console/Interpreter.h
new file mode 100644
index 00000000..9e371179
--- /dev/null
+++ b/3rdparty/python-console/Interpreter.h
@@ -0,0 +1,97 @@
+/**
+python-console
+Copyright (C) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef INTERPRETER_H
+#define INTERPRETER_H
+#include <string>
+#include <list>
+#include <Python.h>
+#if defined(__APPLE__) && defined(__MACH__)
+/*
+ * The following undefs for C standard library macros prevent
+ * build errors of the following type on mac ox 10.7.4 and XCode 4.3.3
+ *
+/usr/include/c++/4.2.1/bits/localefwd.h:57:21: error: too many arguments provided to function-like macro invocation
+ isspace(_CharT, const locale&);
+ ^
+/usr/include/c++/4.2.1/bits/localefwd.h:56:5: error: 'inline' can only appear on functions
+ inline bool
+ ^
+/usr/include/c++/4.2.1/bits/localefwd.h:57:5: error: variable 'isspace' declared as a template
+ isspace(_CharT, const locale&);
+ ^
+*/
+#undef isspace
+#undef isupper
+#undef islower
+#undef isalpha
+#undef isalnum
+#undef toupper
+#undef tolower
+#endif
+
+/**
+Wraps a Python interpreter, which you can pass commands as strings to interpret
+and get strings of output/error in return.
+*/
+class Interpreter
+{
+protected:
+ static PyThreadState* MainThreadState;
+
+ PyThreadState* m_threadState;
+ PyObject* glb;
+ PyObject* loc;
+
+ std::list< std::string > m_suggestions;
+
+public:
+ /**
+ Instantiate a Python interpreter.
+ */
+ Interpreter( );
+ virtual ~Interpreter( );
+
+ void test( );
+ std::string interpret( const std::string& command, int* errorCode );
+ const std::list<std::string>& suggest( const std::string& hint );
+
+ /**
+ Call this before constructing and using Interpreter.
+ */
+ static void Initialize( );
+
+ /**
+ Call this when done using Interpreter.
+ */
+ static void Finalize( );
+
+protected:
+ static void SetupRedirector( PyThreadState* threadState );
+ static PyObject* RedirectorInit(PyObject *, PyObject *);
+ static PyObject* RedirectorWrite(PyObject *, PyObject *args);
+ static std::string& GetResultString( PyThreadState* threadState );
+ static PyMethodDef ModuleMethods[];
+ static PyMethodDef RedirectorMethods[];
+};
+#endif // INTERPRETER_H
diff --git a/3rdparty/python-console/LICENSE b/3rdparty/python-console/LICENSE
new file mode 100644
index 00000000..c53c2635
--- /dev/null
+++ b/3rdparty/python-console/LICENSE
@@ -0,0 +1,19 @@
+Copyright 2018 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/3rdparty/python-console/ParseHelper.BlockParseState.cpp b/3rdparty/python-console/ParseHelper.BlockParseState.cpp
new file mode 100644
index 00000000..4dc568b2
--- /dev/null
+++ b/3rdparty/python-console/ParseHelper.BlockParseState.cpp
@@ -0,0 +1,174 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "ParseHelper.h"
+
+ParseHelper::BlockParseState::
+BlockParseState( ParseHelper& parent ):
+ ParseState( parent )
+{ }
+
+ParseHelper::BlockParseState::
+BlockParseState( ParseHelper& parent, const std::string& indent_ ):
+ ParseState( parent ),
+ indent( indent_ )
+{ }
+
+bool ParseHelper::BlockParseState::
+process(const std::string& str)
+{
+ bool ok = initializeIndent(str);
+ if ( ! ok )
+ {
+ // finish processing
+ return true;
+ }
+
+ Indent ind;
+ bool isIndented = PeekIndent( str, &ind );
+ if ( isIndented )
+ {
+#ifndef NDEBUG
+ std::cout << "current line indent: ";
+ print( ind );
+#endif
+ // check if indent matches
+ if ( ind.Token != indent.Token )
+ {
+ // dedent until we match or empty the stack
+ bool found = false;
+ while ( !found )
+ {
+ parent.stateStack.pop_back( );
+ if ( !parent.stateStack.size( ) )
+ break;
+ boost::shared_ptr<BlockParseState> parseState =
+ boost::dynamic_pointer_cast<BlockParseState>(
+ parent.stateStack.back( ));
+ found = ( ind.Token == parseState->indent.Token );
+ }
+
+ if ( ! found )
+ {
+#ifndef NDEBUG
+ std::cout << "indent mismatch\n";
+#endif
+ parent.reset( );
+ ParseMessage msg( 1, "IndentationError: unexpected indent");
+ parent.broadcast( msg );
+ return true;
+ }
+ }
+
+ // process command
+
+ // enter indented block state
+ if ( str[str.size()-1] == ':' )
+ {
+ parent.commandBuffer.push_back( str );
+ //parent.inBlock = (boost::dynamic_pointer_cast<BlockParseState>(
+ // parent.stateStack.back()));
+
+ //expectingIndent = true;
+ boost::shared_ptr<ParseState> parseState(
+ new BlockParseState( parent ) );
+ parent.stateStack.push_back( parseState );
+ return true;
+ }
+
+ if ( str[str.size()-1] == '\\' )
+ {
+ parent.commandBuffer.push_back( str );
+ boost::shared_ptr<ParseState> parseState(
+ new ContinuationParseState( parent ) );
+ parent.stateStack.push_back( parseState );
+ return true;
+ }
+
+ if (BracketParseState::HasOpenBrackets( str ))
+ {
+ // FIXME: Every parse state should have its own local buffer
+ boost::shared_ptr<ParseState> parseState(
+ new BracketParseState( parent, str ) );
+ parent.stateStack.push_back( parseState );
+ return true;
+ }
+
+ parent.commandBuffer.push_back( str );
+ return true;
+ }
+ else
+ {
+ if ( str.size() )
+ {
+ {
+#ifndef NDEBUG
+ std::cout << "Expected indented block\n";
+#endif
+ parent.reset( );
+ ParseMessage msg( 1, "IndentationError: expected an indented block" );
+ parent.broadcast( msg );
+ return false;
+ }
+ }
+#ifndef NDEBUG
+ std::cout << "Leaving block\n";
+#endif
+ parent.stateStack.pop_back();
+ parent.flush( );
+ parent.reset( );
+ return true;
+ }
+}
+
+bool ParseHelper::BlockParseState::
+initializeIndent(const std::string& str)
+{
+ bool expectingIndent = (indent.Token == "");
+ if ( !expectingIndent )
+ {
+ std::cout << "already initialized indent: ";
+ print( indent );
+ return true;
+ }
+
+ Indent ind;
+ bool isIndented = parent.PeekIndent( str, &ind );
+ if ( !isIndented )
+ {
+#ifndef NDEBUG
+ std::cout << "Expected indented block\n";
+#endif
+ parent.reset( );
+ ParseMessage msg( 1, "IndentationError: expected an indented block" );
+ parent.broadcast( msg );
+ return false;
+ }
+ indent = ind;
+ //parent.currentIndent = ind;
+ //parent.indentStack.push_back( parent.currentIndent );
+ //parent.expectingIndent = false;
+#ifndef NDEBUG
+ std::cout << "initializing indent: ";
+ print( ind );
+#endif
+ return true;
+}
diff --git a/3rdparty/python-console/ParseHelper.BracketParseState.cpp b/3rdparty/python-console/ParseHelper.BracketParseState.cpp
new file mode 100644
index 00000000..c884c77d
--- /dev/null
+++ b/3rdparty/python-console/ParseHelper.BracketParseState.cpp
@@ -0,0 +1,127 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "ParseHelper.h"
+#include <string>
+#include <sstream>
+const std::string ParseHelper::BracketParseState::OpeningBrackets = "[({";
+const std::string ParseHelper::BracketParseState::ClosingBrackets = "])}";
+
+bool ParseHelper::BracketParseState::HasOpenBrackets(const std::string& str)
+{
+ std::list<char> brackets;
+ bool hasOpenBrackets = LoadBrackets(str, &brackets);
+ return hasOpenBrackets;
+}
+
+bool ParseHelper::BracketParseState::LoadBrackets(const std::string& str,
+ std::list<char>* stack)
+{
+ if ( !stack )
+ return false;
+
+ stack->clear();
+ for (int i = 0; i < str.size(); ++i)
+ {
+ if (OpeningBrackets.find_first_of(str[i]) != std::string::npos)
+ {
+ stack->push_back(str[i]);
+ }
+ else
+ {
+ size_t t = ClosingBrackets.find_first_of(str[i]);
+ if (t != std::string::npos)
+ {
+ if (t != OpeningBrackets.find_first_of(stack->back()))
+ return false;
+ stack->pop_back();
+ }
+ }
+ }
+ return stack->size();
+}
+
+ParseHelper::BracketParseState::BracketParseState( ParseHelper& parent, const std::string& firstLine ):
+ ParseState( parent )
+{
+ bool hasOpenBrackets = LoadBrackets( firstLine, &brackets );
+ assert( hasOpenBrackets );
+ m_buffer.push_back( firstLine );
+}
+
+bool ParseHelper::BracketParseState::process(const std::string& str)
+{
+#ifndef NDEBUG
+ std::cout << "(BracketParseState) processing " << str << "\n";
+#endif
+ // update brackets stack
+ for (int i = 0; i < str.size(); ++i)
+ {
+ if (OpeningBrackets.find_first_of(str[i]) != std::string::npos)
+ {
+#ifndef NDEBUG
+ std::cout << "push " << str[i] << "\n";
+#endif
+ brackets.push_back(str[i]);
+ }
+ else
+ {
+ size_t t = ClosingBrackets.find_first_of(str[i]);
+ if (t != std::string::npos)
+ {
+#ifndef NDEBUG
+ std::cout << "pop " << str[i] << "\n";
+#endif
+ // reset state if unmatched brackets seen
+ if (t != OpeningBrackets.find_first_of(brackets.back()))
+ {
+ parent.reset( );
+ ParseMessage msg(1, "Invalid syntax");
+ parent.broadcast( msg );
+ return true;
+ }
+ brackets.pop_back();
+ }
+ }
+ }
+
+ // if we've cleared the stack, we've finished accepting input here
+ if (!brackets.size())
+ {
+ // squash buffered lines and place it on parent::commandBuffer
+ std::stringstream ss;
+ for (std::list<std::string>::const_iterator it = m_buffer.begin();
+ it != m_buffer.end(); ++it )
+ {
+ ss << *it << "\n";
+ }
+ ss << str;
+ parent.commandBuffer.push_back(ss.str());
+ parent.stateStack.pop_back( );
+ return false;
+ }
+ else
+ {
+ // buffer and expect more lines
+ m_buffer.push_back( str );
+ return true;
+ }
+}
diff --git a/3rdparty/python-console/ParseHelper.ContinuationParseState.cpp b/3rdparty/python-console/ParseHelper.ContinuationParseState.cpp
new file mode 100644
index 00000000..6f84d291
--- /dev/null
+++ b/3rdparty/python-console/ParseHelper.ContinuationParseState.cpp
@@ -0,0 +1,61 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "ParseHelper.h"
+#include <list>
+#include <sstream>
+
+ParseHelper::ContinuationParseState::
+ContinuationParseState( ParseHelper& parent_ ):
+ ParseState( parent_ )
+{ }
+
+bool ParseHelper::ContinuationParseState::
+process( const std::string& str )
+{
+ if ( str.size() && str[str.size()-1] == '\\' )
+ {
+ parent.commandBuffer.push_back( str );
+ return true;
+ }
+ else
+ {
+ std::list<std::string> tmp_list;
+ tmp_list.push_back(str);
+ while (parent.commandBuffer.size() && parent.commandBuffer.back()[
+ parent.commandBuffer.back().size()-1] == '\\')
+ {
+ tmp_list.push_back(parent.commandBuffer.back());
+ parent.commandBuffer.pop_back();
+ }
+ std::stringstream ss;
+ while (tmp_list.size())
+ {
+ ss << tmp_list.back();
+ tmp_list.pop_back();
+ if (tmp_list.size())
+ ss << "\n";
+ }
+ parent.commandBuffer.push_back(ss.str());
+ parent.stateStack.pop_back();
+ return false;
+ }
+}
diff --git a/3rdparty/python-console/ParseHelper.cpp b/3rdparty/python-console/ParseHelper.cpp
new file mode 100644
index 00000000..8944a84e
--- /dev/null
+++ b/3rdparty/python-console/ParseHelper.cpp
@@ -0,0 +1,225 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "ParseHelper.h"
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <cstdlib>
+#include "ParseListener.h"
+
+#ifndef NDEBUG
+void print(const ParseHelper::Indent& indent)
+{
+ std::string str = indent.Token;
+ for (int i = 0; i < str.size(); ++i)
+ {
+ switch (str.at(i))
+ {
+ case ' ':
+ str[i] = 's';
+ break;
+ case '\t':
+ str[i] = 't';
+ break;
+ }
+ }
+ std::cout << str << "\n";
+}
+#endif
+
+ParseHelper::Indent::
+Indent( )
+{ }
+
+ParseHelper::Indent::
+Indent( const std::string& indent ):
+ Token( indent )
+{ }
+
+ParseHelper::ParseState::
+ParseState( ParseHelper& parent_ ): parent( parent_ )
+{ }
+
+ParseHelper::ParseState::
+~ParseState( )
+{ }
+
+bool ParseHelper::PeekIndent( const std::string& str, Indent* indent )
+{
+ if ( !str.size() || ! isspace(str[0]) )
+ return false;
+
+ int nonwhitespaceIndex = -1;
+ for (int i = 0; i < str.size(); ++i)
+ {
+ if (!isspace(str[i]))
+ {
+ nonwhitespaceIndex = i;
+ break;
+ }
+ }
+ if (nonwhitespaceIndex == -1)
+ {
+ return false;
+ }
+ std::string indentToken = str.substr(0, nonwhitespaceIndex);
+ indent->Token = indentToken;
+ return true;
+}
+
+ParseHelper::ParseHelper( )
+{ }
+
+void ParseHelper::process( const std::string& str )
+{
+#ifndef NDEBUG
+ std::cout << "processing: (" << str << ")\n";
+#endif
+
+ std::string top;
+ commandBuffer.push_back(str);
+ //std::string top = commandBuffer.back();
+ //commandBuffer.pop_back();
+ boost::shared_ptr<ParseState> blockStatePtr;
+ while (stateStack.size())
+ {
+ top = commandBuffer.back();
+ commandBuffer.pop_back();
+ blockStatePtr = stateStack.back();
+ if (blockStatePtr->process(top))
+ return;
+ }
+
+ if ( ! commandBuffer.size() )
+ return;
+
+ // standard state
+ top = commandBuffer.back();
+ if ( !top.size() )
+ {
+ reset( );
+ broadcast( std::string() );
+ return;
+ }
+#ifndef NDEBUG
+ std::cout << "now processing: (" << top << ")\n";
+#endif
+
+ { // check for unexpected indent
+ Indent ind;
+ bool isIndented = PeekIndent( top, &ind );
+ if ( isIndented &&
+ ! isInContinuation( ) )
+ {
+ reset( );
+ ParseMessage msg( 1, "IndentationError: unexpected indent");
+ broadcast( msg );
+ return;
+ }
+ }
+
+ // enter indented block state
+ if ( top[top.size()-1] == ':' )
+ {
+ boost::shared_ptr<ParseState> parseState(
+ new BlockParseState( *this ) );
+ stateStack.push_back( parseState );
+ return;
+ }
+
+ if ( top[top.size()-1] == '\\' )
+ {
+ boost::shared_ptr<ParseState> parseState(
+ new ContinuationParseState( *this ) );
+ stateStack.push_back( parseState );
+ return;
+ }
+
+ if (BracketParseState::HasOpenBrackets( top ))
+ {
+ // FIXME: Every parse state should have its own local buffer
+ commandBuffer.pop_back( );
+ boost::shared_ptr<ParseState> parseState(
+ new BracketParseState( *this, top ) );
+ stateStack.push_back( parseState );
+ return;
+ }
+
+ // handle single-line statement
+ flush( );
+}
+
+bool ParseHelper::buffered( ) const
+{
+ return commandBuffer.size( ) || stateStack.size( );
+}
+
+void ParseHelper::flush( )
+{
+ std::stringstream ss;
+ for (int i = 0; i < commandBuffer.size(); ++i )
+ {
+ ss << commandBuffer[i] << "\n";
+ }
+ commandBuffer.clear();
+
+ broadcast( ss.str() );
+ // TODO: feed string to interpreter
+}
+
+void ParseHelper::reset( )
+{
+// inContinuation = false;
+ stateStack.clear( );
+ commandBuffer.clear( );
+}
+
+bool ParseHelper::isInContinuation( ) const
+{
+ return (stateStack.size()
+ && (boost::dynamic_pointer_cast<ContinuationParseState>(
+ stateStack.back())));
+}
+
+void ParseHelper::subscribe( ParseListener* listener )
+{
+ listeners.push_back( listener );
+}
+
+void ParseHelper::unsubscribeAll( )
+{
+ listeners.clear( );
+}
+
+void ParseHelper::broadcast( const ParseMessage& msg )
+{
+ // broadcast signal
+ for (int i = 0; i < listeners.size(); ++i)
+ {
+ if (listeners[i])
+ {
+ listeners[i]->parseEvent(msg);
+ }
+ }
+}
diff --git a/3rdparty/python-console/ParseHelper.h b/3rdparty/python-console/ParseHelper.h
new file mode 100644
index 00000000..8824fc39
--- /dev/null
+++ b/3rdparty/python-console/ParseHelper.h
@@ -0,0 +1,169 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef PARSE_HELPER_H
+#define PARSE_HELPER_H
+#include <string>
+#include <vector>
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include "ParseMessage.h"
+
+class ParseListener;
+
+/**
+Helps chunk lines of Python code into compilable statements.
+*/
+class ParseHelper
+{
+public:
+ struct Indent
+ {
+ std::string Token;
+ Indent( );
+ Indent( const std::string& indent );
+ };
+
+ /**
+ Handle different states of parsing. Subclasses override
+ process(const std::string&) to handle different types of multiline
+ statements.
+ */
+ struct ParseState
+ {
+ ParseHelper& parent;
+ ParseState( ParseHelper& parent_ );
+ virtual ~ParseState( );
+
+ /**
+ Processes a single line of user input.
+
+ Subclasses should return false if further processing should be done. In
+ this case, the command buffer should be topped by the next statement to
+ parse.
+
+ \return whether processing of the line is done.
+ */
+ virtual bool process(const std::string& str) = 0;
+ };
+
+ struct ContinuationParseState : public ParseState
+ {
+ using ParseState::parent;
+ ContinuationParseState( ParseHelper& parent_ );
+ virtual bool process( const std::string& str);
+ };
+
+ /**
+ Handle parsing a multiline indented block. Example of such a block:
+
+ for i in range(10):
+ print i
+ print i*i
+
+ */
+ struct BlockParseState : public ParseState
+ {
+ Indent indent;
+
+ BlockParseState( ParseHelper& parent );
+ BlockParseState( ParseHelper& parent, const std::string& indent_ );
+
+ // return whether processing is finished
+ virtual bool process(const std::string& str);
+
+ // return if there was an error
+ bool initializeIndent(const std::string& str);
+ };
+ friend class BlockParseState;
+
+ struct BracketParseState : public ParseState
+ {
+ static const std::string OpeningBrackets;
+ static const std::string ClosingBrackets;
+
+ std::list<char> brackets;
+ std::list<std::string> m_buffer;
+
+ /**
+ Return whether open brackets remain unclosed in \a str.
+ */
+ static bool HasOpenBrackets(const std::string& str);
+ static bool LoadBrackets(const std::string& str,
+ std::list<char>* stack);
+
+ BracketParseState( ParseHelper& parent, const std::string& firstLine );
+
+ virtual bool process(const std::string& str);
+ };
+
+protected:
+ // TODO: Create a ContinuationParseState to handle this
+ bool inContinuation;
+ std::vector< ParseListener* > listeners;
+ std::vector< boost::shared_ptr< ParseState > > stateStack;
+ std::vector< std::string > commandBuffer;
+
+public:
+ static bool PeekIndent( const std::string& str, Indent* indent );
+
+public:
+ ParseHelper( );
+
+public:
+ void process( const std::string& str );
+
+ bool buffered( ) const;
+
+ /**
+ Generate a parse event from the current command buffer.
+ */
+ void flush( );
+
+ /**
+ Reset the state of the helper.
+ */
+ void reset( );
+
+ bool isInContinuation( ) const;
+
+ void subscribe( ParseListener* listener );
+ void unsubscribeAll( );
+ void broadcast( const ParseMessage& msg );
+
+}; // class ParseHelper
+
+inline bool operator== ( const ParseHelper::Indent& a, const ParseHelper::Indent& b )
+{
+ return a.Token == b.Token;
+}
+
+inline bool operator!= ( const ParseHelper::Indent& a, const ParseHelper::Indent& b )
+{
+ return a.Token != b.Token;
+}
+
+#ifndef NDEBUG
+void print(const ParseHelper::Indent& indent);
+#endif
+
+#endif // PARSE_HELPER_H
diff --git a/3rdparty/python-console/ParseListener.cpp b/3rdparty/python-console/ParseListener.cpp
new file mode 100644
index 00000000..36aaad22
--- /dev/null
+++ b/3rdparty/python-console/ParseListener.cpp
@@ -0,0 +1,29 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "ParseListener.h"
+#include <iostream>
+
+void EchoListener::parseEvent( const ParseMessage& msg )
+{
+ std::cout << "echo(" << msg.errorCode << "): " << msg.message << "\n";
+}
+
diff --git a/3rdparty/python-console/ParseListener.h b/3rdparty/python-console/ParseListener.h
new file mode 100644
index 00000000..28c501c4
--- /dev/null
+++ b/3rdparty/python-console/ParseListener.h
@@ -0,0 +1,42 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#ifndef PARSE_LISTENER_H
+#define PARSE_LISTENER_H
+#include "ParseMessage.h"
+
+/**
+Interface to implement to receive parse events.
+*/
+struct ParseListener
+{
+ virtual void parseEvent( const ParseMessage& msg ) = 0;
+};
+
+/**
+Sample implementation of ParseListener that echoes messages.
+*/
+struct EchoListener : public ParseListener
+{
+ virtual void parseEvent( const ParseMessage& msg );
+};
+
+#endif // PARSE_LISTENER_H
diff --git a/3rdparty/python-console/ParseMessage.cpp b/3rdparty/python-console/ParseMessage.cpp
new file mode 100644
index 00000000..29fdbae0
--- /dev/null
+++ b/3rdparty/python-console/ParseMessage.cpp
@@ -0,0 +1,31 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "ParseMessage.h"
+
+ParseMessage::ParseMessage( const std::string& msg ):
+ errorCode( 0 ), message( msg )
+{ }
+
+ParseMessage::ParseMessage( int code, const std::string& msg ):
+ errorCode( code ), message( msg )
+{ }
+
diff --git a/3rdparty/python-console/ParseMessage.h b/3rdparty/python-console/ParseMessage.h
new file mode 100644
index 00000000..9c77519e
--- /dev/null
+++ b/3rdparty/python-console/ParseMessage.h
@@ -0,0 +1,38 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#ifndef PARSE_MESSAGE_H
+#define PARSE_MESSAGE_H
+#include <string>
+
+/**
+Basically a string + error code pair. Generated from ParseHelper so error-free
+codeblocks can be sent to the Interpreter.
+*/
+struct ParseMessage
+{
+ int errorCode;
+ std::string message;
+
+ ParseMessage( const std::string& msg );
+ ParseMessage( int code, const std::string& msg );
+};
+#endif //PARSE_MESSAGE_H
diff --git a/3rdparty/python-console/README.md b/3rdparty/python-console/README.md
new file mode 100644
index 00000000..a80bffe4
--- /dev/null
+++ b/3rdparty/python-console/README.md
@@ -0,0 +1,17 @@
+python-console
+=====
+This is the result of a small side project to write a Qt widget that
+encapsulates an interactive Python shell.
+
+Quickstart
+-----
+You should have Qt4 and Python libraries. You will need CMake to build this
+project as follows:
+
+1. mkdir build
+2. cmake ..
+3. make
+
+License
+-----
+This project is licensed under the [MIT](http://opensource.org/licenses/MIT) license.
diff --git a/3rdparty/python-console/Utils.h b/3rdparty/python-console/Utils.h
new file mode 100644
index 00000000..ebd4234f
--- /dev/null
+++ b/3rdparty/python-console/Utils.h
@@ -0,0 +1,33 @@
+#ifndef PYTHON_CONSOLE_UTILS_H
+#define PYTHON_CONSOLE_UTILS_H
+#include <string>
+#include <vector>
+
+/**
+InputIterator has value type of std::string.
+*/
+template < class InputIterator >
+std::string LongestCommonPrefix( InputIterator begin, InputIterator end )
+{
+ if ( begin == end )
+ return "";
+
+ const std::string& str0 = *begin;
+ if ( ! str0.size() )
+ return "";
+
+ int endIndex = str0.size() - 1;
+ InputIterator it = begin; ++it;
+ for (; it != end; ++it)
+ {
+ const std::string& str = *it;
+ for (int j = 0; j <= endIndex; ++j)
+ {
+ if (j >= str.size() || str[j] != str0[j])
+ endIndex = j - 1;
+ }
+ }
+ return (endIndex > 0)? str0.substr(0, endIndex + 1) : "";
+}
+
+#endif // PYTHON_CONSOLE_UTILS_H
diff --git a/3rdparty/python-console/data/test.py b/3rdparty/python-console/data/test.py
new file mode 100644
index 00000000..18390c0d
--- /dev/null
+++ b/3rdparty/python-console/data/test.py
@@ -0,0 +1,2 @@
+for i in range(1, 10):
+ print i
diff --git a/3rdparty/python-console/data/test2.py b/3rdparty/python-console/data/test2.py
new file mode 100644
index 00000000..9e9725e7
--- /dev/null
+++ b/3rdparty/python-console/data/test2.py
@@ -0,0 +1,6 @@
+for i in range(1, 10):
+ print i
+
+for i in range(1, 10):
+ print i
+ print i
diff --git a/3rdparty/python-console/data/test3.py b/3rdparty/python-console/data/test3.py
new file mode 100644
index 00000000..19ad5eb5
--- /dev/null
+++ b/3rdparty/python-console/data/test3.py
@@ -0,0 +1,4 @@
+for i in range(1, 10):
+ print i
+for i in range(1, 10):
+ print i
diff --git a/3rdparty/python-console/data/test4.py b/3rdparty/python-console/data/test4.py
new file mode 100644
index 00000000..11b64f63
--- /dev/null
+++ b/3rdparty/python-console/data/test4.py
@@ -0,0 +1,4 @@
+for i in range(2):
+ for j in range(3):
+ print j
+ print i
diff --git a/3rdparty/python-console/data/test5.py b/3rdparty/python-console/data/test5.py
new file mode 100644
index 00000000..1f80c998
--- /dev/null
+++ b/3rdparty/python-console/data/test5.py
@@ -0,0 +1,3 @@
+for i in range(2):
+ for j in range(3):
+ print i, j
diff --git a/3rdparty/python-console/test_cli.cpp b/3rdparty/python-console/test_cli.cpp
new file mode 100644
index 00000000..76d0f251
--- /dev/null
+++ b/3rdparty/python-console/test_cli.cpp
@@ -0,0 +1,63 @@
+#include <iostream>
+#include <string>
+#include "ParseHelper.h"
+#include "ParseListener.h"
+#include "Interpreter.h"
+
+const std::string STD_PROMPT = ">>> ";
+const std::string MULTILINE_PROMPT = "... ";
+
+struct InterpreterRelay : public ParseListener
+{
+ Interpreter* m_interpreter;
+
+ InterpreterRelay( ):
+ m_interpreter( new Interpreter )
+ { }
+
+ virtual void parseEvent( const ParseMessage& msg )
+ {
+ if ( msg.errorCode )
+ {
+ std::cout << "(" << msg.errorCode << ") " << msg.message << "\n";
+ return;
+ }
+ else
+ {
+ int err;
+ std::string res = m_interpreter->interpret( msg.message, &err );
+ std::cout << "(" << msg.errorCode << ") " << res << "\n";
+ }
+ }
+};
+
+int main( int argc, char *argv[] )
+{
+ Interpreter::Initialize( );
+ const std::string* prompt = &STD_PROMPT;
+ ParseHelper helper;
+ ParseListener* listener = new InterpreterRelay;
+ helper.subscribe( listener );
+
+ std::string str;
+ std::cout << *prompt;
+ std::getline( std::cin, str );
+ while ( str != "quit" )
+ {
+ std::cout << str << "\n";
+ helper.process( str );
+ if ( helper.buffered( ) )
+ {
+ prompt = &MULTILINE_PROMPT;
+ }
+ else
+ {
+ prompt = &STD_PROMPT;
+ }
+ std::cout << *prompt;
+ std::getline( std::cin, str );
+ }
+
+ Interpreter::Finalize( );
+ return 0;
+}
diff --git a/3rdparty/python-console/test_console.cpp b/3rdparty/python-console/test_console.cpp
new file mode 100644
index 00000000..d025f26b
--- /dev/null
+++ b/3rdparty/python-console/test_console.cpp
@@ -0,0 +1,36 @@
+#include <iostream>
+#include <QApplication>
+#include <QMainWindow>
+#include <QWidget>
+#include <QGridLayout>
+#include <QTextEdit>
+
+#include "Console.h"
+#include "Interpreter.h"
+
+Console* console;
+
+void SetupWindow( int argc, char *argv[] )
+{
+ QMainWindow* window = new QMainWindow;
+ window->resize( 800, 600 );
+ QWidget* centralWidget = new QWidget(window);
+ QGridLayout* layout = new QGridLayout(centralWidget);
+ console = new Console;
+ layout->addWidget(console, 0, 0, 1, 1);
+ window->setCentralWidget(centralWidget);
+ window->show( );
+}
+
+int main( int argc, char *argv[] )
+{
+ QApplication app( argc, argv );
+ Interpreter::Initialize( );
+
+ SetupWindow( argc, argv );
+
+ bool res = app.exec( );
+ delete console;
+ Interpreter::Finalize( );
+ return res;
+}
diff --git a/3rdparty/python-console/test_parse_helper.cpp b/3rdparty/python-console/test_parse_helper.cpp
new file mode 100644
index 00000000..d530a5f1
--- /dev/null
+++ b/3rdparty/python-console/test_parse_helper.cpp
@@ -0,0 +1,51 @@
+/**
+Copyright (c) 2014 Alex Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include <iostream>
+#include <fstream>
+#include "ParseHelper.h"
+#include "ParseListener.h"
+
+/**
+Demonstrates the ParseHelper.
+
+Call it with some of the python files in data/.
+*/
+int main( int argc, char *argv[] )
+{
+ if ( argc < 2 )
+ {
+ std::cout << "Usage: " << argv[0] << " file\n";
+ return 1;
+ }
+ ParseHelper helper;
+ helper.subscribe( new EchoListener );
+
+ std::ifstream ifs( argv[1] );
+ while ( ! ifs.eof( ) )
+ {
+ std::string str;
+ std::getline( ifs, str );
+ helper.process( str );
+ }
+
+ return 0;
+}
diff --git a/3rdparty/python-console/test_python_interpreter.cpp b/3rdparty/python-console/test_python_interpreter.cpp
new file mode 100644
index 00000000..69e16ed1
--- /dev/null
+++ b/3rdparty/python-console/test_python_interpreter.cpp
@@ -0,0 +1,22 @@
+#include <iostream>
+#include "Interpreter.h"
+
+int main( int argc, char *argv[] )
+{
+ std::string commands[] = {
+ "from time import time,ctime\n",
+ "print 'Today is',ctime(time())\n"
+ };
+ Interpreter::Initialize( );
+ Interpreter* interpreter = new Interpreter;
+ for ( int i = 0; i < 2; ++i )
+ {
+ int err;
+ std::string res = interpreter->interpret( commands[i], &err );
+ std::cout << res;
+ }
+ delete interpreter;
+
+ Interpreter::Finalize( );
+ return 0;
+}