mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
#define CODE_APP_H
|
||||
|
||||
#include <escher.h>
|
||||
#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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,62 +1,32 @@
|
||||
#include "console_controller.h"
|
||||
#include <apps/i18n.h>
|
||||
#include "app.h"
|
||||
#include <assert.h>
|
||||
|
||||
extern "C" {
|
||||
#include <stdlib.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,22 +2,26 @@
|
||||
#define CODE_CONSOLE_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include <escher/text_field_delegate.h>
|
||||
#include "console_store.h"
|
||||
#include "console_line_cell.h"
|
||||
#include <python/port/port.h>
|
||||
|
||||
#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. */
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <escher/text_field.h>
|
||||
#include <escher/text_field_delegate.h>
|
||||
#include <escher/message_text_view.h>
|
||||
#include "console_line.h"
|
||||
|
||||
namespace Code {
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
#define CODE_EDITOR_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
#define CODE_MENU_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "console_controller.h"
|
||||
#include "program_parameter_controller.h"
|
||||
#include <apps/shared/new_function_cell.h>
|
||||
#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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
#include "program_store.h"
|
||||
#include "string.h"
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Code {
|
||||
|
||||
ProgramStore::ProgramStore() :
|
||||
m_numberOfPrograms(0),
|
||||
m_lastProgramEdited(-1)
|
||||
{
|
||||
for (int i = 0; i<k_historySize; i ++) {
|
||||
m_history[i] = FreeSpaceMarker;
|
||||
}
|
||||
}
|
||||
|
||||
Program ProgramStore::editableProgram(int i) {
|
||||
assert(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<k_historySize-1; j++) {
|
||||
if (m_history[j] == FreeSpaceMarker && m_history[j+1] != FreeSpaceMarker) {
|
||||
break;
|
||||
}
|
||||
sizeOfEditableProgram++;
|
||||
}
|
||||
m_lastProgramEdited = i;
|
||||
return Program(&m_history[beginningOfProgram], sizeOfEditableProgram);
|
||||
}
|
||||
|
||||
Program ProgramStore::program(int i) {
|
||||
assert(i >= 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<numberOfPrograms(); i++){
|
||||
deleteProgram(i);
|
||||
//TODO moves the free space around, better to just put all the buffer to -1 ??
|
||||
}
|
||||
m_numberOfPrograms = 0;
|
||||
}
|
||||
|
||||
int ProgramStore::indexOfProgram(int i) const {
|
||||
assert (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<k_historySize; j++) {
|
||||
if (m_history[j] == 0) {
|
||||
currentProgramNumber++;
|
||||
if (currentProgramNumber == i) {
|
||||
j++;
|
||||
while (m_history[j] == FreeSpaceMarker) {
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ProgramStore::lastIndexOfProgram(int i) const {
|
||||
int indexOfPrgm = indexOfProgram(i);
|
||||
int lastIndexOfProgram = indexOfPrgm + strlen(&m_history[indexOfPrgm]);
|
||||
return lastIndexOfProgram;
|
||||
}
|
||||
|
||||
int ProgramStore::indexOfFirstFreeSpaceMarker() const {
|
||||
for (int i=0; i<k_historySize; i++) {
|
||||
if (m_history[i] == FreeSpaceMarker) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ProgramStore::sizeOfFreeSpace() const {
|
||||
int sizeOfFreeSpace = 0;
|
||||
for (int i=0; i<k_historySize; i++) {
|
||||
if (m_history[i] == FreeSpaceMarker) {
|
||||
sizeOfFreeSpace++;
|
||||
} else {
|
||||
if (sizeOfFreeSpace > 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<indexOfNextProgram+freeSpaceSize; j++) {
|
||||
m_history[j] = FreeSpaceMarker;
|
||||
}
|
||||
} else {
|
||||
int indexOfPrgm = indexOfProgram(i);
|
||||
int len = lastIndexOfPrgm - (indexOfFreeSpace + freeSpaceSize) + 1;
|
||||
// Use memcopy to avoid overwriting
|
||||
memcpy(&m_history[indexOfFreeSpace], &m_history[indexOfPrgm], len);
|
||||
for (int j = lastIndexOfPrgm - freeSpaceSize + 1 ; j<=lastIndexOfPrgm; j++) {
|
||||
m_history[j] = FreeSpaceMarker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramStore::cleanAndMoveFreeSpaceAfterProgram(int i) {
|
||||
if (i >= 0 && i<numberOfPrograms()){
|
||||
cleanFreeSpace();
|
||||
moveFreeSpaceAfterProgram(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool ProgramStore::addDefaultProgram() {
|
||||
const char program[] = R"(# This program draws a Mandelbrot fractal set
|
||||
# N_iteration: degree of precision
|
||||
import kandinsky
|
||||
N_iteration = 10
|
||||
for x in range(320):
|
||||
for y in range(240):
|
||||
# Compute the mandelbrot sequence for the point c = (c_r, c_i) with start value z = (z_r, z_i)
|
||||
z_r = 0
|
||||
z_i = 0
|
||||
# Rescale to fit the drawing screen 320x222
|
||||
c_r = 2.7*x/319-2.1
|
||||
c_i = -1.87*y/221+0.935
|
||||
i = 0
|
||||
while (i < N_iteration) and ((z_r * z_r) + (z_i * z_i) < 4):
|
||||
i = i + 1
|
||||
stock = z_r
|
||||
z_r = z_r * z_r - z_i * z_i + c_r
|
||||
z_i = 2 * stock * z_i + c_i
|
||||
# Choose the color of the dot from the Mandelbrot sequence
|
||||
rgb = int(255*i/N_iteration)
|
||||
col = kandinsky.color(int(rgb),int(rgb*0.75),int(rgb*0.25))
|
||||
# Draw a pixel colored in 'col' at position (x,y)
|
||||
kandinsky.set_pixel(x,y,col))";
|
||||
|
||||
int len = strlen(program);
|
||||
if (len + 1 > 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
22
apps/code/script.cpp
Normal file
22
apps/code/script.cpp
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
#ifndef CODE_PROGRAM_H
|
||||
#define CODE_PROGRAM_H
|
||||
#ifndef CODE_SCRIPT_H
|
||||
#define CODE_SCRIPT_H
|
||||
|
||||
#include <escher.h>
|
||||
|
||||
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;
|
||||
78
apps/code/script_parameter_controller.cpp
Normal file
78
apps/code/script_parameter_controller.cpp
Normal file
@@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <escher.h>
|
||||
#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;
|
||||
};
|
||||
|
||||
}
|
||||
363
apps/code/script_store.cpp
Normal file
363
apps/code/script_store.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
#include "script_store.h"
|
||||
#include "string.h"
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Code {
|
||||
|
||||
ScriptStore::ScriptStore() :
|
||||
m_numberOfScripts(0),
|
||||
m_lastEditedStringPosition(0)
|
||||
{
|
||||
for (int i = 0; i<k_historySize; i ++) {
|
||||
m_history[i] = FreeSpaceMarker;
|
||||
}
|
||||
}
|
||||
|
||||
Script ScriptStore::editableScript(int i) {
|
||||
assert(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<k_historySize-1; j++) {
|
||||
if (m_history[j] == FreeSpaceMarker && m_history[j+1] != FreeSpaceMarker) {
|
||||
break;
|
||||
}
|
||||
sizeOfEditableScript++;
|
||||
}
|
||||
return Script(&m_history[beginningOfScriptContent], sizeOfEditableScript);
|
||||
}
|
||||
|
||||
Script ScriptStore::script(int i) {
|
||||
assert(i >= 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<numberOfScripts(); i++) {
|
||||
const char * nameScripti = nameOfScript(i);
|
||||
if (strcmp(nameScripti, name) == 0) {
|
||||
return script(i);
|
||||
}
|
||||
}
|
||||
return Script();
|
||||
}
|
||||
|
||||
char * ScriptStore::editableNameOfScript(int i) {
|
||||
assert (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<k_historySize; i++) {
|
||||
m_history[i] = FreeSpaceMarker;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptStore::addMandelbrotScript() {
|
||||
int sizeFreeSpace = sizeOfFreeSpace();
|
||||
if (sizeFreeSpace < 5) {
|
||||
return false;
|
||||
}
|
||||
cleanAndMoveFreeSpaceAfterScriptContent(numberOfScripts()-1);
|
||||
int beginningNewScript = indexOfFirstFreeSpaceMarker();
|
||||
m_history[beginningNewScript] = AutoImportationMarker;
|
||||
if (copyName(beginningNewScript + 1, "mandelbrot.py")) {
|
||||
if (copyMandelbrotScriptOnFreeSpace()) {
|
||||
m_numberOfScripts++;
|
||||
m_lastEditedStringPosition = indexOfScriptContent(numberOfScripts()-1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (int i = beginningNewScript; i<k_historySize; i++) {
|
||||
m_history[i] = FreeSpaceMarker;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptStore::deleteScript(int i) {
|
||||
assert (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<k_historySize; i++){
|
||||
m_history[i] = FreeSpaceMarker;
|
||||
}
|
||||
m_numberOfScripts = 0;
|
||||
}
|
||||
|
||||
const char * ScriptStore::contentOfScript(const char * name) {
|
||||
int filenameIndex = indexBeginningFilename(name);
|
||||
const char * filename = &name[filenameIndex];
|
||||
return script(filename).readOnlyContent();
|
||||
}
|
||||
|
||||
char * ScriptStore::nameOfScript(int i) {
|
||||
return &m_history[indexOfScript(i)+1];
|
||||
}
|
||||
|
||||
bool ScriptStore::copyName(int position, const char * name) {
|
||||
if (name) {
|
||||
int len = strlen(name);
|
||||
if (len > 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<k_historySize; j++) {
|
||||
if (m_history[j] == 0) {
|
||||
if (goingThroughName) {
|
||||
goingThroughName = false;
|
||||
} else {
|
||||
goingThroughName = true;
|
||||
currentScriptNumber++;
|
||||
if (currentScriptNumber == i) {
|
||||
j++;
|
||||
while (m_history[j] == FreeSpaceMarker) {
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScriptStore::lastIndexOfScript(int i) const {
|
||||
int indexOfPrgm = indexOfScriptContent(i);
|
||||
int lastIndexOfScript = indexOfPrgm + strlen(&m_history[indexOfPrgm]);
|
||||
return lastIndexOfScript;
|
||||
}
|
||||
|
||||
int ScriptStore::indexOfFirstFreeSpaceMarker() const {
|
||||
for (int i=0; i<k_historySize; i++) {
|
||||
if (m_history[i] == FreeSpaceMarker) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScriptStore::sizeOfFreeSpace() const {
|
||||
int sizeOfFreeSpace = 0;
|
||||
for (int i=0; i<k_historySize; i++) {
|
||||
if (m_history[i] == FreeSpaceMarker) {
|
||||
sizeOfFreeSpace++;
|
||||
} else {
|
||||
if (sizeOfFreeSpace > 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<i+freeSpaceSize; j++) {
|
||||
m_history[j] = FreeSpaceMarker;
|
||||
}
|
||||
}
|
||||
|
||||
m_lastEditedStringPosition = i-2;
|
||||
while (m_lastEditedStringPosition > 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<numberOfScripts()){
|
||||
cleanFreeSpace();
|
||||
moveFreeSpaceAfterScriptContent(i);
|
||||
}
|
||||
}
|
||||
void ScriptStore::cleanAndMoveFreeSpaceAfterScriptName(int i) {
|
||||
if (i >= 0 && i<numberOfScripts()){
|
||||
cleanFreeSpace();
|
||||
moveFreeSpaceAfterScriptName(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptStore::copyMandelbrotScriptOnFreeSpace() {
|
||||
const char script[] = R"(# This script draws a Mandelbrot fractal set
|
||||
# N_iteration: degree of precision
|
||||
import kandinsky
|
||||
N_iteration = 10
|
||||
for x in range(320):
|
||||
for y in range(240):
|
||||
# Compute the mandelbrot sequence for the point c = (c_r, c_i) with start value z = (z_r, z_i)
|
||||
z_r = 0
|
||||
z_i = 0
|
||||
# Rescale to fit the drawing screen 320x222
|
||||
c_r = 2.7*x/319-2.1
|
||||
c_i = -1.87*y/221+0.935
|
||||
i = 0
|
||||
while (i < N_iteration) and ((z_r * z_r) + (z_i * z_i) < 4):
|
||||
i = i + 1
|
||||
stock = z_r
|
||||
z_r = z_r * z_r - z_i * z_i + c_r
|
||||
z_i = 2 * stock * z_i + c_i
|
||||
# Choose the color of the dot from the Mandelbrot sequence
|
||||
rgb = int(255*i/N_iteration)
|
||||
col = kandinsky.color(int(rgb),int(rgb*0.75),int(rgb*0.25))
|
||||
# Draw a pixel colored in 'col' at position (x,y)
|
||||
kandinsky.set_pixel(x,y,col))";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
72
apps/code/script_store.h
Normal file
72
apps/code/script_store.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef CODE_SCRIPT_STORE_H
|
||||
#define CODE_SCRIPT_STORE_H
|
||||
|
||||
#include "script.h"
|
||||
#include <python/port/port.h>
|
||||
|
||||
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
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
extern "C"
|
||||
void mp_hal_stdout_tx_strn_cooked(const char * str, size_t len) {
|
||||
}
|
||||
|
||||
@@ -107,3 +107,4 @@ Prediction95CommandWithArg = "prediction95(p,n)"
|
||||
PredictionCommandWithArg = "prediction(p,n)"
|
||||
ConfidenceCommandWithArg = "confidence(f,n)"
|
||||
ConsolePrompt = ">>> "
|
||||
ConsoleError = "Error"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
7
python/port/import_helper.c
Normal file
7
python/port/import_helper.c
Normal file
@@ -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);
|
||||
@@ -1,57 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
110
python/port/port.cpp
Normal file
110
python/port/port.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
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 <apps/i18n.h>
|
||||
#include <ion/keyboard.h>
|
||||
#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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user