From 5de4b5cd6770f86311c6c85c457435660ddce588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 19 Oct 2017 13:52:18 +0200 Subject: [PATCH] [code] Each script has a name, stored in the accordeon buffer. A script can now be imported in the console. Moved all the MicroPython functions to python/port files. Change-Id: I0a23e8cb20005719b800e81470e1c574c702c3b7 --- apps/code/Makefile | 9 +- apps/code/app.cpp | 10 +- apps/code/app.h | 8 +- apps/code/base.de.i18n | 17 +- apps/code/base.en.i18n | 7 +- apps/code/base.es.i18n | 17 +- apps/code/base.fr.i18n | 17 +- apps/code/base.pt.i18n | 17 +- apps/code/console_controller.cpp | 126 +++--- apps/code/console_controller.h | 36 +- apps/code/console_edit_cell.h | 1 - apps/code/console_store.cpp | 1 - apps/code/editor_controller.cpp | 4 +- apps/code/editor_controller.h | 4 +- apps/code/menu_controller.cpp | 42 +- apps/code/menu_controller.h | 18 +- apps/code/program.cpp | 22 -- apps/code/program_parameter_controller.cpp | 78 ---- apps/code/program_store.cpp | 208 ---------- apps/code/program_store.h | 46 --- apps/code/script.cpp | 22 ++ apps/code/{program.h => script.h} | 10 +- apps/code/script_parameter_controller.cpp | 78 ++++ ...roller.h => script_parameter_controller.h} | 22 +- apps/code/script_store.cpp | 363 ++++++++++++++++++ apps/code/script_store.h | 72 ++++ apps/code/test/mpprint.cpp | 6 - apps/shared.universal.i18n | 1 + liba/include/setjmp.h | 6 + python/Makefile | 1 + python/port/import_helper.c | 7 + python/port/port.c | 57 --- python/port/port.cpp | 110 ++++++ python/port/port.h | 25 +- 34 files changed, 868 insertions(+), 600 deletions(-) delete mode 100644 apps/code/program.cpp delete mode 100644 apps/code/program_parameter_controller.cpp delete mode 100644 apps/code/program_store.cpp delete mode 100644 apps/code/program_store.h create mode 100644 apps/code/script.cpp rename apps/code/{program.h => script.h} (54%) create mode 100644 apps/code/script_parameter_controller.cpp rename apps/code/{program_parameter_controller.h => script_parameter_controller.h} (56%) create mode 100644 apps/code/script_store.cpp create mode 100644 apps/code/script_store.h delete mode 100644 apps/code/test/mpprint.cpp create mode 100644 python/port/import_helper.c delete mode 100644 python/port/port.c create mode 100644 python/port/port.cpp diff --git a/apps/code/Makefile b/apps/code/Makefile index baa049a72..449f7cbfb 100644 --- a/apps/code/Makefile +++ b/apps/code/Makefile @@ -9,9 +9,9 @@ app_objs += $(addprefix apps/code/,\ console_store.o\ editor_controller.o\ menu_controller.o\ - program.o\ - program_parameter_controller.o\ - program_store.o\ + script.o\ + script_parameter_controller.o\ + script_store.o\ ) i18n_files += $(addprefix apps/code/,\ @@ -22,7 +22,4 @@ i18n_files += $(addprefix apps/code/,\ base.pt.i18n\ ) -tests += $(addprefix apps/code/test/,\ - mpprint.cpp\ -) app_images += apps/code/code_icon.png diff --git a/apps/code/app.cpp b/apps/code/app.cpp index 957aa68e8..2e7c4fd18 100644 --- a/apps/code/app.cpp +++ b/apps/code/app.cpp @@ -18,7 +18,7 @@ const Image * App::Descriptor::icon() { } App::Snapshot::Snapshot() { - m_programStore.addDefaultProgram(); + m_scriptStore.addMandelbrotScript(); } App * App::Snapshot::unpack(Container * container) { @@ -26,7 +26,7 @@ App * App::Snapshot::unpack(Container * container) { } void App::Snapshot::reset() { - m_programStore.deleteAll(); + m_scriptStore.deleteAll(); } App::Descriptor * App::Snapshot::descriptor() { @@ -34,14 +34,14 @@ App::Descriptor * App::Snapshot::descriptor() { return &descriptor; } -ProgramStore * App::Snapshot::programStore() { - return &m_programStore; +ScriptStore * App::Snapshot::scriptStore() { + return &m_scriptStore; } App::App(Container * container, Snapshot * snapshot) : ::App(container, snapshot, &m_codeStackViewController, I18n::Message::Warning), m_listFooter(&m_codeStackViewController, &m_menuController, &m_menuController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey), - m_menuController(&m_listFooter, snapshot->programStore(), &m_listFooter), + m_menuController(&m_listFooter, snapshot->scriptStore(), &m_listFooter), m_codeStackViewController(&m_modalViewController, &m_listFooter) { } diff --git a/apps/code/app.h b/apps/code/app.h index 09638e397..c65d7d328 100644 --- a/apps/code/app.h +++ b/apps/code/app.h @@ -2,9 +2,9 @@ #define CODE_APP_H #include -#include "menu_controller.h" -#include "program_store.h" #include "../shared/message_controller.h" +#include "menu_controller.h" +#include "script_store.h" namespace Code { @@ -22,9 +22,9 @@ public: App * unpack(Container * container) override; void reset() override; Descriptor * descriptor() override; - ProgramStore * programStore(); + ScriptStore * scriptStore(); private: - ProgramStore m_programStore; + ScriptStore m_scriptStore; }; StackViewController * stackViewController() { return &m_codeStackViewController; } private: diff --git a/apps/code/base.de.i18n b/apps/code/base.de.i18n index f61ece54d..41db7c48a 100644 --- a/apps/code/base.de.i18n +++ b/apps/code/base.de.i18n @@ -1,10 +1,7 @@ -AddScript = "Add a script" -ScriptOptions = "Script options" -EditScript = "Edit script" -RenameScript = "Rename script" -AutoImportScript = "Auto import." -DeleteScript = "Delete script" -Console = "Console" -ConsoleError = "Error" -EditProgram = "Programm bearbeiten" -ExecuteProgram = "Programm ausfuhren" +Console = "Interaktive Konsole" +AddScript = "Skript hinzuzufugen" +ScriptOptions = "Optionen Skript" +EditScript = "Skript bearbeiten" +RenameScript = "Skript umbenennen" +AutoImportScript = "Automatischer Import" +DeleteScript = "Skript loschen" diff --git a/apps/code/base.en.i18n b/apps/code/base.en.i18n index 0dfbd1d2f..4c12673b8 100644 --- a/apps/code/base.en.i18n +++ b/apps/code/base.en.i18n @@ -1,10 +1,7 @@ +Console = "Python shell" AddScript = "Add a script" ScriptOptions = "Script options" EditScript = "Edit script" RenameScript = "Rename script" -AutoImportScript = "Auto import." +AutoImportScript = "Auto import" DeleteScript = "Delete script" -Console = "Console" -ConsoleError = "Error" -EditProgram = "Edit program" -ExecuteProgram = "Execute program" diff --git a/apps/code/base.es.i18n b/apps/code/base.es.i18n index 98a2c6152..f8dc0face 100644 --- a/apps/code/base.es.i18n +++ b/apps/code/base.es.i18n @@ -1,10 +1,7 @@ -AddScript = "Add a script" -ScriptOptions = "Script options" -EditScript = "Edit script" -RenameScript = "Rename script" -AutoImportScript = "Auto import." -DeleteScript = "Delete script" -Console = "Console" -ConsoleError = "Error" -EditProgram = "Editar el programa" -ExecuteProgram = "Ejecutar el programa" +Console = "Interprete de comandos" +AddScript = "Agregar un archivo" +ScriptOptions = "Opciones del archivo" +EditScript = "Editar el archivo" +RenameScript = "Renombrar el archivo" +AutoImportScript = "Importacion auto" +DeleteScript = "Eliminar el archivo" diff --git a/apps/code/base.fr.i18n b/apps/code/base.fr.i18n index 7088347f4..0219c5e53 100644 --- a/apps/code/base.fr.i18n +++ b/apps/code/base.fr.i18n @@ -1,10 +1,7 @@ -AddScript = "Add a script" -ScriptOptions = "Script options" -EditScript = "Edit script" -RenameScript = "Rename script" -AutoImportScript = "Auto import." -DeleteScript = "Delete script" -Console = "Console" -ConsoleError = "Error" -EditProgram = "Editer le programme" -ExecuteProgram = "Executer le programme" +Console = "Console d'execution" +AddScript = "Ajouter un script" +ScriptOptions = "Options de script" +EditScript = "Editer le script" +RenameScript = "Renommer le script" +AutoImportScript = "Importation auto" +DeleteScript = "Supprimer le script" diff --git a/apps/code/base.pt.i18n b/apps/code/base.pt.i18n index 1572add77..9818aed1e 100644 --- a/apps/code/base.pt.i18n +++ b/apps/code/base.pt.i18n @@ -1,10 +1,7 @@ -AddScript = "Add a script" -ScriptOptions = "Script options" -EditScript = "Edit script" -RenameScript = "Rename script" -AutoImportScript = "Auto import." -DeleteScript = "Delete script" -Console = "Console" -ConsoleError = "Error" -EditProgram = "Editar programa" -ExecuteProgram = "Executar programa" +Console = "Interpretador interativo" +AddScript = "Adicionar um script" +ScriptOptions = "Opcoes de script" +EditScript = "Editar o script" +RenameScript = "Renomear o script" +AutoImportScript = "Importacao auto" +DeleteScript = "Eliminar o script" diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp index 6639990c0..fea2154cb 100644 --- a/apps/code/console_controller.cpp +++ b/apps/code/console_controller.cpp @@ -1,62 +1,32 @@ #include "console_controller.h" #include #include "app.h" +#include extern "C" { #include -#include "port.h" -#include "py/mphal.h" -#include "py/compile.h" -#include "py/runtime.h" -#include "py/gc.h" -#include "py/stackctrl.h" } namespace Code { -/* mp_hal_stdout_tx_strn_cooked symbol required by micropython at printing - * needs to access information about where to print. This 'context' is provided - * by the global sCurrentConsoleStore that points to the console store. */ - -static ConsoleStore * sCurrentConsoleStore = nullptr; - -extern "C" -void mp_hal_stdout_tx_strn_cooked(const char * str, size_t len) { - assert(sCurrentConsoleStore != nullptr); - sCurrentConsoleStore->pushResult(str, len); -} - -mp_obj_t execute_from_str(const char *str) { - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_lexer_t *lex = mp_lexer_new_from_str_len(0, str, strlen(str), false); - mp_parse_tree_t pt = mp_parse(lex, MP_PARSE_SINGLE_INPUT); - mp_obj_t module_fun = mp_compile(&pt, lex->source_name, MP_EMIT_OPT_NONE, true); - mp_hal_set_interrupt_char((int)Ion::Keyboard::Key::A6); - mp_call_function_0(module_fun); - mp_hal_set_interrupt_char(-1); // Disable interrupt - nlr_pop(); - return 0; - } else { - // Uncaught exception - return (mp_obj_t) nlr.ret_val; - } -} - -ConsoleController::ConsoleController(Responder * parentResponder) : +ConsoleController::ConsoleController(Responder * parentResponder, ScriptStore * scriptStore) : ViewController(parentResponder), TextFieldDelegate(), m_rowHeight(KDText::charSize(k_fontSize).height()), m_tableView(this, this, 0, 0), m_editCell(this, this) { - assert(sCurrentConsoleStore == nullptr); - sCurrentConsoleStore = &m_consoleStore; - initPython(); + m_outputAccumulationBuffer = (char *)malloc(k_outputAccumulationBufferSize); + emptyOutputAccumulationBuffer(); + m_pythonHeap = (char *)malloc(k_pythonHeapSize); + MicroPython::init(m_pythonHeap, m_pythonHeap + k_pythonHeapSize); + MicroPython::registerScriptProvider(scriptStore); } ConsoleController::~ConsoleController() { - stopPython(); + MicroPython::deinit(); + free(m_pythonHeap); + free(m_outputAccumulationBuffer); } void ConsoleController::viewWillAppear() { @@ -131,7 +101,10 @@ bool ConsoleController::textFieldDidReceiveEvent(TextField * textField, Ion::Eve bool ConsoleController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { m_consoleStore.pushCommand(text, strlen(text)); - executePython(text); + assert(m_outputAccumulationBuffer[0] == '\0'); + runCode(text); + flushOutputAccumulationBufferToStore(); + m_consoleStore.deleteLastLineIfEmpty(); textField->setText(""); m_tableView.reloadData(); m_tableView.scrollToCell(0, m_consoleStore.numberOfLines()); @@ -148,26 +121,63 @@ Toolbox * ConsoleController::toolboxForTextField(TextField * textFied) { return nullptr; } -void ConsoleController::initPython() { - mp_stack_set_limit(40000); - mp_port_init_stack_top(); - - m_pythonHeap = (char *)malloc(16384); - gc_init(m_pythonHeap, m_pythonHeap + 16384); - - mp_init(); -} - -void ConsoleController::executePython(const char * str) { - if (execute_from_str(str)) { - mp_hal_stdout_tx_strn_cooked(I18n::translate(I18n::Message::ConsoleError), 5); +/* printText is called by the Python machine. + * The text argument is not always null-terminated. */ +void ConsoleController::printText(const char * text, size_t length) { + int textCutIndex = firstNewLineCharIndex(text, length); + // If there is no new line in text, just append it to the output accumulation + // buffer. + if (textCutIndex >= length) { + appendTextToOutputAccumulationBuffer(text, length); + return; + } + // If there is a new line in the middle of the text, we have to store at least + // two new console lines in the console store. + if (textCutIndex < length - 1) { + printText(text, textCutIndex + 1); + printText(&text[textCutIndex+1], length - (textCutIndex + 1)); + return; + } + // If there is a new line at the end of the text, we have to store the line in + // the console store. + if (textCutIndex == length - 1) { + appendTextToOutputAccumulationBuffer(text, length-1); + flushOutputAccumulationBufferToStore(); } - m_consoleStore.deleteLastLineIfEmpty(); } -void ConsoleController::stopPython() { - free(m_pythonHeap); - sCurrentConsoleStore = nullptr; +void ConsoleController::flushOutputAccumulationBufferToStore() { + m_consoleStore.pushResult(m_outputAccumulationBuffer, strlen(m_outputAccumulationBuffer)); + emptyOutputAccumulationBuffer(); +} + +void ConsoleController::appendTextToOutputAccumulationBuffer(const char * text, size_t length) { + int endOfAccumulatedText = strlen(m_outputAccumulationBuffer); + int spaceLeft = k_outputAccumulationBufferSize - endOfAccumulatedText; + if (spaceLeft > length) { + memcpy(&m_outputAccumulationBuffer[endOfAccumulatedText], text, length); + return; + } + memcpy(&m_outputAccumulationBuffer[endOfAccumulatedText], text, spaceLeft-1); + flushOutputAccumulationBufferToStore(); + appendTextToOutputAccumulationBuffer(&text[spaceLeft-1], length - (spaceLeft - 1)); +} + +void ConsoleController::emptyOutputAccumulationBuffer() { + for (int i = 0; i < k_outputAccumulationBufferSize; i++) { + m_outputAccumulationBuffer[i] = 0; + } +} + +int ConsoleController::firstNewLineCharIndex(const char * text, size_t length) { + int index = 0; + while (index < length) { + if (text[index] == '\n') { + return index; + } + index++; + } + return index; } } diff --git a/apps/code/console_controller.h b/apps/code/console_controller.h index 492a4ea79..65b5a31f9 100644 --- a/apps/code/console_controller.h +++ b/apps/code/console_controller.h @@ -2,22 +2,26 @@ #define CODE_CONSOLE_CONTROLLER_H #include -#include -#include "console_store.h" -#include "console_line_cell.h" +#include + #include "console_edit_cell.h" +#include "console_line_cell.h" +#include "console_store.h" +#include "script_store.h" namespace Code { -class ConsoleController : public ViewController, public ListViewDataSource, public ScrollViewDataSource, public TextFieldDelegate { +class ConsoleController : public ViewController, public ListViewDataSource, public ScrollViewDataSource, public TextFieldDelegate, public MicroPython::ExecutionEnvironment { public: - ConsoleController(Responder * parentResponder); + ConsoleController(Responder * parentResponder, ScriptStore * scriptStore); ~ConsoleController(); ConsoleController(const ConsoleController& other) = delete; ConsoleController(ConsoleController&& other) = delete; ConsoleController operator=(const ConsoleController& other) = delete; ConsoleController& operator=(ConsoleController&& other) = delete; + static constexpr KDText::FontSize k_fontSize = KDText::FontSize::Large; + // ViewController View * view() override { return &m_tableView; } void viewWillAppear() override; @@ -40,23 +44,31 @@ public: bool textFieldDidAbortEditing(TextField * textField, const char * text) override; Toolbox * toolboxForTextField(TextField * textFied) override; - // Python - void initPython(); - void executePython(const char * str); - void stopPython(); + // MicroPython::ExecutionEnvironment + void printText(const char * text, size_t length) override; - // Other - static constexpr KDText::FontSize k_fontSize = KDText::FontSize::Large; private: static constexpr int LineCellType = 0; static constexpr int EditCellType = 1; - static constexpr int k_numberOfLineCells = 15; // May change depending on the height + static constexpr int k_numberOfLineCells = 15; // May change depending on the screen height + static constexpr int k_pythonHeapSize = 16384; + static constexpr int k_outputAccumulationBufferSize = 40; + void flushOutputAccumulationBufferToStore(); + void appendTextToOutputAccumulationBuffer(const char * text, size_t length); + void emptyOutputAccumulationBuffer(); + int firstNewLineCharIndex(const char * text, size_t length); int m_rowHeight; ConsoleStore m_consoleStore; TableView m_tableView; ConsoleLineCell m_cells[k_numberOfLineCells]; ConsoleEditCell m_editCell; char * m_pythonHeap; + char * m_outputAccumulationBuffer; + /* The Python machine might call printText several times to print a single + * string. We thus use m_outputAccumulationBuffer to store and concatenate the + * different strings until a new line char appears in the text. When this + * happens, or when m_outputAccumulationBuffer is full, we create a new + * ConsoleLine in the ConsoleStore and empty m_outputAccumulationBuffer. */ }; } diff --git a/apps/code/console_edit_cell.h b/apps/code/console_edit_cell.h index 034202573..c075648fb 100644 --- a/apps/code/console_edit_cell.h +++ b/apps/code/console_edit_cell.h @@ -6,7 +6,6 @@ #include #include #include -#include "console_line.h" namespace Code { diff --git a/apps/code/console_store.cpp b/apps/code/console_store.cpp index b6c3ad1ea..76ce1a02d 100644 --- a/apps/code/console_store.cpp +++ b/apps/code/console_store.cpp @@ -50,7 +50,6 @@ void ConsoleStore::pushResult(const char * text, size_t length) { push(ResultMarker, text, length); } - void ConsoleStore::deleteLastLineIfEmpty() { ConsoleLine lastLine = lineAtIndex(numberOfLines()-1); char lastLineFirstChar = lastLine.text()[0]; diff --git a/apps/code/editor_controller.cpp b/apps/code/editor_controller.cpp index 091adda0e..cad867ddb 100644 --- a/apps/code/editor_controller.cpp +++ b/apps/code/editor_controller.cpp @@ -8,8 +8,8 @@ EditorController::EditorController() : { } -void EditorController::setProgram(Program program){ - m_view.setText(program.editableContent(), program.bufferSize()); +void EditorController::setScript(Script script){ + m_view.setText(script.editableContent(), script.bufferSize()); } bool EditorController::handleEvent(Ion::Events::Event event) { diff --git a/apps/code/editor_controller.h b/apps/code/editor_controller.h index 1b99805fa..d819a4eff 100644 --- a/apps/code/editor_controller.h +++ b/apps/code/editor_controller.h @@ -2,14 +2,14 @@ #define CODE_EDITOR_CONTROLLER_H #include -#include "program.h" +#include "script.h" namespace Code { class EditorController : public ViewController { public: EditorController(); - void setProgram(Program program); + void setScript(Script script); View * view() override { return &m_view; } bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; diff --git a/apps/code/menu_controller.cpp b/apps/code/menu_controller.cpp index 62a9acf72..51e9045e4 100644 --- a/apps/code/menu_controller.cpp +++ b/apps/code/menu_controller.cpp @@ -4,18 +4,18 @@ namespace Code { -MenuController::MenuController(Responder * parentResponder, ProgramStore * programStore, ButtonRowController * footer) : +MenuController::MenuController(Responder * parentResponder, ScriptStore * scriptStore, ButtonRowController * footer) : ViewController(parentResponder), ButtonRowDelegate(nullptr, footer), - m_programStore(programStore), - m_addNewProgramCell(I18n::Message::AddScript), + m_scriptStore(scriptStore), + m_addNewScriptCell(I18n::Message::AddScript), m_consoleButton(this, I18n::Message::Console, Invocation([](void * context, void * sender) { MenuController * menu = (MenuController *)context; menu->app()->displayModalViewController(menu->consoleController(), 0.5f, 0.5f); }, this)), m_selectableTableView(this, this, 0, 1, 0, 0, 0, 0, this, nullptr, false), - m_consoleController(parentResponder), - m_programParameterController(nullptr, I18n::Message::ScriptOptions, m_programStore) + m_consoleController(parentResponder, m_scriptStore), + m_scriptParameterController(nullptr, I18n::Message::ScriptOptions, m_scriptStore) { for (int i = 0; i< k_maxNumberOfCells; i++) { m_cells[i].setMessageFontSize(KDText::FontSize::Large); @@ -50,32 +50,32 @@ bool MenuController::handleEvent(Ion::Events::Event event) { } if (event == Ion::Events::OK || event == Ion::Events::EXE) { int selectedRow = m_selectableTableView.selectedRow(); - if (selectedRow >= 0 && selectedRow < m_programStore->numberOfPrograms()) { - configureProgram(); + if (selectedRow >= 0 && selectedRow < m_scriptStore->numberOfScripts()) { + configureScript(); return true; - } else if (selectedRow == m_programStore->numberOfPrograms()) { - addProgram(); + } else if (selectedRow == m_scriptStore->numberOfScripts()) { + addScript(); return true; } } return false; } -void MenuController::configureProgram() { - m_programParameterController.setProgram(m_selectableTableView.selectedRow()); - stackViewController()->push(&m_programParameterController); +void MenuController::configureScript() { + m_scriptParameterController.setScript(m_selectableTableView.selectedRow()); + stackViewController()->push(&m_scriptParameterController); } -void MenuController::addProgram() { +void MenuController::addScript() { m_selectableTableView.selectCellAtLocation(0, 0); - m_programStore->addDefaultProgram(); + m_scriptStore->addNewScript(); m_selectableTableView.reloadData(); m_selectableTableView.selectCellAtLocation(0, numberOfRows()-2); } int MenuController::numberOfRows() { - return m_programStore->numberOfPrograms() + 1; - //TODO do not add the addProgram row if there can be no more programs stored. + return m_scriptStore->numberOfScripts() + 1; + //TODO do not add the addScript row if there can be no more scripts stored. }; KDCoordinate MenuController::cellHeight() { @@ -84,19 +84,19 @@ KDCoordinate MenuController::cellHeight() { HighlightCell * MenuController::reusableCell(int index) { assert(index >= 0); - if (index < m_programStore->numberOfPrograms()) { + if (index < m_scriptStore->numberOfScripts()) { return &m_cells[index]; } - assert(index == m_programStore->numberOfPrograms()); - return &m_addNewProgramCell; + assert(index == m_scriptStore->numberOfScripts()); + return &m_addNewScriptCell; } int MenuController::reusableCellCount() { - return m_programStore->numberOfPrograms() + 1; + return m_scriptStore->numberOfScripts() + 1; } void MenuController::willDisplayCellForIndex(HighlightCell * cell, int index) { - if (index < m_programStore->numberOfPrograms()) { + if (index < m_scriptStore->numberOfScripts()) { MessageTableCell * myCell = (MessageTableCell *)cell; // TODO: store script names myCell->setMessage(I18n::Message::Console); diff --git a/apps/code/menu_controller.h b/apps/code/menu_controller.h index fa96374ad..ffadeb063 100644 --- a/apps/code/menu_controller.h +++ b/apps/code/menu_controller.h @@ -2,20 +2,20 @@ #define CODE_MENU_CONTROLLER_H #include -#include "console_controller.h" -#include "program_parameter_controller.h" #include -#include "program_store.h" +#include "console_controller.h" +#include "script_parameter_controller.h" +#include "script_store.h" namespace Code { class MenuController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource, public ButtonRowDelegate { public: - MenuController(Responder * parentResponder, ProgramStore * programStore, ButtonRowController * footer); + MenuController(Responder * parentResponder, ScriptStore * scriptStore, ButtonRowController * footer); ConsoleController * consoleController(); StackViewController * stackViewController(); - void configureProgram(); - void addProgram(); + void configureScript(); + void addScript(); /* ViewController */ View * view() override; @@ -35,13 +35,13 @@ public: private: constexpr static int k_maxNumberOfCells = 5; //TODO static constexpr KDCoordinate k_rowHeight = 50; //TODO create common parent class with Shared::ListController - ProgramStore * m_programStore; + ScriptStore * m_scriptStore; MessageTableCell m_cells[k_maxNumberOfCells]; - Shared::NewFunctionCell m_addNewProgramCell; + Shared::NewFunctionCell m_addNewScriptCell; Button m_consoleButton; SelectableTableView m_selectableTableView; ConsoleController m_consoleController; - ProgramParameterController m_programParameterController; + ScriptParameterController m_scriptParameterController; }; } diff --git a/apps/code/program.cpp b/apps/code/program.cpp deleted file mode 100644 index 9fcb3c29c..000000000 --- a/apps/code/program.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "program.h" - -namespace Code { - -Program::Program(char * textBuffer, size_t sizeOfBuffer) : - m_bufferSize(sizeOfBuffer), - m_textBuffer(textBuffer) -{ -} - -const char * Program::readOnlyContent() const { - return m_textBuffer; -} - -char * Program::editableContent() { - return m_textBuffer; -} -int Program::bufferSize() const { - return m_bufferSize; -} - -} diff --git a/apps/code/program_parameter_controller.cpp b/apps/code/program_parameter_controller.cpp deleted file mode 100644 index 27ed8060e..000000000 --- a/apps/code/program_parameter_controller.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "program_parameter_controller.h" - -namespace Code { - -ProgramParameterController::ProgramParameterController(Responder * parentResponder, I18n::Message title, ProgramStore * programStore) : - ViewController(parentResponder), - m_pageTitle(title), - m_editProgram(I18n::Message::Default), - m_renameProgram(I18n::Message::Default), - m_autoImportProgram(I18n::Message::Default), - m_deleteProgram(I18n::Message::Default), - m_selectableTableView(this, this, 0, 1, Metric::CommonTopMargin, Metric::CommonRightMargin, - Metric::CommonBottomMargin, Metric::CommonLeftMargin, this), - m_programStore(programStore) -{ -} - -void ProgramParameterController::setProgram(int i){ - m_editorController.setProgram(m_programStore->editableProgram(i)); -} - -View * ProgramParameterController::view() { - return &m_selectableTableView; -} - -const char * ProgramParameterController::title() { - return I18n::translate(m_pageTitle); -} - -bool ProgramParameterController::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::OK || event == Ion::Events::EXE || (event == Ion::Events::Right && selectedRow() == 1)) { - switch (selectedRow()) { - case 0: - app()->displayModalViewController(&m_editorController, 0.5f, 0.5f); - return true; - //TODO other cases - default: - assert(false); - return false; - } - } - return false; -} - -void ProgramParameterController::didBecomeFirstResponder() { - if (selectedRow() < 0) { - selectCellAtLocation(0, 0); - } - app()->setFirstResponder(&m_selectableTableView); -} - -KDCoordinate ProgramParameterController::cellHeight() { - return Metric::ParameterCellHeight; -} - -HighlightCell * ProgramParameterController::reusableCell(int index) { - assert(index >= 0); - assert(index < k_totalNumberOfCell); - HighlightCell * cells[] = {&m_editProgram, &m_renameProgram, &m_autoImportProgram, &m_deleteProgram}; - return cells[index]; - -} - -int ProgramParameterController::reusableCellCount() { - return k_totalNumberOfCell; -} - -int ProgramParameterController::numberOfRows() { - return k_totalNumberOfCell; -} - -void ProgramParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) { - MessageTableCell * myCell = (MessageTableCell *)cell; - I18n::Message labels[k_totalNumberOfCell] = {I18n::Message::EditScript, I18n::Message::RenameScript, I18n::Message::AutoImportScript, I18n::Message::DeleteScript}; - myCell->setMessage(labels[index]); -} - -} diff --git a/apps/code/program_store.cpp b/apps/code/program_store.cpp deleted file mode 100644 index f6c69af27..000000000 --- a/apps/code/program_store.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "program_store.h" -#include "string.h" -#include - -namespace Code { - -ProgramStore::ProgramStore() : - m_numberOfPrograms(0), - m_lastProgramEdited(-1) -{ - for (int i = 0; i= 0 && i < numberOfPrograms()); - cleanAndMoveFreeSpaceAfterProgram(i); - int beginningOfProgram = indexOfProgram(i); - - // Compute the size of the program, including the free space of m_history - int sizeOfEditableProgram = 0; - for (int j=beginningOfProgram; j= 0 && i < numberOfPrograms()); - int beginningOfProgram = indexOfProgram(i); - return Program(&m_history[beginningOfProgram], strlen(&m_history[beginningOfProgram] + 1)); -} - -int ProgramStore::numberOfPrograms() const { - return m_numberOfPrograms; -} - -Program ProgramStore::editableNewProgram() { - cleanAndMoveFreeSpaceAfterProgram(numberOfPrograms()-1); - m_numberOfPrograms++; - return Program(&m_history[indexOfFirstFreeSpaceMarker()], sizeOfFreeSpace() - 1); -} - -void ProgramStore::deleteProgram(int i){ - assert (i >= 0 && i < numberOfPrograms()); - cleanAndMoveFreeSpaceAfterProgram(i); - int indexOfCharToDelete = indexOfProgram(i); - while (m_history[indexOfCharToDelete] != FreeSpaceMarker) { - m_history[indexOfCharToDelete] = FreeSpaceMarker; - indexOfCharToDelete++; - } - m_numberOfPrograms--; -} - -void ProgramStore::deleteAll(){ - for (int i = 0; i= 0 && i < numberOfPrograms()); - int currentProgramNumber = 0; - int beginningOfProgram = 0; - while (m_history[beginningOfProgram] == FreeSpaceMarker) { - beginningOfProgram++; - } - if (i == 0) { - return beginningOfProgram; - } - for (int j=beginningOfProgram; j 0) { - return sizeOfFreeSpace; - } - } - } - return sizeOfFreeSpace; -} - -void ProgramStore::cleanFreeSpace() { - if (m_lastProgramEdited < 0 || m_lastProgramEdited >= numberOfPrograms()) { - return; - } - int indexOfCharToChangeIntoFreeSpaceMarker = lastIndexOfProgram(m_lastProgramEdited) + 1; - while (m_history[indexOfCharToChangeIntoFreeSpaceMarker] != FreeSpaceMarker) { - m_history[indexOfCharToChangeIntoFreeSpaceMarker] = FreeSpaceMarker; - indexOfCharToChangeIntoFreeSpaceMarker ++; - } -} - -void ProgramStore::moveFreeSpaceAfterProgram(int i) { - m_lastProgramEdited = i; - - // If the free space is already just after the program, return - int indexOfFreeSpace = indexOfFirstFreeSpaceMarker(); - int lastIndexOfPrgm = lastIndexOfProgram(i); - if (indexOfFreeSpace == lastIndexOfPrgm + 1){ - return; - } - // Else move the free space depending on its relative position with the program - int freeSpaceSize = sizeOfFreeSpace(); - if (indexOfFreeSpace > lastIndexOfPrgm) { - int indexOfNextProgram = lastIndexOfPrgm+1; - int len = indexOfFreeSpace - indexOfNextProgram; - // Use memmove to avoid overwriting - memmove(&m_history[indexOfNextProgram + freeSpaceSize], &m_history[indexOfNextProgram], len); - for (int j = indexOfNextProgram; j= 0 && i sizeOfFreeSpace() - 1) { // We keep at keast one free char. - return false; - } - cleanAndMoveFreeSpaceAfterProgram(numberOfPrograms()-1); - memcpy(&m_history[indexOfFirstFreeSpaceMarker()], program, len+1); - m_numberOfPrograms++; - m_lastProgramEdited = m_numberOfPrograms-1; - return true; -} - -} diff --git a/apps/code/program_store.h b/apps/code/program_store.h deleted file mode 100644 index 72d4cb361..000000000 --- a/apps/code/program_store.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef CODE_PROGRAM_STORE_H -#define CODE_PROGRAM_STORE_H - -#include "program.h" - -namespace Code { - -class ProgramStore { -public: - ProgramStore(); - Program editableProgram(int i); - /* editableProgram moves the free space of m_history at the end of the - * ith script. It returns a Program object that points to the beginning of the - * wanted script and has a length taking into account both the script and the - * free space. */ - Program program(int i); - /* programAtIndex returns a Program object that points to the beginning of the - * ith script and has the length of the script. */ - int numberOfPrograms() const; - Program editableNewProgram(); - bool addDefaultProgram(); - void deleteProgram(int i); - void deleteAll(); -private: - static constexpr char FreeSpaceMarker = 0x01; - /* We made sure that 0x01 is not used in ion/include/ion/charset.h */ - static constexpr int k_historySize = 1024; - int indexOfProgram(int i) const; - int lastIndexOfProgram(int i) const; - int indexOfFirstFreeSpaceMarker() const; - int sizeOfFreeSpace() const; - void cleanFreeSpace(); - void moveFreeSpaceAfterProgram(int i); - void cleanAndMoveFreeSpaceAfterProgram(int i); - char m_history[k_historySize]; - /* The m_history variable sequentially stores scripts as text buffers. The - * free bytes of m_history contain the FreeSpaceMarker. By construction, - * there is always at least one free byte, and the free space is always - * continuous. */ - int m_numberOfPrograms; - int m_lastProgramEdited; -}; - -} - -#endif diff --git a/apps/code/script.cpp b/apps/code/script.cpp new file mode 100644 index 000000000..d2ed1a662 --- /dev/null +++ b/apps/code/script.cpp @@ -0,0 +1,22 @@ +#include "script.h" + +namespace Code { + +Script::Script(char * textBuffer, size_t sizeOfBuffer) : + m_bufferSize(sizeOfBuffer), + m_textBuffer(textBuffer) +{ +} + +const char * Script::readOnlyContent() const { + return m_textBuffer; +} + +char * Script::editableContent() { + return m_textBuffer; +} +size_t Script::bufferSize() const { + return m_bufferSize; +} + +} diff --git a/apps/code/program.h b/apps/code/script.h similarity index 54% rename from apps/code/program.h rename to apps/code/script.h index 7de37c75b..28d30785b 100644 --- a/apps/code/program.h +++ b/apps/code/script.h @@ -1,16 +1,16 @@ -#ifndef CODE_PROGRAM_H -#define CODE_PROGRAM_H +#ifndef CODE_SCRIPT_H +#define CODE_SCRIPT_H #include namespace Code { -class Program { +class Script { public: - Program(char * textBuffer, size_t sizeOfBuffer); + Script(char * textBuffer = nullptr, size_t sizeOfBuffer = 0); const char * readOnlyContent() const; char * editableContent(); - int bufferSize() const; + size_t bufferSize() const; private: size_t m_bufferSize; char * m_textBuffer; diff --git a/apps/code/script_parameter_controller.cpp b/apps/code/script_parameter_controller.cpp new file mode 100644 index 000000000..d940f95f7 --- /dev/null +++ b/apps/code/script_parameter_controller.cpp @@ -0,0 +1,78 @@ +#include "script_parameter_controller.h" + +namespace Code { + +ScriptParameterController::ScriptParameterController(Responder * parentResponder, I18n::Message title, ScriptStore * scriptStore) : + ViewController(parentResponder), + m_pageTitle(title), + m_editScript(I18n::Message::Default), + m_renameScript(I18n::Message::Default), + m_autoImportScript(I18n::Message::Default), + m_deleteScript(I18n::Message::Default), + m_selectableTableView(this, this, 0, 1, Metric::CommonTopMargin, Metric::CommonRightMargin, + Metric::CommonBottomMargin, Metric::CommonLeftMargin, this), + m_scriptStore(scriptStore) +{ +} + +void ScriptParameterController::setScript(int i){ + m_editorController.setScript(m_scriptStore->editableScript(i)); +} + +View * ScriptParameterController::view() { + return &m_selectableTableView; +} + +const char * ScriptParameterController::title() { + return I18n::translate(m_pageTitle); +} + +bool ScriptParameterController::handleEvent(Ion::Events::Event event) { + if (event == Ion::Events::OK || event == Ion::Events::EXE) { + switch (selectedRow()) { + case 0: + app()->displayModalViewController(&m_editorController, 0.5f, 0.5f); + return true; + //TODO other cases + default: + assert(false); + return false; + } + } + return false; +} + +void ScriptParameterController::didBecomeFirstResponder() { + if (selectedRow() < 0) { + selectCellAtLocation(0, 0); + } + app()->setFirstResponder(&m_selectableTableView); +} + +KDCoordinate ScriptParameterController::cellHeight() { + return Metric::ParameterCellHeight; +} + +HighlightCell * ScriptParameterController::reusableCell(int index) { + assert(index >= 0); + assert(index < k_totalNumberOfCell); + HighlightCell * cells[] = {&m_editScript, &m_renameScript, &m_autoImportScript, &m_deleteScript}; + return cells[index]; + +} + +int ScriptParameterController::reusableCellCount() { + return k_totalNumberOfCell; +} + +int ScriptParameterController::numberOfRows() { + return k_totalNumberOfCell; +} + +void ScriptParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) { + MessageTableCell * myCell = (MessageTableCell *)cell; + I18n::Message labels[k_totalNumberOfCell] = {I18n::Message::EditScript, I18n::Message::RenameScript, I18n::Message::AutoImportScript, I18n::Message::DeleteScript}; + myCell->setMessage(labels[index]); +} + +} diff --git a/apps/code/program_parameter_controller.h b/apps/code/script_parameter_controller.h similarity index 56% rename from apps/code/program_parameter_controller.h rename to apps/code/script_parameter_controller.h index ad3920949..398ffe39f 100644 --- a/apps/code/program_parameter_controller.h +++ b/apps/code/script_parameter_controller.h @@ -1,17 +1,17 @@ -#ifndef CODE_PROGRAM_PARAMETER_CONTROLLER_H -#define CODE_PROGRAM_PARAMETER_CONTROLLER_H +#ifndef CODE_SCRIPT_PARAMETER_CONTROLLER_H +#define CODE_SCRIPT_PARAMETER_CONTROLLER_H #include #include "../i18n.h" #include "editor_controller.h" -#include "program_store.h" +#include "script_store.h" namespace Code { -class ProgramParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource { +class ScriptParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource { public: - ProgramParameterController(Responder * parentResponder, I18n::Message title, ProgramStore * programStore); - void setProgram(int i); + ScriptParameterController(Responder * parentResponder, I18n::Message title, ScriptStore * scriptStore); + void setScript(int i); /* ViewController */ View * view() override; @@ -29,13 +29,13 @@ public: private: constexpr static int k_totalNumberOfCell = 4; I18n::Message m_pageTitle; - MessageTableCell m_editProgram; - MessageTableCell m_renameProgram; - MessageTableCellWithSwitch m_autoImportProgram; - MessageTableCell m_deleteProgram; + MessageTableCell m_editScript; + MessageTableCell m_renameScript; + MessageTableCellWithSwitch m_autoImportScript; + MessageTableCell m_deleteScript; SelectableTableView m_selectableTableView; EditorController m_editorController; - ProgramStore * m_programStore; + ScriptStore * m_scriptStore; }; } diff --git a/apps/code/script_store.cpp b/apps/code/script_store.cpp new file mode 100644 index 000000000..6080071b3 --- /dev/null +++ b/apps/code/script_store.cpp @@ -0,0 +1,363 @@ +#include "script_store.h" +#include "string.h" +#include + +namespace Code { + +ScriptStore::ScriptStore() : + m_numberOfScripts(0), + m_lastEditedStringPosition(0) +{ + for (int i = 0; i= 0 && i < numberOfScripts()); + cleanAndMoveFreeSpaceAfterScriptContent(i); + int beginningOfScriptContent = indexOfScriptContent(i); + + // Compute the size of the script, including the free space of m_history + int sizeOfEditableScript = 0; + for (int j=beginningOfScriptContent; j= 0 && i < numberOfScripts()); + int beginningOfScriptContent = indexOfScriptContent(i); + return Script(&m_history[beginningOfScriptContent], strlen(&m_history[beginningOfScriptContent]) + 1); +} + +Script ScriptStore::script(const char * name) { + for (int i=0; i= 0 && i < numberOfScripts()); + cleanAndMoveFreeSpaceAfterScriptName(i); + return &m_history[indexOfScript(i)+1]; +} + +int ScriptStore::sizeOfEditableNameOfScript(int i) { + int namePosition = indexOfScript(i)+1; + int contentPosition = indexOfScriptContent(i); + return contentPosition-namePosition; +} + +int ScriptStore::numberOfScripts() const { + return m_numberOfScripts; +} + +bool ScriptStore::addNewScript() { + int sizeFreeSpace = sizeOfFreeSpace(); + if (sizeFreeSpace < 4) { + return false; + } + cleanAndMoveFreeSpaceAfterScriptContent(numberOfScripts()-1); + int beginningNewScript = indexOfFirstFreeSpaceMarker(); + m_history[beginningNewScript] = AutoImportationMarker; + if (copyName(beginningNewScript + 1)) { + if (copyDefaultScriptOnFreeSpace()) { + m_numberOfScripts++; + m_lastEditedStringPosition = indexOfScriptContent(numberOfScripts()-1); + return true; + } + } + for (int i = beginningNewScript; i= 0 && i < numberOfScripts()); + cleanAndMoveFreeSpaceAfterScriptContent(i); + int indexOfCharToDelete = indexOfScript(i); + while (m_history[indexOfCharToDelete] != FreeSpaceMarker) { + m_history[indexOfCharToDelete] = FreeSpaceMarker; + indexOfCharToDelete++; + } + m_numberOfScripts--; +} + +void ScriptStore::deleteAll() { + for (int i = 0; i sizeOfFreeSpace() - 1) { // We keep at keast one free char. + return false; + } + memcpy(&m_history[position], name, len+1); + return true; + } else { + const char newName[] = "script.py"; + int len = strlen(newName); + if (len > sizeOfFreeSpace() - 1) { // We keep at keast one free char. + return false; + } + memcpy(&m_history[position], &newName, len+1); + return true; + + } +} + +int ScriptStore::indexOfScriptContent(int i) const { + int indexOfScriptContent = indexOfScript(i)+1; + while (m_history[indexOfScriptContent] != 0 ) { + indexOfScriptContent++; + } + indexOfScriptContent++; + return indexOfScriptContent; +} + +int ScriptStore::indexOfScript(int i) const { + assert (i >= 0 && i < numberOfScripts()); + int currentScriptNumber = 0; + int beginningOfScript = 0; + while (m_history[beginningOfScript] == FreeSpaceMarker) { + beginningOfScript++; + } + if (i == 0) { + return beginningOfScript; + } + bool goingThroughName = true; + for (int j=beginningOfScript; j 0) { + return sizeOfFreeSpace; + } + } + } + return sizeOfFreeSpace; +} + +void ScriptStore::cleanFreeSpace() { + if (m_history[m_lastEditedStringPosition] == FreeSpaceMarker + || m_history[m_lastEditedStringPosition] == AutoImportationMarker + || m_history[m_lastEditedStringPosition] == NoAutoImportationMarker) + { + return; + } + int indexOfCharToChangeIntoFreeSpaceMarker = m_lastEditedStringPosition + + strlen (&m_history[m_lastEditedStringPosition]) + 1; + while (m_history[indexOfCharToChangeIntoFreeSpaceMarker] != FreeSpaceMarker) { + m_history[indexOfCharToChangeIntoFreeSpaceMarker] = FreeSpaceMarker; + indexOfCharToChangeIntoFreeSpaceMarker ++; + } +} + +void ScriptStore::moveFreeSpaceAfterScriptContent(int i) { + int indexOfFreeSpace = indexOfFirstFreeSpaceMarker(); + int newFreeSpacePosition = lastIndexOfScript(i) + 1; + if (indexOfFreeSpace < newFreeSpacePosition) { + newFreeSpacePosition -= sizeOfFreeSpace(); + } + moveFreeSpaceAtPosition(newFreeSpacePosition); +} + +void ScriptStore::moveFreeSpaceAfterScriptName(int i) { + int newFreeSpacePosition = indexOfScript(i); + while (m_history[newFreeSpacePosition] != 0) { + newFreeSpacePosition++; + } + newFreeSpacePosition++; + moveFreeSpaceAtPosition(newFreeSpacePosition); +} + +void ScriptStore::moveFreeSpaceAtPosition(int i) { + int indexOfFreeSpace = indexOfFirstFreeSpaceMarker(); + if (indexOfFreeSpace != i){ + + // First, move the chars that would be overriden by the free space. + int freeSpaceSize = sizeOfFreeSpace(); + int len, src, dst; + // The indexes depend on the relative positions of the free space and the + // new destination. + if (indexOfFreeSpace > i) { + len = indexOfFreeSpace - i; + src = i; + dst = i + freeSpaceSize; + } else { + src = indexOfFreeSpace + freeSpaceSize; + len = i + freeSpaceSize - src; + dst = indexOfFreeSpace; + } + memmove(&m_history[dst], &m_history[src], len); + + // Then move the free space. + for (int j = i ; j 1 && m_history[m_lastEditedStringPosition-1] != 0) { + m_lastEditedStringPosition--; + } + if (m_lastEditedStringPosition <= 1) { + m_lastEditedStringPosition = 0; + } +} + +void ScriptStore::cleanAndMoveFreeSpaceAfterScriptContent(int i) { + if (i >= 0 && i= 0 && i sizeOfFreeSpace() - 1) { // We keep at keast one free char. + return false; + } + memcpy(&m_history[indexOfFirstFreeSpaceMarker()], script, len+1); + return true; +} + +bool ScriptStore::copyDefaultScriptOnFreeSpace() { + const char script[] = R"(def factorial(n): + if n == 0: + return 1 + else: + return n * factorial(n-1) +)"; + + int len = strlen(script); + if (len + 1 > sizeOfFreeSpace() - 1) { // We keep at keast one free char. + return false; + } + memcpy(&m_history[indexOfFirstFreeSpaceMarker()], script, len+1); + return true; +} + +int ScriptStore::indexBeginningFilename(const char * path) { + int beginningFilename = strlen(path)-1; + while (beginningFilename > 0 && path[beginningFilename - 1] != '\\') { + beginningFilename--; + } + return beginningFilename; +} + +} diff --git a/apps/code/script_store.h b/apps/code/script_store.h new file mode 100644 index 000000000..b1a2ebb91 --- /dev/null +++ b/apps/code/script_store.h @@ -0,0 +1,72 @@ +#ifndef CODE_SCRIPT_STORE_H +#define CODE_SCRIPT_STORE_H + +#include "script.h" +#include + +namespace Code { + +class ScriptStore : public MicroPython::ScriptProvider { +public: + ScriptStore(); + Script editableScript(int i); + /* editableScript moves the free space of m_history at the end of the + * ith script. It returns a Script object that points to the beginning of the + * wanted script and has a length taking into account both the script and the + * free space. */ + Script script(int i); + /* script returns a Script object that points to the beginning of the + * ith script and has the length of the script. */ + Script script(const char * name); + /* script(const char * name) looks for a script that has the right name and + * returns it. If there is no such script, it returns an empty Script. */ + char * editableNameOfScript(int i); + int sizeOfEditableNameOfScript(int i); + int numberOfScripts() const; + bool addNewScript(); + bool addMandelbrotScript(); + void deleteScript(int i); + void deleteAll(); + + /* MicroPython::ScriptProvider */ + const char * contentOfScript(const char * name) override; + +private: + static constexpr char FreeSpaceMarker = 0x01; + static constexpr char AutoImportationMarker = 0x02; + static constexpr char NoAutoImportationMarker = 0x03; + /* We made sure that these chars are not used in ion/include/ion/charset.h */ + static constexpr int k_historySize = 1024; + char * nameOfScript(int i); + bool copyName(int position, const char * name = nullptr); + int indexOfScriptContent(int i) const; + int indexOfScript(int i) const; + int lastIndexOfScript(int i) const; + int indexOfFirstFreeSpaceMarker() const; + int sizeOfFreeSpace() const; + void cleanFreeSpace(); + void moveFreeSpaceAfterScriptContent(int i); + void moveFreeSpaceAfterScriptName(int i); + void moveFreeSpaceAtPosition(int i); + void cleanAndMoveFreeSpaceAfterScriptContent(int i); + void cleanAndMoveFreeSpaceAfterScriptName(int i); + bool copyMandelbrotScriptOnFreeSpace(); + bool copyDefaultScriptOnFreeSpace(); + int indexBeginningFilename(const char * path); + char m_history[k_historySize]; + /* The m_history variable sequentially stores scripts as text buffers. + * Each script is stored as follow: + * - First, a char that says whether the script should be automatically + * imported in the console. + * - Then, the name of the script. + * - Finally, the content of the script. + * The free bytes of m_history contain the FreeSpaceMarker. By construction, + * there is always at least one free byte, and the free space is always + * continuous. */ + int m_numberOfScripts; + int m_lastEditedStringPosition; +}; + +} + +#endif diff --git a/apps/code/test/mpprint.cpp b/apps/code/test/mpprint.cpp deleted file mode 100644 index 25ae77782..000000000 --- a/apps/code/test/mpprint.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -extern "C" -void mp_hal_stdout_tx_strn_cooked(const char * str, size_t len) { -} - diff --git a/apps/shared.universal.i18n b/apps/shared.universal.i18n index 38b794ea3..3a5c60247 100644 --- a/apps/shared.universal.i18n +++ b/apps/shared.universal.i18n @@ -107,3 +107,4 @@ Prediction95CommandWithArg = "prediction95(p,n)" PredictionCommandWithArg = "prediction(p,n)" ConfidenceCommandWithArg = "confidence(f,n)" ConsolePrompt = ">>> " +ConsoleError = "Error" diff --git a/liba/include/setjmp.h b/liba/include/setjmp.h index f2b992448..6a7ccf2e4 100644 --- a/liba/include/setjmp.h +++ b/liba/include/setjmp.h @@ -1,6 +1,8 @@ #ifndef LIBA_SETJMP_H #define LIBA_SETJMP_H +#include "private/macros.h" + /* We are preseving registers: * - sp & lr -> 2x4 bytes * - General purpose registers: r4-r9, r10 = sl, r11 = fp -> 8x4 bytes @@ -10,8 +12,12 @@ * (See C Library ABI for the ARM architecture documentation) * The minimum buffer size is then (2+8+16+1+4)xsizeof(int64_t). */ +LIBA_BEGIN_DECLS + typedef int jmp_buf[31]; void longjmp(jmp_buf env, int val); int setjmp(jmp_buf env); +LIBA_END_DECLS + #endif diff --git a/python/Makefile b/python/Makefile index 913ccc520..07edec47d 100644 --- a/python/Makefile +++ b/python/Makefile @@ -174,6 +174,7 @@ python/src/py/emitnative.o: CFLAGS += -DN_THUMB port_objs += $(addprefix python/port/,\ port.o \ + import_helper.o\ interrupt_helper.o \ modkandinsky.o \ modkandinsky_impl.o \ diff --git a/python/port/import_helper.c b/python/port/import_helper.c new file mode 100644 index 000000000..6b3566160 --- /dev/null +++ b/python/port/import_helper.c @@ -0,0 +1,7 @@ +#include "py/builtin.h" +#include "py/obj.h" + +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); diff --git a/python/port/port.c b/python/port/port.c deleted file mode 100644 index f11a23416..000000000 --- a/python/port/port.c +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include -#include - -#include "py/nlr.h" -#include "py/compile.h" -#include "py/runtime.h" -#include "py/repl.h" -#include "py/gc.h" -#include "py/mperrno.h" - -typedef jmp_buf regs_t; - -STATIC void gc_helper_get_regs(regs_t arr) { - setjmp(arr); -} - -static char * python_stack_top = NULL; - -void mp_port_init_stack_top() { - char dummy; - python_stack_top = &dummy; -} - -void gc_collect(void) { - assert(python_stack_top != NULL); - gc_collect_start(); - - /* get the registers. - * regs is the also the last object on the stack so the stack is bound by - * ®s and python_stack_top. */ - regs_t regs; - gc_helper_get_regs(regs); - - void **regs_ptr = (void**)(void*)®s; - gc_collect_root(regs_ptr, ((uintptr_t)python_stack_top - (uintptr_t)®s) / sizeof(uintptr_t)); - - gc_collect_end(); -} - -mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - mp_raise_OSError(MP_ENOENT); -} - -mp_import_stat_t mp_import_stat(const char *path) { - return MP_IMPORT_STAT_NO_EXIST; -} - -mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); - -void nlr_jump_fail(void *val) { - while (1); -} diff --git a/python/port/port.cpp b/python/port/port.cpp new file mode 100644 index 000000000..c8eb25416 --- /dev/null +++ b/python/port/port.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +extern "C" { +#include "py/builtin.h" +#include "py/mphal.h" +#include "py/nlr.h" +#include "py/compile.h" +#include "py/lexer.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/stackctrl.h" +#include "py/gc.h" +#include "py/mperrno.h" +} + +#include +#include +#include "port.h" + +static char * python_stack_top = NULL; + +static MicroPython::ScriptProvider * sScriptProvider = nullptr; +static MicroPython::ExecutionEnvironment * sCurrentExecutionEnvironment = nullptr; + +void MicroPython::ExecutionEnvironment::runCode(const char * str) { + assert(sCurrentExecutionEnvironment == nullptr); + sCurrentExecutionEnvironment = this; + + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(0, str, strlen(str), false); + mp_parse_tree_t pt = mp_parse(lex, MP_PARSE_SINGLE_INPUT); + mp_obj_t module_fun = mp_compile(&pt, lex->source_name, MP_EMIT_OPT_NONE, true); + mp_hal_set_interrupt_char((int)Ion::Keyboard::Key::A6); + mp_call_function_0(module_fun); + mp_hal_set_interrupt_char(-1); // Disable interrupt + nlr_pop(); + } else { + // Uncaught exception + //return (mp_obj_t) nlr.ret_val; + printText(I18n::translate(I18n::Message::ConsoleError), 5); + } + + assert(sCurrentExecutionEnvironment == this); + sCurrentExecutionEnvironment = nullptr; +} + +void MicroPython::init(void * heapStart, void * heapEnd) { + mp_stack_set_limit(40000); + char dummy; + python_stack_top = &dummy; + gc_init(heapStart, heapEnd); + mp_init(); +} + +void MicroPython::deinit(){ + mp_deinit(); +} + +void MicroPython::registerScriptProvider(ScriptProvider * s) { + sScriptProvider = s; +} + +void gc_collect(void) { + assert(python_stack_top != NULL); + gc_collect_start(); + + /* get the registers. + * regs is the also the last object on the stack so the stack is bound by + * ®s and python_stack_top. */ + jmp_buf regs; + setjmp(regs); + + void **regs_ptr = (void**)(void*)®s; + gc_collect_root(regs_ptr, ((uintptr_t)python_stack_top - (uintptr_t)®s) / sizeof(uintptr_t)); + + gc_collect_end(); +} + +void nlr_jump_fail(void *val) { + while (1); +} + +mp_lexer_t * mp_lexer_new_from_file(const char * filename) { + if (sScriptProvider != nullptr) { + const char * script = sScriptProvider->contentOfScript(filename); + if (script != nullptr) { + return mp_lexer_new_from_str_len(qstr_from_str(filename), script, strlen(script), 0 /* size_t free_len*/); + } else { + mp_raise_OSError(MP_ENOENT); + } + } else { + mp_raise_OSError(MP_ENOENT); + } +} + +mp_import_stat_t mp_import_stat(const char *path) { + if (sScriptProvider && sScriptProvider->contentOfScript(path)) { + return MP_IMPORT_STAT_FILE; + } + return MP_IMPORT_STAT_NO_EXIST; +} + +void mp_hal_stdout_tx_strn_cooked(const char * str, size_t len) { + assert(sCurrentExecutionEnvironment != nullptr); + sCurrentExecutionEnvironment->printText(str, len); +} diff --git a/python/port/port.h b/python/port/port.h index 7cc204f86..5f42743b6 100644 --- a/python/port/port.h +++ b/python/port/port.h @@ -1,6 +1,29 @@ #ifndef PYTHON_PORT_H #define PYTHON_PORT_H -void mp_port_init_stack_top(); +namespace MicroPython { + +class ScriptProvider { +public: + virtual const char * contentOfScript(const char * name) = 0; +}; + +class ExecutionEnvironment { +public: + void runCode(const char * ); + virtual void printText(const char * text, size_t length) { + } +}; + +void init(void * heapStart, void * heapEnd); +void deinit(); +void registerScriptProvider(ScriptProvider * s); + +}; + +// Will implement : +// mp_lexer_new_from_file -> Ask the context about a file +// mp_import_stat +// mp_hal_stdout_tx_strn_cooked -> Tell the context Python printed text #endif