├── CMakeLists.txt ├── ColumnFormatter.cpp ├── ColumnFormatter.h ├── Console.cpp ├── Console.h ├── Interpreter.cpp ├── Interpreter.h ├── LICENSE ├── ParseHelper.BlockParseState.cpp ├── ParseHelper.BracketParseState.cpp ├── ParseHelper.ContinuationParseState.cpp ├── ParseHelper.cpp ├── ParseHelper.h ├── ParseListener.cpp ├── ParseListener.h ├── ParseMessage.cpp ├── ParseMessage.h ├── README.md ├── Utils.h ├── data ├── test.py ├── test2.py ├── test3.py ├── test4.py └── test5.py ├── test_cli.cpp ├── test_console.cpp ├── test_parse_helper.cpp └── test_python_interpreter.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required( VERSION 2.8 ) 2 | project( PythonInterpreter ) 3 | 4 | find_package( Qt4 REQUIRED ) 5 | include( ${QT_USE_FILE} ) 6 | find_package( PythonLibs REQUIRED ) 7 | include_directories( ${PYTHON_INCLUDE_DIRS} ) 8 | 9 | add_executable( test_python_interpreter test_python_interpreter.cpp Interpreter.cpp ) 10 | target_link_libraries( test_python_interpreter ${PYTHON_LIBRARIES} ) 11 | 12 | qt4_wrap_cpp( Console_MOC Console.h ) 13 | add_executable( test_console test_console.cpp 14 | Console.cpp ${Console_MOC} 15 | ColumnFormatter.cpp 16 | Interpreter.cpp 17 | ParseHelper.cpp 18 | ParseHelper.BlockParseState.cpp 19 | ParseHelper.BracketParseState.cpp 20 | ParseHelper.ContinuationParseState.cpp 21 | ParseMessage.cpp 22 | ) 23 | target_link_libraries( test_console ${QT_LIBRARIES} ${PYTHON_LIBRARIES} ) 24 | 25 | add_executable( test_parse_helper test_parse_helper.cpp 26 | ParseHelper.cpp 27 | ParseHelper.BlockParseState.cpp 28 | ParseHelper.ContinuationParseState.cpp 29 | ParseListener.cpp 30 | ParseMessage.cpp 31 | ) 32 | 33 | add_executable( test_cli test_cli.cpp 34 | ParseHelper.cpp 35 | ParseHelper.BlockParseState.cpp 36 | ParseHelper.ContinuationParseState.cpp 37 | ParseListener.cpp 38 | ParseMessage.cpp 39 | Interpreter.cpp 40 | ) 41 | target_link_libraries( test_cli 42 | ${PYTHON_LIBRARIES} 43 | ) 44 | -------------------------------------------------------------------------------- /ColumnFormatter.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include "ColumnFormatter.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | bool ColumnFormatter::load(const std::string& fn) 33 | { 34 | items.clear(); 35 | std::ifstream ifs(fn.c_str()); 36 | if (!ifs.is_open()) 37 | return false; 38 | std::string str; 39 | while (!ifs.eof()) 40 | { 41 | std::getline( ifs, str ); 42 | if (!ifs.eof()) 43 | items.push_back(str); 44 | } 45 | return true; 46 | } 47 | 48 | int ColumnFormatter::solve(int width) 49 | { 50 | bool fits = true; 51 | int i = 1; 52 | while (fits) 53 | { 54 | ++i; 55 | std::vector widths = divideItems(i); 56 | int columnWidth = width / i; 57 | for (int j = 0; j < widths.size(); ++j) 58 | { 59 | fits &= (widths[j] < columnWidth); 60 | } 61 | if (!fits) 62 | { 63 | --i; 64 | } 65 | } 66 | return i; 67 | } 68 | 69 | std::vector ColumnFormatter::divideItems(int numColumns) 70 | { 71 | columns.clear(); 72 | for (int i = 0; i < numColumns; ++i) 73 | columns.push_back(std::list()); 74 | for (int i = 0; i < items.size(); ++i) 75 | { 76 | columns[i % numColumns].push_back(items[i]); 77 | } 78 | 79 | // count the fattest item in each column 80 | std::vector res(numColumns); 81 | for (int i = 0; i < numColumns; ++i) 82 | { 83 | for (std::list::const_iterator it = 84 | columns[i].begin(); it != columns[i].end(); ++it) 85 | { 86 | if (res[i] < it->size()) 87 | res[i] = it->size(); 88 | } 89 | } 90 | 91 | return res; 92 | } 93 | 94 | void ColumnFormatter::format(int width) 95 | { 96 | m_formattedOutput.clear(); 97 | int cols = solve(width); 98 | std::vector colWidths(cols, width / cols); 99 | int rem = width % cols; 100 | for (int i = 0; i < rem; ++i) 101 | { 102 | colWidths[i]++; 103 | } 104 | divideItems(cols); 105 | std::vector< std::list::const_iterator > its; 106 | std::vector< std::list::const_iterator > it_ends; 107 | for (int i = 0; i < columns.size(); ++i) 108 | { 109 | its.push_back(columns[i].begin()); 110 | it_ends.push_back(columns[i].end()); 111 | } 112 | bool done = false; 113 | while (!done) 114 | { 115 | std::stringstream row_ss; 116 | for (int i = 0; i < columns.size(); ++i) 117 | { 118 | std::stringstream item_ss; 119 | std::string item; 120 | if (its[i] != it_ends[i]) 121 | { 122 | item = *its[i]; 123 | ++its[i]; 124 | } 125 | item_ss << std::left << std::setw(colWidths[i]) << item; 126 | row_ss << item_ss.str(); 127 | } 128 | m_formattedOutput.push_back(row_ss.str()); 129 | 130 | done = true; 131 | for (int i = 0; i < columns.size(); ++i) 132 | { 133 | done &= (its[i] == it_ends[i]); 134 | } 135 | } 136 | } 137 | 138 | const std::list& ColumnFormatter::formattedOutput() const 139 | { 140 | return m_formattedOutput; 141 | } 142 | -------------------------------------------------------------------------------- /ColumnFormatter.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #ifndef COLUMN_FORMATTER_H 23 | #define COLUMN_FORMATTER_H 24 | #include 25 | #include 26 | #include 27 | 28 | /** 29 | Format a list of items into as many columns as width permits. 30 | */ 31 | class ColumnFormatter 32 | { 33 | protected: 34 | std::vector items; 35 | std::vector< std::list > columns; 36 | std::list< std::string > m_formattedOutput; 37 | 38 | public: 39 | /** 40 | Load items from file, one item per line. 41 | */ 42 | bool load(const std::string& fn); 43 | 44 | template 45 | void setItems(InputIterator begin, InputIterator end) 46 | { 47 | items.clear(); 48 | for (InputIterator it = begin; it != end; ++it) 49 | { 50 | items.push_back(*it); 51 | } 52 | } 53 | 54 | /** 55 | Determine the number of columns that the items can be into, given a 56 | width limit. 57 | */ 58 | int solve(int width); 59 | 60 | /** 61 | Divide items into numColumns. 62 | */ 63 | std::vector divideItems(int numColumns); 64 | 65 | /** 66 | Generate formatted output, the items formatted into as many columns as can 67 | be fit in width. 68 | */ 69 | void format(int width); 70 | 71 | /** 72 | Get output. 73 | */ 74 | const std::list& formattedOutput() const; 75 | }; 76 | 77 | #endif // COLUMN_FORMATTER_H 78 | -------------------------------------------------------------------------------- /Console.cpp: -------------------------------------------------------------------------------- 1 | #include "Console.h" 2 | #include "Interpreter.h" 3 | #include "ColumnFormatter.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Utils.h" 10 | 11 | const QString Console::PROMPT = ">>> "; 12 | const QString Console::MULTILINE_PROMPT = "... "; 13 | const QColor Console::NORMAL_COLOR = QColor::fromRgbF( 0, 0, 0 ); 14 | const QColor Console::ERROR_COLOR = QColor::fromRgbF( 1.0, 0, 0 ); 15 | const QColor Console::OUTPUT_COLOR = QColor::fromRgbF( 0, 0, 1.0 ); 16 | 17 | Console::Console( QWidget* parent ): 18 | QTextEdit( parent ), 19 | m_interpreter( new Interpreter ) 20 | { 21 | QFont font; 22 | font.setFamily("Courier New"); 23 | setFont(font); 24 | m_parseHelper.subscribe( this ); 25 | displayPrompt( ); 26 | } 27 | 28 | Console::~Console( ) 29 | { 30 | delete m_interpreter; 31 | } 32 | 33 | void Console::keyPressEvent( QKeyEvent* e ) 34 | { 35 | switch ( e->key() ) 36 | { 37 | case Qt::Key_Return: 38 | handleReturnKeyPress( ); 39 | return; 40 | 41 | case Qt::Key_Tab: 42 | autocomplete( ); 43 | return; 44 | 45 | case Qt::Key_Backspace: 46 | if ( ! canBackspace( ) ) 47 | return; 48 | break; 49 | 50 | case Qt::Key_Up: 51 | previousHistory( ); 52 | return; 53 | 54 | case Qt::Key_Down: 55 | nextHistory( ); 56 | return; 57 | 58 | case Qt::Key_Left: 59 | if ( ! canGoLeft( ) ) 60 | return; 61 | } 62 | 63 | QTextEdit::keyPressEvent( e ); 64 | } 65 | 66 | void Console::handleReturnKeyPress( ) 67 | { 68 | if ( ! cursorIsOnInputLine( ) ) 69 | { 70 | return; 71 | } 72 | 73 | QString line = getLine( ); 74 | 75 | #ifndef NDEBUG 76 | std::cout << line.toStdString( ) << "\n"; 77 | #endif 78 | 79 | m_parseHelper.process( line.toStdString( ) ); 80 | if ( m_parseHelper.buffered( ) ) 81 | { 82 | append(""); 83 | displayPrompt( ); 84 | } 85 | if ( line.size( ) ) 86 | { 87 | m_historyBuffer.push_back( line.toStdString( ) ); 88 | m_historyIt = m_historyBuffer.end(); 89 | } 90 | moveCursorToEnd( ); 91 | } 92 | 93 | void Console::parseEvent( const ParseMessage& message ) 94 | { 95 | // handle invalid user input 96 | if ( message.errorCode ) 97 | { 98 | setTextColor( ERROR_COLOR ); 99 | append(message.message.c_str()); 100 | 101 | setTextColor( NORMAL_COLOR ); 102 | append(""); 103 | displayPrompt( ); 104 | return; 105 | } 106 | 107 | // interpret valid user input 108 | int errorCode; 109 | std::string res; 110 | if ( message.message.size() ) 111 | res = m_interpreter->interpret( message.message, &errorCode ); 112 | if ( errorCode ) 113 | { 114 | setTextColor( ERROR_COLOR ); 115 | } 116 | else 117 | { 118 | setTextColor( OUTPUT_COLOR ); 119 | } 120 | 121 | if ( res.size( ) ) 122 | { 123 | append(res.c_str()); 124 | } 125 | 126 | setTextColor( NORMAL_COLOR ); 127 | 128 | // set up the next line on the console 129 | append(""); 130 | displayPrompt( ); 131 | } 132 | 133 | QString Console::getLine( ) 134 | { 135 | QTextCursor cursor = textCursor(); 136 | cursor.movePosition( QTextCursor::StartOfLine ); 137 | cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, Console::PROMPT.size( ) ); 138 | cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor ); 139 | QString line = cursor.selectedText( ); 140 | cursor.clearSelection( ); 141 | return line; 142 | } 143 | 144 | bool Console::cursorIsOnInputLine( ) 145 | { 146 | int cursorBlock = textCursor( ).blockNumber( ); 147 | QTextCursor bottomCursor = textCursor( ); 148 | bottomCursor.movePosition( QTextCursor::End ); 149 | int bottomBlock = bottomCursor.blockNumber( ); 150 | return ( cursorBlock == bottomBlock ); 151 | } 152 | 153 | bool Console::inputLineIsEmpty( ) 154 | { 155 | QTextCursor bottomCursor = textCursor( ); 156 | bottomCursor.movePosition( QTextCursor::End ); 157 | int col = bottomCursor.columnNumber( ); 158 | return ( col == Console::PROMPT.size( ) ); 159 | } 160 | 161 | bool Console::canBackspace( ) 162 | { 163 | if ( ! cursorIsOnInputLine( ) ) 164 | { 165 | return false; 166 | } 167 | 168 | if ( inputLineIsEmpty( ) ) 169 | { 170 | return false; 171 | } 172 | 173 | return true; 174 | } 175 | 176 | bool Console::canGoLeft( ) 177 | { 178 | if ( cursorIsOnInputLine( ) ) 179 | { 180 | QTextCursor bottomCursor = textCursor( ); 181 | int col = bottomCursor.columnNumber( ); 182 | return (col > Console::PROMPT.size( )); 183 | } 184 | return true; 185 | } 186 | 187 | void Console::displayPrompt( ) 188 | { 189 | QTextCursor cursor = textCursor(); 190 | cursor.movePosition( QTextCursor::End ); 191 | if ( m_parseHelper.buffered( ) ) 192 | { 193 | cursor.insertText( Console::MULTILINE_PROMPT ); 194 | } 195 | else 196 | { 197 | cursor.insertText( Console::PROMPT ); 198 | } 199 | cursor.movePosition( QTextCursor::EndOfLine ); 200 | } 201 | 202 | void Console::autocomplete( ) 203 | { 204 | if ( ! cursorIsOnInputLine( ) ) 205 | return; 206 | 207 | QString line = getLine( ); 208 | const std::list& suggestions = 209 | m_interpreter->suggest( line.toStdString( ) ); 210 | if (suggestions.size() == 1) 211 | { 212 | line = suggestions.back().c_str(); 213 | } 214 | else 215 | { 216 | // try to complete to longest common prefix 217 | std::string prefix = 218 | LongestCommonPrefix(suggestions.begin(), suggestions.end()); 219 | if (prefix.size() > line.size()) 220 | { 221 | line = prefix.c_str(); 222 | } 223 | else 224 | { 225 | ColumnFormatter fmt; 226 | fmt.setItems(suggestions.begin(), suggestions.end()); 227 | fmt.format(width() / 10); 228 | setTextColor( OUTPUT_COLOR ); 229 | const std::list& formatted = fmt.formattedOutput(); 230 | for (std::list::const_iterator it = formatted.begin(); 231 | it != formatted.end(); ++it) 232 | { 233 | append(it->c_str()); 234 | } 235 | std::cout << width() << "\n"; 236 | setTextColor( NORMAL_COLOR ); 237 | } 238 | } 239 | 240 | // set up the next line on the console 241 | append(""); 242 | displayPrompt( ); 243 | moveCursorToEnd( ); 244 | cursor.insertText( line ); 245 | moveCursorToEnd( ); 246 | } 247 | 248 | void Console::previousHistory( ) 249 | { 250 | if ( ! cursorIsOnInputLine( ) ) 251 | return; 252 | 253 | if ( ! m_historyBuffer.size( ) ) 254 | return; 255 | 256 | QTextCursor cursor = textCursor(); 257 | cursor.movePosition( QTextCursor::StartOfLine ); 258 | cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, Console::PROMPT.size( ) ); 259 | cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor ); 260 | cursor.removeSelectedText( ); 261 | if ( m_historyIt != m_historyBuffer.begin( ) ) 262 | { 263 | --m_historyIt; 264 | } 265 | cursor.insertText( m_historyIt->c_str() ); 266 | } 267 | 268 | void Console::nextHistory( ) 269 | { 270 | if ( ! cursorIsOnInputLine( ) ) 271 | return; 272 | 273 | if ( ! m_historyBuffer.size( ) ) 274 | return; 275 | if ( m_historyIt == m_historyBuffer.end( ) ) 276 | { 277 | return; 278 | } 279 | QTextCursor cursor = textCursor(); 280 | cursor.movePosition( QTextCursor::StartOfLine ); 281 | cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, Console::PROMPT.size( ) ); 282 | cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor ); 283 | cursor.removeSelectedText( ); 284 | ++m_historyIt; 285 | if ( m_historyIt == m_historyBuffer.end( ) ) 286 | { 287 | return; 288 | } 289 | cursor.insertText( m_historyIt->c_str() ); 290 | } 291 | 292 | void Console::moveCursorToEnd( ) 293 | { 294 | QTextCursor cursor = textCursor(); 295 | cursor.movePosition( QTextCursor::End ); 296 | setTextCursor( cursor ); 297 | } 298 | -------------------------------------------------------------------------------- /Console.h: -------------------------------------------------------------------------------- 1 | /** 2 | python-console 3 | Copyright (C) 2018 Alex Tsui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | */ 23 | 24 | #ifndef ATSUI_CONSOLE_H 25 | #define ATSUI_CONSOLE_H 26 | #include 27 | #include 28 | #include "ParseHelper.h" 29 | #include "ParseListener.h" 30 | 31 | class QWidget; 32 | class QKeyEvent; 33 | class Interpreter; 34 | 35 | class Console : public QTextEdit, ParseListener 36 | { 37 | Q_OBJECT 38 | 39 | public: 40 | Console( QWidget* parent = 0 ); 41 | virtual ~Console( ); 42 | 43 | protected: 44 | // override QTextEdit 45 | virtual void keyPressEvent( QKeyEvent* e ); 46 | 47 | virtual void handleReturnKeyPress( ); 48 | 49 | /** 50 | Handle a compilable chunk of Python user input. 51 | */ 52 | virtual void parseEvent( const ParseMessage& message ); 53 | 54 | QString getLine( ); 55 | bool cursorIsOnInputLine( ); 56 | bool inputLineIsEmpty( ); 57 | bool canBackspace( ); 58 | bool canGoLeft( ); 59 | void displayPrompt( ); 60 | void autocomplete( ); 61 | void previousHistory( ); 62 | void nextHistory( ); 63 | void moveCursorToEnd( ); 64 | 65 | static const QString PROMPT; 66 | static const QString MULTILINE_PROMPT; 67 | 68 | static const QColor NORMAL_COLOR; 69 | static const QColor ERROR_COLOR; 70 | static const QColor OUTPUT_COLOR; 71 | 72 | Interpreter* m_interpreter; 73 | ParseHelper m_parseHelper; 74 | std::list m_historyBuffer; 75 | std::list::const_iterator m_historyIt; 76 | }; 77 | 78 | #endif // ATSUI_CONSOLE_H 79 | -------------------------------------------------------------------------------- /Interpreter.cpp: -------------------------------------------------------------------------------- 1 | #include "Interpreter.h" 2 | #include 3 | #include 4 | #include 5 | 6 | PyThreadState* Interpreter::MainThreadState = NULL; 7 | 8 | Interpreter::Interpreter( ) 9 | { 10 | PyEval_AcquireLock( ); 11 | m_threadState = Py_NewInterpreter( ); 12 | 13 | PyObject *module = PyImport_ImportModule("__main__"); 14 | loc = glb = PyModule_GetDict(module); 15 | SetupRedirector( m_threadState ); 16 | PyRun_SimpleString("import sys\n" 17 | "import redirector\n" 18 | "sys.path.insert(0, \".\")\n" // add current path 19 | "sys.stdout = redirector.redirector()\n" 20 | "sys.stderr = sys.stdout\n" 21 | "import rlcompleter\n" 22 | "sys.completer = rlcompleter.Completer()\n" 23 | ); 24 | 25 | PyEval_ReleaseThread( m_threadState ); 26 | } 27 | 28 | Interpreter::~Interpreter( ) 29 | { 30 | #ifndef NDEBUG 31 | std::cout << "delete interpreter\n"; 32 | #endif 33 | PyEval_AcquireThread( m_threadState ); 34 | Py_EndInterpreter( m_threadState ); 35 | PyEval_ReleaseLock( ); 36 | } 37 | 38 | void 39 | Interpreter::test( ) 40 | { 41 | PyEval_AcquireThread( m_threadState ); 42 | 43 | PyObject* py_result; 44 | PyObject* dum; 45 | std::string command = "print 'Hello world'\n"; 46 | py_result = Py_CompileString(command.c_str(), "", Py_single_input); 47 | if ( py_result == 0 ) 48 | { 49 | std::cout << "Huh?\n"; 50 | PyEval_ReleaseThread( m_threadState ); 51 | return; 52 | } 53 | dum = PyEval_EvalCode ((PyCodeObject *)py_result, glb, loc); 54 | Py_XDECREF (dum); 55 | Py_XDECREF (py_result); 56 | 57 | std::cout << GetResultString( m_threadState ); 58 | GetResultString( m_threadState ) = ""; 59 | 60 | PyEval_ReleaseThread( m_threadState ); 61 | } 62 | 63 | std::string 64 | Interpreter::interpret( const std::string& command, int* errorCode ) 65 | { 66 | PyEval_AcquireThread( m_threadState ); 67 | *errorCode = 0; 68 | 69 | PyObject* py_result; 70 | PyObject* dum; 71 | std::string res; 72 | #ifndef NDEBUG 73 | std::cout << "interpreting (" << command << ")\n"; 74 | #endif 75 | py_result = Py_CompileString(command.c_str(), "", Py_single_input); 76 | if ( py_result == 0 ) 77 | { 78 | #ifndef NDEBUG 79 | std::cout << "Huh?\n"; 80 | #endif 81 | if ( PyErr_Occurred( ) ) 82 | { 83 | *errorCode = 1; 84 | PyErr_Print( ); 85 | res = GetResultString( m_threadState ); 86 | GetResultString( m_threadState ) = ""; 87 | } 88 | 89 | PyEval_ReleaseThread( m_threadState ); 90 | return res; 91 | } 92 | dum = PyEval_EvalCode ((PyCodeObject *)py_result, glb, loc); 93 | Py_XDECREF (dum); 94 | Py_XDECREF (py_result); 95 | if ( PyErr_Occurred( ) ) 96 | { 97 | *errorCode = 1; 98 | PyErr_Print( ); 99 | } 100 | 101 | res = GetResultString( m_threadState ); 102 | GetResultString( m_threadState ) = ""; 103 | 104 | PyEval_ReleaseThread( m_threadState ); 105 | return res; 106 | } 107 | 108 | const std::list& Interpreter::suggest( const std::string& hint ) 109 | { 110 | PyEval_AcquireThread( m_threadState ); 111 | m_suggestions.clear(); 112 | int i = 0; 113 | std::string command = boost::str( 114 | boost::format("sys.completer.complete('%1%', %2%)\n") 115 | % hint 116 | % i); 117 | #ifndef NDEBUG 118 | std::cout << command << "\n"; 119 | #endif 120 | std::string res; 121 | do 122 | { 123 | PyObject* py_result; 124 | PyObject* dum; 125 | py_result = Py_CompileString(command.c_str(), "", Py_single_input); 126 | dum = PyEval_EvalCode ((PyCodeObject *)py_result, glb, loc); 127 | Py_XDECREF (dum); 128 | Py_XDECREF (py_result); 129 | res = GetResultString( m_threadState ); 130 | GetResultString( m_threadState ) = ""; 131 | ++i; 132 | command = boost::str( 133 | boost::format("sys.completer.complete('%1%', %2%)\n") 134 | % hint 135 | % i); 136 | if (res.size()) 137 | { 138 | // throw away the newline 139 | res = res.substr(1, res.size() - 3); 140 | m_suggestions.push_back(res); 141 | } 142 | } 143 | while (res.size()); 144 | 145 | PyEval_ReleaseThread( m_threadState ); 146 | return m_suggestions; 147 | } 148 | 149 | void 150 | Interpreter::Initialize( ) 151 | { 152 | Py_Initialize( ); 153 | PyEval_InitThreads( ); 154 | MainThreadState = PyEval_SaveThread( ); 155 | } 156 | 157 | void 158 | Interpreter::Finalize( ) 159 | { 160 | PyEval_RestoreThread( MainThreadState ); 161 | Py_Finalize( ); 162 | } 163 | 164 | std::string& Interpreter::GetResultString( PyThreadState* threadState ) 165 | { 166 | static std::map< PyThreadState*, std::string > ResultStrings; 167 | if ( !ResultStrings.count( threadState ) ) 168 | { 169 | ResultStrings[ threadState ] = ""; 170 | } 171 | return ResultStrings[ threadState ]; 172 | } 173 | 174 | PyObject* Interpreter::RedirectorInit(PyObject *, PyObject *) 175 | { 176 | Py_INCREF(Py_None); 177 | return Py_None; 178 | } 179 | 180 | PyObject* Interpreter::RedirectorWrite(PyObject *, PyObject *args) 181 | { 182 | char* output; 183 | PyObject *selfi; 184 | 185 | if (!PyArg_ParseTuple(args,"Os",&selfi,&output)) 186 | { 187 | return NULL; 188 | } 189 | 190 | std::string outputString( output ); 191 | PyThreadState* currentThread = PyThreadState_Get( ); 192 | std::string& resultString = GetResultString( currentThread ); 193 | resultString = resultString + outputString; 194 | Py_INCREF(Py_None); 195 | return Py_None; 196 | } 197 | 198 | PyMethodDef Interpreter::ModuleMethods[] = { {NULL,NULL,0,NULL} }; 199 | PyMethodDef Interpreter::RedirectorMethods[] = 200 | { 201 | {"__init__", Interpreter::RedirectorInit, METH_VARARGS, 202 | "initialize the stdout/err redirector"}, 203 | {"write", Interpreter::RedirectorWrite, METH_VARARGS, 204 | "implement the write method to redirect stdout/err"}, 205 | {NULL,NULL,0,NULL}, 206 | }; 207 | 208 | void Interpreter::SetupRedirector( PyThreadState* threadState ) 209 | { 210 | PyMethodDef *def; 211 | 212 | /* create a new module and class */ 213 | PyObject *module = Py_InitModule("redirector", ModuleMethods); 214 | PyObject *moduleDict = PyModule_GetDict(module); 215 | PyObject *classDict = PyDict_New(); 216 | PyObject *className = PyString_FromString("redirector"); 217 | PyObject *fooClass = PyClass_New(NULL, classDict, className); 218 | PyDict_SetItemString(moduleDict, "redirector", fooClass); 219 | Py_DECREF(classDict); 220 | Py_DECREF(className); 221 | Py_DECREF(fooClass); 222 | 223 | /* add methods to class */ 224 | for (def = RedirectorMethods; def->ml_name != NULL; def++) { 225 | PyObject *func = PyCFunction_New(def, NULL); 226 | PyObject *method = PyMethod_New(func, NULL, fooClass); 227 | PyDict_SetItemString(classDict, def->ml_name, method); 228 | Py_DECREF(func); 229 | Py_DECREF(method); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /Interpreter.h: -------------------------------------------------------------------------------- 1 | /** 2 | python-console 3 | Copyright (C) 2014 Alex Tsui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | */ 23 | 24 | #ifndef INTERPRETER_H 25 | #define INTERPRETER_H 26 | #include 27 | #include 28 | #include 29 | #if defined(__APPLE__) && defined(__MACH__) 30 | /* 31 | * The following undefs for C standard library macros prevent 32 | * build errors of the following type on mac ox 10.7.4 and XCode 4.3.3 33 | * 34 | /usr/include/c++/4.2.1/bits/localefwd.h:57:21: error: too many arguments provided to function-like macro invocation 35 | isspace(_CharT, const locale&); 36 | ^ 37 | /usr/include/c++/4.2.1/bits/localefwd.h:56:5: error: 'inline' can only appear on functions 38 | inline bool 39 | ^ 40 | /usr/include/c++/4.2.1/bits/localefwd.h:57:5: error: variable 'isspace' declared as a template 41 | isspace(_CharT, const locale&); 42 | ^ 43 | */ 44 | #undef isspace 45 | #undef isupper 46 | #undef islower 47 | #undef isalpha 48 | #undef isalnum 49 | #undef toupper 50 | #undef tolower 51 | #endif 52 | 53 | /** 54 | Wraps a Python interpreter, which you can pass commands as strings to interpret 55 | and get strings of output/error in return. 56 | */ 57 | class Interpreter 58 | { 59 | protected: 60 | static PyThreadState* MainThreadState; 61 | 62 | PyThreadState* m_threadState; 63 | PyObject* glb; 64 | PyObject* loc; 65 | 66 | std::list< std::string > m_suggestions; 67 | 68 | public: 69 | /** 70 | Instantiate a Python interpreter. 71 | */ 72 | Interpreter( ); 73 | virtual ~Interpreter( ); 74 | 75 | void test( ); 76 | std::string interpret( const std::string& command, int* errorCode ); 77 | const std::list& suggest( const std::string& hint ); 78 | 79 | /** 80 | Call this before constructing and using Interpreter. 81 | */ 82 | static void Initialize( ); 83 | 84 | /** 85 | Call this when done using Interpreter. 86 | */ 87 | static void Finalize( ); 88 | 89 | protected: 90 | static void SetupRedirector( PyThreadState* threadState ); 91 | static PyObject* RedirectorInit(PyObject *, PyObject *); 92 | static PyObject* RedirectorWrite(PyObject *, PyObject *args); 93 | static std::string& GetResultString( PyThreadState* threadState ); 94 | static PyMethodDef ModuleMethods[]; 95 | static PyMethodDef RedirectorMethods[]; 96 | }; 97 | #endif // INTERPRETER_H 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Alex Tsui 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /ParseHelper.BlockParseState.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include "ParseHelper.h" 23 | 24 | ParseHelper::BlockParseState:: 25 | BlockParseState( ParseHelper& parent ): 26 | ParseState( parent ) 27 | { } 28 | 29 | ParseHelper::BlockParseState:: 30 | BlockParseState( ParseHelper& parent, const std::string& indent_ ): 31 | ParseState( parent ), 32 | indent( indent_ ) 33 | { } 34 | 35 | bool ParseHelper::BlockParseState:: 36 | process(const std::string& str) 37 | { 38 | bool ok = initializeIndent(str); 39 | if ( ! ok ) 40 | { 41 | // finish processing 42 | return true; 43 | } 44 | 45 | Indent ind; 46 | bool isIndented = PeekIndent( str, &ind ); 47 | if ( isIndented ) 48 | { 49 | #ifndef NDEBUG 50 | std::cout << "current line indent: "; 51 | print( ind ); 52 | #endif 53 | // check if indent matches 54 | if ( ind.Token != indent.Token ) 55 | { 56 | // dedent until we match or empty the stack 57 | bool found = false; 58 | while ( !found ) 59 | { 60 | parent.stateStack.pop_back( ); 61 | if ( !parent.stateStack.size( ) ) 62 | break; 63 | boost::shared_ptr parseState = 64 | boost::dynamic_pointer_cast( 65 | parent.stateStack.back( )); 66 | found = ( ind.Token == parseState->indent.Token ); 67 | } 68 | 69 | if ( ! found ) 70 | { 71 | #ifndef NDEBUG 72 | std::cout << "indent mismatch\n"; 73 | #endif 74 | parent.reset( ); 75 | ParseMessage msg( 1, "IndentationError: unexpected indent"); 76 | parent.broadcast( msg ); 77 | return true; 78 | } 79 | } 80 | 81 | // process command 82 | 83 | // enter indented block state 84 | if ( str[str.size()-1] == ':' ) 85 | { 86 | parent.commandBuffer.push_back( str ); 87 | //parent.inBlock = (boost::dynamic_pointer_cast( 88 | // parent.stateStack.back())); 89 | 90 | //expectingIndent = true; 91 | boost::shared_ptr parseState( 92 | new BlockParseState( parent ) ); 93 | parent.stateStack.push_back( parseState ); 94 | return true; 95 | } 96 | 97 | if ( str[str.size()-1] == '\\' ) 98 | { 99 | parent.commandBuffer.push_back( str ); 100 | boost::shared_ptr parseState( 101 | new ContinuationParseState( parent ) ); 102 | parent.stateStack.push_back( parseState ); 103 | return true; 104 | } 105 | 106 | if (BracketParseState::HasOpenBrackets( str )) 107 | { 108 | // FIXME: Every parse state should have its own local buffer 109 | boost::shared_ptr parseState( 110 | new BracketParseState( parent, str ) ); 111 | parent.stateStack.push_back( parseState ); 112 | return true; 113 | } 114 | 115 | parent.commandBuffer.push_back( str ); 116 | return true; 117 | } 118 | else 119 | { 120 | if ( str.size() ) 121 | { 122 | { 123 | #ifndef NDEBUG 124 | std::cout << "Expected indented block\n"; 125 | #endif 126 | parent.reset( ); 127 | ParseMessage msg( 1, "IndentationError: expected an indented block" ); 128 | parent.broadcast( msg ); 129 | return false; 130 | } 131 | } 132 | #ifndef NDEBUG 133 | std::cout << "Leaving block\n"; 134 | #endif 135 | parent.stateStack.pop_back(); 136 | parent.flush( ); 137 | parent.reset( ); 138 | return true; 139 | } 140 | } 141 | 142 | bool ParseHelper::BlockParseState:: 143 | initializeIndent(const std::string& str) 144 | { 145 | bool expectingIndent = (indent.Token == ""); 146 | if ( !expectingIndent ) 147 | { 148 | std::cout << "already initialized indent: "; 149 | print( indent ); 150 | return true; 151 | } 152 | 153 | Indent ind; 154 | bool isIndented = parent.PeekIndent( str, &ind ); 155 | if ( !isIndented ) 156 | { 157 | #ifndef NDEBUG 158 | std::cout << "Expected indented block\n"; 159 | #endif 160 | parent.reset( ); 161 | ParseMessage msg( 1, "IndentationError: expected an indented block" ); 162 | parent.broadcast( msg ); 163 | return false; 164 | } 165 | indent = ind; 166 | //parent.currentIndent = ind; 167 | //parent.indentStack.push_back( parent.currentIndent ); 168 | //parent.expectingIndent = false; 169 | #ifndef NDEBUG 170 | std::cout << "initializing indent: "; 171 | print( ind ); 172 | #endif 173 | return true; 174 | } 175 | -------------------------------------------------------------------------------- /ParseHelper.BracketParseState.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include "ParseHelper.h" 23 | #include 24 | #include 25 | const std::string ParseHelper::BracketParseState::OpeningBrackets = "[({"; 26 | const std::string ParseHelper::BracketParseState::ClosingBrackets = "])}"; 27 | 28 | bool ParseHelper::BracketParseState::HasOpenBrackets(const std::string& str) 29 | { 30 | std::list brackets; 31 | bool hasOpenBrackets = LoadBrackets(str, &brackets); 32 | return hasOpenBrackets; 33 | } 34 | 35 | bool ParseHelper::BracketParseState::LoadBrackets(const std::string& str, 36 | std::list* stack) 37 | { 38 | if ( !stack ) 39 | return false; 40 | 41 | stack->clear(); 42 | for (int i = 0; i < str.size(); ++i) 43 | { 44 | if (OpeningBrackets.find_first_of(str[i]) != std::string::npos) 45 | { 46 | stack->push_back(str[i]); 47 | } 48 | else 49 | { 50 | size_t t = ClosingBrackets.find_first_of(str[i]); 51 | if (t != std::string::npos) 52 | { 53 | if (t != OpeningBrackets.find_first_of(stack->back())) 54 | return false; 55 | stack->pop_back(); 56 | } 57 | } 58 | } 59 | return stack->size(); 60 | } 61 | 62 | ParseHelper::BracketParseState::BracketParseState( ParseHelper& parent, const std::string& firstLine ): 63 | ParseState( parent ) 64 | { 65 | bool hasOpenBrackets = LoadBrackets( firstLine, &brackets ); 66 | assert( hasOpenBrackets ); 67 | m_buffer.push_back( firstLine ); 68 | } 69 | 70 | bool ParseHelper::BracketParseState::process(const std::string& str) 71 | { 72 | #ifndef NDEBUG 73 | std::cout << "(BracketParseState) processing " << str << "\n"; 74 | #endif 75 | // update brackets stack 76 | for (int i = 0; i < str.size(); ++i) 77 | { 78 | if (OpeningBrackets.find_first_of(str[i]) != std::string::npos) 79 | { 80 | #ifndef NDEBUG 81 | std::cout << "push " << str[i] << "\n"; 82 | #endif 83 | brackets.push_back(str[i]); 84 | } 85 | else 86 | { 87 | size_t t = ClosingBrackets.find_first_of(str[i]); 88 | if (t != std::string::npos) 89 | { 90 | #ifndef NDEBUG 91 | std::cout << "pop " << str[i] << "\n"; 92 | #endif 93 | // reset state if unmatched brackets seen 94 | if (t != OpeningBrackets.find_first_of(brackets.back())) 95 | { 96 | parent.reset( ); 97 | ParseMessage msg(1, "Invalid syntax"); 98 | parent.broadcast( msg ); 99 | return true; 100 | } 101 | brackets.pop_back(); 102 | } 103 | } 104 | } 105 | 106 | // if we've cleared the stack, we've finished accepting input here 107 | if (!brackets.size()) 108 | { 109 | // squash buffered lines and place it on parent::commandBuffer 110 | std::stringstream ss; 111 | for (std::list::const_iterator it = m_buffer.begin(); 112 | it != m_buffer.end(); ++it ) 113 | { 114 | ss << *it << "\n"; 115 | } 116 | ss << str; 117 | parent.commandBuffer.push_back(ss.str()); 118 | parent.stateStack.pop_back( ); 119 | return false; 120 | } 121 | else 122 | { 123 | // buffer and expect more lines 124 | m_buffer.push_back( str ); 125 | return true; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /ParseHelper.ContinuationParseState.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include "ParseHelper.h" 23 | #include 24 | #include 25 | 26 | ParseHelper::ContinuationParseState:: 27 | ContinuationParseState( ParseHelper& parent_ ): 28 | ParseState( parent_ ) 29 | { } 30 | 31 | bool ParseHelper::ContinuationParseState:: 32 | process( const std::string& str ) 33 | { 34 | if ( str.size() && str[str.size()-1] == '\\' ) 35 | { 36 | parent.commandBuffer.push_back( str ); 37 | return true; 38 | } 39 | else 40 | { 41 | std::list tmp_list; 42 | tmp_list.push_back(str); 43 | while (parent.commandBuffer.size() && parent.commandBuffer.back()[ 44 | parent.commandBuffer.back().size()-1] == '\\') 45 | { 46 | tmp_list.push_back(parent.commandBuffer.back()); 47 | parent.commandBuffer.pop_back(); 48 | } 49 | std::stringstream ss; 50 | while (tmp_list.size()) 51 | { 52 | ss << tmp_list.back(); 53 | tmp_list.pop_back(); 54 | if (tmp_list.size()) 55 | ss << "\n"; 56 | } 57 | parent.commandBuffer.push_back(ss.str()); 58 | parent.stateStack.pop_back(); 59 | return false; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ParseHelper.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include "ParseHelper.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "ParseListener.h" 30 | 31 | #ifndef NDEBUG 32 | void print(const ParseHelper::Indent& indent) 33 | { 34 | std::string str = indent.Token; 35 | for (int i = 0; i < str.size(); ++i) 36 | { 37 | switch (str.at(i)) 38 | { 39 | case ' ': 40 | str[i] = 's'; 41 | break; 42 | case '\t': 43 | str[i] = 't'; 44 | break; 45 | } 46 | } 47 | std::cout << str << "\n"; 48 | } 49 | #endif 50 | 51 | ParseHelper::Indent:: 52 | Indent( ) 53 | { } 54 | 55 | ParseHelper::Indent:: 56 | Indent( const std::string& indent ): 57 | Token( indent ) 58 | { } 59 | 60 | ParseHelper::ParseState:: 61 | ParseState( ParseHelper& parent_ ): parent( parent_ ) 62 | { } 63 | 64 | ParseHelper::ParseState:: 65 | ~ParseState( ) 66 | { } 67 | 68 | bool ParseHelper::PeekIndent( const std::string& str, Indent* indent ) 69 | { 70 | if ( !str.size() || ! isspace(str[0]) ) 71 | return false; 72 | 73 | int nonwhitespaceIndex = -1; 74 | for (int i = 0; i < str.size(); ++i) 75 | { 76 | if (!isspace(str[i])) 77 | { 78 | nonwhitespaceIndex = i; 79 | break; 80 | } 81 | } 82 | if (nonwhitespaceIndex == -1) 83 | { 84 | return false; 85 | } 86 | std::string indentToken = str.substr(0, nonwhitespaceIndex); 87 | indent->Token = indentToken; 88 | return true; 89 | } 90 | 91 | ParseHelper::ParseHelper( ) 92 | { } 93 | 94 | void ParseHelper::process( const std::string& str ) 95 | { 96 | #ifndef NDEBUG 97 | std::cout << "processing: (" << str << ")\n"; 98 | #endif 99 | 100 | std::string top; 101 | commandBuffer.push_back(str); 102 | //std::string top = commandBuffer.back(); 103 | //commandBuffer.pop_back(); 104 | boost::shared_ptr blockStatePtr; 105 | while (stateStack.size()) 106 | { 107 | top = commandBuffer.back(); 108 | commandBuffer.pop_back(); 109 | blockStatePtr = stateStack.back(); 110 | if (blockStatePtr->process(top)) 111 | return; 112 | } 113 | 114 | if ( ! commandBuffer.size() ) 115 | return; 116 | 117 | // standard state 118 | top = commandBuffer.back(); 119 | if ( !top.size() ) 120 | { 121 | reset( ); 122 | broadcast( std::string() ); 123 | return; 124 | } 125 | #ifndef NDEBUG 126 | std::cout << "now processing: (" << top << ")\n"; 127 | #endif 128 | 129 | { // check for unexpected indent 130 | Indent ind; 131 | bool isIndented = PeekIndent( top, &ind ); 132 | if ( isIndented && 133 | ! isInContinuation( ) ) 134 | { 135 | reset( ); 136 | ParseMessage msg( 1, "IndentationError: unexpected indent"); 137 | broadcast( msg ); 138 | return; 139 | } 140 | } 141 | 142 | // enter indented block state 143 | if ( top[top.size()-1] == ':' ) 144 | { 145 | boost::shared_ptr parseState( 146 | new BlockParseState( *this ) ); 147 | stateStack.push_back( parseState ); 148 | return; 149 | } 150 | 151 | if ( top[top.size()-1] == '\\' ) 152 | { 153 | boost::shared_ptr parseState( 154 | new ContinuationParseState( *this ) ); 155 | stateStack.push_back( parseState ); 156 | return; 157 | } 158 | 159 | if (BracketParseState::HasOpenBrackets( top )) 160 | { 161 | // FIXME: Every parse state should have its own local buffer 162 | commandBuffer.pop_back( ); 163 | boost::shared_ptr parseState( 164 | new BracketParseState( *this, top ) ); 165 | stateStack.push_back( parseState ); 166 | return; 167 | } 168 | 169 | // handle single-line statement 170 | flush( ); 171 | } 172 | 173 | bool ParseHelper::buffered( ) const 174 | { 175 | return commandBuffer.size( ) || stateStack.size( ); 176 | } 177 | 178 | void ParseHelper::flush( ) 179 | { 180 | std::stringstream ss; 181 | for (int i = 0; i < commandBuffer.size(); ++i ) 182 | { 183 | ss << commandBuffer[i] << "\n"; 184 | } 185 | commandBuffer.clear(); 186 | 187 | broadcast( ss.str() ); 188 | // TODO: feed string to interpreter 189 | } 190 | 191 | void ParseHelper::reset( ) 192 | { 193 | // inContinuation = false; 194 | stateStack.clear( ); 195 | commandBuffer.clear( ); 196 | } 197 | 198 | bool ParseHelper::isInContinuation( ) const 199 | { 200 | return (stateStack.size() 201 | && (boost::dynamic_pointer_cast( 202 | stateStack.back()))); 203 | } 204 | 205 | void ParseHelper::subscribe( ParseListener* listener ) 206 | { 207 | listeners.push_back( listener ); 208 | } 209 | 210 | void ParseHelper::unsubscribeAll( ) 211 | { 212 | listeners.clear( ); 213 | } 214 | 215 | void ParseHelper::broadcast( const ParseMessage& msg ) 216 | { 217 | // broadcast signal 218 | for (int i = 0; i < listeners.size(); ++i) 219 | { 220 | if (listeners[i]) 221 | { 222 | listeners[i]->parseEvent(msg); 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /ParseHelper.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef PARSE_HELPER_H 24 | #define PARSE_HELPER_H 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "ParseMessage.h" 30 | 31 | class ParseListener; 32 | 33 | /** 34 | Helps chunk lines of Python code into compilable statements. 35 | */ 36 | class ParseHelper 37 | { 38 | public: 39 | struct Indent 40 | { 41 | std::string Token; 42 | Indent( ); 43 | Indent( const std::string& indent ); 44 | }; 45 | 46 | /** 47 | Handle different states of parsing. Subclasses override 48 | process(const std::string&) to handle different types of multiline 49 | statements. 50 | */ 51 | struct ParseState 52 | { 53 | ParseHelper& parent; 54 | ParseState( ParseHelper& parent_ ); 55 | virtual ~ParseState( ); 56 | 57 | /** 58 | Processes a single line of user input. 59 | 60 | Subclasses should return false if further processing should be done. In 61 | this case, the command buffer should be topped by the next statement to 62 | parse. 63 | 64 | \return whether processing of the line is done. 65 | */ 66 | virtual bool process(const std::string& str) = 0; 67 | }; 68 | 69 | struct ContinuationParseState : public ParseState 70 | { 71 | using ParseState::parent; 72 | ContinuationParseState( ParseHelper& parent_ ); 73 | virtual bool process( const std::string& str); 74 | }; 75 | 76 | /** 77 | Handle parsing a multiline indented block. Example of such a block: 78 | 79 | for i in range(10): 80 | print i 81 | print i*i 82 | 83 | */ 84 | struct BlockParseState : public ParseState 85 | { 86 | Indent indent; 87 | 88 | BlockParseState( ParseHelper& parent ); 89 | BlockParseState( ParseHelper& parent, const std::string& indent_ ); 90 | 91 | // return whether processing is finished 92 | virtual bool process(const std::string& str); 93 | 94 | // return if there was an error 95 | bool initializeIndent(const std::string& str); 96 | }; 97 | friend class BlockParseState; 98 | 99 | struct BracketParseState : public ParseState 100 | { 101 | static const std::string OpeningBrackets; 102 | static const std::string ClosingBrackets; 103 | 104 | std::list brackets; 105 | std::list m_buffer; 106 | 107 | /** 108 | Return whether open brackets remain unclosed in \a str. 109 | */ 110 | static bool HasOpenBrackets(const std::string& str); 111 | static bool LoadBrackets(const std::string& str, 112 | std::list* stack); 113 | 114 | BracketParseState( ParseHelper& parent, const std::string& firstLine ); 115 | 116 | virtual bool process(const std::string& str); 117 | }; 118 | 119 | protected: 120 | // TODO: Create a ContinuationParseState to handle this 121 | bool inContinuation; 122 | std::vector< ParseListener* > listeners; 123 | std::vector< boost::shared_ptr< ParseState > > stateStack; 124 | std::vector< std::string > commandBuffer; 125 | 126 | public: 127 | static bool PeekIndent( const std::string& str, Indent* indent ); 128 | 129 | public: 130 | ParseHelper( ); 131 | 132 | public: 133 | void process( const std::string& str ); 134 | 135 | bool buffered( ) const; 136 | 137 | /** 138 | Generate a parse event from the current command buffer. 139 | */ 140 | void flush( ); 141 | 142 | /** 143 | Reset the state of the helper. 144 | */ 145 | void reset( ); 146 | 147 | bool isInContinuation( ) const; 148 | 149 | void subscribe( ParseListener* listener ); 150 | void unsubscribeAll( ); 151 | void broadcast( const ParseMessage& msg ); 152 | 153 | }; // class ParseHelper 154 | 155 | inline bool operator== ( const ParseHelper::Indent& a, const ParseHelper::Indent& b ) 156 | { 157 | return a.Token == b.Token; 158 | } 159 | 160 | inline bool operator!= ( const ParseHelper::Indent& a, const ParseHelper::Indent& b ) 161 | { 162 | return a.Token != b.Token; 163 | } 164 | 165 | #ifndef NDEBUG 166 | void print(const ParseHelper::Indent& indent); 167 | #endif 168 | 169 | #endif // PARSE_HELPER_H 170 | -------------------------------------------------------------------------------- /ParseListener.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include "ParseListener.h" 23 | #include 24 | 25 | void EchoListener::parseEvent( const ParseMessage& msg ) 26 | { 27 | std::cout << "echo(" << msg.errorCode << "): " << msg.message << "\n"; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /ParseListener.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #ifndef PARSE_LISTENER_H 23 | #define PARSE_LISTENER_H 24 | #include "ParseMessage.h" 25 | 26 | /** 27 | Interface to implement to receive parse events. 28 | */ 29 | struct ParseListener 30 | { 31 | virtual void parseEvent( const ParseMessage& msg ) = 0; 32 | }; 33 | 34 | /** 35 | Sample implementation of ParseListener that echoes messages. 36 | */ 37 | struct EchoListener : public ParseListener 38 | { 39 | virtual void parseEvent( const ParseMessage& msg ); 40 | }; 41 | 42 | #endif // PARSE_LISTENER_H 43 | -------------------------------------------------------------------------------- /ParseMessage.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include "ParseMessage.h" 23 | 24 | ParseMessage::ParseMessage( const std::string& msg ): 25 | errorCode( 0 ), message( msg ) 26 | { } 27 | 28 | ParseMessage::ParseMessage( int code, const std::string& msg ): 29 | errorCode( code ), message( msg ) 30 | { } 31 | 32 | -------------------------------------------------------------------------------- /ParseMessage.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #ifndef PARSE_MESSAGE_H 23 | #define PARSE_MESSAGE_H 24 | #include 25 | 26 | /** 27 | Basically a string + error code pair. Generated from ParseHelper so error-free 28 | codeblocks can be sent to the Interpreter. 29 | */ 30 | struct ParseMessage 31 | { 32 | int errorCode; 33 | std::string message; 34 | 35 | ParseMessage( const std::string& msg ); 36 | ParseMessage( int code, const std::string& msg ); 37 | }; 38 | #endif //PARSE_MESSAGE_H 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python-console 2 | ===== 3 | This is the result of a small side project to write a Qt widget that 4 | encapsulates an interactive Python shell. 5 | 6 | Quickstart 7 | ----- 8 | You should have Qt4 and Python libraries. You will need CMake to build this 9 | project as follows: 10 | 11 | 1. mkdir build 12 | 2. cmake .. 13 | 3. make 14 | 15 | License 16 | ----- 17 | This project is licensed under the [MIT](http://opensource.org/licenses/MIT) license. 18 | -------------------------------------------------------------------------------- /Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef PYTHON_CONSOLE_UTILS_H 2 | #define PYTHON_CONSOLE_UTILS_H 3 | #include 4 | #include 5 | 6 | /** 7 | InputIterator has value type of std::string. 8 | */ 9 | template < class InputIterator > 10 | std::string LongestCommonPrefix( InputIterator begin, InputIterator end ) 11 | { 12 | if ( begin == end ) 13 | return ""; 14 | 15 | const std::string& str0 = *begin; 16 | if ( ! str0.size() ) 17 | return ""; 18 | 19 | int endIndex = str0.size() - 1; 20 | InputIterator it = begin; ++it; 21 | for (; it != end; ++it) 22 | { 23 | const std::string& str = *it; 24 | for (int j = 0; j <= endIndex; ++j) 25 | { 26 | if (j >= str.size() || str[j] != str0[j]) 27 | endIndex = j - 1; 28 | } 29 | } 30 | return (endIndex > 0)? str0.substr(0, endIndex + 1) : ""; 31 | } 32 | 33 | #endif // PYTHON_CONSOLE_UTILS_H 34 | -------------------------------------------------------------------------------- /data/test.py: -------------------------------------------------------------------------------- 1 | for i in range(1, 10): 2 | print i 3 | -------------------------------------------------------------------------------- /data/test2.py: -------------------------------------------------------------------------------- 1 | for i in range(1, 10): 2 | print i 3 | 4 | for i in range(1, 10): 5 | print i 6 | print i 7 | -------------------------------------------------------------------------------- /data/test3.py: -------------------------------------------------------------------------------- 1 | for i in range(1, 10): 2 | print i 3 | for i in range(1, 10): 4 | print i 5 | -------------------------------------------------------------------------------- /data/test4.py: -------------------------------------------------------------------------------- 1 | for i in range(2): 2 | for j in range(3): 3 | print j 4 | print i 5 | -------------------------------------------------------------------------------- /data/test5.py: -------------------------------------------------------------------------------- 1 | for i in range(2): 2 | for j in range(3): 3 | print i, j 4 | -------------------------------------------------------------------------------- /test_cli.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ParseHelper.h" 4 | #include "ParseListener.h" 5 | #include "Interpreter.h" 6 | 7 | const std::string STD_PROMPT = ">>> "; 8 | const std::string MULTILINE_PROMPT = "... "; 9 | 10 | struct InterpreterRelay : public ParseListener 11 | { 12 | Interpreter* m_interpreter; 13 | 14 | InterpreterRelay( ): 15 | m_interpreter( new Interpreter ) 16 | { } 17 | 18 | virtual void parseEvent( const ParseMessage& msg ) 19 | { 20 | if ( msg.errorCode ) 21 | { 22 | std::cout << "(" << msg.errorCode << ") " << msg.message << "\n"; 23 | return; 24 | } 25 | else 26 | { 27 | int err; 28 | std::string res = m_interpreter->interpret( msg.message, &err ); 29 | std::cout << "(" << msg.errorCode << ") " << res << "\n"; 30 | } 31 | } 32 | }; 33 | 34 | int main( int argc, char *argv[] ) 35 | { 36 | Interpreter::Initialize( ); 37 | const std::string* prompt = &STD_PROMPT; 38 | ParseHelper helper; 39 | ParseListener* listener = new InterpreterRelay; 40 | helper.subscribe( listener ); 41 | 42 | std::string str; 43 | std::cout << *prompt; 44 | std::getline( std::cin, str ); 45 | while ( str != "quit" ) 46 | { 47 | std::cout << str << "\n"; 48 | helper.process( str ); 49 | if ( helper.buffered( ) ) 50 | { 51 | prompt = &MULTILINE_PROMPT; 52 | } 53 | else 54 | { 55 | prompt = &STD_PROMPT; 56 | } 57 | std::cout << *prompt; 58 | std::getline( std::cin, str ); 59 | } 60 | 61 | Interpreter::Finalize( ); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /test_console.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Console.h" 9 | #include "Interpreter.h" 10 | 11 | Console* console; 12 | 13 | void SetupWindow( int argc, char *argv[] ) 14 | { 15 | QMainWindow* window = new QMainWindow; 16 | window->resize( 800, 600 ); 17 | QWidget* centralWidget = new QWidget(window); 18 | QGridLayout* layout = new QGridLayout(centralWidget); 19 | console = new Console; 20 | layout->addWidget(console, 0, 0, 1, 1); 21 | window->setCentralWidget(centralWidget); 22 | window->show( ); 23 | } 24 | 25 | int main( int argc, char *argv[] ) 26 | { 27 | QApplication app( argc, argv ); 28 | Interpreter::Initialize( ); 29 | 30 | SetupWindow( argc, argv ); 31 | 32 | bool res = app.exec( ); 33 | delete console; 34 | Interpreter::Finalize( ); 35 | return res; 36 | } 37 | -------------------------------------------------------------------------------- /test_parse_helper.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014 Alex Tsui 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include "ParseHelper.h" 25 | #include "ParseListener.h" 26 | 27 | /** 28 | Demonstrates the ParseHelper. 29 | 30 | Call it with some of the python files in data/. 31 | */ 32 | int main( int argc, char *argv[] ) 33 | { 34 | if ( argc < 2 ) 35 | { 36 | std::cout << "Usage: " << argv[0] << " file\n"; 37 | return 1; 38 | } 39 | ParseHelper helper; 40 | helper.subscribe( new EchoListener ); 41 | 42 | std::ifstream ifs( argv[1] ); 43 | while ( ! ifs.eof( ) ) 44 | { 45 | std::string str; 46 | std::getline( ifs, str ); 47 | helper.process( str ); 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /test_python_interpreter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Interpreter.h" 3 | 4 | int main( int argc, char *argv[] ) 5 | { 6 | std::string commands[] = { 7 | "from time import time,ctime\n", 8 | "print 'Today is',ctime(time())\n" 9 | }; 10 | Interpreter::Initialize( ); 11 | Interpreter* interpreter = new Interpreter; 12 | for ( int i = 0; i < 2; ++i ) 13 | { 14 | int err; 15 | std::string res = interpreter->interpret( commands[i], &err ); 16 | std::cout << res; 17 | } 18 | delete interpreter; 19 | 20 | Interpreter::Finalize( ); 21 | return 0; 22 | } 23 | --------------------------------------------------------------------------------